5 Declarative Specifications Using the GBuilder

When implementing a user interface, and in particular when refining it for visual effect, you'll find that a significant number of parameters need to be set on its component widgets. The API then requires you to invoke a large number of functions, which is cumbersome and yields code that is hard to maintain.

Declarative Specification

The GBuilder is an abstraction providing for declarative specification of user interfaces. It allows to create widget hierarchies, configure them, and attach signal handlers. A widget description is a record whose label names the widget class and whose features configure its arguments, signal handlers, and children.

Robustness

Using the GBuilder also provides for more robust programs than programs implemented using the binding API directly: You get more checking, in particular type-checking of arguments, and many errors are reported by Oz exceptions.

Related Work

The design of the GBuilder has been inspired by QTk, a description-based user interface builder for Tk, available in the Mozart Standard Library.

5.1 ``Hello, World'' Revisited

The following is an example of a specification. It is a formulation of the ``hello world'' application from Chapter 1 using the GBuilder.

functor 
import 
   Application(exit)
   System(showInfo)
   GBuilder(create)
define 
   proc {DeleteEventCallback Args}
      {System.showInfo 'handling deleteEvent'}
      {Application.exit 0}
   end 
   proc {ClickedCallback Args}
      {System.showInfo 'handling clicked'}
   end 
   Spec = window(type: toplevel
                 borderWidth: 10
                 title: 'Hello, World!' 
                 deleteEvent: DeleteEventCallback
                 add(button(label: 'Hello, World!' 
                            clicked: ClickedCallback)))
   {{GBuilder.create Spec} showAll()}
end

A Closer Look

When we look at the core of the program, namely the definition of Spec, we notice a number of things. The record's label tells that it specifies a window widget. Some features, namely type, borderWidth and title, set some of the window's arguments. Other features are used to connect handlers to signals, such as deleteEvent. Children can be specified under integer features: These name the method used to add the child as a record label and give the child's specification under feature 1. The GBuilder.create method takes a specification, creates the corresponding widget tree, and returns the root widget.

5.2 Specifications

This section describes specifications in more detail, and explains how the GTK+ reference documentation maps to GBuilder specifications.

Verbatim Objects

A specification is either a verbatim object, which is an already instantiated widget, or it is a record describing a widget that is to be constructed and configured. This allows to freely mix widgets created through the binding API and through the GBuilder. When a specification is a widget description, it is interpreted as described in the following sections.

5.2.1 Widget Classes

The widget class is given by the label of the specification record. The different API namespaces are not distinguished. Currently, the following widget classes are supported by the GBuilder:

GDK classes.

Only color and font are provided as GBuilder classes.

GTK classes.

All GTK widget classes except cTree, image, and pixmap should be implemented.

GTK Canvas classes.

Only the canvas class is provided.

Note that you can freely mix widgets created with the GBuilder and with the binding API, so you can easily work around these limitations.

5.2.2 Handles

When you need to work with GBuilder-created widgets using the binding API, you need a reference to the widget object proper: When a specification record contains a handle feature, the corresponding subtree is expected to be a logic variable which will be bound to the widget when it is created.

5.2.3 Arguments

Arguments are configured by atom features. Argument names always adhere to the camel-casing scheme, with the first letter downcased. In general, argument values are type-checked before they are passed to the binding API, so that you get a meaningful Oz exception in the case of an ill-typed argument.

Argument Kinds

The GBuilder distinguishes arguments of several kinds:

Generic GTK+ arguments.

All arguments listed by the GTK+ reference documentation are available as GBuilder arguments.

Accessors.

The GTK+ API provides a number of accessor methods (i. e., functions containing _get_ or _set_ in their name). Not all of these are available as generic GTK+ arguments. The GBuilder provides arguments for all accessor methods, where the argument's name is derived from the accessor method names.

Constructor parameters.

Some constructors in the GTK+ API reference documentation accept parameters, and not all of these are defined as generic GTK+ arguments, or are gettable and settable using accessors. These parameters are also available through GBuilder arguments; their name is then usually derived from the formal parameter name given in the reference documentation.

5.2.4 Types

For increased convenience, types are mapping differently in GBuilder than they are by the binding API. Integers, floats, and (virtual) strings are handled identically. Booleans are represented as Oz booleans (true and false) instead of integers. String arrays are converted implicitly from and to lists of virtual strings instead of requiring GTK.makeStrArr and GTK.getStrArr. (Due to a current limitation, however, this may create a memory leak, since they are not freed automatically.)

Enumerations

Values of enumeration types are represented as atoms instead of integers, i. e., you can write toplevel instead of GTK.'WINDOW_TOPLEVEL' in GBuilder specifications: The type name prefix is cut off from the constant's name and it is camel-cased, with the first letter downcased.

Flags

Flag types are represented as lists of atoms instead of integers. Individual flag names are represented similarly to enumeration values. In GBuilder, you'd use [expand fillX fillY] instead of GTK.'PACK_EXPAND' + GTK.'FILL_X' + GTK.'FILL_Y'.

5.2.5 Signal Handlers

Signal handlers are configured by atom features that carry the signal's name. Dash-separated signal names from the reference documentation are converted to atoms by camel-casing, removing the dashes, and downcasing the first letter. The GBuilder will check that the signal is actually defined for the corresponding widget class.

Callbacks

The following values are supported for callbacks:

Procedures.

A standard unary procedure accepting the signal's arguments can be provided as a callback.

Port sends.

A pair of a port and a message can be given as a callback. When the callback is invoked, the message is sent on the port. The signal's arguments are ignored.

Object messages.

A pair of an object and a message can be given as a callback. When the callback is invoked, the objects is applied to the message. The signal's arguments are ignored.

5.2.6 Children

For container widgets, a number of children can be specified. These are either defined by consecutive integer features starting with 1, or given as a list under feature children.

Adding Children

Different container widgets provide different ways of adding children, and some container widgets support several ways. For this reason, the child is not direcly given as a specification, but within an attacher specification. Furthermore, attachers support different parameters configuring how to lay out the child.

Attachers

Therefore, an attacher is also a record. The label gives the method used to attach the child (e. g., containers have a generic add; boxes provide packStart and packEnd methods, tables use attach). The attacher record must give the child widget's specification under feature 1. Other atom features can parameterize the attacher. Section 5.4.1 gives a list of all currently defined attachers.

5.3 Configuration and Access

In addition to specifications for creating widgets, the GBuilder provides new widget methods to reconfigure widgets and to access arguments. Note that these methods are not available to objects created using the binding API, only to those created using the GBuilder's create function.

conf(...)

reconfigures the widget. Any mutable arguments can be set by including atom features with the argument names in the message. Any signal handlers can be added (in addition to existing ones) by including atom features with the signal names in the message. New children can be added (to those already present) by including integer features or the atom feature children in the message.

return(...)

accesses argument values. Only atom features representing argument names can be given in the message. If the argument is readable, then the corresponding subtree will be bound to the argument's current value, else an exception is raised.

5.4 Reference

This section provides a reference to the GBuilder functionality as far as it is not defined by the generic mapping described before.

5.4.1 Attachers

The following lists all attachers supported by the GBuilder.

All subclasses of container

provide a generic add method to add a child. add cannot be parameterized; its default behaviour depends on the container.

Classes hBox, vBox, hButtonBox, vButtonBox

packStart
packEnd

packs a new child with reference to the start (packStart) or the end (packEnd) of the box, adding later children away from the reference. Both support the same set of parameters:

Parameter

Type

Default

expand

boolean

true

fill

boolean

true

padding

int

0

pack

packs a new child, with the reference point specified as a parameter (in addition to the above parameters):

Parameter

Type

Default

atEnd

boolean

false

Class table

attach

Parameter

Type

Default

side

sideType

N/A

anchor

anchorType

center

options

packerOptions

[expand fillX fillY]

borderWidth

int

0

padX

int

0

padY

int

0

iPadX

int

0

iPadY

int

0

Class packer

add

packs a new child.

Parameter

Type

Default

side

sideType

N/A

anchor

anchorType

center

options

packerOptions

[expand fillX fillY]

borderWidth

int

0

padX

int

0

padY

int

0

iPadX

int

0

iPadY

int

0

Classes menuBar, menu

append

adds a menu to a menu bar or a menu item to a menu.

Classes hPaned, vPaned

add1
add2

uses the add attacher of pane 1 or 2, respectively.

pack1
pack2

uses the pack attacher of pane 1 or 2, respectively, with the following parameters:

Parameter

Type

Default

resize

boolean

true

shrink

boolean

false

Class menuItem

submenu

attaches a submenu to a menu item. The child must be a menu widget.

Class scrolledWindow

addWithViewport

adds a child without native scrolling capacities. If a child has native scrolling, use the generic add attacher instead.

Class notebook

appendPage

appends a widget as a new page to a notebook.

Parameter

Type

Default

tabLabel

widget

N/A

menuLabel

widget

unit

Class tree

append

adds a treeItem child to the end of the items in the tree list.

Class dialog

is a compound widget containing a vertical box and an action area. Attachers are provided that use either of these.

vBox

packs a child into the vertical box. This supports the same parameters as the pack attacher from vBox.

actionArea

packs a child into the action area. This supports the same parameters as the pack attacher from hBox.

Classes fixed, layout

put

adds a child at a fixed position:

Parameter

Type

Default

x

int

N/A

y

int

N/A


Thorsten Brunklaus and Leif Kornstaedt
Version 1.3.1 (20040823)