197

Chapter 14:

GUI Toolbox Modules

The medium is the metaphor.

- Neil Postman

Possibly the biggest reason people use Mac OS is for its renowned ease of
use. This manifests itself in every aspect of the Macintosh, both hardware
and software, but software is the most profound part of this ease-of-use
experience for most users.

The Mac OS, due in no small part to the tremendous work Apple has done in
human interface research, has a nicer Graphical User Interface (GUI) than
its Microsoft Windows or X11
1counterparts. The widgets are easier on the
eye and the menus and dialog boxes are more intuitive. Overall, the entire
experience is more integrated.

Thanks to several of MacPerl's toolbox modules, this aspect of Mac OS pro-
gramming can be extended to your MacPerl programs as well. And, if you
program Mac applications in C or Pascal, these modules help turn MacPerl
into a powerful prototyping tool.

There are volumes of information in the Inside Macintoshseries detailing
how to use windows, menus, dialogs, and other GUI features of Mac OS. We
can't cover all of this information here. Consequently, this chapter will
function primarily as a "cookbook", introducing some common tasks and

IMAGE imgs/325.GUI01.gif

1X11 is a windowing system that is common on Unix systems.


IMAGE imgs/325.GUI02.gif

concepts that can be used directly, reworked into other programs, or built
upon for more complex tasks.

The standard toolbox module disclaimers apply: these operations should
not be used without access to Inside Macintosh. In addition, these modules
are not as well-tested as some of the others; if you are venturing into more
obscure portions of the modules, bugs and misfeatures become more likely.

We recommend that you read Macintosh Human Interface Guidelines (a
part of Inside Macintosh), which is designed to help "link the philosophy
behind the Macintosh interface to the actual implementation of interface
elements."
2

Windows

GUI programming in MacPerl often requires multiple modules for simple
tasks. For example,
Mac::Windowsmust be used to create a window,
Mac::QuickDrawto place it, Mac::Fontsto put some text in it, and
Mac::Eventsto respond to mouse clicks on it.

The basic building block of the window, and many other GUI elements, is
the rectangle, which is accessed via the
Rectclass.3It is comprised of four
numbers, which comprise two (X, Y) coordinate sets. X is a horizontal offset
in pixels (picture elements, i.e., dots on the screen); Y is a vertical offset.

The first coordinate set defines the top left corner of the rectangle (e.g., 50,
50), which is the starting point of the rectangle in the frame (which is, for
a window, your screen). The second set (e.g., 300, 100) defines the bottom
right corner of the rectangle. The other two corners are extrapolated auto-
matically.
4

50,50

300, 50

IMAGE imgs/325.GUI03.gif

50, 100

300, 100

IMAGE imgs/325.GUI01.gif

2For the sake of example, we may break one or more of the guidelines proffered there.
3From Mac::QuickDraw.
4The created rectangle's four elements can be accessed via $rect->left(), $rect->
top(), $rect->right(), and $rect->bottom().


IMAGE imgs/325.GUI05.gif

The MacWindowclass (from Mac::Windows), with its new()constructor,
does all of the background magic behind window handling. It accepts five
primary arguments to create a window: a boundary (a rectangle), title bar
text, a visibility boolean, a window style, and a close-box boolean.
5

The script below will stay open until the window's close box is clicked.
WaitNextEvent()waits for a new event to come; then the loop continues.
If the window is gone after the event arrives (e.g., the close box is clicked),
$win->window()evaluates to false and the whileloop ends.

If the window is still open when the script ends, the ENDblock will remove
it. Normally this would be the result of a premature ending to the script,
because of an error or a
Command-. key sequence.6

#!perl
use strict;
use Mac::Windows;
use Mac::QuickDraw;
use Mac::Events;
my($style, $title, $win, $winr);

$style = floatProc();
$title = 'Welcome to MacPerl';
$winr = Rect->new(75, 75, 425, 250);
$win = MacWindow->new(
$winr, $title, 1, $style, 1
);
$win->sethook('redraw' => \&draw_it);

while ($win->window()) {
WaitNextEvent();
}

END {
$win->dispose() if (defined($win));
}

sub draw_it {}

IMAGE imgs/325.GUI01.gif

5If no close box can be used with a given style, this parameter has no effect.
6We'll see how to handle keyboard and mouse events, later in the chapter.


IMAGE imgs/325.GUI07.gif

Of course, this window is not very interesting. It should display items, res-
pond to other events beyond clicking on the close box, etc. Nonetheless, it
does contain the basic framework for the window; the additional function-
ality can be written into the routine
draw_it().

Note that, instead of calling draw_it()directly, the program sets a hook
with
$win->sethook(). This attaches the subroutine to the window.7

Window Definitions

There are many different styles of windows,8as you probably know if you
have used Mac OS much.
floatProc()is just one that we decided to use.
Before we add more complexity to our example window, though, we should
explore the features and characteristics of the available window styles.

Some windows have the title bar on the side (or no title bar at all), some
zoom, some are resizable, and some have no close box. Pick the style of win-
dow that best suits the task at hand. Don't forget that the rectangle defines
the usable area of a window, not including any borders or title bar. So, a
window with no title bar might fit in a place that the same-sized window
with a title bar might not.
9

There are three basic types of windows: dialog windows, standard windows,
and floating windows. The tables below give information about each win-
dow style. The styles are accessed via subroutines that return integers repre-
senting that style (the integers given in the table can be used in place of the
subroutines, of course).

Also given in the tables are five characteristics of the window. A bullet (*)
or a dash (-) is used to indicate whether the window does or does not have a
title bar (T), a drag bar (D), a close box (C), or a grow box (G).
10

IMAGE imgs/325.GUI01.gif

7This is an example of setting an event handler.
8The toolbox modules even allow custom windows, but we won't discuss that here.
There is an example of custom windows in the MacPerl distribution.
9Windows may appear differently on different versions of Mac OS or if extensions
such as Kaleidoscope are installed. The windows shown here are the Mac OS 8
windows, and are 130 pixels wide by 40 pixels tall.
10You can put a grow box on a window that cannot use it, and it will be non-
functional. To remove a non-functional grow box, use $win->sethook
('drawgrowicon' => sub{})
.


IMAGE imgs/325.GUI09.gif

If the window has a zoom box (Z), then the zoom box's behavior is noted. It
either toggles between the current size/position and full screen (A), or the
last size/position and the default size/position (B).

All of the windows have borders (B), using one of four types: the normal Mac
OS border (N), a chiseled border (C), a simple shadowed border (S), or a
one-pixel border (P).

Dialog Windows

Dialog windows have no zoom, grow, or close box, and only the movable
DBoxProc
is movable. Dialog windows are used primarily for dialog box
functions, discussed later in this chapter.

Because these windows do not have close boxes, they must be closed in some
other fashion. If there is a call in the
ENDblock to $win->dispose(), it
can be closed with
Command-., but normally some sort of eventwill close the
window, such as a click on a button, which sends a signal to the program that
can trigger a function. This is also discussed later in the chapter.

IMAGE imgs/325.GUI11.gif
IMAGE imgs/325.GUI10.gif
IMAGE imgs/325.GUI12.gif
IMAGE imgs/325.GUI13.gif

Subroutine Name

#

T

D

C

Z

G

B

dBoxProc
plainDBox
altDBoxProc
movableDBoxProc

1
2
3
5

---
---
---
**-

-
-
-
-

-
-
-
-

C
P
S
C


IMAGE imgs/325.GUI14.gif

Standard Windows

These windows are the standard ones that are used by most non-dialog Mac
OS window functions. Which one is used is determined by whether or not the
window needs a growor zoomfunction. The fifth window in this table,
rDoc
Proc
, is a special window that used to be used primarily by desk accessories.
It is rarely used anymore.

IMAGE imgs/325.GUI15.gif IMAGE imgs/325.GUI16.gif
IMAGE imgs/325.GUI17.gif IMAGE imgs/325.GUI18.gif
IMAGE imgs/325.GUI19.gif

Subroutine Name

#

T

D

C

Z

G

B

documentProc                       0   *  *  *  -  *  N
noGrowDocProc                      4   *  *  *  -  -  N
zoomDocProc                        8   *  *  *  A  *  N
zoomNoGrow                        12   *  *  *  A  -  P
rBoxProc                          16   *  *  *  -  -  P
		

Floating Windows

Floating windows are used primarily for special functions (e.g., palettes,
status, and help windows), that normally "float" on top of the other win-


IMAGE imgs/325.GUI20.gif

dows in an application. The "side" floating windows don't have a title bar,
as such; instead, they have a drag baron the left-hand side.

IMAGE imgs/325.GUI21.gif IMAGE imgs/325.GUI22.gif
IMAGE imgs/325.GUI23.gif IMAGE imgs/325.GUI24.gif
IMAGE imgs/325.GUI25.gif IMAGE imgs/325.GUI26.gif
IMAGE imgs/325.GUI27.gif IMAGE imgs/325.GUI28.gif
Subroutine Name                                                                #        T     D     C      Z     G      B

floatProc                      1985   *  *  *  -  -  N
floatGrowProc                  1987   *  *  *  -  *  N
floatZoomProc                  1987   *  *  *  B  -  N
floatZoomGrowProc              1989   *  *  *  B  *  N
floatSideProc                  1991   -  *  *  -  -  N
floatSideGrowProc              1993   -  *  *  -  *  N
floatSideZoomProc              1995   -  *  *  B  -  N
floatSideZoomGrowProc          1997   -  *  *  B  *  N
		

IMAGE imgs/325.GUI29.gif

Drawing And Text

The Mac::QuickDrawmodule can be used for drawing things, not just for
placing them with rectangles. There are all sorts of functions in the module
for drawing lines and shapes and for manipulating images.

Go Ahead, Draw Something

To draw a shape, we only need to add functionality to the draw_it()rou-
tine. Let's draw an oval in the window.

sub draw_it {
PaintOval(
Rect->new(105, 15, 245, 90)
);
}

IMAGE imgs/325.GUI30.gif

Whenever the redrawevent happens for the window - such as when the
window is moved or another window passes in front of it - the routine
draw
_it()
is executed, because that is the routine we attached to the redraw
event for this window.

PaintOval()paints an oval that is bounded by a specified rectangle. We
use
Rect->new()again, defining a rectangle relative to the enclosing
window (not to the screen, as was the case with the "window" rectangle).

Not all drawing requires rectangles, though. Lines just require a start and
end point.
MoveTo()and LineTo()are used, respectively, to move the
"pen" to a specified point and to draw a line to another point.


IMAGE imgs/325.GUI31.gif

can also be used to make a starting point for drawing text,11in conjunction
with
DrawString(). So, let's rewrite the draw_it()function.

sub draw_it {
MoveTo(120, 65);
LineTo(223, 65);

MoveTo(130, 55);
TextSize(20);
DrawString('MacPerl');
}

IMAGE imgs/325.GUI32.gif

A Dash of Color

Let's put the oval back into the draw_it()function, but in another color, so
that the drawings on top of it can be seen. In order to use color in the window
(more than just the 8 default colors), our window must be created with
NewC
Window()
. This routine uses the same arguments, so the change is minor.

$win = MacWindow->new(
NewCWindow($winr, $title, 1, $style, 1)
);

There are two color characteristics of the window that we work with: the
background color and the foreground color. By default, the background is
white and the foreground is black. Before we can draw something in a given
color, however, we need to specify the color.

IMAGE imgs/325.GUI01.gif

11To change the font, use the functions in Mac::Fonts.


IMAGE imgs/325.GUI34.gif

Color specifications are composed of three colors - red, green, and blue -
ranging from 0 (off) to 65535 (full on). The values are packed into a single
value. For convenience, we will use a function to create the value and bless
it into the
RGBColorclass:12

sub new_color {
bless(\pack('SSS', @_[0..2]), 'RGBColor');
}

Calls to SetForeColor()then change the color for us as needed:

sub draw_it {
RGBForeColor(new_color(0, 50000, 50000));# aqua
PaintOval(
Rect->new(105, 15, 245, 90)# oval
);

RGBForeColor(
new_color(16384, 16384, 16384));# grey
MoveTo(120, 65);
LineTo(223, 65);# line

RGBForeColor(new_color(0, 0, 0));# black
MoveTo(130, 55);
TextSize(20);
DrawString('MacPerl');# text
}

IMAGE imgs/325.GUI35.gif
IMAGE imgs/325.GUI01.gif

12RGBColor->new()may soon be added to Mac::QuickDraw, to do this task.


IMAGE imgs/325.GUI37.gif

Now For An Image ...

Using GetPicture(), the program can import a PICTresource from the
open resource file (by default, the resource fork of MacPerl). Resource 128 is
the main image in MacPerl's about box (the word "MacPerl", in black).
13

DrawPicture()is used to draw the pictures in the window. It is passed
the picture and a rectangle that gives the coordinates of the picture, which
happen to be saved in the picture's
picFrame()method.

Note:PICT rectangles do not always start at (0, 0); they may start at
something completely different.
OffsetRect()changes the starting
location; it is passed the rectangle and a starting (X,Y) offset.

sub draw_it {
RGBForeColor(new_color(0, 50000, 50000));
PaintOval(
Rect->new(105, 15, 245, 90)
);

RGBForeColor(new_color(16384, 16384, 16384));
MoveTo(120, 65);
LineTo(223, 65);

RGBForeColor(new_color(0, 0, 0));
MoveTo(120, 55);
TextSize(20);
DrawString('Welcome to');

my($p, $r);
$p = GetPicture(128);
$r = $p->picFrame();
DrawPicture($p, OffsetRect($r,
(30 - $r->left()), (100 - $r->top())
));
}

IMAGE imgs/325.GUI01.gif

13A PICTfilecan be opened and made into a picture object, too. Just open()the file
and skip the first 512 bytes of the file and pass the rest of the data as a scalar to
PicHandle->new(DATA).


IMAGE imgs/325.GUI39.gif
IMAGE imgs/325.GUI40.gif

Events

If a window always stays "in front", it will never need to be redrawn. Users
have a nasty habit of moving things around, however, so provision must be
made for the fact that all or part of a window may be temporarily obscured,
then exposed to view.

The previous example used eventsto redraw the screen as necessary. The
MacWindowclass took care of whento redraw the window and the redraw
event handler took care of whatto draw in the window.

An event handleris a function that is called whenever a particular event is
detected. So, when the window needs to be redrawn, the
redrawevent han-
dler is called. If a key is pressed, the
keyevent handler is called. Mac OS
windows normally close when a
Command-wkey sequence is detected. This
might be passed to the application's
keyevent handler.

Event handlers are passed two parameters; the event's object (such as our
window object) and a value. If a user types the
wkey, the keyevent han-
dler gets the decimal value of
win ASCII (119, as returned by ord('w')).

The event handler should return trueif it handles the key.

sub handle_keys {
my($my, $v) = @_;
my($mod) = $Mac::Events::CurrentEvent->modifiers();
if ($v == ord('w') &&
($mod & cmdKey()) == cmdKey()) {
$my->dispose();# handle Command-w


IMAGE imgs/325.GUI41.gif

return(1);# note that key was handled
}
}

Modifier keys (command, option, control, shift, and caps lock) do not trigger
the
keyevent handler by themselves, but are stored in $Mac::Events::
CurrentEvent->modifiers()
. At the moment of the event, this value
can be examined to see if a particular modifier key has been pressed. Each
modifier key is assigned a unique bit in the value, so we can use a bitmaskto
find out whether a given key is pressed.

And as you might guess, the event handler would be set beneath the similar
call to the
redrawhandler, and would look like this:

$win->sethook('key' => \&handle_keys);

Controls

Mac::Controlsprovides access to all of the extra widgets that sometimes
are provided in windows, including radio buttons, check boxes, scroll bars,
popup menus,
14and push buttons.

There are three steps to using a control. First, it must be created. Second, it
should be given one or two event handlers. Third, its events must be han-
dled. The best way to create a control is with the
new_control()method
of the window object, which accepts seven parameters (enumerated below).

There are two event handlers that can be set for a control; the primary one
is
click, which is activated when the control is clicked, returning the
value of what was clicked. The other handler is
track, which returns the
mouse's coordinates as of the time when it was clicked on the control.

Push Button

A push button needs no value, so those three parameters can be 0. The title
is the label on the button.

$con = $win->new_control(
Rect->new(20, 20, 100, 40),# rectangle

IMAGE imgs/325.GUI01.gif

14Popup menus work similarly to the others, but they must be defined in a resource
file. An example is included with MacPerl, but we don't cover it here.


IMAGE imgs/325.GUI43.gif

'Hit Me',# title
1,# visibility boolean
0, 0, 0,# default, min., and max. values
pushButProc()# type
);
$con->sethook('hit' => \&hit_me);

IMAGE imgs/325.GUI44.gif

The value that is passed to hit_me()normally isn't important, but it can
be retrieved from
kControlButtonPart().

sub hit_me {
if ($_[1] == kControlButtonPart()) {
MacPerl::Answer('Ouch!');
return(1);
}
}

Radio Button / Check Box

Radio buttons and check boxes are created in the same way, differing only
in what they look like and how they are used. Radio buttons are used if
only one value can exist for a given group of choices; check boxes are used
when multiple values can exist. Here are some sample images:

IMAGE imgs/325.GUI45.gif

Button and box values can be 0 (off), 1 (on), or 2 (mixed). Mixed is almost
never used for radio buttons, but is sometimes used for check boxes.

$con0 = $win->new_control(
Rect->new(20, 20, 100, 40),
'Off', 1, 0, 0, 1, radioButProc()
);
$con1 = $win->new_control(
Rect->new(20, 40, 100, 60),
'On' , 1, 1, 0, 1, radioButProc()
);


IMAGE imgs/325.GUI46.gif

$con0->sethook('hit' => \&hit_off);
$con1->sethook('hit' => \&hit_on);

This create a vertically-arranged pair of radio buttons. $con0starts with
a default value of 0,
$con1with 1. Whichever button is clicked is turned
"on"; the other button is turned "off".

sub hit_off {
SetControlValue($con0->{control}, 1);
SetControlValue($con1->{control}, 0);
$val = 'Off';
return 1;
}
sub hit_on {
SetControlValue($con0->{control}, 0);
SetControlValue($con1->{control}, 1);
$val = 'On';
return(1);
}

Because we saved the value of which button was on in $val, we can print
out the final selection when the main
whileloop ends.

print "You selected: $val\n";

Scroll Bar

A scroll bar has no title. It has two "direction" buttons, a position indicator,
and two bar segments, runningbetween the indicator and the buttons. A user
can click on any of these, returning one of five values to the event handler:

kControlUpButtonPartkControlDownButtonPart
kControlPageUpPartkControlPageDownPart
kControlIndicatorPart

The value of the control is the indicator's position on the bar, ranging from
the minimum (top/left) to the maximum (bottom/right).

The narrow dimension of the scroll bar's rectangle should be 16 pixels. The
enclosing rectangle should be spaced away by one pixel on all sides, so that
the borders will overlap. The rectangle below places the scroll bar along
the bottom of a 350 by 175 pixel window , as in the first example.


IMAGE imgs/325.GUI47.gif

$con = $win->new_control(
Rect->new(-1, 160, 351, 176),
0, 1, 0, 0, 1, scrollBarProc()
);
$con->sethook('hit' => \&hit_me);

IMAGE imgs/325.GUI48.gif

By examinig the return values, we can determine whether the user hit one
of the buttons, hit a position within one of the bars, or clicked and dragged
the indicator, letting go at a new position.

sub hit_me {
my($my, $v)= @_;
my($cv) = GetControlValue($my->{control});
if ($v == kControlUpButtonPart()||
$v == kControlPageUpPart()||
($v == kControlIndicatorPart() && $cv == 0)) {
SetControlValue($my->{control}, 0);
return(1);
} elsif ($v == kControlDownButtonPart() ||
$v == kControlPageDownPart()||
($v == kControlIndicatorPart() && $cv == 1)) {
SetControlValue($my->{control}, 1);
return(1);
}
}

Menus

Menus, supported byMac::Menus, are comparatively simple to create,
primarily because no coordinates are involved. A menu is constructed with
MacMenu->new(). Every menu gets an ID and a title, and can include any
number of anonymous arrays, each representing an item in the menu.

It is important to use an ID that is not already taken by the MacPerl appli-
cation. Menus must be installed with
insert()and, like windows, must be
disposed of after use.

Each menu item can have several characteristics. Titles are required, as are
event handlers (except in the case of submenus, described below). Keyboard
shortcuts are optional, as are icons and marks to put next to the items.


IMAGE imgs/325.GUI49.gif

$m1 = MacMenu->new(2048, 'Test Menu', (
[ 'Submenu',# title
undef,# event handler (null)
chr(27),# shortcut keystroke
chr(221)],# ID of $m2 sub-menu
[],# adds divider line
[ 'Quit',# title
\&menu_hit,# event handler
'W'],# shortcut keystroke
));
$m2 = MacHierMenu->new(221, 'Submenu', (
[ 'Hit me',# title
\&menu_hit,# event handler
'M'],# shortcut keystroke
));
$m1->insert();
$m2->insert();
WaitNextEvent while (1);

END {
$m1->dispose() if (defined($m1));
$m2->dispose() if (defined($m2));
}

Hierarchical Menus

A submenu, or hierarchical menu, can be added into a menu. A hierarchical
menu is created in the same manner as a normal menu, except that its class is
MacHierMenuand its ID number must be in the range 1-255. The item where
the submenu is to be inserted should have a key of
hMenuCmd()15and a
mark of
chr(ID), where IDis the submenu's ID number.

Instead of the menu object, the (sub-)menu event handler is passed the menu
ID. This allows a single handler to respond properly to any of several men-
us. The value passed is the item number, where 1 is the first menu item.

sub menu_hit {
my($m, $v) = @_;

IMAGE imgs/325.GUI01.gif

15In older versions of MacPerl, where this function is not exported by Mac::Menus,
chr(27)can be used instead.


IMAGE imgs/325.GUI51.gif

if ($m == $m1->{id} && $v == 3) {
exit;
} elsif ($m == $m2->{id} && $v == 1) {
MacPerl::Answer('Ouch!');
return(1);
}
}

Dialogs

Dialog boxes are similar to windows, but normally serve different functions,
as in the case of an
Open...dialog box. They can have controls, but use their
own kind of control routines, not the ones provided by
Mac::Controls.

Window items are drawn, then controls are attached via calls to new_con
trol()
. In dialog boxes, items are stored in an item list and accessed via an
item number.
16A dialog item has three parameters: type, rectangle, and a
title / value.

In this example,17a list of dialog items is added to the call to MacDialog
->new()
. OKand Cancelbuttons are created, followed by some static text, a
checkbox, and an editable text box.
18

$dlg = MacDialog->new(
Rect->new(50, 50, 450, 155),# dialog rectangle
'Welcome to MacPerl',# dialog title
1,# is visible?
movableDBoxProc(),# window style
0,# has go away box?
[ kButtonDialogItem(),
Rect->new(300, 30, 380, 50), 'Cancel'],
[ kButtonDialogItem(),
Rect->new(300, 70, 380, 90), 'OK'],
[ kStaticTextDialogItem(),
Rect->new(10, 10, 220, 30),

IMAGE imgs/325.GUI01.gif

16See the Mac::Dialogsdocumentation for a list of different items that can be used.
17Don't forget to declare your variables with my()and use strict, Mac::Windows,
Mac::Events, and Mac::Dialogs.
18Note that the checkbox comes before the edit box in the list, but is placed below it in
the dialog box itself. Placement in the box has nothing to do with order in the list.


IMAGE imgs/325.GUI53.gif

'How do you like MacPerl?'],
[ kCheckBoxDialogItem(),
Rect->new(15, 80, 180, 95),
'First time user?'],
[ kEditTextDialogItem(),
Rect->new(15, 50, 180, 65), ''],
);

Using some standard calls, the OKand Cancelbuttons can be attached to the
associated key sequences (e.g.,
return, enter, escape, Command-.) and the
text box can be selected so that typed text will go there by default. These
calls access the items by their order in the list. The
Cancelbutton is first,
the
OKbutton second, and the text box fifth.

SetDialogCancelItem ($dlg->window(), 1);
SetDialogDefaultItem($dlg->window(), 2);
SelectDialogItemText($dlg->window(), 5);

Hint:You may prefer to set up a hash for these button numbers, such as
%dlgh = (cancel=>1, ok=>2, ..., edit=>5), so that you can
use
$dlgh{ok}instead of trying to remember that its value is 2.

IMAGE imgs/325.GUI54.gif

Then, of course, event handlers must be set. This dialog box needs three: one
each for the
OKand Cancelbuttons and the checkbox.

$dlg->item_hit(1 => \&d1);
$dlg->item_hit(2 => \&d2);
$dlg->item_hit(4 => \&d4);

Because d4()should toggle, its checkbox value needs to be set to the oppo-
site of the value it had at the time it was clicked (0 becomes 1; 1 becomes 0).
Note that the dialog box object and the item number that is hit are passed
as the two arguments to the handler.


IMAGE imgs/325.GUI55.gif

sub d4 {
my($dlg, $item) = @_;
if ($dlg->item_value($item) == 0) {
$dlg->item_value($item, 1);
return(1);
} elsif ($dlg->item_value($item) == 1) {
$dlg->item_value($item, 0);
return(1);
}
}

Or, more tersely:

sub d4 {
my($dlg, $item) = @_;
$dlg->item_value($item, 1-$dlg->item_value($item));
return(1);
}

d1()prints a message and closes the box, ending the main loop.

sub d1 {
my($dlg) = @_;
print "You clicked Cancel\n";
$dlg->dispose();
return(1);
}

d2()also prints a message and closes the box, but its message is the text
that was typed and an indication of whether the box was checked.

sub d2 {
my($dlg) = @_;
printf "You typed: %s\n", $dlg->item_text(5);
printf "You are %s first time user\n",
$dlg->item_value(4) ? 'a' : 'not a';
$dlg->dispose();
return(1);
}

If the script ends prematurely, the dialog box should be disposed of.

END { $dlg->dispose() if (defined($dlg)) }


IMAGE imgs/325.GUI56.gif

Now all that's left is to determine the behavior of the dialog box while it
waits.

There are two major types of dialog boxes, modaland nonmodal. The differ-
ence lies in how they are used, not in how they are created.
19A modal dia-
log box allows no other actions to take place while it is open. It forces the
user to deal with the dialog box in some way. A nonmodal dialog box acts
more like a regular window, allowing the user to perform other actions not
related to the dialog box (e.g., creating new windows).

The whileloops are similar, except that the modal version will call $dlg
->modal()
in the loop, and the nonmodal one will perform the same type
of action as a regular window (e.g., calling
WaitNext- Event()).

Modal:

while ($dlg->window()) {
$dlg->modal();
}

Nonmodal:

while ($dlg->window()) {
WaitNextEvent();
}

Roll Your Own

There is much about the GUI toolbox modules that we did not cover. By now,
however, you should be able to learn more on your own and understand new
concepts that build on this foundation, such as adding lists with the
Mac::
Lists
module, or creating text edit boxes with Mac::TextEdit.

Put together your own examples and play around. The point of providing all
of this flexibility is that application programmers, and now MacPerl pro-
grammers, can customize their user interfaces to meet specific needs of the
application and its users.

IMAGE imgs/325.GUI01.gif

19Sometimes they will differ in style (e.g., nonmodal dialogs might have a close box).

Copyright © 1997-1998 by Prime Time Freeware. All Rights Reserved.