Glimmr Graphic Hyperlinks by Erik Temple


Glimmr Graphic Hyperlinks allows us to identify any number of rectangular areas of a Glulx graphic window as "hotlinked". When the player clicks within one of these zones, a command will be entered on behalf of the player, or we can specify some appropriate response of our own. Graphic links ("graphlinks") can be defined in any number of graphics windows simultaneously. At minimum, the extension requires Jon Ingold's Flexible Windows extension, which is used to define and manage the windowing system. It can also be used with Glimmr Canvas-Based Drawing to fully automate the maintenance of the list of graphlinks.

Glimmr Graphic Hyperlinks (GGH) is an adaptation and amplification of Jeff Sheets' Graphic Links extension to the framework provided by Jon Ingold's Flexible Windows. Like the original Graphic Links, it is not really intended to be complete in itself, but rather to be a relatively flexible template for more specific applications. The major limitation of both extensions is their reliance on the coordinate system of the window itself to define links; an image that has been scaled and centered in a window will move relative to the window coordinates any time the window is resized. For nearly all purposes, then, authors will want to provide a conversion from a more stable coordinate system to the window's system. The Glimmr Canvas-Based Drawing extension does just this, while also providing completely automated updating of links by binding them to graphic elements.

Section: Basic usage

In order to use graphic hyperlinks in a window, we must identify that window as receptive to links. This is as simple as writing:

The graphics-window is g-graphlinked.

Alternatively, we can define our window as of the kind "graphlink g-window". This will automatically set the g-graphlinked property:

The graphics-window is a graphlink g-window.

Note that Glimmr Graphic Hyperlinks only works with graphics windows. A text hyperlinking solution, such as Inline Hyperlinks or the system built into Flexible Windows, is needed for text windows.

The list of rectangular areas that are to be hyperlinked, regardless of how many windows are represented, are stored in a single table, the Table of Graphlink Glulx Replacement Commands. These hotlinked rectangular zones are called graphic hyperlinks or "graphlinks," and they can be identified in one of two ways: If we are using Glimmr Canvas-Based Drawing (GCBD), they are identified with the name of an object (usually a g-element); if we are not, we identify them using a text. While manually interacting with the graphlink list is rarely required in GCBD, it is a must for any other project. We add a link to the table using the phrase:

set a graphlink in the graphics-window identified as "undo button" from 0 by 0 to 10 by 10 as "Undo"

This adds a graphlink called "undo button" to the Table of Graphlink Glulx Replacement Commands. When the link is clicked it will--if we use the default rules--enter "Undo" on the command line on behalf of the player. Note the string of coordinates, which are entered LEFT by UPPER to RIGHT by LOWER.

There are two phrases that can be used to remove links from the table. To clear a single link:

clear the graphlink identified as "undo button"

Or we can clear the whole table at once with:

zero the link-table

Note that the graphlink identifier we use should--at least generally--be unique. This is simply because a request to clear a graphlink would otherwise be ambiguous (the clear command only clears the first graphlink it encounters). By default, the "set a graphlink" phrase will not allow us to have more than one row in the table with the same identifier. If we set a second graphlink using an identify that is already in the table, the original will be overwritten. To avoid this, add the "ignoring redundant links" phrase option:

set a graphlink in the graphics-window identified as "undo button" from 0 by 0 to 10 by 10 as "Undo", ignoring redundant links.

The Table of Graphlink Glulx Replacement Commands starts with 120 entries, which is likely to be sufficient for nearly all purposes. We can expand the table if need be, of course. To add another 50 rows, for example:

Table of Graphlink Glulx Replacement Commands (continued)
linkid     g-win     p-top     p-bottom     p-left     p-right     replacement     alt     action
with 50 blank rows

Section: Advanced usage

There may be times where what we want from a graphlink is something other than that it enter a command on the player's behalf. For these cases, we need to write new one or more new "graphlink processing" or "clicking graphlink rules".

The "clicking graphlink rules" are triggered when a graphlinked window receives mouse input. The rule should test to see whether the mouse input hit a graphlink, and then instruct Inform what to do about it. We can test whether a graphlink was clicked on by using the phrase "if the click hit a hot link". Here is the default rule:

A clicking graphlink rule (this is the default command replacement by graphlinks rule):
     if the click hit a hot link:
         follow the graphlink processing rules;
         rule succeeds;
     otherwise:
         now glulx replacement command is "".

We look through the table for entries that apply to the window that was clicked (the "current graphlink window"), and when we find one, we test whether the click was within the bounds of that entry's rectangle. If it was, the player has successfully hit a graphlink: we pass handling off to the graphlink processing rules. Note that, when a hot link is detected, the "candidate replacement command" variable is set to the command provided when the graphlink was set up, and the "current graphlink" is set to the identifier (these are assigned within the decide phrase).

If the click failed to hit a link, we simply set the glulx replacement command to null. Glulx Entry Points handles the transformation of the glulx replacement command into a "typed" command. If there is any text at all in the variable, then that text will be pasted as a command. If the glulx replacement command is null, nothing will happen except that the window will once again become receptive to input.

We will rarely need to adjust the clicking graphlink rules--most often, we would adjust them when we want to bypass the Table of Glulx Graphlink Replacement Commands altogether. More often, we will want to intervene in the graphlink processing rules. The graphlink processing rules is a rulebook that runs only after a graphlink has definitively been clicked. (Note: If we are using GGH with Glimmr Canvas-Based Drawing, the graphlink processing rules will be an object-based rulebook, with the object generally being the g-element that was clicked on to trigger the graphlink.)

The default graphlink processing rule changes the glulx replacement command to the candidate replacement command (which was set in the clicking graphlink rules):

A graphlink processing rule (this is the default graphlink processing rule):
     cancel input in main window;
     now the glulx replacement command is the candidate replacement command;
     now the candidate replacement command is "";
     rule succeeds.

Because this will result in the command being printed to the window, we need to cancel input in the window (a requirement of Glk, the interface layer of Glulx). We also reset the candidate replacement command.

There may be times when we do not want to use the glulx replacement command to communicate the outcome of a graphlink, and we do have two other options. Neither of these, however, will have any effect unless we write our own graphlink processing rule. We use variants of the "set graphlink" phrase to prepare these alternate outcomes:

set a graphlink in the graphics-window identified as "undo button" from { 10, 10 } to { 40, 25 } as 2.

set a graphlink in the graphics-window identified as "jump button" from { 10, 10 } to { 40, 25 } as the action of jumping.

The first phrase sets the "alt" entry in the table to the number 2. We can later read this number and make an appropriate decision about how to respond (perhaps looking up the result in a separate table). The second phrase sets the "action" entry to a stored action (jumping, in this case), which allows us to specify an action without specifying the text of the command. This means that we can fire the command "silently," without printing the command to the screen.

Again, we need to write our own graphlink processing rules to deal with such alternatives. Here's how we might write a rule to execute a stored action:

First graphlink processing rule (this is the direct action firing rule):
     choose row (current graphlink row) from the Table of Graphlink Glulx Replacement Commands;
     if there is an action entry:
         try the action entry;
     rule succeeds.

Note the use of the "current graphlink row" variable. This is set when a graphlink is clicked and can be used to quickly and accurately refer to the row containing the graphlink (from the Table of Graphlink Glulx Replacement Commands). If our linkid entries are not unique, this allows us to avoid difficulties in reselecting the proper graphlink.

Section: Using GGH with Glimmr Canvas-Based Drawing

When used with Glimmr Canvas-Based Drawing, GGH becomes almost fully automatic. The only time we will manually need to set graphlinks is when we are creating a new type of element, or writing a custom window-drawing rule. (Only if we are seriously hacking the extension will ever need to clear a graphlink.) When we do set graphlinks, it is important to be aware that, when Glimmr Canvas-Based Drawing is included, the link identifier is an *object* (usually this will be a g-element) rather than a text as it is in the absence of GCBD. Here are the three graphlink-setting phrases mentioned above as they might be entered in the Canvas-Based Drawing context:

set a graphlink in the graphics-window identified as the undo-button from 0 by 0 to 10 by 10 as "Undo".

set a graphlink in the graphics-window identified as the undo-button from { 10, 10 } to { 40, 25 } as 2.

set a graphlink in the graphics-window identified as the jump-button from { 10, 10 } to { 40, 25 } as the action of jumping.

The other major change is that, under Canvas-Based Drawing, the graphlink processing rules are an object-based rulebook. This rule header, for example, will allow us to treat the undo-button g-element differently from the others:

A graphlink processing rule for the undo-button:
     (etc.)

Without GCBD, we would need to write this as "A graphlink processing rule when the current graphlink is 'undo button'".

Section: Debugging

When used with either Glimmr Drawing Commands or Glimmr Canvas-Based Drawing, GGH will be able to generate Glimmr log messages. These generally identify the window and coordinates clicked, as well as the graphlink or g-element involved. As with all Glimmr log messages, logging must be enabled with the use option:

Use Glimmr debugging.

If we are not using these Glimmr extensions (and even if we are), we can access the same information by entering coordinate-trace mode. To do this, we just type COORDINATES at the command line (typing the command again turns coordinate-trace off again).

Another debugging command available to us is the GRAPHLINKS command. This will provide a summary of the current state of the graphlinks table. Sample output:

Table of Graphlink Glulx Replacement Commands (presented in reverse order)

There are 2 entries in the table.

undo button (graphics-window): (0,0) - (100,100): "Undo"
transcript button (graphics-window): (100,100) - (200,200): "Transcript"

Last window to be clicked: graphics-window

Chapter: Change Log

Version 2: Updated for 6M62 by Dannii Willis

Version 1: Initial release.

Section: Contact info

This extension is released under the Creative Commons Attribution licence. Bug reports, feature requests or questions should be made at <https://github.com/i7/extensions/issues>.

For questions about Glimmr, please consider posting to either the rec.arts.int-fiction newsgroup or at the intfiction forum (http://www.intfiction.org/forum/). This allows questions to be public, where the answers can also benefit others.