4.1 Fundamental classes
In GWindows, you define a user interface as a tree of objects. gwincls.h provides the following fundamental classes:
WindowPair - This is the class that represents a pair window in the window tree. Generally, you won't want to change any of the properties on a WindowPair. Window pairs provide a checkredraw method, which will redraw the window tree from this point, as needed. Redrawing a window pair redraws its children, and likewise, updating a WindowPair updates its children.
It is important that every WindowPair has exactly two children.
GWindow - This class represents concrete, visible screen
windows. The properties split and split_dir
are
used to determine the stated size of the window. GWindow
class objects may provide methods to redraw and update
the window, and may specify stylehints
it wishes to
use. The attribute abssplit
is used to determine the kind
of split. If it's set, the window's
size is "absolute" -- the window is that many lines (for text
windows) or that many pixels (for graphics windows). If not, the size
is a percentage. Remember from chapter 2, however, that a window's
size metrics may not refer to the window itself, but to a window pair
somewhere further up the tree.
GWindow
is further separated into three classes
reflecting the basic window types under Glk.
TextBuffer
- This is a scrolling window of text. You can print to it, or draw graphics in it, but you can't reposition the cursor. Many Glk implementations allowTextBuffer
windows to be scrolled. In a TextBuffer
, the text
may be displayed in a proportional font, depending on the
implementation and user preferences. Generally, the main output window
will be a TextBuffer
. TextBuffer
s support
single-character and line input, as well as hyperlinks if available,
but do not support mouse input (except in the form of hyperlinks **
Note: Hyperlinks aren't really mouse input; in GlkDOS, for example,
you select hyperlinks by picking the link you wish to activate from a
list at the bottom of the screen. However, most interpreters use the
mouse to select hyperlinks. A hyperlink is a region of text which,
when selected, generates a hyperlink event. The system is similar to
links in a web page.)
TextGrid
- This is a non-scrolling window which displays fixed-width text arranged on a grid. You can position the cursor within the window.TextGrids
support character,
line, and mouse input. Some Glk implementations also support
hyperlinks in grid windows. The status line and menus are usually
TextGrid
windows.
GraphWin
- This is a window which exists purely for graphics. Text cannot be printed in aGraphWin
, and they support neither keyboard input nor
hyperlinks. They do support mouse input, however. Output in a
GraphWin
is, obviously, graphical. You can draw blorb
images in a GraphWin
, or draw manually using Glk's
drawing primitives (which, at this time, consist of drawing and
erasing colored rectangles). Because graphics are an optional
capability, GWindows will replace GraphWin
windows
with blank windows if the system detects that graphics are not
available.
GWindow
and WindowPair
objects provide a
winid
property, which contains the Glk opaque reference
for the window. If you wish to use a Glk function on a window, you
pass this winid
property to Glk:
Wrong:
glk_window_move_cursor(mywin,2,2);
Right:
glk_window_move_cursor(mywin.winid,2,2);
GWindows also provide a way for determining their actual
size on the screen. The properties width
and height
contain the actual size of the window, in whatever units are
appropriate (characters or pixels). These values are updated whenever
the window is redrawn, so that they will be correct even if the user
maliciously changes his screen size.
4.2 Widgets
Some techniques are common enough that GWindows provides predefined classes for a special-purpose window.
A widget is a GWindow
which has special code to
make the window do a certain task. GWindows ships with a large
number of widgets to provide common tasks. Some examples of widgets
are: GStatusLine
(a widget which emulates the standard
status line), GPopupWin
(a widget which provides a window
which changes its size when activated), and GMenu
(a
widget which generates a menu of options). Though new widgets are
being created all the time, the ones which existed when this manual
was written are documented in section 7.
Because you may not want all of the widgets, and because you may want to add new widgets later, each widget is kept in a separate file. Some widgets are based on other widgets. A well-written widget file will include its required widgets for you.
There are two fundamental types of widget:
An abstract widget encapsulates a single specific
kind of behavior. An abstract widget isn't a complete
GWindow
on its own; it has to be combined with another
widget. For example, GPopupWin
is a widget for any kind
of window that pops up. It is not a window on it's own though; a
GPopupWin
has to be combined with a
TextGrid
, TextBuffer
, or GraphWin
(or with another widget) to specify what sort of window should
actually pop up.
There is a special abstract widget called
GCombiner
. This widget doesn't do anything on its own;
its purpose is to combine two widgets which offer overlapping
properties into a single widget. For example, a GPopupWin
changes its size when it is "activated". A GMenu
loads a new menu when it is "activated". To have both happen,
the classes have to be combined using a GCombiner
which
connects the "activate" methods. gpopmen.h
shows the
GPopupMenu
class, which does exactly this. Because
GCombiner
is such a powerful widget, it's recommended
that you steer clear of it until you're very comfortable with GWindows
programming.
A concrete widget is a widget which is a complete GWindow in
and of itself. GMenu
, for example, is a concrete
widget. You can create a GMenu
, and insert it directly in
your user interface as you would a TextGrid
or
TextBuffer
window.
4.4 Printables
This document will use the term printable for any value
which can be output by the PrintAnything()
library
function. GWindows often uses printables to generate its output.
4.5 Event Handlers
An event handler is a function which is run in response
to some event in the outside world. Examples of user events include
character events (the player just typed a character), mouse
events (The player just clicked with the mouse), and timer
events (The computer's clock just went "tick"). Glk offers a wide
variety of events. Line input events (The event caused when the
player types out a whole command and presses enter) are handled by the
standard library. GWindows offers event handlers for mouse and
character events, and takes care of redraw events (The
interpreter has lost track of the screen, and needs to have it
redrawn) and arrange events (Something has caused some windows
to change their sizes, so they should be updated to fit the new
size). Other events are handled by your GWindowsGlkEvent
function.
4.6 GWindows Installation
Like Inform's standard library, GWindows consists of three
files, gwincls.h
, gwindefs.h
, and
gwindows.h
. To use GWindows in a program, you must
include gwindefs.h
before parser.h
,
gwincls.h
after parser.h
, and
gwindows.h
before grammar.h
.
Any configuration constants you wish to use should go before the
inclusion of gwindefs.h
. Any entry points you wish
to use should go before the inclusion of gwindows.h
.
Any GWindows widgets you wish to use should be included after
gwincls.h
.
GWindows requires L. Ross Raszewski's utility.h
(version 4.0 or greater) and John Cater's infglk.h
. These
will be automatically included by GWindows.
GWindows uses the following global variables to monitor the state of the interface:
The first group of variables help GWindows interact with the standard library. You can read them whenever you like, but if you change them, you should re-start the window system to make the changes take effect. Failure to do this might confuse GWindows.
Main_GWindow
holds the
GWindow
which serves as the main output window. It is
always the case that the library variable gg_mainwin
will
contain the same value as Main_GWindow.winid
Main_GWindow
, as it does in the
standard library. However, you might prefer input to come from a
separate window (The way that input into, say, a mud client might
occur in a separate window at the bottom of the screen). If you set
the Input_GWindow
to another window, input will come from
that window instead. The next group of variables communicate information to GWindows. You can set them at any time.
cmd_override
is set to some printable, this will
happen. Generally, you will want to do this only within an event
handler. The final global variables are read-only.
GW_Abilities
& X
is nonzero, where X
is one of the
following:
- GWIN_GWOK
: Graphics windows are available
- GWIN_DROK
: Image drawing in graphics windows is available
- GWIN_MTOK
: Mouse input is available in TextGrid
windows
- GWIN_MGOK
: Mouse input is available in GraphWin
windows.@throw GW_Massive_Error errtype;
,
where errtype is some value identifying the error (Preferably a constant
string describing the problem.)
To be nice to players, you might want to check
GW_Abilities
when setting up your interface; if you're
using a graphical interface like the one above, you should also create
a textual version, and use that if GW_Abilities
doesn't
list GWIN_GWOK
.
4.8 GWindows configuration constants
Main_GWindow
(if Main_GWindow
is not equal to Input_GWindow
)
Set_Prompt
is nonzero.
4.9 GWindows Entry Points
GWindows installs itself using the Glk entry points of the standard library. As such, it offers its own entry points to replace them:
GWindowsGlkEvent(x,y,z)
: Replaces HandleGlkEvent
GWindowsGlkIdentify(w,x,y,z)
: Replaces
IdentifyGlkObject
GWindows also uses the InitGlkWindow
entrypoint. It provides the entrypoint InitGWindows
(which takes no parameters), which is equivalent to
InitGlkWindow(0)
, and is called whenever the user
interface is being created fresh. You are required to provide
this function. It should, at the very least, do the following things:
Make sure Active_UI
is set correctly.
Make sure Main_GWindow
is set correctly.
If you're using a separate input window, make sure
Input_GWindow
is set correctly.
By "correctly", we mean that the variables have the proper
values for this point in the game. If you use more than one user
interface, then InitGWindows
should set the
variables to their initial states if they are zero -- if they are
nonzero, you can assume that the interface is being recreated (say,
after a restore), and the values are already set correctly.
GWindowsHandleError(x)
is called whenever an
otherwise-irrecoverable error occurs (That is, it will only be called for errors
so heinous that the system cannot continue) . Return 2 from this function
to indicate that the problem has been dealt with -- though it's usually
far too late by then. The parameter describes the error. GWindows itself uses
the following error codes:
GW_ERR_ABS_GRAPH
"Attempt to coerce a window of absolute size into a blank window." --
GWindows will automatically replace graphics windows with blank windows if graphics are not available, but if
the graphics window has an absolute size, this is not possible. This error will generally
be raised if you attempt to use a graphical UI on an interpreter not supporting graphics.
GW_ERR_NO_OPEN
"Attempt to open a window failed."
This is raised if glk refused to open a requested window.
GW_ERR_RED_NO_WIN
"Attempt to redraw a window which is not open."
This is raised if a window which is not part of the current active interface
receives a redraw message (for example, because you called GW_ForceRedraw on
the wrong UI)