GEL: A definition-based graphical environment language for EDEN

Antony Harfield
Empirical Modelling Research Group
http://www.dcs.warwick.ac.uk/modelling/

27th October 2006 (with minor edits from WMB 24th October 2011)

Also available as pdf.

Introduction

The Empirical Modelling tool tkeden is an environment for creating and experimenting with computer-based artefacts. It contains a number of definition-based notations that can be used for displaying graphical artefacts. The SCreen layOUT (SCOUT) notation is used for the layout of simple graphical components (buttons, labels and textboxes) on a canvas. The Definitive Notation for Line Drawing (DoNaLD) is used for placing points, lines, and other shapes on a canvas.

The subject of this paper is a notation for graphical user interface (GUI) components. The Graphical Environment Language (GEL) is a definition-based notation for creating and modifying GUIs. The components in the GUI are based on common components found in libraries for programming languages (for example: the Tk toolkit in Tcl, Perl and C, Swing in Java, etc).

The current GEL notation is called %angel (ANt's prototype GEL) which is an experimental version being developed using the Agent-Oriented Parser (AOP).

Definition-based notations

In order to understand definition-based notations (sometimes referred to as 'definitive notations'), it is first necessary to be familiar with the Empirical Modelling concepts of observables and dependencies. Where traditional procedural programming languages use variables and procedures for describing computer-based artefacts, Empirical Modelling describes artefacts in terms of what observables and dependencies they embody. An observable is a feature, property or value; for example, a bird might have a length and a colour associated with it. A dependency is a relationship between one or more observables; for example, the speed at which a bird can fly at is dependent on it's wing span. In Empirical Modelling, artefacts are developed by a series of observations and experimentations: experimenting with the observables and dependencies and then observing the effect which in turn may inform further experimentation.

A definition-based notation is a language that supports the creation and experimentation of observables and dependencies. EDEN is the general purpose definition-based notation in tkeden for defining observables and dependencies. In EDEN, observables can be thought of as variables containing regular values like integers, strings, lists. Dependencies are definitions for the relationship between observables. Consider the example of an artefact modelling the weight of a car. We might create observables for the weight of the passengers, and the weight of the chassis and engine. Then a dependency could be created to represent the overall weight as a sum of the observables. In a procedural programming language you could define the artefact as:

passenger_weight := 140
car_weight := 3000
total_weight := passenger_weight + car_weight
The problem with this is that there is a relationship at the point of assignment, there is no persistent dependency between the overall weight and the weight of the car and passengers. If at any point the weight of the passengers changes then the relationship is no longer consistent. Procedural programmers avoid this by writing explicit dependency maintaining procedures that ensure whenever the passenger or car weight are updated then the overall weight is also re-assigned. In a definition-based notation the dependencies are maintained automatically in the tool, much like the dependency between cells in a spreadsheet. The equivalent description of our artefact could be framed in traditional spreadsheet syntax as:
passenger_weight = 140
car_weight = 3000
total_weight = passenger_weight + car_weight
A collection or list of definitions is called a script. Note that instead of the assignment operator ':=', the definition operator '=' is used. This is to differentiate between assignments and dependency definitions. In the tkeden environment, as with a spreadsheet, if any changes (redefinitions) are made to either the passenger or car weight then the change will also be reflected in the value of the total weight. (Note that in tkeden the definition operator is denoted by 'is' and '=' denotes assignment.)

Observables and dependencies become even more powerful when the types of observables become more complicated. The DoNaLD notation (Definitive Notation for Line Drawing) supports observables that can be points, lines, circles and other shapes. In the DoNaLD notation, dependency can be used to define particular points, lines and shapes to be dependent on other points, lines and shapes. Complex objects can be moved, rotated and resized with simple redefinitions of single observables which propagates changes to the entire object.

The agent-oriented parser (AOP) is a notation that supports the development of new notations in the tkeden environment. GEL was developed using the AOP notation. For more information on constructing notations see the guide to the AOP at: http://www2.warwick.ac.uk/fac/sci/dcs/research/em/notations/aop/.

To summarise, definition-based notations are languages that allow the investigation, creation and manipulation of observables and dependencies.

Notation specification

The GEL notation is designed to be independent of the implementation language. However, given that the first implementation %angel is in Tcl/Tk, there are some properties in the language that are fairly specific to Tk (i.e. the way packing is managed). For further information on the properties of components see the Tcl/Tk man pages.

Definitions

A GEL script is a list of definitions. Each definition has on the left hand side a definition name and on the right hand side a definition formula, separated by the definition operator (an equals sign in GEL) and terminated by a semicolon.

script:
    definition script
definition:
    definition_name = definition_formula;

A definition name can be the name of an instance of a component. It can be an identifier consisting of a sequence of letters and digits. The first character must be a letter. The underscore character _ is considered a letter. You cannot use EDEN reserved words as identifiers.

A definition name can also represent a property within a component. In this case, there are two identifiers, separated by a dot. The first identifier is the name of the component, and the second identifier is the name of the property with the component.

definition_name:
    identifier
    identifier.identifier

Valid definition names are: mywindow, mybutton.text, frame5, a_label. Invalid definition names are: 4thframe, else, mywin.mybut.mytext.

There is no variable declaration or type declaration in GEL. This means that you can create and redefine a definition with the same statement construction.

A definition formula is the recipe for the GEL observable. The observable should have an observable type if it is a GEL component. GEL components consist of a number of properties (also observables). For example, a button can have a colour, a text string and a border.

definition_formula:
    observable_type { property_list }
    eden_expression

In all the illustrative examples below, the strings that are used to specify properties etc are explicitly defined, but they can also be specified implicitly by Eden expressions.

Respective examples of valid definition formulae include:

window { title = "My Window Title" }
"This is only a string"

An observable type is one of the supported components or containers in GEL. Types currently supported are: window, frame, button, textbox, label, checkbutton, radiobutton, scrollbar, scale, scout, html, image.

An argument list is a sequence of property expressions separated by commas.

A property list is a sequence of property definitions separated by semicolons.

argument_list:
    property_expression
    property_expression, argument_list
property_list:
    property_definition
    property_definition; property_list
property_definition:
    identifier = property_expression
property_expression:
    eden_expression
    [ identifier_list ]
identifier_list:
    identifier
    identifier, identifier_list

An EDEN expression is any combination of EDEN functions, observables or values. EDEN values have can be of type: boolean (false is represented by 0 and everything else is true), integer (e.g. 1234), real (e.g. 9.876), string (e.g. "a string"), and list (e.g. [ 1, 5, "string", 6.78 ]).

Containers

There are two types of components that can contain other components: the window and the frame. The window is a special case as it is the only component that cannot be placed inside other containers.

The special property that containers possess is the content property. This specifies what components are contained with the window or frame. For more information on how components are arranged within containers see the next section on 'packing properties'.

The window

Special properties: content, title, and visible.

The window component is a container and therefore it has the property content to define the components that are inside the component. This property can only be set to a list of identifiers that represent GEL component observables.

The following example creates a new window called mywindow with a specific title, height and width. The content of the window is two components named myframe and anotherframe.

%angel
mywindow = window {
    title = "New window";
    height = 200;
    width = 400;
    content = [ myframe, anotherframe ] };

A window can have a title which is a string that is displayed in the title bar. Windows can be hidden and unhidden with the visible boolean property -- set this to 0 to hide the window (e.g. mywindow.visible = 0 ).

The frame

Special properties: content.

A frame is a basic container whose contents are set with the content property. Frames are useful for grouping and arranging components together.

The next example defines a frame called myframe which contains three components.

myframe = frame {
    content = [ mybutton, mylabel, mytextbox ] };

Components

The currently implemented components are described below. Each component has some special properties. The properties that are standard to all components are described in the next section.

The button

Special properties: text.

The button is a basic component that allows the user to perform a mouse click on the component that results in an action. A button has a special property called text which specifies the label (i.e. a string) that is placed on the button. When the user interacts with the button there are two observables which represent the current state: mybutton_mouseover and mybutton_mouseclick (where 'mybutton' is the name of the button). If the cursor is within the boundary of the button then mybutton_mouseover has the value 1, else it has the value 0 (zero). If the mouse button is pressed down on the button then mybutton_mouseclick has the value 1, else if the mouse button is up then it has the value 0 (zero).

%eden
/* Example of triggered procedure that performs an action on a mouse click */
proc mybutton_mouseclick_trigger : mybutton_mouseclick {
   if (mybutton_mouseclick == 0) {
      writeln("the user pressed mybutton");
   }
}

The textbox

Special properties: text, height, and width.

A textbox is a component for text entry. The size of the textbox can be defined by the width and height properties (specifying the size in characters, as opposed to pixels in other components). The observable mytb_text (where mytb is the name of the textbox), or the property text, represents the text in the textbox. If the user enters or modifies the text in the textbox then the observable mytb_text will always be representative of the current value (and it updates after each keypress). Also, if the user changes the string value of mytb_text, then textbox is likewise updated.

%angel
/* Example of a textbox */
mytb = textbox {
   text = "some initial text" };

The label

Special properties: text.

A label is a simple component for displaying text. It has only one special property, text, which defines the text to be displayed in the component.

%angel
/* Example of a label */
mylabel = label {
   text = "white";
   foreground = "white";
   background = "black" };

The checkbutton

Special properties: text, variable, offvalue, onvalue, indicatoron, and selectcolor.

The checkbutton is a special type of button which can be toggled on or off. The state of the button (whether it is on or off) is represented by the observable specified by the variable property.

The radiobutton

Special properties: text, variable, value, indicatoron, and selectcolor.

The radiobutton is a special type of button that can be used to select from a group of options. The radio buttons are grouped by an observable which is specified by variable. When a radio button is selected, the variable is set to the value of that particular radio button. The indicatoron property (set to 1) defines which radio button in a group is switched on.

The scrollbar

Special properties: scrolls, activerelief, elementborderwidth, jump, orient, repeatdelay, repeatinteval, takefocus, and troughcolor.

The scrollbar component is used to scroll other components, e.g. textbox, html, and scout. Every scrollbar must have an orientation, which can be set by the orient property to either "vertical" or "horizontal". The property scrolls determines which component the scrollbar effects, and should be a string containing the name of the component to be scrolled.

%angel
/* Example of a scrollbar on a textbox */
scrollbarexample = window {
   title = "Scrollbar Example";
   content = [mys,myt] };
mys = scrollbar {
   scrolls = "myt";
   orient = "vertical";
   side = "right";
   fill = "y" };
myt = textbox {
   side = "right";
   fill = "both";
   expand = 1 };

Scrollbars are usually packed on the right. If you want your scrolled component to stick to the scrollbar then you should also pack your scrolled component to the right also. Scrollbars can have a specific height but more often they expand the entirety of the axis. So in the above example the scrollbar has fill = "y" to indicate it stretches the y-axis.

The scale component

Special properties: bigincrement, digits, from, label, length, orient, repeatdelay, repeatinteval, resolution, showvalue, sliderlength, sliderrelief, state, takefocus, tickinteval, to, and troughcolor.

The scale component is similar to the scrollbar except that, instead of scrolling a component, it has a value. Like the scrollbar, the orient property can be set to either "horizontal" or "vertical". The extent of a scale can be set by the from and to properties. The value of a scale component is a number representing the current value, which can be used in a dependency/trigger (as below) or to set the current value.

%angel
/* Example of a window containing a scale component */
mywin = window {
   content = [myscale] };
myscale = scale {
   from = 0;
   to = 100;
   orient = "horizontal";
   showvalue = 1;
   fill = "x" };
%eden
proc myscale_value_trigger : myscale_value {
   writeln("myscale_value = "//str(myscale_value));
}

The scout component

Special properties: display.

The scout component can be used to display SCOUT inside GEL. The display property can be set to a string that is the name of any SCOUT display (e.g. "screen").

In order to display DoNaLD inside GEL, a scout component must be created and a DoNaLD window placed in the SCOUT display. The size of the DoNaLD window can be made dependent on the scout component size using the width and height properties.

The html component

Special properties: text, appletcommand, fontcommand, formcommand, framecommand, hyperlinkcommand, imagecommand, isvisitedcommand, rulerelief, scriptcommand, tablerelief, unvisitedcolor, underlinehyperlinks, and visitedcolor.

The html component is a text component that displays fully formatted HTML. The text property sets the HTML text to display in the component.

This component is based on the Tkhtml widget. The documentation for Tkhtml provides further information on how to use the special properties (configuration options) of this component.

The image

Special properties: file.

The image component has only one special property which specifies the file to be displayed. It must be a filename as a string, either a full path name or a relative path.

The listbox

Special properties: items, selecteditems, and selectmode.

The selectmode property specifies the style for manipulating the selection and takes any of the accepted values: "single", "browse", "multiple", or "extended". The default style is "browse", which enables the user to select one item from the list. The "multiple" and "extended" styles enable the user to select more than one list item.

The items property specifies the items in the listbox. The value of this property must be a list of strings. The selecteditems property specifies the selected items in the listbox component. The value of this property should be a list of strings which are a subset of the items property. When the user modifies the selection in the listbox then this observable is updated.

Standard properties

This section describes the properties that can be used with any component. Properties that relate to the layout of components in a container are described in the next section.

Layout properties

This section describes the properties relating to the layout of components within a container.

The content of frames and windows are arranged by the GEL packer. The %angel packer is based on the Tcl/Tk Pack Geometry Manager. The basic rules are:

The full list of layout properties are:

Bindings property

The bindings property is used to define bindings for user interactions. Several default bindings are setup for some components, for example, if a textbox changes then the mytextbox_text observable is updated. The bindings property enables new bindings to be created to respond to keyboard, mouse and other user events.

The bindings property must be specified as a list of bind pairs separated by commas and surrounded by square brackets. The first item in a bind pair is the action to 'bind to' (usually surrounded in chevons <>). The second item should be surrounded by curly braces and can contaion any EDEN code to be executed when this action occurs.

Some standard bind event types are: Activate, Deactivate, MouseWheel, KeyPress, KeyRelease, ButtonPress, ButtonRelease, Motion, Destroy, FocusIn, FocusOut, Enter, Leave.

Some of these event types (i.e. KeyPress, KeyRelease, ButtonPress, ButtonRelease) can be modified with: Control, Alt, Shift, Button1, B1, Button2, B2, Button3, B3, Double, Triple, Quadruple.

Combinations take on the form <modifier-modifier-type-detail>. Some examples: <KeyPress-a>, <Escape>, <B1-Motion>, <Control-ButtonPress-1>.

For example, the following binds two actions, holding Ctrl with a button press, and a double click.

%angel
/* Binding example */
mywin = window {
   title = "Click me!";
   bindings = [
     <Control-ButtonPress-1> { writeln("You pressed control with mouse button 1"); },
     <Double-ButtonPress-1> { writeln("You double clicked mouse button 1"); } ] };

Examples

The example below explains how to use some basic components to create an interface, and how GEL can be linked to EDEN. Further examples containing other components can be found in the examples directory of the GEL package.

Baby Eden

This example is a baby version of the tkeden input window. It contains a textbox for entering definitions, a button for accepting definitions, and a button for exiting from the application.

The first part of the script defines a window, with a title, and containing three components. This is defined in the GEL notation (%angel).

%angel

babyedenwin = window {
   title = "Baby Eden";
   content = [accept,txt,exit] };
And then the three components are created. One textbox for the input, one button for accepting the input, and one button for exiting tkeden.
accept = button {
   text = "Accept";
   side = "left" };

txt = textbox {
   width = 30;
   height = 1;
   side = "left" };

exit = button {
   text = "Quit";
   side = "left" };
After entering the above GEL definitions, the interface is displayed but the components are only a collection of observables (i.e. the buttons do not have any behaviour). In the following script, EDEN is used to define triggered procedures on each of button click actions.
%eden

proc trigger_execute : accept_mouseclick {
   if (accept_mouseclick == 0) {
      if (txt_text != @)
         execute(txt_text);
      txt_text = "";
   }
}

proc trigger_exit : exit_mouseclick {
   if (exit_mouseclick == 0) {
      exit();
   }
}

References