new gui reference work

svn: r7106
This commit is contained in:
Matthew Flatt 2007-08-16 18:48:47 +00:00
parent fcf31d7086
commit 33d7e3ded3
24 changed files with 1999 additions and 746 deletions

View File

@ -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

View File

@ -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)])

View File

@ -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)))

View File

@ -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)])

View File

@ -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

View File

@ -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{
}}}
}}

View File

@ -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
#<<DIAG
area<%>
______________________|_______________
| | |
subarea<%> window<%> area-container<%>
|____ _______|__________ |
| | | |
subwindow<%> area-container-window<%>
________|________ |
| | |
control<%> canvas<%> top-level-window<%>
DIAG
)
(define windowing-diagram
#<<DIAG
area<%>
_____________________|_______________
| | |
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
#<<DIAG
event% timer%
|- key-event% cursor%
|- mouse-event%
|- scroll-event% clipboard<%>
|- control-event% clipboard-client%
DIAG
)
(define menu-diagram
#<<DIAG
menu-item<%> menu-item-container<%>
| |
|- separator-menu-item% _____|___
|- labelled-menu-item<%> | |- menu-bar%
_________|_________ | |- popup-menu%
| | |
| menu%
|
|- selectable-menu-item<%>
|- menu-item%
|- checkable-menu-item%
DIAG
))

View File

@ -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.

View File

@ -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"]

View File

@ -1,11 +1,7 @@
#reader(lib "docreader.ss" "scribble")
@require["common.ss"]
@title{Eventspaces}
@title[#:tag "mr:eventspace-funcs"]{Eventspaces}
@defproc[(make-eventspace)
eventspace]{

View File

@ -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"]

View File

@ -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{
}}}
}}

View File

@ -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|}.
}}}

View File

@ -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].
}}

View File

@ -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.
}}

View File

@ -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.
}}

View File

@ -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|}.
}}}
}}

View File

@ -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|.
}}

View File

@ -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}]
}}

View File

@ -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{
}}}
}}

View File

@ -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<%>]

View File

@ -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"]

View File

@ -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[
#<<IMG
------------------------------------------------------
| ------------------------------------- |
| Your name: | | |
| ------------------------------------- |
| -------- ---- |
| ( Cancel ) ( OK ) |
| -------- ---- |
------------------------------------------------------
IMG
]
with the following program:
@schemeblock[
(code:comment #, @t{Create a dialog})
(define dialog (instantiate dialog\% ("Example")))
(code:comment #, @t{Add a text field to the dialog})
(new text-field% [parent dialog] [label "Your name"])
(code:comment #, @t{Add a horizontal panel to the dialog, with centering for buttons})
(define panel (new horizontal-panel% [parent dialog]
[alignment '(center center)]))
(code:comment #, @t{Add @onscreen{Cancel} and @onscreen{Ok} buttons to the horizontal panel})
(new button% [parent parent] [label "Cancel"])
(new button% [parent parent] [label "Ok"])
(code:comment #, @t{Show the dialog})
(send dialog #,(:: dialog% show) #t)
]
Each container arranges its children using the natural size of each
child, which usually depends on instantiation parameters of the
child, such as the label on a button or the number of choices in a
radio box. In the above example, the dialog stretches horizontally to
match the minimum width of the text field, and it stretches
vertically to match the total height of the field and the
buttons. The dialog then stretches the horizontal panel to fill the
bottom half of the dialog. Finally, the horizontal panel uses the sum
of the buttons' minimum widths to center them horizontally.
As the example demonstrates, a stretchable container grows to fill its
environment, and it distributes extra space among its stretchable
children. By default, panels are stretchable in both directions,
whereas buttons are not stretchable in either direction. The
programmer can change whether an individual GUI element is
stretchable.
The following subsections describe the container system in detail,
first discussing the attributes of a containee in
@secref["mr:containees"], and then describing
the attributes of a container in
@secref["mr:containers"]. In addition to the
built-in vertical and horizontal containers, programmers can define
new types of containers as discussed in the final subsection,
@secref["mr:new-containers"].
@subsection[#:tag "mr:containees"]{Containees}
Each @tech{containee}, or child, has the following properties:
@itemize{
@item{a @deftech{graphical minimum width} and a @deftech{graphical minimum height};}
@item{a @deftech{requested minimum width} and a @deftech{requested minimum height};}
@item{horizontal and vertical @deftech{stretchability} (on or off); and}
@item{horizontal and vertical @deftech{margins}.}
}
A @tech{container} arranges its children based on these four
properties of each @tech{containee}. A @tech{containee}'s parent
container is specified when the @tech{containee} is created, and the
parent cannot be changed. However, a @tech{containee} can be
@tech{hidden} or @tech{deleted} within its parent, as described in
@secref["mr:containers"].
The @deftech{graphical minimum size} of a particular containee, as
reported by @method[area<%> 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].

View File

@ -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"]