Notes on the new eXene widgets


[Editor's note: this document is an HTML version of notes written by Emden Gansner on the new widget set.]

The eXene widget set has undergone major changes. Although the new set is largely upward compatible with the old (version 0.4) in terms of function and use, the new set has new widgets, old widgets have new features, the widgets have a Motif-like 3D look, and the widgets use X resources to allow customization, especially of such visual aspects as color and font.

Using Resources

The use of resources allows us to make the creation of widgets more uniform. By convention, the creation function for a widget will have the form

val gadget : (Widget.root * Widget.view * Widget.arg list)
      [ -> optional additional arguments ] -> gadget
The root argument, as before, corresponds to a screen in X terminology. The view argument provides a "view" of the widget as parameterized by X resources. At present, a view is the pair
type view = Styles.style_view * Styles.style

The style component can be viewed as a database of string values keyed by regular expressions, following the conventions of standard X resource files. Thus, a typical entry might be

plotos*TextEntry.background:    red

The style_view component corresponds to a collection of names and aliases the widget will use for itself when it looks up resources in the style. If widgetB is to be a child of widgetA, a programmer will typically make a style_view for widgetB by extending the style_view of widgetA (using Styles.extendView and Styles.extendName) with some specific name for widgetB. In addition, if widgetB belongs to some logical class, names for the logical class can be added to widgetB's style_view as aliases.

For example, suppose we have a pair widget (we don't) that takes two child widgets and displays them next to each other.

val pair : (Widget.root * Widget.view * Widget.arg list)
      -> (Widget.widget * Widget.widget) -> pair
and suppose we have a style and have constructed a style_view for the pair widget:
val style : style
val pairName : style_view
We want to create two text buttons that will be put into the pair widget. We will name one button "leftButton" and the other "rightButton". In addition, for our application, the left button will belong to a special class of alarm buttons. We would then do the following:
structure S = Styles
structure B = Button

val rightButtonName = S.extendView (pairName, "rightButton")
val leftButtonName =
      S.prependAlias (S.extendView (pairName, "leftButton"), "Alarm")

val leftButton = B.widgetOf (B.textBtn (root, (leftButtonName,style), []))
val rightButton = B.widgetOf (B.textBtn (root, (rightButtonName,style), []))
val pair = pair (root, (pairName,style), []) (leftButton, rightButton)
Concerning the style, usually a single style is created for an application and is passed unchanged to all the widgets. At present, a style can be created from a list of strings using Widget.styleFromStrings. In the near future, we will provide functions for creating styles using user and application default resource files, the X resource database and command line arguments.

As noted above, the first argument used to create a widget is the triple

(Widget.root * Widget.view * Widget.arg list)
We've discussed the first two components. The argument list allows the programmer to have specific control over the resources of a widget. Normally, a widget uses the style in the view to find its resources. The end user determines the style by altering her X resource database. However, for a given application, certain parameters of a widget might be critical enough that the user should not be able to affect them. In these cases, the programmer can specify these parameters using the argument list. The attributes specified in the argument list override the style provided in the view.

At present, an arg is a name-value pair

type arg = Attrs.attr_name * Attrs.attr_value
Obviously, attr_name corresponds to the name of the resource and attr_value corresponds to its value. (The Attrs structure provides internal representation of the names and values used in styles. In addition, it provides a collection of common attr_names.) Thus, if a programmer insists that a button have a red background, she can use
structure Attrs = A

val bttn = B.textBtn (root,view,[(A.attr_background, A.AV_Str "red")])
For further information on the creation of styles and style_views, see the styles directory.

New startup

To simplify the startup of an eXene application, there is a new module RunEXene that provides a function that takes an argument of type Widget.root -> unit, looks for the shell environment variable DISPLAY to specify the display, starts CML, creates a root, and then calls the function provided.

Example

Below is a sketch of a typical eXene application called "draw":

structure S = Styles
val defaults = [
        "draw*background:           gray80",
        "draw*foreground:           black",
        "draw*selectColor:          yellow",
        "draw*Button.background:    gray70",
        "draw*font:                 -Adobe-Helvetica-Bold-R-Normal--*-120-*"
      ]

fun main root = let
      val style = Widget.styleFromStrings (root, defaults)
      val name = S.mkView {name = S.styleName ["draw"], aliases = []}
        (* make widget name hierarchy *)
        (* create widgets button up *)
      val layout = ...   (* top-level widget *)
      val shellArgs = [
              (Attrs.attr_title, Attrs.AV_Str "draw")),
              (Attrs.attr_iconName, Attrs.AV_Str "draw")
            ]
      val shell = Shell.shell (root,(name,style),shellArgs) layout
      in
        Shell.init shell
      end

fun doit () = RunEXene.run main

Caveats

This version of the eXene widgets represents the widgets as they are right now. There are many bugs; many of the widgets have not been converted to a 3D look or to use resources; there will probably be additional changes in the design.

The use of resources is neither complete nor uniform. There will probably be some convention by which a widget will automatically add some class name to its style_view. Widgets that create internal subwidgets (e.g., scrollPorts) do not as yet provide them with new style_views.

The idea of the argument list used at widget creation is right, but its form is not. A flat name-value list does not allow a programmer to control the resources of specific internal widgets. The name component of an argument needs to reflect the hierarchy. This suggests replacing the type of name with a list of names, a list of strings or perhaps strings following the conventions of resource databases, i.e.,
    "plotos*TextEntry.background"

There is no new documentation reflecting the new interfaces and features. At this point, even comments are minimal. If a widget supports resources, these can be deduced from a list of attribute names and default values found in the source file.


Last updated on March 28, 2003.
Comments to John Reppy.