From 33d7e3ded3aece1850cdf02e9d9db6702dec146d Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Thu, 16 Aug 2007 18:48:47 +0000 Subject: [PATCH] new gui reference work svn: r7106 --- collects/scribble/base-render.ss | 4 +- collects/scribble/basic.ss | 8 +- collects/scribble/decode.ss | 13 +- collects/scribble/html-render.ss | 4 +- collects/scribble/manual.ss | 12 +- .../gui/checkable-menu-item-class.scrbl | 64 +- collects/scribblings/gui/diagrams.ss | 106 ++ collects/scribblings/gui/draw-overview.scrbl | 276 +++++ collects/scribblings/gui/drawing.scrbl | 9 + .../scribblings/gui/eventspace-funcs.scrbl | 6 +- collects/scribblings/gui/gui.scrbl | 62 ++ .../scribblings/gui/popup-menu-class.scrbl | 78 +- .../scribblings/gui/radio-box-class.scrbl | 181 ++-- .../scribblings/gui/scroll-event-class.scrbl | 85 +- .../gui/selectable-menu-item-intf.scrbl | 133 ++- .../gui/separator-menu-item-class.scrbl | 6 +- collects/scribblings/gui/slider-class.scrbl | 76 +- .../scribblings/gui/tab-panel-class.scrbl | 148 +-- .../scribblings/gui/text-field-class.scrbl | 146 ++- collects/scribblings/gui/timer-class.scrbl | 94 +- collects/scribblings/gui/win-classes.scrbl | 125 +++ collects/scribblings/gui/win-funcs.scrbl | 11 + collects/scribblings/gui/win-overview.scrbl | 950 ++++++++++++++++++ collects/scribblings/gui/windowing.scrbl | 148 +-- 24 files changed, 1999 insertions(+), 746 deletions(-) create mode 100644 collects/scribblings/gui/diagrams.ss create mode 100644 collects/scribblings/gui/draw-overview.scrbl create mode 100644 collects/scribblings/gui/drawing.scrbl create mode 100644 collects/scribblings/gui/win-classes.scrbl create mode 100644 collects/scribblings/gui/win-funcs.scrbl create mode 100644 collects/scribblings/gui/win-overview.scrbl diff --git a/collects/scribble/base-render.ss b/collects/scribble/base-render.ss index f4236ff79b..40ec68a6fb 100644 --- a/collects/scribble/base-render.ss +++ b/collects/scribble/base-render.ss @@ -296,7 +296,9 @@ (let ([number (collected-info-number (part-collected-info part))]) (let ([subs (if (quiet (and (styled-part? part) - (eq? 'quiet (styled-part-style part)) + (let ([st(styled-part-style part)]) + (or (eq? 'quiet st) + (and (list? st) (memq 'quiet st)))) (not (= base-len (sub1 (length number)))))) (apply append diff --git a/collects/scribble/basic.ss b/collects/scribble/basic.ss index d312467800..8a60e1657b 100644 --- a/collects/scribble/basic.ss +++ b/collects/scribble/basic.ss @@ -22,17 +22,17 @@ (let ([content (decode-content str)]) (make-title-decl (or tag (gen-tag content)) style content))) - (define (section #:tag [tag #f] . str) + (define (section #:tag [tag #f] #:style [style #f] . str) (let ([content (decode-content str)]) - (make-part-start 0 (or tag (gen-tag content)) content))) + (make-part-start 0 (or tag (gen-tag content)) style content))) (define (subsection #:tag [tag #f] . str) (let ([content (decode-content str)]) - (make-part-start 1 (or tag (gen-tag content)) content))) + (make-part-start 1 (or tag (gen-tag content)) #f content))) (define (subsubsection #:tag [tag #f] . str) (let ([content (decode-content str)]) - (make-part-start 2 (or tag (gen-tag content)) content))) + (make-part-start 2 (or tag (gen-tag content)) #f content))) (define (subsubsub*section #:tag [tag #f] . str) (let ([content (decode-content str)]) diff --git a/collects/scribble/decode.ss b/collects/scribble/decode.ss index f81a9d11ab..a21c3ca44c 100644 --- a/collects/scribble/decode.ss +++ b/collects/scribble/decode.ss @@ -18,6 +18,7 @@ [content list?])] [part-start ([depth integer?] [tag (or/c false/c string?)] + [style any/c] [title list?])] [splice ([run list?])] [part-index-decl ([plain-seq (listof string?)] @@ -136,10 +137,11 @@ ((part-start-depth (car l)) . <= . part-depth)) (part? (car l)))) (let ([para (decode-accum-para accum)] - [s (decode-part (reverse s-accum) - (part-start-tag s) - (part-start-title s) - (add1 part-depth))] + [s (decode-styled-part (reverse s-accum) + (part-start-tag s) + (part-start-style s) + (part-start-title s) + (add1 part-depth))] [part (decode-flow* l keys tag style title part-depth)]) (make-styled-part (part-tags part) (part-title-content part) @@ -179,6 +181,9 @@ (define (decode-part l tag title depth) (decode-flow* l null tag #f title depth)) + (define (decode-styled-part l tag style title depth) + (decode-flow* l null tag style title depth)) + (define (decode-flow l) (part-flow (decode-flow* l null #f #f #f #f))) diff --git a/collects/scribble/html-render.ss b/collects/scribble/html-render.ss index 8a4f256e69..f9783ba465 100644 --- a/collects/scribble/html-render.ss +++ b/collects/scribble/html-render.ss @@ -454,7 +454,9 @@ (define/private (toc-part? d) (and (styled-part? d) - (eq? 'toc (styled-part-style d)))) + (let ([st (styled-part-style d)]) + (or (eq? 'toc st) + (and (list? st) (memq 'toc st)))))) (define/override (collect-part d parent ht number) (let ([prev-sub (collecting-sub)]) diff --git a/collects/scribble/manual.ss b/collects/scribble/manual.ss index dc6eeec3e5..076bb0e382 100644 --- a/collects/scribble/manual.ss +++ b/collects/scribble/manual.ss @@ -84,10 +84,14 @@ #f (map (lambda (s) (list (make-flow (list (make-paragraph - (let ([spaces (cdar (regexp-match-positions #rx"^ *" s))]) - (list - (hspace spaces) - (make-element 'tt (list (substring s spaces)))))))))) + (let loop ([s s]) + (let ([spaces (regexp-match-positions #rx"(?:^| ) +" s)]) + (if spaces + (append + (loop (substring s 0 (caar spaces))) + (list (hspace (- (cdar spaces) (caar spaces)))) + (loop (substring s (cdar spaces)))) + (list (make-element 'tt (list s))))))))))) strs)))) (define-syntax indexed-scheme diff --git a/collects/scribblings/gui/checkable-menu-item-class.scrbl b/collects/scribblings/gui/checkable-menu-item-class.scrbl index 1ba0c201d6..735759ff33 100644 --- a/collects/scribblings/gui/checkable-menu-item-class.scrbl +++ b/collects/scribblings/gui/checkable-menu-item-class.scrbl @@ -9,31 +9,34 @@ A @scheme[checkable-menu-item%] is a string-labelled menu item that item's check mark is toggled and its callback procedure is called. +@defconstructor[([label label-string?] + [parent (or/c (is-a?/c menu% popup-menu%))] + [callback ((is-a?/c checkable-menu-item%) (is-a?/c control-event%) . -> . any) + (lambda (i e) (void))] + [shortcut (or/c char? false/c) #f] + [help-string (or/c label-string? false/c) #f] + [demand-callback ((is-a?/c menu-item%) . -> . any) + (lambda (i) (void))] + [checked any/c #f] + [shortcut-prefix (listof (one-of/c 'alt 'cmd 'meta 'ctl + 'shift 'option)) + (get-default-shortcut-prefix)])] { +Creates a new menu item in @scheme[parent]. The item is initially + shown, appended to the end of its parent, and unchecked. The + @scheme[callback] procedure is called (with the event type + @indexed-scheme['menu]) when the menu item is selected (either via a + menu bar, @xmethod[window<%> popup-menu], or @xmethod[editor-admin% + popup-menu]). -@defconstructor[[label label-string?] - [parent @scheme[menu%] or @scheme[popup-menu%] object] - [callback procedure of two arguments: a @scheme[menu-item%] object and a @scheme[control-event%] object] - [shortcut (or/c character false/c) #f] - [help-string (or/c label-string? false/c) #f] - [demand-callback procedure of one argument: a @scheme[checkable-menu-item%] object @scheme[void]] - [checked any/c #f] - [shortcut-prefix (symbols/c option shift ctl meta cmd alt) @scheme[(\iscmprocedure{get-default-shortcut-prefix])}]]{ - -Creates a new menu item in @scheme[parent]. The item is initially shown, - appended to the end of its parent, and unchecked. The @scheme[callback] - procedure is called (with the event type @indexed-scheme['menu]) when the - menu item is selected (either via a menu bar, -@xmethod[window<%> popup-menu], or -@xmethod[editor-admin% popup-menu]). - -See -@method[labelled-menu-item<%> set-label] for information about mnemonic ampersands (``\&'') in @scheme[label]. +See @method[labelled-menu-item<%> set-label] for information about +mnemonic @litchar{&}s in @scheme[label]. If @scheme[shortcut] is not @scheme[#f], the item has a shortcut. See -@method[selectable-menu-item<%> get-shortcut] for more information. The @scheme[shortcut-prefix] argument determines the -set of modifier keys for the shortcut; see -@method[selectable-menu-item<%> get-shortcut-prefix]. +@method[selectable-menu-item<%> get-shortcut] for more information. +The @scheme[shortcut-prefix] argument determines the set of modifier +keys for the shortcut; see @method[selectable-menu-item<%> +get-shortcut-prefix]. If @scheme[help] is not @scheme[#f], the item has a help string. See @method[labelled-menu-item<%> get-help-string] for more information. @@ -41,34 +44,25 @@ If @scheme[help] is not @scheme[#f], the item has a help string. See The @scheme[demand-callback] procedure is called by the default @method[labelled-menu-item<%> on-demand] method with the object itself. -By default, the menu item is initially unchecked. If @scheme[checked] is - true, then -@method[checkable-menu-item% check] is called so that the menu item is initially checked. - - +By default, the menu item is initially unchecked. If @scheme[checked] + is true, then @method[checkable-menu-item% check] is called so that + the menu item is initially checked. } @defmethod[(check [check? any/c]) void?]{ -@spec{ Checks or unchecks the menu item. @MonitorCallbackX[@elem{A menu item's check state} @elem{the user selecting the item} @elem{check state} @elem{menu item}] -}} +} @defmethod[(is-checked?) boolean?]{ -@spec{ Returns @scheme[#t] if the item is checked, {\#f} otherwise. -} -@impl{ - - - -}}} +}} diff --git a/collects/scribblings/gui/diagrams.ss b/collects/scribblings/gui/diagrams.ss new file mode 100644 index 0000000000..3c717f7dd4 --- /dev/null +++ b/collects/scribblings/gui/diagrams.ss @@ -0,0 +1,106 @@ +(module diagrams mzscheme + (require (lib "string.ss") + (lib "struct.ss" "scribble") + (lib "scheme.ss" "scribble") + (lib "manual.ss" "scribble")) + + (provide diagram->table + short-windowing-diagram + windowing-diagram + event-diagram + menu-diagram) + + (define (diagram->table d) + (make-table + #f + (map (lambda (line) + (list (make-flow + (list (make-paragraph + (let loop ([line line]) + (cond + [(regexp-match #rx"(.*)( +)(.*)" line) + => (lambda (m) + (append (loop (cadr m)) + (list (hspace (string-length (caddr m)))) + (loop (cadddr m))))] + [(regexp-match #rx"([^-a-zA-Z0-9]*)([-a-zA-Z0-9<%>]+)(.*)" line) + => (lambda (m) + (append (loop (cadr m)) + (list (to-element (string->symbol (caddr m)))) + (loop (cadddr m))))] + [else (list (make-element 'tt (list line)))]))))))) + (regexp-split #rx"[\r\n]+" d)))) + + (define short-windowing-diagram +#< + ______________________|_______________ + | | | + subarea<%> window<%> area-container<%> + |____ _______|__________ | + | | | | + subwindow<%> area-container-window<%> + ________|________ | + | | | + control<%> canvas<%> top-level-window<%> +DIAG +) + + (define windowing-diagram +#< + _____________________|_______________ + | | | + subarea<%> window<%> area-container<%> +<<<____|____ _____|__________ __|___ ___________________<<< + | | | | | | + subwindow<%> | | | | +<<<______________|___________ | | | | _<<< + | | | | pane% | + control<%> | | | |- horizontal-pane% | + |- message% | | | |- vertical-pane% | + |- button% | | | | + |- check-box% | area-container-window<%> | + |- slider% | | | + |- gauge% | | __________________| + |- text-field% | | | + |- combo-field% | |-------- panel% + |- radio-box% | | |- horizontal-panel% + |- list-control<%> | | |- vertical-panel% + |- choice% | | |- tab-panel% + |- list-box% | | |- group-box-panel% + | | + | |- top-level-window<%> + | |- frame% + canvas<%> |- dialog% + |- canvas% + |- editor-canvas% +DIAG +) + + + (define event-diagram +#< + |- control-event% clipboard-client% +DIAG +) + + (define menu-diagram +#< menu-item-container<%> + | | + |- separator-menu-item% _____|___ + |- labelled-menu-item<%> | |- menu-bar% + _________|_________ | |- popup-menu% + | | | + | menu% + | + |- selectable-menu-item<%> + |- menu-item% + |- checkable-menu-item% +DIAG +)) diff --git a/collects/scribblings/gui/draw-overview.scrbl b/collects/scribblings/gui/draw-overview.scrbl new file mode 100644 index 0000000000..b01daf4972 --- /dev/null +++ b/collects/scribblings/gui/draw-overview.scrbl @@ -0,0 +1,276 @@ +#reader(lib "docreader.ss" "scribble") +@require[(lib "eval.ss" "scribble")] +@require["common.ss"] +@require["diagrams.ss"] + +@title[#:tag "mred:drawing-toolbox"]{Drawing Toolbox Overview} + +Drawing in MrEd requires a @deftech{device context} (@deftech{DC}), + which is an instance of the @scheme[dc<%>] interface. For example, + the @method[canvas<%> get-dc] method of a canvas returns a + @scheme[dc<%>] instance for drawing into the canvas window. Other + kinds of DCs draw to different kinds of devices: + +@itemize{ + + @item{@scheme[bitmap-dc%] --- a @deftech{bitmap DC} draws to an + offscreen bitmap.} + + @item{@scheme[post-script-dc%] --- a @deftech{PostScript DC} + records drawing commands to a PostScript file.} + + @item{@scheme[printer-dc%] --- a @deftech{printer DC} draws to a + platform-specific printer device (Windows, Mac OS X).} + +} + +Tools that are used for drawing include the following: @scheme[pen%] + objects for drawing lines and shape outlines, @scheme[brush%] + objects for filling shapes, @scheme[bitmap%] objects for storing + bitmaps, and @scheme[dc-path%] objects for describing paths to draw + and fill. + +The following example creates a frame with a drawing canvas, and then + draws a round, blue face with square, yellow eyes and a smiling, red + mouth: + +@schemeblock[ +(code:comment #, @t{Make a 300 x 300 frame}) +(define frame (new frame% [label "Drawing Example"] + [width 300] + [height 300])) +(code:comment #, @t{Make the drawing area}) +(define canvas (new canvas% [parent frame])) +(code:comment #, @t{Get the canvas's drawing context}) +(define dc (send canvas #,(:: canvas% get-dc))) + +(code:comment #, @t{Make some pens and brushes}) +(define no-pen (make-object pen% "BLACK" 1 'transparent)) +(define no-brush (make-object brush% "BLACK" 'transparent)) +(define blue-brush (make-object brush% "BLUE" 'solid)) +(define yellow-brush (make-object brush% "YELLOW" 'solid)) +(define red-pen (make-object pen% "RED" 2 'solid)) + +(code:comment #, @t{Define a procedure to draw a face}) +(define (draw-face dc) + (send dc #,(:: dc<%> set-pen) no-pen) + (send dc #,(:: dc<%> set-brush) blue-brush) + (send dc #,(:: dc<%> draw-ellipse) 50 50 200 200) + + (send dc #,(:: dc<%> set-brush) yellow-brush) + (send dc #,(:: dc<%> draw-rectangle) 100 100 10 10) + (send dc #,(:: dc<%> draw-rectangle) 200 100 10 10) + + (send dc #,(:: dc<%> set-brush) no-brush) + (send dc #,(:: dc<%> set-pen) red-pen) + (let ([-pi (atan 0 -1)]) + (send dc #,(:: dc<%> draw-arc) 75 75 150 150 (* 5/4 -pi) (* 7/4 -pi)))) + +(code:comment #, @t{Show the frame}) +(send frame top-level-window::show #t) +(code:comment #, @t{Wait a second to let the window get ready}) +(sleep/yield 1) +(code:comment #, @t{Draw the face}) +(draw-face dc) +] + +The @scheme[sleep/yield] call is necessary under X because + drawing to the canvas has no effect when the canvas is not + shown. Although the @scheme[(send frame top-level-window::show #t)] + expression queues a show request for the frame, the actual display of + the frame and its canvas requires handling several events. The + @scheme[sleep/yield] procedure pauses for a specified number + of seconds, handling events while it pauses. + +One second is plenty of time for the frame to show itself, but a + better solution is to create a canvas with a paint callback function + (or overriding @method[canvas<%> on-paint]). Using a paint + callback function is better for all platforms; when the canvas in the + above example is resized or temporarily covered by another window, + the face disappears. To ensure that the face is redrawn whenever the + canvas itself is repainted, we provide a paint callback when creating + the canvas: + +@schemeblock[ +(code:comment #, @t{Make a 300 x 300 frame}) +(define frame (new frame% [label "Drawing Example"] + [width 300] + [height 300])) + +(code:comment #, @t{Make the drawing area with a paint callback}) +(define canvas + (new canvas% [parent frame] + [paint-callback + (lambda (canvas dc) (draw-face dc))])) + +(code:comment #, @t{... pens, brushes, and @scheme[draw-face] are the same as above ...}) + +(code:comment #, @t{Show the frame}) +(send frame top-level-window::show #t) +] + +Suppose that @scheme[draw-face] creates a particularly complex face that + takes a long time to draw. We might want to draw the face once into + an offscreen bitmap, and then have the paint callback copy the cached + bitmap image onto the canvas whenever the canvas is updated. To draw + into a bitmap, we first create a @scheme[bitmap%] object, and then + we create a @scheme[bitmap-dc%] to direct drawing commands into the + bitmap: + +@schemeblock[ +(code:comment #, @t{... pens, brushes, and @scheme[draw-face] are the same as above ...}) + +(code:comment #, @t{Create a 300 x 300 bitmap}) +(define face-bitmap (make-object bitmap% 300 300)) +(code:comment #, @t{Create a drawing context for the bitmap}) +(define bm-dc (make-object bitmap-dc% face-bitmap)) +(code:comment #, @t{A bitmap's initial content is undefined; clear it before drawing}) +(send bm-dc #,(:: dc<%> clear)) + +(code:comment #, @t{Draw the face into the bitmap}) +(draw-face bm-dc) + +(code:comment #, @t{Make a 300 x 300 frame}) +(define frame (new frame% [label "Drawing Example"] + [width 300] + [height 300])) + +(code:comment #, @t{Make the drawing area with a paint callback that copies the bitmap}) +(define canvas + (new canvas% [parent frame] + [paint-callback + (lambda (canvas dc) + (send dc #,(:: dc<%> draw-bitmap) face-bitmap 0 0))])) + +(code:comment #, @t{Show the frame}) +(send frame #,(:: top-level-window<%> show) #t) +] + +For all types of DCs, the drawing origin is the top-left corner of the + DC. When drawing to a window or bitmap, DC units initially correspond + to pixels, but the @method[dc<%> set-scale] method changes the + scale. When drawing to a PostScript or printer device, DC units + initially correspond to points (1/72 of an inch). + +More complex shapes are typically best implemented with + @deftech{paths}. The following example uses paths to draw the PLT + Scheme logo. It also enables smoothing, so that the logo's curves are + anti-aliased when smoothing is available. (Smoothing is always + available under Mac OS X, smoothing is available under Windows XP or + when @file{gdiplus.dll} is installed, and smoothing is available + under X when Cairo is installed before MrEd is compiled.) + +@begin[ +#reader(lib "comment-reader.ss" "scribble") +[schemeblock +(require (lib "math.ss")) ; for @scheme[pi] + +;; Construct paths for a 630 x 630 logo + +(define left-lambda-path ;; left side of the lambda + (let ([p (new dc-path%)]) + (send p #,(:: dc-path% move-to) 153 44) + (send p #,(:: dc-path% line-to) 161.5 60) + (send p #,(:: dc-path% curve-to) 202.5 49 230 42 245 61) + (send p #,(:: dc-path% curve-to) 280.06 105.41 287.5 141 296.5 186) + (send p #,(:: dc-path% curve-to) 301.12 209.08 299.11 223.38 293.96 244) + (send p #,(:: dc-path% curve-to) 281.34 294.54 259.18 331.61 233.5 375) + (send p #,(:: dc-path% curve-to) 198.21 434.63 164.68 505.6 125.5 564) + (send p #,(:: dc-path% line-to) 135 572) + p)) + +(define left-logo-path ;; left side of the lambda and circle + (let ([p (new dc-path%)]) + (send p #,(:: dc-path% append) left-lambda-path) + (send p #,(:: dc-path% arc) 0 0 630 630 (* 235/360 2 pi) (* 121/360 2 pi) #f) + p)) + +(define bottom-lambda-path + (let ([p (new dc-path%)]) + (send p #,(:: dc-path% move-to) 135 572) + (send p #,(:: dc-path% line-to) 188.5 564) + (send p #,(:: dc-path% curve-to) 208.5 517 230.91 465.21 251 420) + (send p #,(:: dc-path% curve-to) 267 384 278.5 348 296.5 312) + (send p #,(:: dc-path% curve-to) 301.01 302.98 318 258 329 274) + (send p #,(:: dc-path% curve-to) 338.89 288.39 351 314 358 332) + (send p #,(:: dc-path% curve-to) 377.28 381.58 395.57 429.61 414 477) + (send p #,(:: dc-path% curve-to) 428 513 436.5 540 449.5 573) + (send p #,(:: dc-path% line-to) 465 580) + (send p #,(:: dc-path% line-to) 529 545) + p)) + +(define bottom-logo-path + (let ([p (new dc-path%)]) + (send p #,(:: dc-path% append) bottom-lambda-path) + (send p #,(:: dc-path% arc) 0 0 630 630 (* 314/360 2 pi) (* 235/360 2 pi) #f) + p)) + +(define right-lambda-path + (let ([p (new dc-path%)]) + (send p #,(:: dc-path% move-to) 153 44) + (send p #,(:: dc-path% curve-to) 192.21 30.69 233.21 14.23 275 20) + (send p #,(:: dc-path% curve-to) 328.6 27.4 350.23 103.08 364 151) + (send p #,(:: dc-path% curve-to) 378.75 202.32 400.5 244 418 294) + (send p #,(:: dc-path% curve-to) 446.56 375.6 494.5 456 530.5 537) + (send p #,(:: dc-path% line-to) 529 545) + p)) + +(define right-logo-path + (let ([p (new dc-path%)]) + (send p #,(:: dc-path% append) right-lambda-path) + (send p #,(:: dc-path% arc) 0 0 630 630 (* 314/360 2 pi) (* 121/360 2 pi) #t) + p)) + +(define lambda-path ;; the lambda by itself (no circle) + (let ([p (new dc-path%)]) + (send p #,(:: dc-path% append) left-lambda-path) + (send p #,(:: dc-path% append) bottom-lambda-path) + (let ([t (make-object dc-path%)]) + (send t #,(:: dc-path% append) right-lambda-path) + (send t #,(:: dc-path% reverse)) + (send p #,(:: dc-path% append) t)) + (send p #,(:: dc-path% close)) + p)) + +;; This function draws the paths with suitable colors: +(define (paint-plt dc) + ;; Paint white lambda, no outline: + (send dc #,(:: dc<%> set-pen) "BLACK" 0 'transparent) + (send dc #,(:: dc<%> set-brush) "WHITE" 'solid) + (send dc #,(:: dc<%> draw-path) lambda-path) + ;; Paint outline and colors... + (send dc #,(:: dc<%> set-pen) "BLACK" 0 'solid) + ;; Draw red regions + (send dc #,(:: dc<%> set-brush) "RED" 'solid) + (send dc #,(:: dc<%> draw-path) left-logo-path) + (send dc #,(:: dc<%> draw-path) bottom-logo-path) + ;; Draw blue region + (send dc #,(:: dc<%> set-brush) "BLUE" 'solid) + (send dc #,(:: dc<%> draw-path) right-logo-path)) + +;; Create a frame to display the logo on a light-purple background: +(define f (new frame% [label "PLT Logo"])) +(define c + (new canvas% + [parent f] + [paint-callback + (lambda (c dc) + (send dc #,(:: dc<%> set-background) (make-object color\% 220 200 255)) + (send dc #,(:: dc<%> clear)) + (send dc #,(:: dc<%> set-smoothing) 'smoothed) + (send dc #,(:: dc<%> set-origin) 5 5) + (send dc #,(:: dc<%> set-scale) 0.5 0.5) + (paint-plt dc))])) +(send c canvas::min-client-width (/ 650 2)) +(send c canvas::min-client-height (/ 650 2)) +(send f show #t) +]] + +Drawing effects are not completely portable across platforms or across + types of DC. Drawing in smoothed mode tends to produce more reliable + and portable results than in unsmoothed mode, and drawing with paths + tends to produce more reliable results even in unsmoothed + mode. Drawing with a pen of width 0 or 1 in unsmoothed mode in an + unscaled DC produces relatively consistent results for all platforms, + but a pen width of 2 or drawing to a scaled DC looks significantly + different in unsmoothed mode on different platforms and destinations. diff --git a/collects/scribblings/gui/drawing.scrbl b/collects/scribblings/gui/drawing.scrbl new file mode 100644 index 0000000000..67cf845ffe --- /dev/null +++ b/collects/scribblings/gui/drawing.scrbl @@ -0,0 +1,9 @@ +#reader(lib "docreader.ss" "scribble") +@require["common.ss"] +@require["diagrams.ss"] + +@title[#:tag "mr:drawing" #:style 'toc]{Drawing Toolbox} + +@local-table-of-contents[] + +@include-section["draw-overview.scrbl"] diff --git a/collects/scribblings/gui/eventspace-funcs.scrbl b/collects/scribblings/gui/eventspace-funcs.scrbl index 4d55282603..800429140d 100644 --- a/collects/scribblings/gui/eventspace-funcs.scrbl +++ b/collects/scribblings/gui/eventspace-funcs.scrbl @@ -1,11 +1,7 @@ #reader(lib "docreader.ss" "scribble") @require["common.ss"] -@title{Eventspaces} - - - - +@title[#:tag "mr:eventspace-funcs"]{Eventspaces} @defproc[(make-eventspace) eventspace]{ diff --git a/collects/scribblings/gui/gui.scrbl b/collects/scribblings/gui/gui.scrbl index 327f304443..66582644e7 100644 --- a/collects/scribblings/gui/gui.scrbl +++ b/collects/scribblings/gui/gui.scrbl @@ -10,8 +10,70 @@ This manual describes MrEd. @table-of-contents[] +@;------------------------------------------------------------------------ + +@section{GUI Toolbox Organization} + +For documentation purposes, the MrEd toolbox is organized into three +parts: + +@itemize{ + + @item{The @deftech{windowing} toolbox, for implementing form-filling + GUI programs (such as a database query window) using buttons, menus, + text fields, and events. The windowing toolbox is documented in + @secref["mr:windowing"].} + + @item{The @deftech{drawing} toolbox, for drawing pictures or + implementing dynamic GUI programs (such as a video game) using + drawing canvases, pens, and brushes. The drawing toolbox is + documented in @secref["mr:drawing"].} + + @item{The @deftech{editor} toolbox, for developing traditional text + editors, editors that mix text and graphics, or free-form layout + editors (such as a word processor, HTML editor, or icon-based file + browser). The editor toolbox is documented in @secref["mr:editor"].} + +} + +These three parts roughly represent layers of increasing + sophistication. Simple GUI programs access only the windowing toolbox + directly, more complex programs use both the windowing and drawing + toolboxes, and large-scale applications rely on all three + toolboxes. This three-layer view of the toolbox breaks down under + close scrutiny, because the windowing, drawing, and editor toolboxes + are actually interdependent and intertwined. Nevertheless, the + layered separation is a good approximation. + +All three parts are immediately available when MrEd is started, as + well as the base class system from MzLib. The @indexed-file{mred.ss} + library module of the @file{mred} collection provides all of the + class, interface, and procedure names defined in this manual. When + MrEd starts up, it imports the @file{mred.ss} module and MzLib's + @indexed-file{class.ss} module into the initial namespace (so no + knowledge about modules is required to understand this manual). + +The module @indexed-scheme[#%mred-kernel] is built into the MrEd + executable, and intended for use only by @file{mred.ss}. Attempting + to require @file{mred.ss} in a plain MzScheme executable will result + in a run-time error, because @scheme[#%mred-kernel] will not be + available. + +To create a namespace in which the @file{mred.ss} module will be used, + call the @scheme[make-namespace-with-mred] procedure. That + procedure attaches the @file{mred.ss} instance of the current + namespace to the created namespace. Otherwise, different namespaces + create different instances of the @file{mred.ss} module, which in + turn generate distinct classes. + +@;------------------------------------------------------------------------ + @include-section["windowing.scrbl"] @;------------------------------------------------------------------------ +@include-section["drawing.scrbl"] + +@;------------------------------------------------------------------------ + @index-section["mred-index"] diff --git a/collects/scribblings/gui/popup-menu-class.scrbl b/collects/scribblings/gui/popup-menu-class.scrbl index 6e55a351fc..92e703d06c 100644 --- a/collects/scribblings/gui/popup-menu-class.scrbl +++ b/collects/scribblings/gui/popup-menu-class.scrbl @@ -3,32 +3,30 @@ @defclass[popup-menu% object% (menu-item-container<%>)]{ -A @scheme[popup-menu%] object is created without a - parent. Dynamically display a @scheme[popup-menu%] with -, -@xmethod[window<%> popup-menu], or -@xmethod[editor-admin% popup-menu]. +A @scheme[popup-menu%] object is created without a parent. Dynamically + display a @scheme[popup-menu%] with @xmethod[window<%> popup-menu] + or @xmethod[editor-admin% popup-menu]. -A popup menu is {\em not} a control. A @scheme[choice%] control, - however, displays a single value that the user selects from a popup +A popup menu is @italic{not} a control. A @scheme[choice%] control, +however, displays a single value that the user selects from a popup menu. A @scheme[choice%] control's popup menu is built into the control, and it is not accessible to the programmer. +@defconstructor[([title (or/c label-string? false/c) #f] + [popdown-callback ((is-a?/c popup-menu%) (is-a?/c control-event%) + . -> . any) + (lambda (p e) (void))] + [demand-callback ((is-a?/c popup-menu%) . -> . any) + (lambda (p) (void))] + [font (is-a?/c font%) normal-control-font])]{ +If @scheme[title] is not @scheme[#f], it is used as a displayed title + at the top of the popup menu. -@defconstructor[[title (or/c label-string? false/c) #f] - [popdown-callback procedure of two arguments: a @scheme[popup-menu%] object and a @scheme[control-event%] object @scheme[(\scmk{lambda] (@scheme[m] @scheme[e]) (void))}] - [demand-callback procedure of one argument: a @scheme[popup-menu%] object @scheme[void]] - [font (is-a?/c font%) @scheme[normal-control-font]]]{ - -If @scheme[title] is not @scheme[#f], it is used as a displayed title at - the top of the popup menu. - -If @scheme[title] contains an ampersand (``\&''), it is handled - specially, the same as for @scheme[menu%] titles. A popup menu - mnemonic is not useful, but it is supported for consistency with - other menu labels. +If @scheme[title] contains @litchar{&}, it is handled specially, the + same as for @scheme[menu%] titles. A popup menu mnemonic is not + useful, but it is supported for consistency with other menu labels. The @scheme[popdown-callback] procedure is invoked when a popup menu is dismissed. If the popup menu is dismissed without an item being @@ -44,44 +42,36 @@ The @scheme[demand-callback] procedure is called by the default The @scheme[font] argument determines the font for the popup menu's items. - - - } -@defmethod[(get-popup-target) - (or/c (or/c (is-a?/c window<%>) (is-a?/c editor<%>)) false/c)]{ -@spec{ - -Returns the context in which the popup menu is currently displayed, or - @scheme[#f] if it is not popped up in any window. - -The context is set before the -@method[menu-item-container<%> on-demand] method is called, and it is not removed until after the popup-menu's - callback is invoked. (Consequently, it is also set while an item - callback is invoked, if the user selected an item.) - -}} @defmethod[(get-font) (is-a?/c font%)]{ -@spec{ Returns the font used for the popup menu's items, which is optionally supplied when a popup menu is created. -}} +} + + +@defmethod[(get-popup-target) + (or/c (is-a?/c window<%>) (is-a?/c editor<%>) false/c)]{ + +Returns the context in which the popup menu is currently displayed, or + @scheme[#f] if it is not popped up in any window. + +The context is set before the @method[menu-item-container<%> +on-demand] method is called, and it is not removed until after the +popup-menu's callback is invoked. (Consequently, it is also set while +an item callback is invoked, if the user selected an item.) + +} + @defmethod[(set-min-width [width (integer-in 0 10000)]) void?]{ -@spec{ Sets the popup menu's minimum width in pixels. -} -@impl{ - - - -}}} +}} diff --git a/collects/scribblings/gui/radio-box-class.scrbl b/collects/scribblings/gui/radio-box-class.scrbl index c03dd844a0..672584feed 100644 --- a/collects/scribblings/gui/radio-box-class.scrbl +++ b/collects/scribblings/gui/radio-box-class.scrbl @@ -17,20 +17,26 @@ Whenever the user changes the selected radio button, the radio box's -@defconstructor[[label (or/c label-string? false/c)] - [choices list of {\labelstrings} or @scheme[bitmap%] objects] - [parent (or/c (is-a?/c frame%) (is-a?/c dialog%) (is-a?/c panel%) (is-a?/c pane%))] - [callback procedure of two arguments: a @scheme[radio-box%] object and a @scheme[control-event%] object @scheme[(\scmk{lambda] (@scheme[rb] @scheme[e]) (void))}] - [style (symbols/c deleted horizontal-label vertical-label vertical horizontal) '(vertical)] - [selection nonnegative-exact-integer? 0] - [font (is-a?/c font%) @scheme[normal-control-font]] - [enabled any/c #t] - [vert-margin (integer-in 0 1000) 2] - [horiz-margin (integer-in 0 1000) 2] - [min-width (integer-in 0 10000) {\rm graphical minimum width}] - [min-height (integer-in 0 10000) {\rm graphical minimum height}] - [stretchable-width any/c #f] - [stretchable-height any/c #f]]{ +@defconstructor[([label (or/c label-string? false/c)] + [choices (or/c (listof label-string?) (listof (is-a?/c bitmap%)))] + [parent (or/c (is-a?/c frame%) (is-a?/c dialog%) + (is-a?/c panel%) (is-a?/c pane%))] + [callback ((is-a?/c radio-box%) (is-a?/c control-event%) + . -> . any) + (lambda (r e) (void))] + [style (listof (one-of/c 'horizontal 'vertical + 'vertical-label 'horizontal-label + 'deleted)) + '(vertical)] + [selection nonnegative-exact-integer? 0] + [font (is-a?/c font%) normal-control-font] + [enabled any/c #t] + [vert-margin (integer-in 0 1000) 2] + [horiz-margin (integer-in 0 1000) 2] + [min-width (integer-in 0 10000) _graphical-minimum-width] + [min-height (integer-in 0 10000) _graphical-minimum-height] + [stretchable-width any/c #f] + [stretchable-height any/c #f])]{ Creates a radio button set with string or bitmap labels. The @scheme[choices] list specifies the radio button labels; the list of @@ -38,10 +44,9 @@ Creates a radio button set with string or bitmap labels. The @labelstripped[(scheme label) @elem{} @elem{move the keyboard focus to the radio box}] -Each string in @scheme[choices] can also contain an ampersand, which +Each string in @scheme[choices] can also contain a @litchar{&}, which creates a mnemonic for clicking the corresponding radio button. As - for @scheme[label], a double ampersand is converted to a single - ampersand. + for @scheme[label], a @litchar{&&} is converted to a @litchar{&}. @bitmaplabelusearray[choices] @@ -56,141 +61,93 @@ The @scheme[callback] procedure is called (with the event type The @scheme[style] argument must include either @scheme['vertical] for a collection of radio buttons vertically arranged, or @scheme['horizontal] for a horizontal arrangement. - \HVLabelNote{radio box} \DeletedStyleNote{radio box} + @HVLabelNote{radio box} @DeletedStyleNote{radio box} By default, the first radio button is initially selected. If - @scheme[selection] is positive, it is passed to -@method[radio-box% set-selection] to set the initial radio button selection. + @scheme[selection] is positive, it is passed to @method[radio-box% + set-selection] to set the initial radio button selection. @FontKWs[] @WindowKWs[] @SubareaKWs[] @AreaKWs[] - - } + @defmethod*[#:mode 'override ([(enable [enable? any/c]) void?] [(enable [n nonnegative-exact-integer?] [enable? any/c]) void?])]{ -@impl{ -First case: + +If a single argument is provided, the entire radio box is enabled or disabled. + +If two arguments are provided, then if @scheme[enable?] is + @scheme[#f], the @scheme[n]th radio button is disabled, otherwise it + is enabled (assuming the entire radio box is enabled). Radio buttons + are numbered from @scheme[0]. If @scheme[n] is equal to or larger + than the number of radio buttons in the radio box, @|MismatchExn|. + +} -If @scheme[enable?] is @scheme[#f], the entire radio box is disabled, - otherwise it is enabled. +@defmethod[(get-item-label [n nonnegative-exact-integer?]) + string?]{ +Gets the label of a radio button by position. Radio buttons are + numbered from @scheme[0]. If @scheme[n] is equal to or larger than + the number of radio buttons in the radio box, @|MismatchExn|. +} -Second case: +@defmethod[(get-item-plain-label [n nonnegative-exact-integer?]) + string?]{ +Like @method[radio-box% get-item-label], except that the label must be +a string and @litchar{&}s in the label are removed. -If @scheme[enable?] is @scheme[#f], the @scheme[n]th radio button is disabled, - otherwise it is enabled (assuming the entire radio box is - enabled). Radio buttons are numbered from @scheme[0]. - If @scheme[n] is equal to or larger than the number of radio buttons in - the radio box, @|MismatchExn|}. +} +@defmethod[(get-number) + nonnegative-exact-integer?]{ +Returns the number of radio buttons in the radio box. -}} +} + +@defmethod[(get-selection) + nonnegative-exact-integer?]{ + +Gets the position of the selected radio button. Radio buttons are +numbered from @scheme[0]. + +} @defmethod*[#:mode 'override ([(is-enabled?) boolean?] [(is-enabled? [n nonnegative-exact-integer?]) boolean?])]{ -@impl{ -First case: +If no arguments are provided, the enable state of the entire radio box +is reported. -Returns @scheme[#f] if the entire radio box is disabled, - @scheme[#t] otherwise. +Otherwise, returns @scheme[#f] if @scheme[n]th radio button is +disabled (independent of disabling the entire radio box), @scheme[#t] +otherwise. Radio buttons are numbered from @scheme[0]. If @scheme[n] +is equal to or larger than the number of radio buttons in the radio +box, @|MismatchExn|. - - -Second case: - - -Returns @scheme[#f] if @scheme[n]th radio button is disabled (independent of - disabling the entire radio box), @scheme[#t] otherwise. Radio buttons - are numbered from @scheme[0]. If @scheme[n] is equal to or larger than the - number of radio buttons in the radio box, @|MismatchExn|}. - - - -}} - -@defmethod[(get-selection) - nonnegative-exact-integer?]{ -@spec{ - -Gets the position of the selected radio button. Radio buttons are - numbered from @scheme[0]. - -}} - -@defmethod[(get-number) - nonnegative-exact-integer?]{ -@spec{ - -Returns the number of radio buttons in the radio box. - -}} +} @defmethod[(set-selection [n nonnegative-exact-integer?]) void?]{ -@spec{ Sets the selected radio button by position. (The control's callback procedure is {\em not} invoked.) Radio buttons are numbered from - @scheme[0]. + @scheme[0]. If @scheme[n] is equal to or larger than the number of + radio buttons in the radio box, @|MismatchExn|. @MonitorCallback[@elem{A radio box's selection} @elem{the user clicking the control} @elem{selection}] -} -@impl{ - -If @scheme[n] is equal to or larger than the number of radio buttons in - the radio box, @|MismatchExn|}. - - - }} -@defmethod[(get-item-label [n nonnegative-exact-integer?]) - string]{ -@spec{ - -Gets the label of a radio button by position. Radio buttons are - numbered from @scheme[0]. - -} -@impl{ - -If @scheme[n] is equal to or larger than the number of radio buttons in - the radio box, @|MismatchExn|}. - - - -}} - -@defmethod[(get-item-plain-label [n nonnegative-exact-integer?]) - string]{ -@spec{ - -Like -@method[radio-box% get-item-label], except that the label must be a string and ampersands in the label - are removed. - -} -@impl{ - -If @scheme[n] is equal to or larger than the number of radio buttons in - the radio box, @|MismatchExn|}. - - - -}}} - diff --git a/collects/scribblings/gui/scroll-event-class.scrbl b/collects/scribblings/gui/scroll-event-class.scrbl index 2f03262a1b..6cd3233609 100644 --- a/collects/scribblings/gui/scroll-event-class.scrbl +++ b/collects/scribblings/gui/scroll-event-class.scrbl @@ -13,24 +13,34 @@ See -@defconstructor[[event-type (symbols/c thumb page-down page-up line-down line-up bottom top) 'thumb] - [direction (symbols/c vertical horizontal) 'vertical] - [position (integer-in 0 10000) 0] - [time-stamp (and/c exact? integer?) 0]]{ +@defconstructor[([event-type (one-of/c 'top 'bottom 'line-up 'line-down + 'page-up 'page-down 'thumb) + 'thumb] + [direction (one-of/c 'horizontal 'vertical) 'vertical] + [position (integer-in 0 10000) 0] + [time-stamp (and/c exact? integer?) 0])]{ See the corresponding @scheme[get-] and @scheme[set-] methods for information about @scheme[event-type], @scheme[direction], @scheme[position], and @scheme[time-stamp]. +} +@defmethod[(get-direction) + (one-of/c 'horizontal 'vertical)]{ + +Gets the identity of the scrollbar that was modified by the event, + either the horizontal scrollbar or the vertical scrollbar, as + @scheme['horizontal] or @scheme['vertical], respectively. See also + @method[scroll-event% set-direction]. } @defmethod[(get-event-type) - (symbols/c thumb page-down page-up line-down line-up bottom top)]{ -@spec{ + (one-of/c 'top 'bottom 'line-up 'line-down 'page-up 'page-down 'thumb)]{ Returns the type of the event, one of the following: + @itemize{ @item{@scheme['top] --- user clicked a scroll-to-top button} @item{@scheme['bottom] --- user clicked a scroll-to-bottom button} @@ -41,64 +51,41 @@ Returns the type of the event, one of the following: @item{@scheme['thumb] --- user dragged the scroll position indicator} } -}} - -@defmethod[(set-event-type [type (symbols/c thumb page-down page-up line-down line-up bottom top)]) - void?]{ -@spec{ - -Sets the type of the event. See -@method[scroll-event% get-event-type] for information about each event type. - -}} +} @defmethod[(get-position) (integer-in 0 10000)]{ -@spec{ Returns the position of the scrollbar after the action triggering the - event. See also -@method[scroll-event% set-position]. + event. See also @method[scroll-event% set-position]. -}} +} -@defmethod[(set-position [position (integer-in 0 10000)]) +@defmethod[(set-direction [direction (one-of/c 'horizontal 'vertical)]) void?]{ -@spec{ - -Records the position of the scrollbar after the action triggering the - event. (The scrollbar itself is unaffected). See also -@method[scroll-event% get-position]. - -}} - -@defmethod[(get-direction) - (symbols/c vertical horizontal)]{ -@spec{ - -Gets the identity of the scrollbar that was modified by the event, - either the horizontal scrollbar or the vertical scrollbar, as - @scheme['horizontal] or @scheme['vertical], respectively. See - also -@method[scroll-event% set-direction]. - -}} - -@defmethod[(set-direction [direction (symbols/c vertical horizontal)]) - void?]{ -@spec{ Sets the identity of the scrollbar that was modified by the event, either the horizontal scrollbar or the vertical scrollbar, as - @scheme['horizontal] or @scheme['vertical], respectively. See - also -@method[scroll-event% get-direction]. + @scheme['horizontal] or @scheme['vertical], respectively. See also + @method[scroll-event% get-direction]. } -@impl{ +@defmethod[(set-event-type [type (one-of/c 'top 'bottom 'line-up 'line-down + 'page-up 'page-down 'thumb)]) + void?]{ +Sets the type of the event. See @method[scroll-event% get-event-type] +for information about each event type. +} -}}} +@defmethod[(set-position [position (integer-in 0 10000)]) + void?]{ + +Records the position of the scrollbar after the action triggering the + event. (The scrollbar itself is unaffected). See also + @method[scroll-event% get-position]. + +}} diff --git a/collects/scribblings/gui/selectable-menu-item-intf.scrbl b/collects/scribblings/gui/selectable-menu-item-intf.scrbl index 6a0af1d934..e3b94c0515 100644 --- a/collects/scribblings/gui/selectable-menu-item-intf.scrbl +++ b/collects/scribblings/gui/selectable-menu-item-intf.scrbl @@ -6,50 +6,45 @@ A @scheme[selectable-menu-item<%>] object is a @scheme[labelled-menu-item<%>] that the user can select. It may also have a keyboard shortcut; the shortcut is displayed in the menu, and - the default -@method[frame% on-subwindow-char] method in the menu's frame dispatches to the menu item when the - shortcut key combination is pressed. + the default @method[frame% on-subwindow-char] method in the menu's + frame dispatches to the menu item when the shortcut key combination + is pressed. +@defmethod[(command [event (is-a?/c control-event%)]) + void?]{ + +Invoke's the menu item's callback procedure, which is supplied when an + instance of +@scheme[menu-item%] or +@scheme[checkable-menu-item%] is created. + +} @defmethod[(get-shortcut) - character, symbol, @scheme[#f]]{ -@spec{ + (or/c char? symbol? false/c)]{ Gets the keyboard shortcut character or virtual key for the menu item. This character or key is combined with the shortcut prefix, - which is reported by -@method[selectable-menu-item<%> get-shortcut-prefix]. + which is reported by @method[selectable-menu-item<%> + get-shortcut-prefix]. If the menu item has no shortcut, @scheme[#f] is returned. The shortcut part of a menu item name is not included in the label - returned by -@method[labelled-menu-item<%> get-label]. + returned by @method[labelled-menu-item<%> get-label]. -For a list of allowed key symbols, see -@xmethod[key-event% get-key-code] . +For a list of allowed key symbols, see @xmethod[key-event% + get-key-code]. -}} - -@defmethod[(set-shortcut [shortcut (or/c character, symbol, false/c)]) - void?]{ -@spec{ - -Sets the keyboard shortcut character for the menu item. See -@method[selectable-menu-item<%> get-shortcut] for more information. - -If the shortcut character is set to @scheme[#f], then menu item has no - keyboard shortcut. - -}} +} @defmethod[(get-shortcut-prefix) - (symbols/c option shift ctl meta cmd alt)]{ -@spec{ + (listof (one-of/c 'alt 'cmd 'meta 'ctl 'shift 'option))]{ Returns a list of symbols that indicates the keyboard prefix used for the menu item's keyboard shortcut. The allowed symbols for the list are the following: + @itemize{ @item{@scheme['alt] --- Meta (Windows and X only)} @item{@scheme['cmd] --- Command (Mac OS X only)} @@ -58,59 +53,57 @@ Returns a list of symbols that indicates the keyboard prefix used for the menu @item{@scheme['shift] --- Shift} @item{@scheme['opt] --- Option (Mac OS X only)} } + Under X, at most one of @scheme['alt] and @scheme['meta] can be - supplied; the only difference between @scheme['alt] and @scheme['meta] - is the key combination's display in a menu. + supplied; the only difference between @scheme['alt] and + @scheme['meta] is the key combination's display in a menu. The default shortcut prefix is available from -@scheme[get-default-shortcut-prefix] . + @scheme[get-default-shortcut-prefix]. -The shortcut key, as determined by -@method[selectable-menu-item<%> get-shortcut], matches a key event using either the normally reported key code or -the other-Shift/AltGr key code (as produced by -@xmethod[key-event% get-other-shift-key-code], etc.). When the shortcut key is a key-code symbol or an ASCII letter or -digit, then the shortcut matches only the exact combination of -modifier keys listed in the prefix. For character shortcuts other than -ASCII letters and digits, however, then the shortcut prefix merely -determines a minimum set of modifier keys, because additional -modifiers may be needed to access the character; an exception is that, -under Windows or X, the Alt/Meta key press must match the prefix -exactly (i.e., included or not). In all -cases, the most precise match takes precedence; see -@xmethod[keymap% map-function] for more information on match ranking. +The shortcut key, as determined by @method[selectable-menu-item<%> + get-shortcut], matches a key event using either the normally reported + key code or the other-Shift/AltGr key code (as produced by + @xmethod[key-event% get-other-shift-key-code], etc.). When the + shortcut key is a key-code symbol or an ASCII letter or digit, then + the shortcut matches only the exact combination of modifier keys + listed in the prefix. For character shortcuts other than ASCII + letters and digits, however, then the shortcut prefix merely + determines a minimum set of modifier keys, because additional + modifiers may be needed to access the character; an exception is + that, under Windows or X, the Alt/Meta key press must match the + prefix exactly (i.e., included or not). In all cases, the most + precise match takes precedence; see @xmethod[keymap% map-function] + for more information on match ranking. An empty list can be used for a shortcut prefix. However, the default -@xmethod[frame% on-menu-char] method checks for menu shortcuts only when the key event includes -either a non-Shift modifer or a Function key. Thus, an empty shortcut -prefix is normally useful only if the shortcut key is a Function key. - -}} - -@defmethod[(set-shortcut-prefix [prefix (symbols/c option shift ctl meta cmd alt)]) - void?]{ -@spec{ - -Sets a list of symbols to indicates the keyboard prefix used for the menu - item's keyboard shortcut. - -See -@method[selectable-menu-item<%> get-shortcut-prefix] for more information. - -}} - -@defmethod[(command [event (is-a?/c control-event%)]) - void?]{ -@spec{ - -Invoke's the menu item's callback procedure, which is supplied when an - instance of -@scheme[menu-item%] or -@scheme[checkable-menu-item%] is created. + @xmethod[frame% on-menu-char] method checks for menu shortcuts only + when the key event includes either a non-Shift modifer or a Function + key. Thus, an empty shortcut prefix is normally useful only if the + shortcut key is a Function key. } -@impl{ +@defmethod[(set-shortcut [shortcut (or/c char? symbol? false/c)]) + void?]{ -}}} +Sets the keyboard shortcut character for the menu item. See +@method[selectable-menu-item<%> get-shortcut] for more information. + +If the shortcut character is set to @scheme[#f], then menu item has no +keyboard shortcut. + +} + +@defmethod[(set-shortcut-prefix [prefix (listof (one-of/c 'alt 'cmd 'meta 'ctl 'shift 'option))]) + void?]{ + +Sets a list of symbols to indicates the keyboard prefix used for the +menu item's keyboard shortcut. + +See @method[selectable-menu-item<%> get-shortcut-prefix] for more +information. + +}} diff --git a/collects/scribblings/gui/separator-menu-item-class.scrbl b/collects/scribblings/gui/separator-menu-item-class.scrbl index 052a370a2d..b34c800532 100644 --- a/collects/scribblings/gui/separator-menu-item-class.scrbl +++ b/collects/scribblings/gui/separator-menu-item-class.scrbl @@ -7,13 +7,9 @@ A separator is an unselectable line in a menu. Its parent must be a @scheme[menu%] or @scheme[popup-menu%]. - - -@defconstructor[[parent @scheme[menu%] or @scheme[popup-menu%] object]]{ +@defconstructor[([parent (or/c (is-a?/c menu%) (is-a?/c popup-menu%))])]{ Creates a new separator in the menu. - - }} diff --git a/collects/scribblings/gui/slider-class.scrbl b/collects/scribblings/gui/slider-class.scrbl index 92cc7580e4..027bc2406e 100644 --- a/collects/scribblings/gui/slider-class.scrbl +++ b/collects/scribblings/gui/slider-class.scrbl @@ -14,74 +14,66 @@ Whenever the user changes the value of a slider, its callback -@defconstructor[[label (or/c label-string? false/c)] - [min-value (integer-in -10000 10000)] - [max-value (integer-in -10000 10000)] - [parent (or/c (is-a?/c frame%) (is-a?/c dialog%) (is-a?/c panel%) (is-a?/c pane%))] - [callback procedure of two arguments: a @scheme[slider%] object and a @scheme[control-event%] object @scheme[(\scmk{lambda] (@scheme[s] @scheme[e]) (void))}] - [init-value (integer-in -10000 10000) @scheme[min-value]] - [style (symbols/c deleted horizontal-label vertical-label plain vertical horizontal) '(horizontal)] - [font (is-a?/c font%) @scheme[normal-control-font]] - [enabled any/c #t] - [vert-margin (integer-in 0 1000) 2] - [horiz-margin (integer-in 0 1000) 2] - [min-width (integer-in 0 10000) {\rm graphical minimum width}] - [min-height (integer-in 0 10000) {\rm graphical minimum height}] - [stretchable-width any/c \#t {\rm for} @scheme['horizontal] {\rm style}, \#f {\rm for} @scheme['vertical]] - [stretchable-height any/c \#t {\rm for} @scheme['vertical] {\rm style}, \#f {\rm for} @scheme['horizontal]]]{ +@defconstructor[([label (or/c label-string? false/c)] + [min-value (integer-in -10000 10000)] + [max-value (integer-in -10000 10000)] + [parent (or/c (is-a?/c frame%) (is-a?/c dialog%) + (is-a?/c panel%) (is-a?/c pane%))] + [callback ((is-a?/c slider%) (is-a?/c control-event%) . -> . any) (lambda (b e) (void))] + [init-value (integer-in -10000 10000) min-value] + [style (listof (one-of/c 'horizontal 'vertical 'plain + 'vertical-label 'horizontal-label + 'deleted)) + '(horizontal)] + [font (is-a?/c font%) normal-control-font] + [enabled any/c #t] + [vert-margin (integer-in 0 1000) 2] + [horiz-margin (integer-in 0 1000) 2] + [min-width (integer-in 0 10000) _graphical-minimum-width] + [min-height (integer-in 0 10000) _graphical-minimum-height] + [stretchable-width any/c (memq 'horizontal style)] + [stretchable-height any/c (memq 'vertical style)])]{ If @scheme[label] is a string, it is used as the label for the slider. - Otherwise, the slider does not display its - label. + Otherwise, the slider does not display its label. @labelstripped[(scheme label) @elem{} @elem{move the keyboard focus to the slider}] -The @scheme[min-value] and @scheme[max-value] arguments specify the range of - the slider, inclusive. The @scheme[init-value] argument optionally - specifies the slider's initial value. If the sequence - [@scheme[min-value], @scheme[initial-value], @scheme[maximum-value]] is not - increasing, @|MismatchExn|}. +The @scheme[min-value] and @scheme[max-value] arguments specify the + range of the slider, inclusive. The @scheme[init-value] argument + optionally specifies the slider's initial value. If the sequence + [@scheme[min-value], @scheme[initial-value], @scheme[maximum-value]] + is not increasing, @|MismatchExn|. The @scheme[callback] procedure is called (with the event type @indexed-scheme['slider]) when the user changes the slider's value. -The @scheme[style] argument must include either @scheme['vertical] for a - vertical slider, or @scheme['horizontal] for a horizontal slider. If - @scheme[style] includes @scheme['plain], the slider does not display - numbers for its range and current value to the user. - \HVLabelNote{slider} \DeletedStyleNote{slider} +The @scheme[style] argument must include either @scheme['vertical] for + a vertical slider, or @scheme['horizontal] for a horizontal + slider. If @scheme[style] includes @scheme['plain], the slider does + not display numbers for its range and current value to the user. + @HVLabelNote{slider} @DeletedStyleNote{slider} @FontKWs[] @WindowKWs[] @SubareaKWs[] @AreaKWs[] - } @defmethod[(get-value) (integer-in -10000 10000)]{ -@spec{ Gets the current slider value. -}} +} @defmethod[(set-value [value (integer-in -10000 10000)]) void?]{ -@spec{ Sets the value (and displayed position) of the slider. (The control's - callback procedure is {\em not} invoked.) + callback procedure is @italic{not} invoked.) If @scheme[value] is + outside the slider's minimum and maximum range, @|MismatchExn|. @MonitorCallback[@elem{A slider's value} @elem{the user clicking the control} @elem{value}] -} -@impl{ - -If @scheme[value] is outside the slider's minimum and maximum range, - @|MismatchExn|}. - - - - -}}} +}} diff --git a/collects/scribblings/gui/tab-panel-class.scrbl b/collects/scribblings/gui/tab-panel-class.scrbl index 5f4ed3d622..36f351f638 100644 --- a/collects/scribblings/gui/tab-panel-class.scrbl +++ b/collects/scribblings/gui/tab-panel-class.scrbl @@ -15,24 +15,29 @@ The @scheme[tab-panel%] class does not implement the virtual -@defconstructor[[choices list of {\labelstrings}] - [parent (or/c (is-a?/c frame%) (is-a?/c dialog%) (is-a?/c panel%) (is-a?/c pane%))] - [callback procedure of two arguments: a @scheme[tab-panel%] object and a @scheme[control-event%] object @scheme[(\scmk{lambda] (@scheme[tp] @scheme[e]) (void))}] - [style (symbols/c deleted no-border) null] - [font (is-a?/c font%) @scheme[normal-control-font]] - [enabled any/c #t] - [vert-margin (integer-in 0 1000) 0] - [horiz-margin (integer-in 0 1000) 0] - [border (integer-in 0 1000) 0] - [spacing (integer-in 0 1000) 0] - [alignment two-element list: @scheme['left], @scheme['center], or @scheme['right] and @scheme['top], @scheme['center], or @scheme['bottom] '(center top)] - [min-width (integer-in 0 10000) {\rm graphical minimum width}] - [min-height (integer-in 0 10000) {\rm graphical minimum height}] - [stretchable-width any/c #t] - [stretchable-height any/c #t]]{ +@defconstructor[([choices (listof label-string?)] + [parent (or/c (is-a?/c frame%) (is-a?/c dialog%) + (is-a?/c panel%) (is-a?/c pane%))] + [callback ((is-a?/c tab-panel%) (is-a?/c control-event%) + . -> . any) + (lambda (b e) (void))] + [style (listof (one-of/c 'no-border 'deleted)) null] + [font (is-a?/c font%) normal-control-font] + [enabled any/c #t] + [vert-margin (integer-in 0 1000) 0] + [horiz-margin (integer-in 0 1000) 0] + [border (integer-in 0 1000) 0] + [spacing (integer-in 0 1000) 0] + [alignment (list/c (one-of/c 'left 'center 'right) + (one-of/c 'top 'center 'bottom)) + '(center top)] + [min-width (integer-in 0 10000) _graphical-minimum-width] + [min-height (integer-in 0 10000) _graphical-minimum-height] + [stretchable-width any/c #t] + [stretchable-height any/c #t])]{ -Creates a tab pane, where the - @scheme[choices] list specifies the tab labels. +Creates a tab pane, where the @scheme[choices] list specifies the tab + labels. Each string in @scheme[choices] can contain an ampersand, which (in the future) may create a mnemonic for clicking the corresponding tab. A @@ -42,7 +47,7 @@ The @scheme[callback] procedure is called (with the event type @indexed-scheme['tab-panel]) when the user changes the tab selection. If the @scheme[style] list includes @scheme['no-border], no border is - drawn around the panel content. \DeletedStyleNote{tab panel} + drawn around the panel content. @DeletedStyleNote{tab panel} @FontKWs[] @WindowKWs[] @SubareaKWs[] @AreaKWs[] @@ -52,115 +57,70 @@ If the @scheme[style] list includes @scheme['no-border], no border is @defmethod[(append [choice label-string?]) void?]{ -@spec{ Adds a tab to the right end of panel's top row of tabs. +The label string @scheme[choice] can contain @litchar{&}, which (in + the future) may create a mnemonic for clicking the new tab. A + @litchar{&&} is converted to @litchar{&}. + } -@impl{ - -The label string @scheme[choice] can contain an ampersand, which (in the - future) may create a mnemonic for clicking the new tab. A double - ampersand is converted to a single ampersand. - - - -}} @defmethod[(delete [n nonnegative-exact-integer?]) void?]{ -@spec{ -Deletes an existing tab. +Deletes an existing tab. If @scheme[n] is equal to or larger than the + number of tabs on the panel, @|MismatchExn|. } -@impl{ -If @scheme[n] is equal to or larger than the number of tabs on the panel, - @|MismatchExn|}. - - - -}} - -@defmethod[(get-selection) - (or/c nonnegative-exact-integer? false/c)]{ -@spec{ - -Returns the index (counting from 0) of the currently selected tab. - If the panel has no tabs, the result is @scheme[#f]. - -}} - -@defmethod[(set-selection [n nonnegative-exact-integer?]) - void?]{ -@spec{ - -Sets the currently selected tab by index (counting from 0). - -} -@impl{ +@defmethod[(get-item-label [n nonnegative-exact-integer?]) + string?]{ +Gets the label of a tab by position. Tabs are numbered from @scheme[0]. If @scheme[n] is equal to or larger than the number of tabs in the panel, - @|MismatchExn|}. + @|MismatchExn|. - - -}} +} @defmethod[(get-number) nonnegative-exact-integer?]{ -@spec{ Returns the number of tabs on the panel. -}} +} -@defmethod[(get-item-label [n nonnegative-exact-integer?]) - string]{ -@spec{ +@defmethod[(get-selection) + (or/c nonnegative-exact-integer? false/c)]{ -Gets the label of a tab by position. Tabs are numbered from @scheme[0]. +Returns the index (counting from 0) of the currently selected tab. If + the panel has no tabs, the result is @scheme[#f]. } -@impl{ -If @scheme[n] is equal to or larger than the number of tabs in the panel, - @|MismatchExn|}. - - - -}} - -@defmethod[(set-item-label [n nonnegative-exact-integer?] - [label label-string?]) - string]{ -@spec{ - -Sets the label of a tab by position. Tabs are numbered from @scheme[0]. - -} -@impl{ - -Set the label for tab @scheme[n] to @scheme[label]. If @scheme[n] is equal to - or larger than the number of tabs in the panel, @|MismatchExn|}. - - - -}} - -@defmethod[(set [choices list of {\labelstrings}]) +@defmethod[(set [choices (listof label-string?)]) void?]{ -@spec{ Removes all tabs from the panel and installs tabs with the given labels. } -@impl{ +@defmethod[(set-item-label [n nonnegative-exact-integer?] + [label label-string?]) + string?]{ +Set the label for tab @scheme[n] to @scheme[label]. If @scheme[n] is equal to + or larger than the number of tabs in the panel, @|MismatchExn|. +} -}}} +@defmethod[(set-selection [n nonnegative-exact-integer?]) + void?]{ + +Sets the currently selected tab by index (counting from 0). +If @scheme[n] is equal to or larger than the number of tabs in the panel, + @|MismatchExn|. + +}} diff --git a/collects/scribblings/gui/text-field-class.scrbl b/collects/scribblings/gui/text-field-class.scrbl index 11aefe97b4..0c5a093fb8 100644 --- a/collects/scribblings/gui/text-field-class.scrbl +++ b/collects/scribblings/gui/text-field-class.scrbl @@ -6,13 +6,13 @@ A @scheme[text-field%] object is an editable text field with an optional label displayed in front of it. There are two text field styles: + @itemize{ @item{A single line of text is visible, and a special control event is generated when the user presses Enter (when the text field has the focus) and the event is not handled by the text field's frame or - dialog (see -@xmethod[top-level-window<%> on-traverse-char] ).} + dialog (see @xmethod[top-level-window<%> on-traverse-char] ).} @item{Multiple lines of text are visible, and Enter is not handled specially.} @@ -25,93 +25,77 @@ Whenever the user changes the content of a text field, its callback The text field is implemented using a @scheme[text%] editor (with an inaccessible display). Thus, whereas @scheme[text-field%] provides - only -@method[text-field% get-value] and -@method[text-field% set-value] to manipulate the text in a text field, the -@method[text-field% get-editor] returns the field's editor, which provides a vast collection of - methods for more sophisticated operations on the text. + only @method[text-field% get-value] and @method[text-field% + set-value] to manipulate the text in a text field, the + @method[text-field% get-editor] returns the field's editor, which + provides a vast collection of methods for more sophisticated + operations on the text. The keymap for the text field's editor is initialized by calling the current keymap initializer procedure, which is determined by the -@scheme[current-text-keymap-initializer] parameter. + @scheme[current-text-keymap-initializer] parameter. +@defconstructor[([label (or/c label-string? false/c)] + [parent (or/c (is-a?/c frame%) (is-a?/c dialog%) + (is-a?/c panel%) (is-a?/c pane%))] + [callback ((is-a?/c text-field%) (is-a?/c control-event%) + . -> . any) + (lambda (t e) (void))] + [init-value string? ""] + [style (listof (one-of/c 'single 'multiple 'hscroll 'password + 'vertical-label 'horizontal-label + 'deleted)) + '(single)] + [font (is-a?/c font%) @scheme[normal-control-font]] + [enabled any/c #t] + [vert-margin (integer-in 0 1000) 2] + [horiz-margin (integer-in 0 1000) 2] + [min-width (integer-in 0 10000) _graphical-minimum-width] + [min-height (integer-in 0 10000) _graphical-minimum-height] + [stretchable-width any/c #t] + [stretchable-height any/c (memq 'multiple style)])]{ - -@defconstructor[[label (or/c label-string? false/c)] - [parent (or/c (is-a?/c frame%) (is-a?/c dialog%) (is-a?/c panel%) (is-a?/c pane%))] - [callback procedure of two arguments: a @scheme[text-field%] object and a @scheme[control-event%] object @scheme[(\scmk{lambda] (@scheme[tf] @scheme[e]) (void))}] - [init-value string ""] - [style (symbols/c deleted horizontal-label vertical-label password hscroll multiple single) '(single)] - [font (is-a?/c font%) @scheme[normal-control-font]] - [enabled any/c #t] - [vert-margin (integer-in 0 1000) 2] - [horiz-margin (integer-in 0 1000) 2] - [min-width (integer-in 0 10000) {\rm graphical minimum width}] - [min-height (integer-in 0 10000) {\rm graphical minimum height}] - [stretchable-width any/c #t] - [stretchable-height any/c \#t {\rm for} @scheme['multiple] {\rm style}, \#f {\rm otherwise}]]{ - -If @scheme[label] is not @scheme[#f], it is used as the text field label. - Otherwise, the text field does not display its - label. +If @scheme[label] is not @scheme[#f], it is used as the text field + label. Otherwise, the text field does not display its label. @labelstripped[(scheme label) @elem{} @elem{move the keyboard focus to the text field}] -The @scheme[callback] procedure is called when the user changes the text - in the text field or presses the Enter key (and Enter is not handled by - the text field's frame or dialog; see -@xmethod[top-level-window<%> on-traverse-char] ). If the user presses Enter, the type of event passed to the callback - is @indexed-scheme['text-field-enter], otherwise it is +The @scheme[callback] procedure is called when the user changes the + text in the text field or presses the Enter key (and Enter is not + handled by the text field's frame or dialog; see + @xmethod[top-level-window<%> on-traverse-char]). If the user presses + Enter, the type of event passed to the callback is + @indexed-scheme['text-field-enter], otherwise it is @indexed-scheme['text-field]. -If @scheme[init-value] is not @scheme[""], the minimum width of the text item - is made wide enough to show @scheme[init-value]. Otherwise, a built-in - default width is selected. For a text field in single-line mode, the - minimum height is set to show one line and only the control's width - is stretchable. For a multiple-line text field, the minimum height - shows three lines of text and is stretchable in both directions. +If @scheme[init-value] is not @scheme[""], the minimum width of the + text item is made wide enough to show @scheme[init-value]. Otherwise, + a built-in default width is selected. For a text field in single-line + mode, the minimum height is set to show one line and only the + control's width is stretchable. For a multiple-line text field, the + minimum height shows three lines of text and is stretchable in both + directions. The style must contain exactly one of @scheme['single] or - @scheme['multiple]; the former specifies a single-line field and - the latter specifies a multiple-line field. The @scheme['hscroll] - style applies only to multiple-line fields; when - @scheme['hscroll] is specified, the field has a horizontal - scrollbar and autowrapping is disabled; otherwise, the field has no - horizontal scrollbar and autowrapping is enabled. A multiple-line text - field always has a vertical scrollbar. The @scheme['password] - style indicates that the field should draw each character of - its content using a generic symbol instead of the actual character. - \HVLabelNote{text field} \DeletedStyleNote{text field}. + @scheme['multiple]; the former specifies a single-line field and the + latter specifies a multiple-line field. The @scheme['hscroll] style + applies only to multiple-line fields; when @scheme['hscroll] is + specified, the field has a horizontal scrollbar and autowrapping is + disabled; otherwise, the field has no horizontal scrollbar and + autowrapping is enabled. A multiple-line text field always has a + vertical scrollbar. The @scheme['password] style indicates that the + field should draw each character of its content using a generic + symbol instead of the actual character. @HVLabelNote{text field} + @DeletedStyleNote{text field}. @FontKWs[] @WindowKWs[] @SubareaKWs[] @AreaKWs[] - - } -@defmethod[(get-value) - string]{ -@spec{ - -Returns the text currently in the text field. - -}} - -@defmethod[(set-value [val string]) - void?]{ -@spec{ - -Sets the text currently in the text field. (The control's callback - procedure is {\em not} invoked.) - -@MonitorCallback[@elem{A text field's value} @elem{the user typing into the control} @elem{value}] - -}} @defmethod[(get-editor) (is-a?/c text%)]{ -@spec{ Returns the editor used to implement the text field. @@ -119,21 +103,33 @@ For a text field, the most useful methods of a @scheme[text%] object are the following: @itemize{ - @item{@scheme[(send @scheme[a-text] @method[text% get-text])] returns + @item{@scheme[(send a-text @method[text% get-text])] returns the current text of the editor.} - @item{@scheme[(send @scheme[a-text] @method[text% erase])] deletes all text from + @item{@scheme[(send a-text @method[text% erase])] deletes all text from the editor.} - @item{@scheme[(send @scheme[a-text] @method[text% insert] \var{string])} - inserts @scheme[str] into the editor at the current caret position.} + @item{@scheme[(send a-text @method[text% insert] _str)] inserts + @scheme[_str] into the editor at the current caret position.} + +} +} + + +@defmethod[(get-value) + string?]{ + +Returns the text currently in the text field. } -} -@impl{ +@defmethod[(set-value [val string?]) + void?]{ +Sets the text currently in the text field. (The control's callback + procedure is @italic{not} invoked.) -}}} +@MonitorCallback[@elem{A text field's value} @elem{the user typing into the control} @elem{value}] +}} diff --git a/collects/scribblings/gui/timer-class.scrbl b/collects/scribblings/gui/timer-class.scrbl index 936bce33d7..2810ef376d 100644 --- a/collects/scribblings/gui/timer-class.scrbl +++ b/collects/scribblings/gui/timer-class.scrbl @@ -4,106 +4,86 @@ @defclass[timer% object% ()]{ A @scheme[timer%] object encapsulates an event-based alarm. To use a - timer, either instantiate it with a @scheme[timer-callback] thunk to perform the alarm-based action, to derive a new class and override the -@method[timer% notify] method to perform the alarm-based action. Start a timer with -@method[timer% start] and stop it with -@method[timer% stop]. Supplying an initial @scheme[interval] (in milliseconds) when creating - a timer also starts the timer. + timer, either instantiate it with a @scheme[timer-callback] thunk to + perform the alarm-based action, to derive a new class and override + the @method[timer% notify] method to perform the alarm-based + action. Start a timer with @method[timer% start] and stop it with + @method[timer% stop]. Supplying an initial @scheme[interval] (in + milliseconds) when creating a timer also starts the timer. Timers have a relatively high priority in the event queue. Thus, if the timer delay is set low enough, repeated notification for a timer can preempt user activities (which might be directed at stopping the - timer). For timers with relatively short delays, call -@scheme[yield] within the -@method[timer% notify] procedure to allow guaranteed event processing. + timer). For timers with relatively short delays, call @scheme[yield] + within the @method[timer% notify] procedure to allow guaranteed event + processing. -See @secref["mr:eventspaceinfo"] for more information - about event priorities. +See @secref["mr:eventspaceinfo"] for more information about event + priorities. - - - -@defconstructor[[notify-callback (-> any) @scheme[void]] - [interval (or/c (integer-in 0 1000000000) false/c) #f] - [just-once? any/c #f]]{ - -Creates a timer. +@defconstructor[([notify-callback (-> any) @scheme[void]] + [interval (or/c (integer-in 0 1000000000) false/c) #f] + [just-once? any/c #f])]{ The @scheme[notify-callback] thunk is called by the default @method[timer% notify] method when the timer expires. If @scheme[interval] is @scheme[#f] (the default), the timer is not - started; in that case, -@method[timer% start] must be called explicitly. If @scheme[interval] is a number (in - milliseconds), then -@method[timer% start] is called with @scheme[interval] and @scheme[just-once?]. + started; in that case, @method[timer% start] must be called + explicitly. If @scheme[interval] is a number (in milliseconds), then + @method[timer% start] is called with @scheme[interval] and + @scheme[just-once?]. + +} +@defmethod[(interval) + (integer-in 0 1000000000)]{ + +Returns the number of milliseconds between each timer expiration (when + the timer is running). } @defmethod[(notify) void?]{ -@spec{ + +@methspec{ Called (on an event boundary) when the timer's alarm expires. } -@impl{ +@methimpl{ Calls the @scheme[notify-callback] procedure that was provided when the object was created. - - - -}} - -@defmethod[(interval) - (integer-in 0 1000000000)]{ -@spec{ - -Returns the number of milliseconds between each timer expiration - (when the timer is running). - }} @defmethod[(start [msec (integer-in 0 1000000000)] [just-once? any/c #f]) void?]{ -@spec{ Starts (or restarts) the timer. If the timer is already running, its alarm time is not changed. +The timer's alarm expires after @scheme[msec] milliseconds, at which +point @method[timer% notify] is called (on an event boundary). If +@scheme[just-once?]\ is @scheme[#f], the timer expires {\em every} +@scheme[msec] milliseconds until the timer is explicitly +stopped;\footnote{More precisely, the timer expires @scheme[msec] +milliseconds after @method[timer% notify] returns each time} +otherwise, the timer expires only once. + } -@impl{ - -The timer's alarm expires after @scheme[msec] milliseconds, at which point -@method[timer% notify] is called (on an event boundary). If @scheme[just-once?]\ is @scheme[#f], - the timer expires {\em every} @scheme[msec] milliseconds until the timer is - explicitly stopped;\footnote{More precisely, the timer expires @scheme[msec] - milliseconds after -@method[timer% notify] returns each time} otherwise, the timer expires only once. - - - -}} @defmethod[(stop) void?]{ -@spec{ Stops the timer. A stopped timer never calls @method[timer% notify]. If the timer has expired but the call to @method[timer% notify] has not yet been dispatched, the call is removed from the event queue. -} -@impl{ - - - - -}}} +}} diff --git a/collects/scribblings/gui/win-classes.scrbl b/collects/scribblings/gui/win-classes.scrbl new file mode 100644 index 0000000000..8374c8f891 --- /dev/null +++ b/collects/scribblings/gui/win-classes.scrbl @@ -0,0 +1,125 @@ +#reader(lib "docreader.ss" "scribble") +@require["common.ss"] +@require["diagrams.ss"] + +@title[#:style '(toc quiet)]{Windowing Class Reference} + +Windows and controls: + +@diagram->table[windowing-diagram] + +Menus: + +@diagram->table[menu-diagram] + +Events and other: + +@diagram->table[event-diagram] + +Alphabetical: + +@local-table-of-contents[] + +@require["area-intf.scrbl"] +@require["area-container-intf.scrbl"] +@require["area-container-window-intf.scrbl"] +@require["button-class.scrbl"] +@require["canvas-intf.scrbl"] +@require["canvas-class.scrbl"] +@require["check-box-class.scrbl"] +@require["checkable-menu-item-class.scrbl"] +@require["choice-class.scrbl"] +@require["clipboard-client-class.scrbl"] +@require["clipboard-intf.scrbl"] +@require["combo-field-class.scrbl"] +@require["control-intf.scrbl"] +@require["control-event-class.scrbl"] +@require["cursor-class.scrbl"] +@require["dialog-class.scrbl"] +@require["event-class.scrbl"] +@require["frame-class.scrbl"] +@require["gauge-class.scrbl"] +@require["group-box-panel-class.scrbl"] +@require["grow-box-spacer-pane-class.scrbl"] +@require["horizontal-pane-class.scrbl"] +@require["horizontal-panel-class.scrbl"] +@require["key-event-class.scrbl"] +@require["labelled-menu-item-intf.scrbl"] +@require["list-box-class.scrbl"] +@require["list-control-intf.scrbl"] +@require["menu-class.scrbl"] +@require["menu-bar-class.scrbl"] +@require["menu-item-intf.scrbl"] +@require["menu-item-class.scrbl"] +@require["menu-item-container-intf.scrbl"] +@require["message-class.scrbl"] +@require["mouse-event-class.scrbl"] +@require["pane-class.scrbl"] +@require["panel-class.scrbl"] +@require["popup-menu-class.scrbl"] +@require["radio-box-class.scrbl"] +@require["selectable-menu-item-intf.scrbl"] +@require["separator-menu-item-class.scrbl"] +@require["scroll-event-class.scrbl"] +@require["slider-class.scrbl"] +@require["subarea-intf.scrbl"] +@require["subwindow-intf.scrbl"] +@require["tab-panel-class.scrbl"] +@require["text-field-class.scrbl"] +@require["timer-class.scrbl"] +@require["top-level-window-intf.scrbl"] +@require["vertical-pane-class.scrbl"] +@require["vertical-panel-class.scrbl"] +@require["window-intf.scrbl"] + +@include-class[area<%>] +@include-class[area-container<%>] +@include-class[area-container-window<%>] +@include-class[button%] +@include-class[canvas<%>] +@include-class[canvas%] +@include-class[check-box%] +@include-class[checkable-menu-item%] +@include-class[choice%] +@include-class[clipboard<%>] +@include-class[clipboard-client%] +@include-class[combo-field%] +@include-class[control<%>] +@include-class[control-event%] +@include-class[cursor%] +@include-class[dialog%] +@include-class[event%] +@include-class[frame%] +@include-class[gauge%] +@include-class[group-box-panel%] +@include-class[grow-box-spacer-pane%] +@include-class[horizontal-pane%] +@include-class[horizontal-panel%] +@include-class[key-event%] +@include-class[labelled-menu-item<%>] +@include-class[list-box%] +@include-class[list-control<%>] +@include-class[menu%] +@include-class[menu-bar%] +@include-class[menu-item<%>] +@include-class[menu-item%] +@include-class[menu-item-container<%>] +@include-class[message%] +@include-class[mouse-event%] +@include-class[pane%] +@include-class[panel%] +@include-class[popup-menu%] +@include-class[radio-box%] +@include-class[scroll-event%] +@include-class[selectable-menu-item<%>] +@include-class[separator-menu-item%] +@include-class[slider%] +@include-class[subarea<%>] +@include-class[subwindow<%>] +@include-class[tab-panel%] +@include-class[text-field%] +@include-class[timer%] +@include-class[top-level-window<%>] +@include-class[vertical-pane%] +@include-class[vertical-panel%] +@include-class[window<%>] diff --git a/collects/scribblings/gui/win-funcs.scrbl b/collects/scribblings/gui/win-funcs.scrbl new file mode 100644 index 0000000000..c671b756e6 --- /dev/null +++ b/collects/scribblings/gui/win-funcs.scrbl @@ -0,0 +1,11 @@ +#reader(lib "docreader.ss" "scribble") +@require["common.ss"] + +@title[#:style 'toc]{Windowing Function Reference} + +@local-table-of-contents[] + +@include-section["dialog-funcs.scrbl"] +@include-section["eventspace-funcs.scrbl"] +@include-section["system-menu-funcs.scrbl"] +@include-section["miscwin-funcs.scrbl"] diff --git a/collects/scribblings/gui/win-overview.scrbl b/collects/scribblings/gui/win-overview.scrbl new file mode 100644 index 0000000000..0f6a0bf06d --- /dev/null +++ b/collects/scribblings/gui/win-overview.scrbl @@ -0,0 +1,950 @@ +#reader(lib "docreader.ss" "scribble") +@require[(lib "eval.ss" "scribble")] +@require["common.ss"] +@require["diagrams.ss"] + +@title[#:tag "mr:windowing-overview" #:style 'toc]{Windowing Toolbox Overview} + +@local-table-of-contents[] + +@section{Basic GUI Building Blocks} + +MrEd's windowing toolbox provides the basic building blocks of GUI + programs, including frames (top-level windows), modal dialogs, menus, + buttons, check boxes, text fields, and radio buttons. The toolbox + provides these building blocks via built-in classes, such as the + @scheme[frame%] class: + +@schemeblock[ +(code:comment #, @t{Make a frame by instantiating the @scheme[frame%] class}) +(define frame (new frame% [label "Example"])) + +(code:comment #, @t{Show the frame by calling its @method[top-level-window<%> show] method}) +(send frame top-level-window::show #t) +] + +The built-in classes provide various mechanisms for handling GUI + events. For example, when instantiating the @scheme[button%] class, + the programmer supplies an event callback procedure to be invoked + when the user clicks the button. The following example program + creates a frame with a text message and a button; when the user + clicks the button, the message changes: + +@schemeblock[ +(code:comment #, @t{Make a frame by instantiating the @scheme[frame%] class}) +(define frame (new frame% [label "Example"])) + +(code:comment #, @t{Make a static text message in the frame}) +(define msg (new message% [parent frame] + [label "No events so far..."])) + +(code:comment #, @t{Make a button in the frame}) +(new button% [parent frame] + [label "Click Me"] + (code:comment #, @t{Callback procedure for a button click:}) + (callback (lambda (button event) + (send msg #,(method message% set-label) "Button click")))) + +(code:comment #, @t{Show the frame by calling its @scheme[show] method}) +(send frame top-level-window::show #t) +] + +Programmers never implement the GUI event loop directly. Instead, the + system automatically pulls each event from an internal queue and + dispatches the event to an appropriate window. The dispatch invokes + the window's callback procedure or calls one of the window's + methods. In the above program, the system automatically invokes the + button's callback procedure whenever the user clicks @onscreen{Click + Me}. + +If a window receives multiple kinds of events, the events are + dispatched to methods of the window's class instead of to a callback + procedure. For example, a drawing canvas receives update events, + mouse events, keyboard events, and sizing events; to handle them, a + programmer must derive a new class from the built-in + @scheme[canvas%] class and override the event-handling methods. The + following expression extends the frame created above with a canvas + that handles mouse and keyboard events: + +@schemeblock[ +(code:comment #, @t{Derive a new canvas (a drawing window) class to handle events}) +(define my-canvas% + (class canvas% (code:comment #, @t{The base class is @scheme[canvas%]}) + (code:comment #, @t{Define overriding method to handle mouse events}) + (define/override (#,(:: canvas<%> on-event) event) + (send msg #,(:: message% set-label) "Canvas mouse")) + (code:comment #, @t{Define overriding method to handle keyboard events}) + (define/override (#,(:: canvas<%> on-char) event) + (send msg #,(:: message% set-label) "Canvas keyboard")) + (code:comment #, @t{Call the superclass initialization (and pass on all init args)}) + (super-new))) + +(code:comment #, @t{Make a canvas that handles events in the frame}) +(new my-canvas% [parent frame]) +] + +After running the above code, manually resize the frame to see the + new canvas. Moving the cursor over the canvas calls the canvas's + @method[canvas<%> on-event] method with an object representing a + motion event. Clicking on the canvas calls @method[canvas<%> + on-event]. While the canvas has the keyboard focus, typing on the + keyboard invokes the canvas's @method[canvas<%> on-char] method. + +The system dispatches GUI events sequentially; that is, after invoking + an event-handling callback or method, the system waits until the + handler returns before dispatching the next event. To illustrate the + sequential nature of events, we extend the frame again, adding a + @onscreen{Pause} button: + +@schemeblock[ +(new button% [parent frame] + [label "Pause"] + [callback (lambda (button event) (sleep 5))]) +] + +After the user clicks @onscreen{Pause}, the entire frame becomes + unresponsive for five seconds; the system cannot dispatch more events + until the call to @scheme[sleep] returns. For more information about + event dispatching, see @secref["mr:eventspaceinfo"]. + +In addition to dispatching events, the GUI classes also handle the + graphical layout of windows. Our example frame demonstrates a simple + layout; the frame's elements are lined up top-to-bottom. In general, + a programmer specifies the layout of a window by assigning each GUI + element to a parent @deftech{container}. A vertical container, such + as a frame, arranges its children in a column, and a horizontal + container arranges its children in a row. A container can be a child + of another container; for example, to place two buttons side-by-side + in our frame, we create a horizontal panel for the new buttons: + +@schemeblock[ +(define panel (new horizontal-panel% [parent frame])) +(new button% [parent panel] + [label "Left"] + [callback (lambda (button event) + (send msg #,(:: message% set-label) "Left click"))]) +(new button% [parent panel] + [label "Right"] + [callback (lambda (button event) + (send msg #,(:: message% set-label) "Right click"))]) +] + +For more information about window layout and containers, see + @secref["mr:containeroverview"]. + +@section{Core Windowing Classes} + +The fundamental graphical element in MrEd's windowing toolbox is an + @deftech{area}. The following classes implement the different types + of areas in the windowing toolbox: + +@itemize{ + + @item{@deftech{Containers} --- areas that can + contain other areas: + + @itemize{ + + @item{@scheme[frame%] --- a @deftech{frame} is a top-level window + that the user can move and resize.} + + @item{@scheme[dialog%] --- a @deftech{dialog} is a modal top-level + window; when a dialog is shown, other top-level windows are disabled + until the dialog is dismissed.} + + @item{@scheme[panel%] --- a @deftech{panel} is a subcontainer + within a container. The toolbox provides three subclasses of + @scheme[panel%]: @scheme[vertical-panel%], + @scheme[horizontal-panel%], and @scheme[tab-panel%].} + + @item{@scheme[pane%] --- a @deftech{pane} is a lightweight panel. + It has no graphical representation or event-handling capabilities. + The @scheme[pane%] class has three subclasses: + @scheme[vertical-pane%], @scheme[horizontal-pane%], and + @scheme[grow-box-spacer-pane%].} + + }} + + @item{@deftech{Containees} --- areas that must be + contained within other areas: + + @itemize{ + + @item{@scheme[panel%] --- a panel is a containee as well as + a container.} + + @item{@scheme[pane%] --- a pane is a containee as well as a + container.} + + @item{@scheme[canvas%] --- a @deftech{canvas} is a subwindow for + drawing on the screen.} + + @item{@scheme[editor-canvas%] --- an @deftech{editor canvas} is a + subwindow for displaying a text editor or pasteboard editor. The + @scheme[editor-canvas%] class is documented with the editor classes + in @secref["mr:editoredit"].} + + @item{@deftech{Controls} --- containees that the user can manipulate: + + @itemize{ + + @item{@scheme[message%] --- a @deftech{message} is a static + text field or bitmap with no user interaction.} + + @item{@scheme[button%] --- a @deftech{button} is a clickable + control.} + + @item{@scheme[check-box%] --- a @deftech{check box} is a + clickable control; the user clicks the control to set or remove + its check mark.} + + @item{@scheme[radio-box%] --- a @deftech{radio box} is a + collection of mutually exclusive @deftech{radio buttons}; when the + user clicks a radio button, it is selected and the radio box's + previously selected radio button is deselected.} + + @item{@scheme[choice%] --- a @deftech{choice item} is a pop-up + menu of text choices; the user selects one item in the control.} + + @item{@scheme[list-box%] --- a @deftech{list box} is a + scrollable lists of text choices; the user selects one or more + items in the list (depending on the style of the list box).} + + @item{@scheme[text-field%] --- a @deftech{text field} is a box + for simple text entry.} + + @item{@scheme[combo-field%] --- a @deftech{combo field} combines + a text field with a pop-up menu of choices.} + + @item{@scheme[slider%] --- a @deftech{slider} is a dragable + control that selects an integer value within a fixed range.} + + @item{@scheme[gauge%] --- a @deftech{gauge} is an output-only + control (the user cannot change the value) for reporting an integer + value within a fixed range.} + + }} + + }} + +} + +As suggested by the above listing, certain @tech{areas}, called + @tech{containers}, manage certain other areas, called + @tech{containees}. Some areas, such as panels, are both + @tech{containers} and @tech{containees}. + +Most areas are @deftech{windows}, but some are + @deftech{non-windows}. A @tech{window}, such as a @tech{panel}, has a + graphical representation, receives keyboard and mouse events, and can + be disabled or hidden. In contrast, a @tech{non-window}, such as a + @tech{pane}, is useful only for geometry management; a + @tech{non-window} does not receive mouse events, and it cannot be + disabled or hidden. + +Every @tech{area} is an instance of the @scheme[area<%>] + interface. Each @tech{container} is also an instance of the + @scheme[area-container<%>] interface, whereas each @tech{containee} + is an instance of @scheme[subarea<%>]. @tech{Windows} are instances + of @scheme[window<%>]. The @scheme[area-container<%>], + @scheme[subarea<%>], and @scheme[window<%>] interfaces are + subinterfaces of @scheme[area<%>]. + +The following diagram shows more of the type hierarchy under + @scheme[area<%>]: + +@diagram->table[short-windowing-diagram] + +The diagram below extends the one above to show the complete type + hierarchy under @scheme[area<%>]. (Some of the types are represented + by interfaces, and some types are represented by classes. In + principle, every area type should be represented by an interface, but + whenever the windowing toolbox provides a concrete implementation, + the corresponding interface is omitted from the toolbox.) To avoid + intersecting lines, the hierarchy is drawn for a cylindrical surface; + lines from @scheme[subarea<%>] and @scheme[subwindow<%>] wrap from + the left edge of the diagram to the right edge. + +@diagram->table[windowing-diagram] + +Menu bars, menus, and menu items are graphical elements, but not areas + (i.e., they do not have all of the properties that are common to + areas, such as an adjustable graphical size). Instead, the menu + classes form a separate container--containee hierarchy: + +@itemize{ + + @item{@deftech{Menu Item Containers} + + @itemize{ + + @item{@scheme[menu-bar%] --- a @deftech{menu bar} is a top-level + collection of menus that are associated with a frame.} + + @item{@scheme[menu%] --- a @deftech{menu} contains a set of menu + items. The menu can appear in a menu bar, in a popup menu, or as a + submenu in another menu.} + + @item{@scheme[popup-menu%] --- a @deftech{popup menu} is a + top-level menu that is dynamically displayed in a canvas or + editor canvas.} + + }} + + @item{@deftech{Menu Items} + + @itemize{ + + @item{@scheme[separator-menu-item%] --- a @deftech{separator} is + an unselectable line in a menu or popup menu.} + + @item{@scheme[menu-item%] --- a @deftech{menu item} is a selectable + text item in a menu. When the item is selected, its callback procedure + is invoked.} + + @item{@scheme[checkable-menu-item%] --- a @deftech{checkable menu + item} is a text item in a menu; the user selects a checkable menu + item to toggle a check mark next to the item.} + + @item{@scheme[menu%] --- a menu is a menu item as well as a menu + item container.} + + }} + +} + +The following diagram shows the complete type hierarchy for the menu +system: + +@diagram->table[menu-diagram] + +@; ------------------------------------------------------------------------ + +@section[#:tag "mr:containeroverview"]{Geometry Management} + +MrEd's geometry management makes it easy to design windows that look + right on all platforms, despite different graphical representations + of GUI elements. Geometry management is based on containers; each + container arranges its children based on simple constraints, such as + the current size of a frame and the natural size of a button. + +The built-in container classes include horizontal panels (and panes), + which align their children in a row, and vertical panels (and panes), + which align their children in a column. By nesting horizontal and + vertical containers, a programmer can achieve most any layout. For + example, we can construct a dialog with the following shape: + +@verbatim[ +#< get-graphical-min-size], depends on the + platform, the label of the containee (for a control), and style + attributes specified when creating the containee. For example, a + button's minimum graphical size ensures that the entire text of the + label is visible. The graphical minimum size of a control (such as a + button) cannot be changed; it is fixed at creation time. (A control's + minimum size is @italic{not} recalculated when its label is changed.) + The graphical minimum size of a panel or pane depends on the total + minimum size of its children and the way that they are arranged. + +To select a size for a containee, its parent container considers the + containee's @deftech{requested minimum size} rather than its + graphical minimum size (assuming the requested minimum is larger than + the graphical minimum). Unlike the graphical minimum, the requested + minimum size of a containee can be changed by a programmer at any + time using the @method[area<%> min-width] and + @method[area<%> min-height] methods. + +Unless a containee is stretchable (in a particular direction), it + always shrinks to its minimum size (in the corresponding + direction). Otherwise, containees are stretched to fill all available + space in a container. Each containee begins with a default + stretchability. For example, buttons are not initially stretchable, + whereas a one-line text field is initially stretchable in the + horizontal direction. A programmer can change the stretchability of a + containee at any time using the @method[area<%> stretchable-width] + and @method[area<%> stretchable-height] methods. + +A @deftech{margin} is space surrounding a containee. Each containee's + margin is independent of its minimum size, but from the container's + point of view, a margin effectively increases the minimum size of the + containee. For example, if a button has a vertical margin of + @scheme[2], then the container must allocate enough room to leave two + pixels of space above and below the button, in addition to the space + that is allocated for the button's minimum height. A programmer can + adjust a containee's margin with @method[subarea<%> horiz-margin] and + @method[subarea<%> vert-margin]. The default margin is @scheme[2] for + a control, and @scheme[0] for any other type of containee. + +In practice, the @tech{requested minimum size} and @tech{margin} of a + control are rarely changed, although they are often changed for a + canvas. @tech{Stretchability} is commonly adjusted for any type of + containee, depending on the visual effect desired by the programmer. + + +@subsection[#:tag "mr:containers"]{Containers} + +A container has the following properties: + +@itemize{ + + @item{a list of (non-deleted) children containees;} + + @item{a requested minimum width and a requested minimum height;} + + @item{a spacing used between the children;} + + @item{a border margin used around the total set of children;} + + @item{horizontal and vertical stretchability (on or off); and} + + @item{an alignment setting for positioning leftover space.} + +} + +These properties are factored into the container's calculation of its + own size and the arrangement of its children. For a container that is + also a containee (e.g., a panel), the container's requested minimum + size and stretchability are the same as for its containee aspect. + +A containee's parent container is specified when the containee is + created, and the parent cannot be changed. However, a containee + window can be @tech{hidden} or @tech{deleted} within its parent + container (but a non-window containee cannot be @tech{hidden} or + @tech{deleted}): + +@itemize{ + + @item{A @deftech{hidden} child is invisible to the user, but space is + still allocated for each hidden child within a container. To hide or + show a child, call the child's @method[window<%> show] method.} + + @item{A @deftech{deleted} child is hidden @italic{and} ignored by + container as it arranges its other children, so no space is reserved + in the container for a deleted child. To make a child deleted or + non-deleted, call the container's @method[area-container<%> + delete-child] or @method[area-container<%> add-child] method (which + calls the child's @method[window<%> show] method).} + +} + +When a child is created, it is initially shown and non-deleted. A + deleted child is subject to garbage collection when no external + reference to the child exists. A list of non-deleted children (hidden + or not) is available from a container through its + @method[area-container<%> get-children] method. + +The order of the children in a container's non-deleted list is + significant. For example, a vertical panel puts the first child in + its list at the top of the panel, and so on. When a new child is + created, it is put at the end of its container's list of + children. The order of a container's list can be changed dynamically + via the @method[area-container<%> change-children] method. (The + @method[area-container<%> change-children] method can also be used to + activate or deactivate children.) + +The @tech{graphical minimum size} of a container, as reported by + @method[area<%> get-graphical-min-size], is calculated by combining + the minimum sizes of its children (summing them or taking the + maximum, as appropriate to the layout strategy of the container) + along with the spacing and border margins of the container. A larger + minimum may be specified by the programmer using @method[area<%> + min-width] and @method[area<%> min-height] methods; when the computed + minimum for a container is larger than the programmer-specified + minimum, then the programmer-specified minimum is ignored. + +A container's spacing determines the amount of space left between + adjacent children in the container, in addition to any space required + by the children's margins. A container's border margin determines the + amount of space to add around the collection of children; it + effectively decreases the area within the container where children + can be placed. A programmer can adjust a container's border and + spacing dynamically via the @method[area-container<%> border] and + @method[area-container<%> spacing] methods. The default border and + spacing are @scheme[0] for all container types. + +Because a panel or pane is a containee as well as a container, it has + a containee margin in addition to its border margin. For a panel, + these margins are not redundant because the panel can have a + graphical border; the border is drawn inside the panel's containee + margin, but outside the panel's border margin. + +For a top-level-window container, such as a frame or dialog, the + container's stretchability determines whether the user can resize the + window to something larger than its minimum size. Thus, the user + cannot resize a frame that is not stretchable. For other types of + containers (i.e., panels and panes), the container's stretchability + is its stretchability as a containee in some other container. All + types of containers are initially stretchable in both + directions---except instances of @scheme[grow-box-spacer-pane%], + which is intended as a lightweight spacer class rather than a useful + container class---but a programmer can change the stretchability of + an area at any time via the @method[area<%> stretchable-width] and + @method[area<%> stretchable-height] methods. + +The alignment specification for a container determines how it + positions its children when the container has leftover space. (A + container can only have leftover space in a particular direction when + none of its children are stretchable in that direction.) For example, + when the container's horizontal alignment is @indexed-scheme['left], + the children are left-aligned in the container and leftover space is + accumulated to the right. When the container's horizontal alignment + is @indexed-scheme['center], each child is horizontally centered in + the container. A container's alignment is changed with the + @method[area-container<%> set-alignment] method. + +@subsection[#:tag "mr:new-containers"]{Defining New Types of Containers} + +Although nested horizontal and vertical containers can express most + layout patterns, a programmer can define a new type of container with + an explicit layout procedure. A programmer defines a new type of + container by deriving a class from @scheme[panel%] or @scheme[pane%] + and overriding the @method[area-container<%> container-size] and + @method[area-container<%> place-children] methods. The + @method[area-container<%> container-size] method takes a list of size + specifications for each child and returns two values: the minimum + width and height of the container. The @method[area-container<%> + place-children] method takes the container's size and a list of size + specifications for each child, and returns a list of sizes and + placements (in parallel to the original list). + +An input size specification is a list of four values: + +@itemize{ + @item{the child's minimum width;} + @item{the child's minimum height;} + @item{the child's horizontal stretchability (@scheme[#t] means stretchable, @scheme[#f] means not stretchable); and} + @item{the child's vertical stretchability.} +} + +For @method[area-container<%> place-children], an output + position and size specification is a list of four values: + +@itemize{ + @item{the child's new horizontal position (relative to the parent);} + @item{the child's new vertical position;} + @item{the child's new actual width;} + @item{the child's new actual height.} +} + +The widths and heights for both the input and output include the + children's margins. The returned position for each child is + automatically incremented to account for the child's margin in + placing the control. + + +@section[#:tag "mr:mouseandkey"]{Mouse and Keyboard Events} + +Whenever the user moves the mouse, clicks or releases a mouse button, + or presses a key on the keyboard, an event is generated for some + window. The window that receives the event depends on the current + state of the graphic display: + +@itemize{ + + @item{@index['("mouse events" "overview")]{The} receiving window of a + mouse event is usually the window under the cursor when the mouse is + moved or clicked. If the mouse is over a child window, the child + window receives the event rather than its parent. + + When the user clicks in a window, the window ``grabs'' the mouse, so + that @italic{all} mouse events go to that window until the mouse + button is released (regardless of the location of the cursor). As a + result, a user can click on a scrollbar thumb and drag it without + keeping the cursor strictly inside the scrollbar control. + + A mouse button-release event is normally generated for each mouse + button-down event, but a button-release event might get dropped. For + example, a modal dialog might appear and take over the mouse. More + generally, any kind of mouse event can get dropped in principle, so + avoid algorithms that depend on precise mouse-event sequences. For + example, a mouse tracking handler should reset the tracking state + when it receives an event other than a dragging event.} + + @item{@index['("keyboard focus" "overview")]{@index['("keyboard + events" "overview")]{The}} receiving window of a keyboard event is + the window that owns the @deftech{keyboard focus} at the time of the + event. Only one window owns the focus at any time, and focus + ownership is typically displayed by a window in some manner. For + example, a text field control shows focus ownership by displaying a + blinking caret. + + Within a top-level window, only certain kinds of subwindows can have + the focus, depending on the conventions of the platform. Furthermore, + the subwindow that initially owns the focus is platform-specific. A + user can moves the focus in various ways, usually by clicking the + target window. A program can use the @method[window<%> focus] method + to move the focus to a subwindow or to set the initial focus. + + Under X, a @indexed-scheme['wheel-up] or @indexed-scheme['wheel-down] + event may be sent to a window other than the one with the keyboard + focus, because X generates wheel events based on the location of the + mouse pointer. + + A key-press event may correspond to either an actual key press or an + auto-key repeat. Multiple key-press events without intervening + key-release events normally indicate an auto-key. Like any input + event, however, key-release events sometimes get dropped (e.g., due + to the appearance of a modal dialog).} + +} + +Controls, such as buttons and list boxes, handle keyboard and mouse + events automatically, eventually invoking the callback procedure that + was provided when the control was created. A canvas propagates mouse + and keyboard events to its @method[canvas<%> on-event] and + @method[canvas<%> on-char] methods, respectively. + +@index['("events" "delivery")]{A} mouse and keyboard event is + delivered in a special way to its window. Each ancestor of the + receiving window gets a chance to intercept the event through the + @method[window<%> on-subwindow-event] and @method[window<%> + on-subwindow-char] methods. See the method descriptions for more + information. + +@index['("keyboard focus" "navigation")]{The} default + @method[window<%> on-subwindow-char] method for a top-level window + intercepts keyboard events to detect menu-shortcut events and + focus-navigation events. See @xmethod[frame% on-subwindow-char] and + @xmethod[dialog% on-subwindow-char] for details. Certain OS-specific + key combinations are captured at a low level, and cannot be + overridden. For example, under Windows and X, pressing and releasing + Alt always moves the keyboard focus to the menu bar. Similarly, + Alt-Tab switches to a different application under Windows. (Alt-Space + invokes the system menu under Windows, but this shortcut is + implemented by @method[top-level-window<%> on-system-menu-char], + which is called by @xmethod[frame% on-subwindow-char] and + @xmethod[dialog% on-subwindow-char].) + +@; ------------------------------------------------------------------------ + +@section[#:tag "mr:eventspaceinfo"]{Event Dispatching and Eventspaces} + +@section-index['("events" "dispatching")] + +A graphical user interface is an inherently multi-threaded system: one + thread is the program managing windows on the screen, and the other + thread is the user moving the mouse and typing at the keyboard. GUI + programs typically use an @deftech{event queue} to translate this + multi-threaded system into a sequential one, at least from the + programmer's point of view. Each user action is handled one at a + time, ignoring further user actions until the previous one is + completely handled. The conversion from a multi-threaded process to a + single-threaded one greatly simplifies the implementation of GUI + programs. + +Despite the programming convenience provided by a purely sequential + event queue, certain situations require a less rigid dialog with + the user: + +@itemize{ + + @item{@italic{Nested event handling:} In the process of handling an + event, it may be necessary to obtain further information from the + user. Usually, such information is obtained via a modal dialog; in + whatever fashion the input is obtained, more user events must be + received and handled before the original event is completely + handled. To allow the further processing of events, the handler for + the original event must explicitly @deftech{yield} to the + system. Yielding causes events to be handled in a nested manner, + rather than in a purely sequential manner.} + + @item{@italic{Asynchronous event handling:} An application may + consist of windows that represent independent dialogs with the + user. For example, a drawing program might support multiple drawing + windows, and a particularly time-consuming task in one window (e.g., + a special filter effect on an image) should not prevent the user from + working in a different window. Such an application needs sequential + event handling for each individual window, but asynchronous + (potentially parallel) event handling across windows. In other words, + the application needs a separate event queue for each window, and a + separate event-handling thread for each event queue.} + +} + +In MrEd, an @deftech{eventspace} is a context for processing GUI + events. Each eventspace maintains its own queue of events, and events + in a single eventspace are dispatched sequentially by a designated + @deftech{handler thread}. An event-handling procedure running in this + handler thread can yield to the system by calling @scheme[yield], in + which case other event-handling procedures may be called in a nested + (but single-threaded) manner within the same handler thread. Events + from different eventspaces are dispatched asynchronously by separate + handler threads. + +@index['("dialogs" "modal")]{When} a frame or dialog is created + without a parent, it is associated with the @tech{current eventspace} + as described in @secref["mr:currenteventspace"]. Events for a + top-level window and its descendants are always dispatched in the + window's eventspace. Every dialog is modal; a dialog's + @method[dialog% show] method implicitly calls @scheme[yield] to + handle events while the dialog is shown. (See also + @secref["mr:espacethreads"] for information about threads and modal + dialogs.) Furthermore, when a modal dialog is shown, the system + disables all other top-level windows in the dialog's eventspace, but + windows in other eventspaces are unaffected by the modal dialog. + (Disabling a window prevents mouse and keyboard events from reaching + the window, but other kinds of events, such as update events, are + still delivered.) + + +@subsection{Event Types and Priorities} + +@section-index['("events" "timer")] +@section-index['("events" "explicitly queued")] + +In addition to events corresponding to user and windowing actions, + such as button clicks, key presses, and updates, the system + dispatches two kinds of internal events: @tech{timer events} and + @tech{explicitly queued events}. + +@tech{Timer events} are created by instances of @scheme[timer%]. When + a timer is started and then expires, the timer queues an event to + call the timer's @method[timer% notify] method. Like a top-level + window, each timer is associated with a particular eventspace (the + @tech{current eventspace} as described in + @secref["mr:currenteventspace"]) when it is created, and the timer + queues the event in its eventspace. + +@deftech{Explicitly queued} events are created with + @scheme[queue-callback], which accepts a callback procedure to handle + the event. The event is enqueued in the current eventspace at the + time of the call to @scheme[queue-callback], with either a high or + low priority as specified by the (optional) second argument to + @scheme[queue-callback]. + +An eventspace's event queue is actually a priority queue with events + sorted according to their kind, from highest-priority (dispatched + first) to lowest-priority (dispatched last): + +@itemize{ + + @item{The highest-priority events are high-priority events installed + with @scheme[queue-callback].} + + @item{Timer events have the second-highest priority.} + + @item{Graphical events, such as mouse clicks or window updates, have + the second-lowest priority.} + + @item{The lowest-priority events are low-priority events installed + with @scheme[queue-callback].} + +} + +Although a programmer has no direct control over the order in which + events are dispatched, a programmer can control the timing of + dispatches by setting the event dispatch handler via the + @scheme[event-dispatch-handler] parameter. This parameter and other + eventspace procedures are described in more detail in + @secref["mr:eventspace-funcs"]. + + +@subsection[#:tag "mr:espacethreads"]{Eventspaces and Threads} + +When a new eventspace is created, a corresponding @deftech{handler + thread} is created for the eventspace. When the system dispatches an + event for an eventspace, it always does so in the eventspace's + handler thread. A handler procedure can create new threads that run + indefinitely, but as long as the handler thread is running a handler + procedure, no new events can be dispatched for the corresponding + eventspace. + +When a handler thread shows a dialog, the dialog's @method[dialog% + show] method implicitly calls @scheme[yield] for as long as the + dialog is shown. When a non-handler thread shows a dialog, the + non-handler thread simply blocks until the dialog is + dismissed. Calling @scheme[yield] with no arguments from a + non-handler thread has no effect. Calling @scheme[yield] with a + semaphore from a non-handler thread is equivalent to calling + @scheme[semaphore-wait]. + + +@subsection[#:tag "mr:currenteventspace"]{Creating and Setting the Eventspace} + +Whenever a frame, dialog, or timer is created, it is associated with + the eventspace specified by the @scheme[current-eventspace] parameter + @|SeeMzParam|. When the @scheme[current-eventspace] procedure is + called with no arguments, it returns the current eventspace value. + When @scheme[current-eventspace] is called with an eventspace value, + it changes the current eventspace to the provided one. + +The @scheme[make-eventspace] procedure creates a new + eventspace. The following example creates a new eventspace and a new + frame in the eventspace (the @scheme[parameterize] syntactic form + temporary sets a parameter value): + +@schemeblock[ +(let ([new-es (make-eventspace)]) + (parameterize ([current-eventspace new-es]) + (new frame% [label "Example"]))) +] + +When an eventspace is created, it is placed under the management of + the @tech{current custodian}. When a custodian shuts down an + eventspace, all frames and dialogs associated with the eventspace are + destroyed (without calling @method[top-level-window<%> can-close?] + or @xmethod[top-level-window% on-close]), all timers in the + eventspace are stopped, and all enqueued callbacks are removed. + Attempting to create a new window, timer, or explicitly queued event + in a shut-down eventspace raises the @scheme[exn:misc] exception. + +An eventspace is a @techlink{synchronizable event} (not to be confused + with a GUI event), so it can be used with @scheme[sync]. As a + synchronizable event, an eventspace is in a blocking state when a + frame is visible, a timer is active, a callback is queued, or a + @scheme[menu-bar%] is created with a @scheme['root] parent. (Note + that the blocking state of an eventspace is unrelated to whether an + event is ready for dispatching.) + +@subsection[#:tag "mr:evtcontjump"]{Exceptions and Continuation Jumps} + +Whenever the system dispatches an event, the call to the handler + procedure is wrapped so that full continuation jumps are not allowed + to escape from the dispatch, and escape continuation jumps are + blocked at the dispatch site. The following @scheme[block] procedure + illustrates how the system blocks escape continuation jumps: + +@def+int[ +(define (block f) + (code:comment #, @t{calls @scheme[f] and returns void if @scheme[f] tries to escape}) + (let ([done? #f]) + (let/ec k + (dynamic-wind + void + (lambda () (begin0 (f) (set! done? #t))) + (lambda () (unless done? (k (void)))))))) + +(block (lambda () 5)) +(let/ec k (block (lambda () (k 10)))) +(let/ec k ((lambda () (k 10))) 11) +(let/ec k (block (lambda () (k 10))) 11) +] + +Calls to the event dispatch handler are also protected with + @scheme[block]. + +This blocking of continuation jumps complicates the interaction + between @scheme[with-handlers] and @scheme[yield] (or the default + event dispatch handler). For example, in evaluating the expression + +@schemeblock[ +(with-handlers ([(lambda (x) #t) + (lambda (x) (error "error during yield"))]) + (yield)) +] + +the @scheme["error during yield"] handler is @italic{never} called, + even if a callback procedure invoked by @scheme[yield] raises an + exception. The @scheme[with-handlers] expression installs an + exception handler that tries to jump back to the context of the + @scheme[with-handlers] expression before invoking a handler + procedure; this jump is blocked by the dispatch within + @scheme[yield], so @scheme["error during yield"] is never + printed. Exceptions during @scheme[yield] are ``handled'' in the + sense that control jumps out of the event handler, but @scheme[yield] + may dispatch another event rather than escaping or returning. + +The following expression demonstrates a more useful way to handle + exceptions within @scheme[yield], for the rare cases where + such handling is useful: + +@schemeblock[ +(let/ec k + (call-with-exception-handler + (lambda (x) + (error "error during yield") + (k)) + (lambda () + (yield)))) +] + +This expression installs an exception handler that prints an error + message @italic{before} trying to escape. Like the continuation escape + associated with @scheme[with-handlers], the escape to @scheme[k] never + succeeds. Nevertheless, if an exception is raised by an event + handler during the call to @scheme[yield], an error message is + printed before control returns to the event dispatcher within + @scheme[yield]. diff --git a/collects/scribblings/gui/windowing.scrbl b/collects/scribblings/gui/windowing.scrbl index 59bc8aa4e5..259b682527 100644 --- a/collects/scribblings/gui/windowing.scrbl +++ b/collects/scribblings/gui/windowing.scrbl @@ -1,151 +1,11 @@ #reader(lib "docreader.ss" "scribble") @require["common.ss"] +@require["diagrams.ss"] @title[#:tag "mr:windowing" #:style 'toc]{Windowing Toolbox} -The windowing toolbox. - @local-table-of-contents[] -@require["area-intf.scrbl"] -@require["area-container-intf.scrbl"] -@require["area-container-window-intf.scrbl"] -@require["button-class.scrbl"] -@require["canvas-intf.scrbl"] -@require["canvas-class.scrbl"] -@require["check-box-class.scrbl"] -@require["choice-class.scrbl"] -@require["clipboard-client-class.scrbl"] -@require["clipboard-intf.scrbl"] -@require["combo-field-class.scrbl"] -@require["control-intf.scrbl"] -@require["control-event-class.scrbl"] -@require["cursor-class.scrbl"] -@require["dialog-class.scrbl"] -@require["event-class.scrbl"] -@require["frame-class.scrbl"] -@require["gauge-class.scrbl"] -@require["group-box-panel-class.scrbl"] -@require["grow-box-spacer-pane-class.scrbl"] -@require["horizontal-pane-class.scrbl"] -@require["horizontal-panel-class.scrbl"] -@require["key-event-class.scrbl"] -@require["labelled-menu-item-intf.scrbl"] -@require["list-box-class.scrbl"] -@require["list-control-intf.scrbl"] -@require["menu-class.scrbl"] -@require["menu-bar-class.scrbl"] -@require["menu-item-intf.scrbl"] -@require["menu-item-class.scrbl"] -@require["menu-item-container-intf.scrbl"] -@require["message-class.scrbl"] -@require["mouse-event-class.scrbl"] -@require["pane-class.scrbl"] -@require["panel-class.scrbl"] -@require["subarea-intf.scrbl"] -@require["subwindow-intf.scrbl"] -@require["top-level-window-intf.scrbl"] -@require["vertical-pane-class.scrbl"] -@require["vertical-panel-class.scrbl"] -@require["window-intf.scrbl"] - -@include-class[area<%>] -@include-class[area-container<%>] -@include-class[area-container-window<%>] -@include-class[button%] -@include-class[canvas<%>] -@include-class[canvas%] -@include-class[check-box%] -@include-class[choice%] -@include-class[clipboard-client%] -@include-class[clipboard<%>] -@include-class[combo-field%] -@include-class[control<%>] -@include-class[control-event%] -@include-class[cursor%] -@include-class[dialog%] -@include-class[event%] -@include-class[frame%] -@include-class[gauge%] -@include-class[group-box-panel%] -@include-class[grow-box-spacer-pane%] -@include-class[horizontal-pane%] -@include-class[horizontal-panel%] -@include-class[key-event%] -@include-class[labelled-menu-item<%>] -@include-class[list-control<%>] -@include-class[list-box%] -@include-class[menu%] -@include-class[menu-bar%] -@include-class[menu-item<%>] -@include-class[menu-item%] -@include-class[menu-item-container<%>] -@include-class[message%] -@include-class[mouse-event%] -@include-class[pane%] -@include-class[panel%] -@include-class[subarea<%>] -@include-class[subwindow<%>] -@include-class[top-level-window<%>] -@include-class[vertical-pane%] -@include-class[vertical-panel%] -@include-class[window<%>] - -@include-section["dialog-funcs.scrbl"] -@include-section["eventspace-funcs.scrbl"] -@include-section["system-menu-funcs.scrbl"] -@include-section["miscwin-funcs.scrbl"] - -@;{ -@require["button-class.scrbl"] -@require["canvas-intf.scrbl"] -@require["canvas-class.scrbl"] -@require["check-box-class.scrbl"] -@require["list-control-intf.scrbl"] -@require["choice-class.scrbl"] -@require["control-event-class.scrbl"] -@require["scroll-event-class.scrbl"] -@require["cursor-class.scrbl"] -@require["dialog-class.scrbl"] -@require["event-class.scrbl"] -@require["top-level-window-intf.scrbl"] -@require["frame-class.scrbl"] -@require["gauge-class.scrbl"] -@require["control-intf.scrbl"] -@require["key-event-class.scrbl"] -@require["list-box-class.scrbl"] -@require["menu-item-intf.scrbl"] -@require["separator-menu-item-class.scrbl"] -@require["labelled-menu-item-intf.scrbl"] -@require["selectable-menu-item-intf.scrbl"] -@require["menu-item-class.scrbl"] -@require["checkable-menu-item-class.scrbl"] -@require["menu-item-container-intf.scrbl"] -@require["menu-class.scrbl"] -@require["popup-menu-class.scrbl"] -@require["menu-bar-class.scrbl"] -@require["message-class.scrbl"] -@require["mouse-event-class.scrbl"] -@require["panel-class.scrbl"] -@require["horizontal-panel-class.scrbl"] -@require["vertical-panel-class.scrbl"] -@require["tab-panel-class.scrbl"] -@require["group-box-panel-class.scrbl"] -@require["pane-class.scrbl"] -@require["horizontal-pane-class.scrbl"] -@require["vertical-pane-class.scrbl"] -@require["grow-box-spacer-pane-class.scrbl"] -@require["area-container-window-intf.scrbl"] -@require["radio-box-class.scrbl"] -@require["slider-class.scrbl"] -@require["text-field-class.scrbl"] -@require["combo-field-class.scrbl"] -@require["timer-class.scrbl"] -@require["area-intf.scrbl"] -@require["area-container-intf.scrbl"] -@require["subarea-intf.scrbl"] -@require["window-intf.scrbl"] -@require["subwindow-intf.scrbl"] -@require["clipboard-intf.scrbl"] -@require["clipboard-client-class.scrbl"] -} +@include-section["win-overview.scrbl"] +@include-section["win-classes.scrbl"] +@include-section["win-funcs.scrbl"]