Tutorial

To use FormsVBT, you need a copy of SRC Modula-3 (Version 3.3 or later) and an X server for your system. If you have these, you may want to compile and run the example programs as you read this chapter.

Getting Started

The first example program is in the file Hello.m3:


MODULE Hello EXPORTS Main;
IMPORT FormsVBT, Trestle;
VAR fv := FormsVBT.NewFromFile("Hello.fv"); BEGIN
  Trestle.Install(fv);
  Trestle.AwaitDelete(fv)
END Hello.

The program builds a form (sometimes called a ``dialog box'' or a ``user interface'') whose description is contained in a file named Hello.fv. It installs the form in a top-level window, and then waits until that window is deleted by the user. The window installed by the program is shown in the left half of Fig. [fig:hello] .

The ``Hello FormsVBT!'' example program. The initial version is on the left; the second version on the right.

The file Hello.fv contains the following S-expression:


(VBox  
  (Text "Hello FormsVBT!")
  (Bar)
  (HBox (Text "Left") (Bar) (Text "Right")))

The top-level component is a VBox. A VBox takes an arbitrary number of ``children'' (sub-components) and arranges them vertically from top to bottom. This VBox has 3 children: Text, Bar, and HBox. A Text displays a text string, a Bar draws a line orthogonal to the orientation of its parent, and an HBox arranges its children horizontally, from left to right. The HBox has 3 children, two Texts and one Bar.

The standard way that you compile and link your programs is to use m3build. The m3makefile for the ``Hello FormsVBT!'' application is as follows:


import         (formsvbt)
implementation (Hello)
program        (Hello)

Then you can compile and link the Hello program by typing the shell-command m3build -S in the directory containing the source code.

Actually, most Modula-3 programmers follow the convention of storing all of the source files for an application in a directory called src. The m3build command, when run from src's parent directory, stores all of its derived files (including the executable) in a subdirectory whose name depends on the platform on which you are running. For example, on DECstations, the derived directory is DS; on an Alpha running OSF, the directory is AOSF. When you follow this directory structure, you should invoke m3build without any arguments.

Here's a slightly fancier version of the interface (shown in the right half of Fig. [fig:hello] ):


(Rim (Pen 20)
  (Border (Pen 1)
    (Rim (Pen 2)
      (Border (Pen 2)
        (VTile
          (Text "Hello FormsVBT!")
          (HBox
            (LabelFont (PointSize 240))
            (Color "White")
            (Text (BgColor "Pink") "Left")
            (Bar)
            (Text (BgColor "VividBlue") "Right")))))))

The top-level component is a Rim whose Pen property has a value of 20. A Rim must contain exactly one child (a Border in this case), and it surrounds its child with some background space. Here, the Rim provides 20 points of background space between each edge of the window manager's window frame and the rest of the interface. A Border is just like a Rim, but draws with the foreground color instead of the background color. We replaced the VBox with a VTile, and deleted its Bar child. A VTile is like a VBox, but it also automatically inserts a dividing bar between its children; by dragging the dividing bar, the user can control the division of space among the children. In this example, the HBox has been given two properties, Color and LabelFont. These control the foreground color and font used by the HBox and all of its descendants. Similarly, the BgColor property changes the background color used.

The fancy version of ``Hello FormsVBT!'' is in the file HelloFancy.fv. To run the application using this file, either modify the application to use HelloFancy.fv or rename the file HelloFancy.fv to be Hello.fv. Alternatively, you might find it enjoyable to run the FormsVBT interactive UI builder, formsedit. Just type the shell-command


    formsedit HelloFancy.fv


Exercise 1 Write the FormsVBT S-expression for T^4, a Trestle Tiling Monster of Order 4. (See the Trestle Tutorial, Fig. 2 on page 5.)


Resources

A resource is constant data needed by an application program at runtime; often it is ``loaded'' at startup time. Almost all FormsVBT programs have resources, such as the .fv (pronounced ``dot ef vee'') files that specify the user interface. Other typical resources specific to an application include bitmaps, cursors, and help-texts.

When an application is built, its resources can be ``bundled'' with the executable image. The primary benefit of this feature is that applications are self-contained with respect to the resources they need. Thus, you can copy an executable to a remote site and you won't need to copy the resource files and install them in the same place as they were when the application was built. Also, your application will be insulated against changes in library resources.

The easiest way to do this is to name the resources and the bundle in the m3makefile, as in this example:


import         (formsvbt)
resource       (Hello.fv)
bundle         (HelloBundle)
implementation (Hello)
program        (Hello)

The second line declares that there is a resource named Hello.fv. The third line has the effect of collecting all the named resources (only one in this case) and creating an interface called HelloBundle that provides access to them. The program would then be modified to look like this:


MODULE Hello EXPORTS Main;
IMPORT FormsVBT, HelloBundle, Rsrc, Trestle;
VAR 
  path := Rsrc.BuildPath(HelloBundle.Get());
  fv   := NEW (FormsVBT.T).initFromRsrc ("Hello.fv", path);
BEGIN
  Trestle.Install(fv);
  Trestle.AwaitDelete(fv)
END Hello.

The call to HelloBundle.Get returns a bundle that is used to create a resource-path, which is then searched by the initFromRsrc method.

But what if you want the application to use new resource files? For example, you might have changed some details of the .fv file that don't require any changes to the application code. Do you have to rebuild the entire application?

Fortunately, the answer is no. However, you do need to tell FormsVBT that you want it to look for those resources in the file system before it looks for them among the resources that were bundled into the application. You do this by changing the resource-path so that it includes one or more directories before the bundle.

The convention is to use environment variables whose names are spelled by combining the program's name with the string "PATH". This variable should be set to a list of directory-names, each separated by a colon. So, if you want to run the Hello program using the Hello.fv file that's in Smith's home directory instead of the one that's bundled with the application, you would type something like this shell command:


    setenv HelloPATH /user/smith

In the program, you would construct a resource-path that included this directory by adding the name HelloPATH, prefixed with a dollar sign:


MODULE Hello EXPORTS Main;
IMPORT FormsVBT, HelloBundle, Rsrc, Trestle;
VAR 
  path := Rsrc.BuildPath("$HelloPATH", HelloBundle.Get());
  fv   := NEW (FormsVBT.T).initFromRsrc ("Hello.fv", path);
BEGIN
  Trestle.Install(fv);
  Trestle.AwaitDelete(fv)
END Hello.

The FormsVBT Language

Syntactically, there are three types of components in FormsVBT: leaves, filters, and splits. A leaf has no children; a filter has exactly one child; and a split has any number of children.

The FormsVBT leaf components include passive objects like texts and pixmaps, as well as interactive objects like scrollbars and type-in fields.

A filter modifies its child's looks or behavior in some way. We've seen how a Border draws a border around its child. Another common filter is Boolean. It adds a check box to the left of its child and makes the box and the child sensitive to mouse clicks. It's important to realize that the child may be any arbitrarily complex arrangement of components, although a Text component is the most common.

The purpose of most splits is to divide the display area among its component-children (sub-components). In addition to the horizontal and vertical splits that we've seen, FormsVBT provides a temporal split (TSplit) to display exactly one child at any given time, and a z-axis split (ZSplit) to display children as overlapping subwindows.

Components are written as lists containing the component's type, followed by some number of properties, followed by some number of sub-components. Properties are written as lists containing a keyword and a value. For example, in the S-expression:


  (HBox
    (LabelFont (PointSize 240)) 
    (Color "White")
    (Text "Left") 
    (Bar) 
    (Text "Right")

the parent-component's type is HBox. This component has two properties; the first property has the keyword LabelFont and the value (PointSize 240); the second has the keyword Color and the value "White". It has three sub-components: (Text "Left"), (Bar), and (Text "Right").

The value of each property is type-checked when the description is parsed. The possible types include strings, integers, and real numbers, as well as more complicated types like color and font specifications.

So far, we have seen two kinds of properties. Class properties, like Pen, are defined in conjunction with specific components, and are allowed only on components of that class. Inherited properties, like Color and LabelFont, may be specified for any component, though they are not relevant to all component types. The inherited properties have the feature that a value specified for one component becomes the default value for all descendants of that component. Thus an inherited property applies not to one component, but to an entire subtree.

FormsVBT supports a third type of property, universal properties. A universal property can be specified on any component, and its value applies only to that component.