8. The GWindows Widget Set
8.1 Abstract Widgets
As discussed in 6.4, if you want the appearance of a layout while the game is in progress, the easiest way to do this is via a window or windows which begin with a size of zero, and which are enlarged to become visible when needed (or vice versa). Because this is such a common trick, GWindows provides an abstract widget encapsulating this ability.
deactivateswitch the window between its states. You can test which state is current by checking the
splitproperty, which will be equal to the
asplitwhen the window is active, and the
dsplitwhen it is inactive. Before the window is activated or deactivated, its initial size is stored in
split_key is the window pair you wish to
resize. Recalling the discussion from chapter 3, we know that
younger windows size themselves within their window pairs. For
such a window, popping it up or down only affects the window
itself. When you resize the window, the other window in its pair will
be resized to fill the vacated space. The
in this case is the parent window pair of the popup window. If you
do not specify a
split_key, GWindows will assume it to be
the parent window pair.
Resizing an elder window is more complicated. Because an elder window's size is implicit, you must resize it by resizing the younger window in its pair; (well, Glk allows you to resize the window itself, but GWindows is unable to preserve such a structure, so don't do it.) For example:
TextGrid -> popup;
GPopupWin -> mainwin
with split 100,
When the game starts, the popup window is invisible,
because its younger brother is taking up the entire window pair. When
mainwin, it reduces its size
to 90% of the screen, causing the popup window to become
visible in the vacated space.
What if you want to pop up an entire window pair? Remember from chapter 3 that some elder windows "size" window pairs further up the tree. You can think of these windows as the "foundation" on which the window pair is built. If you were to pop one of these foundation windows up or down, it would resize the entire pair. For example:
TextBuffer -> mainwin;
WindowPair -> popuparea;
WindowPair -> ->;
GraphWin -> -> -> pic_win
with asplit 50,
GraphWin -> -> -> pic2
with split_dir winmethod_Below,
TextGrid -> ->
with split 10,
popuparea is initially invisible,
because, according to the rules of chapter 3, it gets its size from
pic_win, which has a default size of zero. When you
pic_win, the entirety of
One small point, though, is that GWindows can't determine what
window is sized by the elder window. So you have to specify the
split_key in this case as the parent of the window you
want to "pop up".
is a powerful tool for making the interface change as you see fit. Some of its most useful applications have their own widgets, the abstract
GTransient, and the concrete
Sometimes, you'll want a popup window to become visible,
then hide itself again after a fixed amount of time has
GTransient defines a popup window which installs
a timer to re-hide itself.
windows behave identically to
GPopupWinwindows, except that its
activatemethod is called with a parameter.
MyTransientWindowfor five turns, then hide it again.
The most common use for a transient window, displaying a quote box,
is handled by the derived class
is a powerful widget, which automatically chains together the methods of other inherited classes.
with redraw [; self.thread(redraw); ];
This is a class which combines redraw events. You could use it to create a window which redraws itself according to two different rules:
will now redraw by first calling the redraw method inherited from
class GCombineRedraw Class1 Class2;
Class1, then from
One tricky point with
GCombiner is that if you create
GCombiner derived classes, you also have to add
code like this:
Constant HAVE_GCOMBINER_LIST 1;
Array GCOMBINER_LIST table GCombineRedraw;
GCombiner has to know which
classes are also
GCombiners so that it doesn't try to run
itself more than once. Also, the
GCombiner always has to
be the first class in the inheritance list.
The most common methods to combine via
gcombine.h defines the classes
GCombinerADUR for combining these methods.
is a very powerful class, and therefore fairly dangerous. You should try to avoid using it unless you are absolutely sure what you are doing.
8.1.4 HereMenu (New in GWindows 1.0)
is not, technically, a widget. It is a set of predefined Menu Data Objects intended to be used with any map or menu (see 8.2.3 and later).
When a HereMenu object is passed to a menu, it generates a
continually-updated menu or map of the contents of a particular thing.
HereMen.h defines several useful menus:
HM_Simple_Location_Menu - A menu of all objects in the current location
HM_Simple_Inventory_Menu - A menu of all items in inventory
HM_Simple_Scope_Menu; - A menu of all items in scope
HM_Sack_Inventory_Menu - As Simple_Inventory, but includes the contents of the player's sack-object.
HM_Obvious_Location_Menu - As Simple_Location, but omits scenery and concealed objects
HM_Obvious_Scope_Menu - As Simple_Scope, but omits scenery and concealed objects
See Heremen.h for details on how to expand upon this functionality.
8.2 Concrete Widgets
Many of the concrete windows have means of forcing themselves to appropriate sizes. It's generally best, therefore, to define these as younger windows.
is a widget for implementing the standard statusline. Simply include a
GStatusWinin your window tree, and a normal-looking statusline will appear.
The statusline generated by
GStatusWin is visually
similar to that used by z-code inform rather than Glulx; the window is
displayed in reversed colors. Also,
better behavior when the window is very small.
Though the library quote box is still available in GWindows,
it is comparatively inflexible (and frankly, quite ugly). A much more
useful quote box is available in the
You can insert a
GQuoteWin anywhere in your tree, but
it should always size itself, and its
split_dir should be
horizontal (that is,
GQuoteWin is activated by calling
text is an
table array (a table array is an array whose first element is the
number of entries in it) of printables. The window will automatically
choose the apropriate size for itself, and pop up, disappearing one
If you like, you can use a
GQuoteWin to replace the
library's quote window. The
box command will use the
GQuoteWin instead of the default library mechanism.
To do this, you must add the line:
before including either
parser.h, and you must define the variable
Quote_GWindow to your own
is the standard menu-drawing window. It provides many hooks for deriving custom menu views, which are described in the comments to
GMenuwindows support mouse and keyboard input, and can draw multiple pages to accommodate menus longer than a single window.
To activate a
x is the menu
data object. Here's an example of a menu data object which
provides a menu of game commands:
class cmd_word with select [; cmd_override=self; ];
cmd_word -> "RESTART";
cmd_word -> "RESTORE";
cmd_word -> "SAVE";
cmd_word -> "QUIT";
If you have a
GMenu called 'menu',
menu.activate(mdo); will provide a menu with options to
restart, restore, save, and quit. The
select property of
the selected option is called when the user clicks or presses
return. You can disable the menu by calling
If the menu data object provides an
function, it will be called once per turn, so that the menu can alter
itself (for example, a menu of all objects currently in scope would
need to be redrawn once per turn). It should return
if the menu needs to be redrawn.
provides many ways to customize its behavior. Here are the most common ways:
: This property contains the character which is printed beside the current menu selection.
: This printable is printed on the first line if the window can be paged upward
: This printable is printed on the last line if the menu can be paged downward
: If this attribute is set, the menu will not display <previous> and <next> indicators or allow paging, even if the menu is longer than the available space.
Additionally, a menu item which has the absent attribute set will behave differently from
normal menu items: if the player is navigating the menu via the cursor keys,
the cursor will skip over such elements. This can be used to place a separator
within a menu. (However, placing such an item at the beginning or end of a menu may have undesired effects.)
Clicking directly on the item with the mouse will still send a
select message, however.
Note: In previous versions, the
current_menu property of a GMenu
could be used to determine the current menu data object. This practice is deprecated. To
find the current menu data object, use the method
will still hold the menu data object for simple menus, but may not be correct
for all subclasses (Most notably, WrapMenu).
GPopupMenu is the combination of a
GPopupWin and a
GMenu. When it is activated,
it resizes itself and displays a menu.
GMenu shows a single column of menu entries,
GColumnMenu allows multiple columns. The menu data object
Columns in a
GColumnMenu have a default width of 15
characters, but you can change this by specifying a
is a column menu which automatically adjusts the width of its columns to fit the longest entry in the menu. You will generally prefer this to
GColumnMenuunless you want to force a very specific appearance of the menu.
is an easy way to define a window which displays an image. In addition to setting its
imageproperty, as we have seen in 5.3, you can change the image at a later time by calling
xis the number of a Blorb image. The image will be automatically rescaled to fill the window. If the attribute
aspectedis set, the image will be scaled to fit the window as closely as possible without altering the aspect ratio of the image and centered.
GImageWin has a further property,
col which is the background
color of the window (white by default). This is relevant in three cases:
An image map is an image which is divided into rectangular regions which can be clicked.
is, in essence, the graphical version of
GMenu. The format of the image map data object is compatible with that of a
GMenu's menu data object, so you can use the same data to generate either an image map or a menu (and it is a good idea to allow users who can't cope with graphics to view the map as a menu, if it is essential to the game).
You activate a
GImageMap just as you would a
GMenu. The only difference in the data is that each
option must provide information about its location within the image:
cmd_word -> "RESTART" with xpos 50, ypos 10, width 10, height 10;
cmd_word -> "RESTORE" with xpos 60, ypos 10, width 10, height 10;
cmd_word -> "SAVE" with xpos 50, ypos 30, width 10, height 10;
cmd_word -> "QUIT" with xpos 55, ypos 40, width 10, height 10;
Each option defines a rectangular region, beginning at
ypos) of size
height. These will be scaled appropriately if the image
GMenu, if the map data object provides an
update method, it will be called once per turn. It is not
necessary to return true from this function, since the map's
appearance will not be changed by changing the clickable regions
is a version of
GImageMapwhich allows the map items to draw themselves.
Each item may additionally provide an
property. If this is set, the image will be drawn over the background
of the window in the position specified. If the widget is set
the map items will be scaled proportionally.
GImageMap is powerful, it can be a
little slow, due to the processing involved in finding which item was
clicked. If the map has a very regular layout, you might prefer to use
When you declare a
GImageGrid, you use the
cols properties to specify the
number of rows and columns in the grid. The window is divided evenly
into rectangular cells. When a cell is clicked, the corresponding
entry is selected.
Entries are numbered across each row, so if the user clicks on the second element of the second row, and each row has 5 columns, the 7th entry is selected (7=5+2).
Image Grids are also pageable. If the grid data object has more
entries than will fit on the grid, you can specify where the grid is
to start via the
start property of the Image Grid
window. So, setting
start to 5 means that the first four
entries are ignored.
GDrawImageGrid is the image grid counterpart
GDrawImageMap. This could easily be used to create,
say, a graphical inventory:
GDrawImageGrid -> invwin
with rows 4, cols 4;
GImage -> invscroll
with image BUTTONS_PIC,
click_event [ x y;
if (y>(self.height/2)) invwin.start++;
Then, make all takeable objects inherit from a class:
with select [; StreamWord(self);],
Clicking on a cell in the inventory window will insert the object's name in the current command line. If the object provides an image of its own, that image will be drawn in its cell. Otherwise, the "generic" picture will be used.
We define the other window,
invscroll, to allow the
user to scroll the window.
A real implementation would want to redefine
invscroll.click_event so that it doesn't allow
invwin.start to take an unreasonable value, and give the
player object an
update method which returns true if the
player's inventory has changed this turn.
8.2.11 WrapMenu (New in GWindows .9B)
is an enhanced menuing widget. It may be used just like any other GMenu, however, in a
WrapMenu, if an entry takes up more than a single line, it will be folded over onto the next one, rather than being truncated.
If a menu item takes up more than one line, subsequent lines will be indented. Furthermore, when the player cycles through options using the cursor keys, the selection marker will skip over the subsequent lines, moving only between the first lines of each entry. Clicking on any line of the entry will, however, trigger the option.
If you only wish to use a single
WrapMenu, you may treat it identically to
GMenu. However, if more than one
WrapMenu is to be active at the same time,
it must specify a wrapper proxy:
WrapMenu wmen with wrapper my_proxy;
WrapMenus which do not specify a wrapper proxy will use the system
default proxy. At most one menu at a time may use this proxy. A wrapper proxy is
simply an object used to hold virtual menu objects while the menu is being
constructed. Wrapper proxies inherit from the class
Other than creating them for
WrapMenus you may declare, you need
never deal with the proxy yourself.
WrapMenu defines two constants to modify its behavior:
GW_MAX_MENU_SIZE(Default: 256) - This is the maximum number of lines which can appear in any WrapMenu. That is, if each option takes up a single line, the largest legal menu can have 256 options. If each option takes two lines, the menu can have at most 128 options. If, as is highly unlikely (actually, impossible, since GMenu will truncate the name at 128 characters no matter what, unless GW_BUFFER_SIZE has been modified), a you have an option which is 256 lines long, it must be the only thing in the menu. Of course, you cannot be absolutely certain how many lines a given option will take up on the user's screen, but it is unlikely that you will run out of room as long as this exceeds twice the size of the largest possible menu.
GW_MAX_WRAPS(Default: 256) - This is the maximum number of lines to be given to any single menu item. Longer items will be truncated.
WrapMenu can be combined with
GPopupWin without the use of a
GCombiner; simply inherit from both classes.
8.2.12 GTileWin (New in GWindows .9B)
is a widget which tiles an image, as usually occurs with background images on webpages. It behaves like a GImageWin, except that rather than stretching an image to fit the window, the image is simply repeated in a grid pattern as many times as will fit in the window.
8.2.13 GVTileWin, GHTileWin (New in GWindows .9B)
GHTileWinare subclasses of
GVTileWintiles images vertically, while
GHTileWintiles them horizontally. Unless the widget is set
aspected, the image is stretched along the untiled dimension. Which widget will suit a particular need will depend on the nature of the image to be tiled. For example, consider an image consisting of four sqares, two black, two white, in a checkerboard configuration.
GTileWinwould produce a checkerboard pattern.
GVTileWinwould produce two columns of alternating black and white rows.
GHTileWinwould produce two rows of alternating black and white columns.
8.2.14 GForm (New in GWindows 1.0)
is a widget for generating form-based dialogues similar to those used by standard windowing toolkits. By placing GForm components inside a GForm, they will render a text-based form with which the user can interact. Form components include such objects as radio buttons, check boxes, clickable buttons, and selection lists.
GForm is still experimental and care should be exercised. See the documentation in gform.h for more details of its use.