The FormsVBT Language

The FormsVBT language provides a mechanism for textually describing a user interface. The language is not a general-purpose programming language. It has no variables, control structure, or runtime computation. Rather, it's a declarative textual description of the hierarchical arrangement of components that make up the user interface, written as an S-expression. Fig. [fig:horn] shows a simple user interface and its S-expression.

Basic Syntax

Each component is written as an S-expression that begins with type of the component, such as Border, Button, or VBox. Following the type are subexpressions that describe eitherproperties or children (sub-components). Properties provide additional information that control the appearance or behavior of the component. The S-expression in Fig. [fig:horn] contains properties Pen, Pattern, Width, Height, BgColor, and Color. Property-expressions are distinguished from child-expressions by their names. There is one important rule to remember:

In any S-expression, all property expressions must appear before any sub-components.

Properties are discussed in detail in Section [sec:language-properties] .


(Border (Pen 20) (Pattern "NWDiagonal")
  (Button %cornet
    (VBox
      (Shape (Width 50) (Height 40) 
        (Pixmap "Trumpet"))
      (Text (BgColor "Black") (Color "White") "Horn"))))

A very simple user interface and its description in the FormsVBT language. The user interface consists of a button that is surrounded by a border. The button itself displays a pixmap of a trumpet above the word ``Horn''. At runtime, the application will register an event-handler for the component named ``cornet''; the event-handler will be invoked when the user clicks on the button.

Components

The components of FormsVBT can be categorized in two ways: by the number of children that they take, or by their function. In the first categorization, we have leaves, filters, and splits:

In Fig. [fig:horn] , Text and Pixmap are leaf components; Border, Button, and Shape are filters; and VBox is the only component in the form that is a split.

The second way to categorize the components is by their function:

Properties

Properties provide information that modifies the appearance or behavior of a component. A property subexpression has the following format:


    (keyword value)

The keyword implies the expected type of the value; values are type-checked when the description is parsed by FormsVBT. Nearly all properties have default values and can be omitted. If you are developing a form using the formsedit interface-builder, then type- and syntax-errors will be reported and highlighted each time that you issue the ``Do It'' command. If an application gives the FormsVBT runtime system a syntactically incorrect S-expression to parse, FormsVBT will raise an exception to signal the syntax error. For example, the expression


    (Boolean (CheckMark TRUE) (MenuStyle TRUE) "Gravity")

defines a Boolean interactor with two properties. The CheckMark property says to use a check mark rather than a check box for visual feedback, and the MenuStyle property says that the Boolean should be responsive to a mouse rolling into it, rather than responding only to a mouse click. You would use MenuStyle when the Boolean is an element of a menu.

A value must have one of the following types:

Text
A quoted string.

  (Border (Pattern "NWDiagonals") ...)

Cardinal
A positive integer.

  (TSplit (Value 4) ...)

Integer
An integer.

  (Numeric (Min -100) (Max 100))

Real
A real number. A whole number does not need a decimal point, and a number between -1 and 1 does not need a leading zero.

  (Border (Pen 4.25) ...)

Boolean
The token TRUE or FALSE.

  (Boolean (MenuStyle TRUE) ...)

CardinalList
A list of positive integers.

  (MultiBrowser (Value 1 5 3 19))

TextList
A list of quoted strings.

  (FileBrowser (Suffixes "i3" "m3"))

Symbol
A name. For example, the For and Name properties are of this type:

  (PageButton (For letters) "Next")
  (Button (Name no) "Cancel")

Names are either identifiers (a letter followed by any number of letters, digits, or underscores), or non-empty sequences of characters from the set


	! # $ % & * + - . / : < = > ? @ [ ] ^ _ { } ~

or a sequence of characters and escape sequences surrounded by vertical bars (e.g., |Sue's button|). The escape sequences are


	\n \t \r \f \\ \|

and \ followed by three octal digits.

Font
The name of a font conforming to the specifications in ``X Logical Font Descriptions'' [XWindowSystem] . The font can be specified in two ways: A quoted string in the form that xlsfonts prints and accepts as a pattern (we call this the string format), or a list of parenthesized keyword pairs for the parts of a font (we call this the list format). Consider the following example:

  (VBox 
    (Text (LabelFont "helvetica_bold14") 
        "Helvetica Bold @ 14pts")
    (Text (LabelFont "-*-courier-medium-*-140-*") 
        "Courier @ 14pt")
    (HBox (LabelFont (Family "Times")
                     (PointSize 140))
      (Text "Times@14pt")
      (Text (LabelFont Reset 
                       (Family "*")
                       (Width "semicondensed")) 
         "SemiCondensed@any")
      (Text (LabelFont (PointSize 180)
                       (Slant "i")) 
          "Italics@14pt"))))

The font specification for the top two children of the VBox use the string format. The other font specifications are in the list format. Here's what the example looks like:

In the list format, the keywords for the parts of a font are

Foundry

Family

WeightName

Slant

Width

PointSize

HRes

VRes

Spacing

AvgWidth

Registry

Encoding

PointSize, HRes, VRes, and AvgWidth take cardinal values or the string "*". All the others take strings. Unspecified parts of a font take on the value of the nearest ancestor component for which the part was specified using the list format. However, the keyword Reset causes all unspecified parts of a font to take on the default values assigned by FormsVBT.
Color
The description of a color either as a triplet of real numbers between 0.0 and 1.0 representing RGB or HSV values, or as a string. The following example shows both formats:

  (Pixmap
    (BgColor .5 0.23 1.0)
    (Color "VeryPaleRed") "MailBox")

The triplet may be preceded by one of the symbols RGB or HSV. The default is RGB. The symbol HSV represents hue-saturation-value. Example:


  (BgColor HSV 0.1 0.45 0.222)

Appendix [ap:colornames] describes the conventions used for naming colors.

Sx
An S-expression in the FormsVBT language. For example, the Title property of a ZChassis (a frame for a subwindow) has this type:

  (ZSplit
    (ZBackground ...)
    (ZChassis
      (Title 
        (HBox 
          Fill 
          "Window # "
          (Border (Text %wid =""))
          Fill))
      ...))

Enumeration
A set of mutually exclusive tokens. FormsVBT supports the following enumerations:
Alignment
Center, LeftAlign, RightAlign}
Axis
Horizontal, Vertical
FeedbackStyle
CheckBox, CheckMark, Inverting
Reactivity
Active, Passive, Dormant, Vanish
ScrollStyle
HorOnly, VerOnly, NoScroll, AlaViewport, Auto
ShadowStyle
Flat, Raised, Lowered, Ridged, Chiseled
Think of each enumeration as a collection of Boolean properties, at most one of which may be specified as TRUE. If no choices are specified, then it's as if the default choice was given. Here are some examples:

(Viewport (VerOnly TRUE) (Horizontal TRUE) ...)
(Filter (Dormant TRUE) (Vanish FALSE)
    (Button ...))
(Frame (Raised TRUE)
    (Rim (Frame (Lowered TRUE) ...)))

Size
The description of the dimensions of a component along some axis. It has the syntax

    [size] [+ stretch] [- shrink]

where size, stretch, and shrink are specified as points in real numbers. Stretch and shrink, if both specified, may be in either order. Spaces are required around the plus and minus signs. The keyword Inf is used to indicate a very large value for stretch. See Section [sec:language-sizespec] for more details.

At
The location of a subwindow component relative to its parent. There are two ways to specify this location: you can say where the center or a particular corner should be positioned; or you can specify where the four edges should appear. To position a subwindow by its center or corner, you write

(At h v [Center | NW | NE | SE | SW] [Scaled | Absolute])

If you don't specify the center or a corner, the default is Center. If you don't specify whether h and v are scaled or absolute, the default for this form is Scaled, which means that h and v indicate the proportionate placement in the horizontal and vertical directions of the center or corner; in this case, h and v must be numbers in the range 0--1. Otherwise, in the absolute case, h and v represent the horizontal and vertical distance, in points, between the subwindow's center or corner, and the parent window's northwest corner.

To position a subwindow by its edges, you write


(At west east north south [Absolute | Scaled])

The default in this case is Absolute, not Scaled; it indicates the distance in points between the subwindow's edges and the parent's west and north edges. Note that this is the only case in which you can specify the subwindow's exact size.

Scaled indicates the proportion of the parent window's width and height that mark the subwindow's boundaries. For example,


(At .10 .10 .90 .90 Scaled)

is effectively a ``10% Rim'' around the subwindow, while


(At .50 .50 1 1 Scaled)

places the subwindow in the parent window's southeast quadrant. See Section [sec:language-atspec] for more details.

Varieties of Properties

Properties come in three varieties: class-specific, inherited, and universal.

Class-Specific Properties

Class-specific properties are defined in conjunction with a specific component class, and are allowed only on components of that class. It is fine for several components to use the same specific property.

There are two very common class-specific properties: Main and Value.

Main
Many passive leaf components exist to display some object; such an object is specified by a property called Main, whose type varies. If this property exists, it is usually required. Its value is usually specified by a shorthand: it simply follows the component keyword, without the word Main or parentheses. Here are two examples, in the abbreviated format:

    (Texture "Gray")
    (Pixmap "OpenRightArrow")

Value
The Value property specifies the initial state of some user-modifiable value. The type of this property depends upon the value type of the interactor. No class has both a Main property and a Value property. Here are two examples, in the abbreviated format:

    (Numeric =5)
    (TextBrowser (From "choices.txt") =(2 5 1))

Other specific properties (and there are many) are described with their component classes in Appendix [ap:longcatalog] .

Universal Properties

Universal properties are applicable to components of all classes, and have a system-defined meaning. There is currently only one such property: Name.

Name (type: Symbol; default: none)
The name of a component, for access by the application. The type of the Name property is a Symbol and it has no default value. A form may not contain duplicate names; not all components need to have a Name property. The property may be abbreviated: (Name goButton) can be written as %goButton.

Inherited Properties

Inherited properties, like universal properties, may be specified for any component, though they are not relevant to all classes. But they have the special feature that a value specified for one component becomes the default value for all descendants of that component. Thus an inheritable property specification applies not to one VBT, but to an entire subtree. The inherited properties are: Font and LabelFont; Color and BgColor; LightShadow, DarkShadow, and ShadowSize. In essence, the inherited properties determine the overall ``look and feel'' of the user interface.

Font (type: Font; default: see below)
The font for components that display selectable text, such as TextEdit and the type-in part of a Numeric. The type of the Font property is Font, and the default value would be written in list format as follows:

  (Font
    (Foundry "*")
    (Family "fixed")
    (WeightName "medium")
    (Slant "r")
    (Width "normal")
    (PointSize 120)
    (HRes "*")
    (VRes "*")
    (Spacing "*")
    (AvgWidth "*")
    (Registry "iso8859")
    (Encoding "1"))

Essentially, it's a 12-point, fixed-width font that can be scaled (using the Scale component).

LabelFont (type: Font; default: see below)
The font for components that display non-selectable text, such as Text, and various browsers such as MultiBrowser and FileBrowser. The type of the LabelFont property is Font, and the default value would be written in list format as follows:

  (LabelFont
    (Foundry "*")
    (Family "helvetica")
    (WeightName "bold")
    (Slant "r")
    (Width "*")
    (PointSize 120)
    (HRes "*")
    (VRes "*")
    (Spacing "*")
    (AvgWidth "*")
    (Registry "iso8859")
    (Encoding "1"))

Essentially, it's a 12-point, boldface helvetica font that can be scaled (using the Scale component).

Color (type: Color; default: 0 0 0)
The foreground color; used for displaying text, bars, borders, the ``on'' pixels of pixmaps and textures, and so on. The default foreground color is black.
BgColor (type: Color; default: .8 .8 .8)
The background color; used for displaying text background, glue, and the ``off'' pixels of textures. The default background color is a light gray.
LightShadow (type: Color; default: 1 1 1)
The color used for the ``light shadow'' in implementing a Motif-like 3-d look. The default light shadow is white.
DarkShadow (type: Color; default: .333 .333 .333).
The color used for the ``dark shadow'' in implementing a Motif-like 3-d look. The default dark shadow is a dark gray.
ShadowSize (type: Real; default: 1.5).
The absolute value of this property is the size of the ``shadow'' in implementing a Motif-like 3-d looks. The default value is 1.5 points.

Look and Feel

In order to have an effective Motif-like look and feel, you need to change the LightShadow and DarkShadow whenever you change the BgColor. Shiz Kobara [kobara] provides an excellent set of guidelines for choosing harmonious color triples.

On a grayscale monitor, objects are displayed using the intensity of their color.

On a monochrome monitor, FormsVBT does not support a Motif-like look and feel. Rather, the user interface appears ``Macintosh-like.'' For example, feedback on buttons is given by inverting the image of an object rather than raising and lowering the object; a Radio button uses bitmaps showing a filled or empty circle rather than a 3-d diamond that is either raised or recessed. Behind the scenes, two things are happening. First, on monochrome displays, BgColor displays as background and the other colors display in foreground. Second, the FormsVBT interactors are implemented in such a way as to give feedback using a 2-d style when displaying on a monochrome dispay.

Actually, the FormsVBT interactors use the Motif-like style only when they are on a non-monochrome display and the ShadowSize property is positive. Therefore, you can force a non 3-d look for a color or gray-scale monitor by setting the ShadowSize to be 0. You should probably also change the BgColor to be white in this case. Alternatively, you may find it convenient to set ShadowSize to be a negative number and the shadow colors to be black, as follows:


  (BgColor "White") 
  (LightShadow "Black") 
  (DarkShadow "Black") 
  (ShadowSize -1.5)

This setup will cause the shadows on various objects, like buttons, to appear as black borders.

Syntactic Shortcuts

This section describes various shortcuts that make FormsVBT descriptions more readable.

  1. The Main property may be given simply by giving its value, without the keyword or parentheses. This is allowed only for leaf components.

    (Texture (Main "LightGray")) is same as (Texture "LightGray")

  2. The Value property may be abbreviated by an equal sign, without parentheses, and with no intervening space.

    (Numeric (Value 27)) is same as (Numeric =27)

    Exception: If it's a TextList or CardinalList, then parentheses are needed.

    (MultiBrowser (Value 4 9 2)) is same as (MultiBrowser =(4 9 2))

  3. The Name property may be abbreviated by a percent sign.

    (Button (Name xyz) ...) is same as (Button %xyz ...)

  4. Any Boolean-valued property may be set true simply by giving its name, without parentheses. By convention, the default value of all Boolean properties is false.

    (TypeIn (Scrollable TRUE)) is same as (TypeIn Scrollable)

    An element of an enumeration is a Boolean.

    (Viewport (Auto TRUE) ...) is same as (Viewport Auto ...)

  5. A component of class Text may be given simply as a quoted string, provided that no properties other than the string are specified.

    (Text "Hello") is same as "Hello"

  6. Any leaf component from the following list

    Bar Chisel Fill Glue Ridge

    may be given by its keyword, without parentheses.

    (HBox "A Heading" (Fill)) is same as (HBox "A Heading" Fill)

Macros

The FormsVBT language supports macros. A macro is a procedure that returns an S-expression, called the expansion, that replaces the macro call; that is, the expansion is itself a FormsVBT expression. The parameters passed to the macro are not evaluated by the call, although they may be evaluated in the body of the macro. A macro definition can appear anywhere a component or property can appear.

A macro-definition has the following syntax:

(Macro name [BOA] (formal ... formal) expression)

A formal parameter is either a name or a list of the form

(name default)

where default is any S-expression, the default value for the parameter.

A macro uses either positional binding or keyword binding, but not both. If the definition includes the keyword BOA (``By Order of Arguments''), then the macro uses positional binding, and the macro-call must have the form

(name actual ... actual)

The actuals are bound to the formals in left-to-right order.

If the definition does not include the keyword BOA, then the macro uses keyword binding, and the macro-call must have the form

(name (formal actual) ... (formal actual))

The actuals are bound to the formal parameters with corresponding names.

The number of actual parameters may not exceed the number of formal parameters. If there are fewer actuals than formals, then all the remaining formals must have default values.

The body of the macro-definition is an expression that is evaluated (expanded) when the macro is called. Typically, the body is a quoted or backquoted S-expression. As in Common Lisp macros, quoted S-expressions are constants; they expand into themselves. Backquoted expressions are templates; all of the subexpressions are treated as constants except for expressions preceded by a comma or a comma-atsign combination. In the expression `(A ,x B), the value of x is substituted as the second element of the list; the expanded list will always have length 3. In the expression `(A ,@x B), the value of x must be a list, and the elements of that list are ``spliced in'' between A and B; the expanded list will have length 2 (if the value of x is the empty list) or more. For example, here is a macro that puts a 2-point border around its argument, after surrounding the argument by 16 points of background space on all four sides:

    
    (Macro Boxed (x)
      `(Border (Pen 2) (Rim (Pen 16) ,x)))

The call (Boxed (x (Text (BgColor "Red") "Warning"))) expands to

    
    (Border (Pen 2)
      (Rim (Pen 16)
        (Text (BgColor "Red") "Warning"))))

If the definition of Boxed had included the keyword BOA then the expression could have been written as

    
(Boxed (Text (BgColor "Red") "Warning"))

Thus, for all practical purposes, we've effectively added a new filter-component called Boxed to the FormsVBT language.

Here is an example showing the use of default values:


    (Macro Ht BOA (v (n 16)) `(Shape (Height ,n) ,v))

With this definition, the call (Ht (Button "Go!") 20)

expands into


    (Shape (Height 20) (Button "Go!"))

The call (Ht (Button "Stop"))

uses the default value of n and expands into


    (Shape (Height 16) (Button "Stop"))

An example using comma-atsign:


    (Macro V (items) 
      `(VBox (Color "Red") Fill ,@items Fill)) 

Given this definition, the call (V (items ("abc" "def" "hij"))) expands into


    (VBox (Color "Red") Fill "abc" "def" "ghi" Fill)

Macros must be defined before they are called. The effect of using a macro to redefine an existing name (e.g., VBox) is undefined.

It is permitted for a macro to expand into another Macro-expression, or into an expression containing another Macro-expression. Nested backquotes are permitted; they follow Common Lisp evaluation-semantics.

The expressions that are permitted in the body of a macro are not restricted to quoted and backquoted expressions. As we have already seen, an expression may be the name of a formal parameter; the value of such an expression is the value of the corresponding actual parameter. Other expressions that are permitted include the following:

(Cat x y z ... )
There must be at least two arguments, and all of them must have type TEXT. The result has type TEXT. Example: (Cat "Gate-" x "-button")
(Empty x)
The argument must be a TEXT; the result has type BOOLEAN.
(Equal x y)
The arguments may have any type; the result has type BOOLEAN.
(Length x)
The argument must be a TEXT or a list; the result has type INTEGER. (FormsVBT does not support a separate type for cardinals.)
(Sub s start count)
The argument s must be a TEXT; start and count must be non-negative integers. The result is a TEXT.
(SymbolName x)
The argument must be a symbol; the result is a TEXT.
(Intern x)
The argument must be a TEXT; the result is a symbol.
(Cons x y)
The first argument may have any type. (All S-expressions are REFs.) The second argument must be a list. The result is a list.
(List x y z ...)
The arguments may have any type; the result is a list.
(List* x y ... z)
There must be at least two arguments. The last argument must be a list; the others may have any type, and they are ``consed'' onto the front of the last argument. Example: (List* 1 2 3 '(a b)) is same as (1 2 3 a b)
(Append x y z ...)
All the arguments must be lists; the result is a list.
(Nth x n)
The first argument must be a list; the second argument must be an integer in the range [0 .. RefList.Length(x) - 1]. The result is the nth element of the list.
(NthTail x n)
The first argument must be a list; the second argument must be an integer in the range [0 .. RefList.Length(x) - 1]. The result is the nth tail of the list.
(IF pred x y)
The value of the first argument must be a BOOLEAN. If the value is TRUE, then x is evaluated, and its value is the value of this expression. Otherwise, y is evaluated, and its value is the value of this expression. I.e., this is IF as in Lisp, not as in Modula-3.
(AND x y z ...)
All the arguments must be of type BOOLEAN, as is the result. The arguments are evaluated from left to right. If any argument evaluates to FALSE, the value of this expression is FALSE, and the remaining arguments are not evaluated. If all the arguments evaluate to TRUE, or if there are no arguments, then the value of this expression is TRUE.
(OR x y z ...)
All the arguments must be of type BOOLEAN, as is the result. The arguments are evaluated from left to right. If any argument evaluates to TRUE, the value of this expression is TRUE, and the remaining arguments are not evaluated. If all the arguments evaluate to FALSE, or if there are no arguments, then the value of this expression is FALSE.
(NOT x)
The argument must be a BOOLEAN, as is the result.
(= x y z ...)
There must be at least two arguments. If x is a number (integer or real), then all the other arguments must be numbers of the same type as x, and the result is TRUE if they are all equal, and FALSE otherwise. If x is not a number, then the result is TRUE if all the arguments are the same REF.
(< x y z ...) (<= x y z ...) (> x y z ...) (>= x y z ...)
There must be at least two arguments, and they must all be numbers of the same type as x. The result is of type BOOLEAN.

(< x y z ...) is same as (AND (< x y) (< y z) ...)

Likewise for the other operations.

NIL
This is a constant.

Macros provide one kind of extensibility to the FormsVBT language. Another kind of extensibility is provided by the realize method for a FormsVBT.T object. The realize method allows the programmer to define subtypes of the VBT classes that FormsVBT uses, such as the FVTypes.FVButton. However, it is not currently possible for the client to extend the language with any other VBT classes, such as TranslateVBT.T or a client-defined subtype of VBT.Leaf. See Section [realize] for details.

Layout

Every component has a natural size in the horizontal and vertical axes; these are its width and height. It may also have shrinkability and stretchability in each axis, to allow it to adapt in a visually pleasing way as the window is resized. The minimum size of a child in each axis is its natural size minus its shrinkability, and the maximum size in each axis is its natural size plus its stretchability. The size range of a component in each axis is the interval between its minimum and its maximum.

The size ranges in each axis are computed for a top-level window by a bottom-up process. Each split computes its ranges as a function of the size ranges of its children; the function used depends on the type of the split.

FormsVBT uses TeX's ``boxes-and-glue'' layout model. At the center of the layout strategy are two split classes, HBox and VBox. These organize the layout of their children along the horizontal and vertical axes, respectively. To keep the discussion simple, we will explain the algorithm for HBox.

An HBox reports its size as follows. An HBox's natural width is the sum of the natural widths of its children; its width shrinkability is the sum of the width shrinkabilities of its children (but no more than its natural size), and its width stretchability is the sum of the width stretchabilities of its children. An HBox's height range is the intersection of the height ranges of its children (if the intersection is empty, the children's maximum heights are increased until the intersection is non-empty). The HBox's natural height is the maximum of the natural heights of its children, projected into the range.

Ultimately, the shape of each top-level window is controlled by the user through a window manager. The window manager allows the user to shrink and grow a top-level window in each axis. However, the window manager will not let user grow a top-level window beyond its maximum size bounds, or shrink a top-level window below its minumum size bounds, in each axis. When a top-level window's size is changed, the new size-information is propagated down through the top-level window's tree of subwindows. How each split component communicates this information to its children depends on the type of the component.

When an HBox is given some screen real estate to allocate among its children, here's what happens. In the vertical dimension, it gives each child the same vertical height it has been given; that's easy. In the horizontal dimension, things are more interesting. The HBox computes the sum of the natural widths of the children; this is the natural width of the HBox. Ideally, the HBox would give each child its natural width and that's all there is to it. If this is not possible, then either the HBox has extra space it must divide among the children, or the HBox must take away space from its children.

In the first case, the HBox allocates its extra space in proportion to the children's stretchabilities. For example, if the first child has twice as much stretchability as the middle child, and three times as much as the third and last child, then the extra space is divided 6/11, 3/11, and 2/11 to the children, from left to right. In the second case, the HBox takes away space from the children in proportion to the amount that each child can shrink.

If the sum of the minimum sizes of the children is greater than the size of the HBox, then the HBox is said to be overfull. In this case the children are considered in order and given their minimum sizes, as long as there is room. The first child that doesn't fit is given all the space that's left, and the remaining children are given size zero.

If the sum of the maximum sizes of the children is less than the size of the parent, the split is said to be underfull. This produces a state in which the children are stretched larger than their maximum sizes, but in proportion to their relative stretchabilities.

How Sizes are Specified

Most of the time, sizes are not given explicitly; natural sizes are allowed to take effect. Leaf components have an inherent natural size that is usually data-dependent. For example, the size of a Text is the size of the rectangle needed to display it in the appropriate font. In each axis, it has no shrinkability but ``infinite'' stretchability. A vertical Scroller has a fixed width (a natural size with no stretch and no shrink). Its natural vertical size is quite small (enough to show a ``thumb''), it has no vertical shrinkability, and it has infinite vertical stretchability. In practice, a vertical scrollbar is an element of an HBox, so it takes on the size of the other elements of the HBox.

A filter component derives its size information from its child. A Border component, for example, takes the size of its child, but adds twice the border's thickness in both dimensions. A Guard takes on precisely the size of its child. Appendix [ap:longcatalog] describes how each component computes its shape.

A property of type Size is used to describe the size of a component along one dimension. It has the syntax


    [size] [+ stretch] [- shrink]

where size, stretch, and shrink are specified as points in real numbers. Stretch and shrink, if both specified, may be in either order. Spaces are required around the plus and minus signs. The keyword Inf is used to indicate a very large value for stretch; it may also be spelled inf or INF.

A natural size may be overridden, completely or in part, by specifying the Width and Height properties on a Shape filter. For the sake of simplicity, let's consider just the Width property. There are eight situations to consider: when size, stretch, and shrink are all missing; when just size is given; when just stretch and shrink appear; and so on.

See Table [flexvbt] for details.

This table describes what Shape reports, as a function of its child's size. The notation <q-p, q, q+r> refers to the child's size: the natural size is q; it has p shrinkability, so it can shrink to a minimum of q-p, and it can stretch to a maximum of q+r.

all missing

<q-p, q, q+r>

A no-op; reports the child's size

size

<size, size, size>

Constrains child's natural size to size, with no stretch or shrink

- shrink

^lt;q-shrink, q, q+r>

Forces child's shrink to be shrink; doesn't change child's natural size or stretchability

+ stretch

<q-p, q, q+stretch>

Forces child's stretch to be stretch; doesn't change child's natural size or shrinkability

- shrink + stretch

<q-shrink, q, q+stretch>

Changes child's shrink to be shrink and its stretch to be stretch; doesn't change child's natural size

size - shrink

<size-shrink, size, size>

Changes child's size to be size with no stretchability and with shrink shrinkability

size + stretch

<size, size, size+stretch>

Changes child's size to be size with no shrinkability and with stretch stretchability

size - shrink + stretch

<size-shrink, size, size+stretch>

Changes child's size to be size with shrink shrinkability and with stretch stretchability

A few common paradigms merit mention. First, to remove whatever inherent stretchability a component has, use


    (Shape (Width + 0) ...)

Second, to make a component stretchy regardless of its inherent stretchiness, use


    (Shape (Width + Inf) ...)

And third, to set a component to a particular size, e.g, 100, use:


    (Shape (Width 100) ...)

Precedence of Size Constraints

The various constraints on the size of an object sometimes come into conflict. They take precedence as follows:

  1. Downward-propagating constraints: window size forced by Trestle, vertical size forced by an HBox, and so on.
  2. Explicit size information, given by a Shape filter.
  3. Upward-propagating natural size information: inherent size of leaves, filters taking size from their children, an HBox taking width from the sum of its children's widths, on so on.

Subwindows

In addition to organizing child components by grouping them horizontally or vertically, FormsVBT allows child components to overlap. The split that does this organization is called a ZSplit.

There are two very different ways to use a ZSplit.

  1. If you don't like arranging elements in horizontal and vertical boxes, you could place each element at a specific location. Many UI Builders follow this model; it has its advantages and disadvantages. We rarely use this style at SRC.
  2. You can use ZSplits as a container for overlapping, often transient subwindows that are not installed as top-level windows.

Without loss of generality, we'll talk just about the second style of use.

A ZSplit is written like this:


(ZSplit
      The first child:
  (ZBackground ...)
      Any number of other z-children:
  (ZChild ...)
  (ZChassis ...)
  ...
  )

The first child is called the background. It is displayed below all other children.

The other children are ordered from bottom to top in the z-dimension. A non-background child of a ZSplit should be a ZChild or a ZChassis. It has an Open property to say if it should be initially visible (``mapped'') or invisible (``unmapped'').

It also has an At property to control where it should appear when it is made visible. The syntax of the At property is described below.

A PopButton is a button that causes a named subwindow to appear. A PopMButton is a version of PopButton that is appropriate for inclusion in a menu.

A CloseButton is a button that causes a named subwindow to disappear.

A ZGrow is a button that is used to change the size of a subwindow. A ZMove-component is used to change a subwindow's position by dragging.

A ZChassis is just a ZChild that has a standard configuration, including a frame whose banner includes a CloseButton, a title inside a ZMove, and a ZGrow.

The ``At'' Property

The location of a subwindow (denoted by a ZChild and ZChassis component) is specified using a property named At. The At property is a list that can take one of two forms: the ``corner'' form (two numbers, an optional corner, and an optional coordinate type); or the ``edges'' form (four numbers and an optional coordinate type). The coordinate types are either Absolute, which means that the coordinates represent the distance in points from the background window's northwest corner, or Scaled, which means that the coordinates represent a fraction (a number in the range 0--1) of the background window's width or height. Here are the two forms of the At property: (At h v [Center | NW | SW | NE | SE] [Scaled | Absolute] ) (At west east north south [Absolute | Scaled] )

If the list contains two numbers, then these coordinates specify the center of the subwindow. If the list contains two numbers and a corner, then these coordinates specify the position of that corner of the subwindow. In either case the default coordinate type is Scaled.

For example, (At 0.5 0.5) means that the center of the subwindow should be placed halfway across and halfway down the background window, i.e., that it should be centered in the background window. (If there is no At property, this is the default.)

(At 0.2 0.3 NW) means that the northwest corner of the subwinbdow should be place 20% of the way across the background window, and 30% down. This can also be written as (At 0.2 0.3 NW Scaled). Scaled coordinates must be written as numbers (integers or reals) in the range 0--1.

(At 100 237.5 Absolute) means that the center of the subwindow should be placed 100 points east and 237.5 points south of the background window's northwest corner.

(At 100 237.5 SE Absolute) means that the subwindow's southeast corner should be placed at that position.

Alternatively, you may specify the edges of the subwindow by using a list with four numbers, representing the west, east, north, and south edges, in that order. The numbers may be followed by a coordinate type; the default coordinate type in the 4-number form is Absolute, not Scaled as it is in the 2-number form.

If the coordinate type is Absolute, then the coordinates represent the distance in points from the background window's northwest corner; this is the only form in which you can specify the subwindow's actual size. For example, (At 20 120 60 300) means that the subwindow's width should be 100 points wide (120 - 20) and 240 points tall (300 - 60), and that its northwest corner should be 20 points east and 60 points south of the background window's northwest corner.

(At .10 .90 .30 1 Scaled) means that the subwindow occupies the middle 80% horizontally (.90 - .10) and the bottom 70% (1 - .30) of the background window.

(At .5 1 .5 1 Scaled) would place the subwindow in the southeast quadrant of the background window.

Here are some additional examples:


    (ZChassis %A (At .2 .3 NW) ...)
    (ZChassis %B (At 130 200 SE) ...)
    (ZChassis %C (At .1 .6 .2 1) ...)
    (ZChassis %D (At 20 120 60 300)

If the ZSplit containing the subwindow is 200 points wide and 300 points high, here is what each specification means:

The actual location of a subwindow has two additional restrictions. First, FormsVBT will ensure that the size it gives a subwindow will be within the subwindow's acceptable dimensions: the northwest corner stays fixed, and the southwest corner is adjusted. Second, FormsVBT will not pop up a subwindow with its northwest corner north or west of the visible portion of its parent; it will move the subwindow away from the specified position in order to bring it into view.

Catalog of Components

This section provides a brief description of current FormsVBT components. Appendix [ap:longcatalog] describes the details of each component.

Visual components

These leaf and filter components have no interactive behavior; they are used to provide appearance and positioning control.

Border
displays space, in the foreground color, around its child
Rim
displays space, in the background color, around its child
Pixmap
displays a pixmap, centered
Text
displays a single-line text
Texture
displays a textured rectangle
Bar
a line in the foreground color; child of HBox or VBox
Glue
a piece of background filler; child of HBox or VBox
Fill
an infinitely stretchable background filler; child of HBox or VBox
Frame
draws a 3-d border around its child
Chisel
like Bar, but line appears 3-d, chiseled into background
Ridge
like Bar, but line appears 3-d, raised above background
Scale
enlarges or shrinks child
Shape
constrains shape of child

The following filters respond to mouse activity. They do not report events to the application program.

Viewport
adds scrollbars around a child for panning
Filter
controls reactivity and visibility of child

Basic Interactors

These are leaf components that have a user-modifiable value. They should always be named so that the application will have access to the value.

Numeric
an editable integer
Browser
a group of lines of text, of which one may be selected
MultiBrowser
a Browser in which multiple lines may be selected
Generic
a placeholder to be taken over by the application
Scroller
a vertical or horizontal scrollbar

Text Editing Interactors

These leaf components provide extensive text editing facilities.

TypeIn
an editable-text region, typically single-line
TextEdit
a scrollable text-editing area
Typescript
a TextEdit with a reader and writer

File Browser Interactors

A FileBrowser displays the names of the files in a directory, initially the current working directory. The user can traverse the file system by double-clicking on elements in the browser. There are two related leaf interactors that facilitate traversal.

FileBrowser
the list of filenames
Helper
an area for typing filenames
DirMenu
a pulldown menu containing the names of parent directories

Basic Buttons

These filters take a child and add some interactive behavior to it.

Button
generates an event when clicked
Guard
click once to remove and expose underlying component
TrillButton
generates an event while mouse is down

Boolean and Radio Buttons

A Boolean and Choice are types of buttons that also maintain some state. A Radio is a ``grouper'': it takes a child and changes neither its appearance nor its behavior. Rather, it specifies that it and all its descendants are members of one ``group'' for some particular purpose.

Boolean
toggles on/off when clicked
Choice
a radio button; selects itself when clicked
Radio
defines a group of Choice components

Drag and Drop

These buttons provide a way to implement ``drag-n-drop'' and to get semantic feedback.

Source
a button that is dragged
Target
the thing into which a Source is dropped

Menus

Menu
a pull-down menu; pulls down when anchor is clicked
MButton
a pull-down menu element; generates an event on up-click

Other buttons that can be put into a menu are Boolean, Choice, and PopMButton.

Horizontal and Vertical Splits

Splits take an arbitrary number of children and lay them out in some fashion. These splits implement the TeX-like ``boxes-and-glue'' layout model.

HBox
horizontal layout
VBox
vertical layout
HTile
an HBox with user-adjustable divider bars between children
VTile
VBox with user-adjustable divider bars between children
HPackSplit
arranges children like words in a paragraph
VPackSplit
arranges children like paragraphs in a multi-column newspaper

The layout algorithm for HTile and VTile is slightly different than for HBox and VBox when the size of one of its children changes. In the case of the tiles, the algorithm tries to keep existing children with their same relative sizes, which might have been adjusted by the user from their initial assignments. The HBox and VBox always re-assign sizes, independent of the current sizes of the children.

Subwindows

A ZSplit organizes its children as overlapping subwindows. The following components allow the user to control the appearance, location, and size of subwindows.

CloseButton
closes a subwindow
PopButton
pops up a subwindow
PopMButton
a menu item that pops up a subwindow
ZBackground
needed around the background child
ZChild
needed around non-background children ZGrow
a button for resizing a subwindow
ZMove
a button for repositioning a subwindow
ZChassis
a handy combination of ZChild, CloseButton, ZMove, and ZGrow

Temporal Windows

TSplit
a temporal window that organizes its children so that exactly one child is visible at any given time.
LinkButton
displays a specific child in a TSplit.
PageButton
switches children displayed in a TSplit.

A common use of a TSplit is to make an arbitrary component appear or disappear under user control. The component whose visibility is to be toggled is put into a TSplit with one sibling: a component with no size. A LinkButton to the component will cause it to appear, and another LinkButton to the sibling will effectively cause the component to disappear.