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
frame% class:
; Show the frame by calling its show method
(send frame show #t)
; Make a frame by instantiating the frame% class
(define frame (make-object frame% "Example"))
The built-in classes provide various mechanisms for handling GUI
events. For example, when instantiating the 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:
; Make a static text message in the frame
(define msg (make-object message% "No events so far..." frame))
; Make a button in the frame
(make-object button% "Click Me" frame
; Callback procedure for a button click
(lambda (button event) (send msg set-label "Button click")))
; Show the frame by calling its show method
(send frame show #t)
; Make a frame by instantiating the frame% class
(define frame (make-object frame% "Example"))
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 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
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:
; Make a canvas that handles events in the frame
(make-object my-canvas% frame)
; Derive a new canvas (a generic drawing window) class to handle events
(define my-canvas%
(class canvas% ; The base class is canvas%
(frame) ; one extra argument, frame, is provided to make-object
(override
; Method to handle mouse events
[on-event (lambda (event) (send msg set-label "Canvas mouse"))]
; Method to handle keyboard events
[on-char (lambda (event) (send msg set-label "Canvas keyboard"))])
; Call the superclass initialization, providing frame
(sequence (super-init frame))))
(It may be neceesary to enlarge the frame to see the new canvas.)
Moving the cursor over the canvas calls the canvas's
on-event method with an object representing
a motion event. Clicking on the canvas calls
on-event. While the canvas has the keyboard
focus, typing on the keyboard invokes the canvas's
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
Pause button:
(make-object button% "Pause" frame (lambda (button event) (sleep 5)))
After the user clicks Pause, the entire frame becomes
unresponsive for five seconds; the system cannot dispatch more events
until the call to sleep returns. For more information about
event dispatching, see Eventspaces.
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 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:
(define panel (make-object horizontal-panel% frame))
(make-object button% "Left" panel
(lambda (button event) (send msg set-label "Left button click")))
(make-object button% "Right" panel
(lambda (button event) (send msg set-label "Right button click")))
For more information about window layout and containers, see
Geometry Management.