Introduction to Screen Management


Getting Started

A+ provides a straightforward, easy to understand scheme for visually representing variables in a variety of ways, including graphs. Compound representations can be formed for collections of variables and used as screen interfaces for applications. The connection between the values of A+ variables and their visual representations is maintained by A+ so that the two are always in agreement: a change to the value of a variable will be immediately visible on the screen, and a change to the screen will be immediately reflected in the value of the appropriate variable. (If, however, a mapped file is displayed in one process and a change to that file is made in another process, the change will not be reflected automatically in the display, since the displaying process is not notified of changes made to the file in other processes.) As a simple example, consider:
     $load s    All the screen management functions are in s;
     b2 3     the principal six are loaded into the root context also.
     `b is `array
     show `b
Run this example and you will see a display of the array b on the screen. Change a number on the screen and then examine the value of b in the A+ session; it has changed accordingly. Change an element of b and note the corresponding change to the screen display. And the same consistency between the view of b on the screen and value of b in the A+ session is maintained for any visual representation of b, even a graph. Not only are changes to values reflected in changes to graphs, but when points on a graph are dragged to new positions, values change accordingly.

(At various points throughout this chapter examples of visual displays will be discussed, and it is assumed that the reader is actually running these examples and has the displays available for reference. There are tutorial scripts available under the "Tutorials" item on the "On-Line Documents" section of the home page. They are ASCII text files; use Emacs to access them and the F2 key to execute the lines of code in them.)

The phrase `b is `array in the above example specifies the way in which the variable b will be visually represented, in the display format called array. Array is said to be the name of a class of A+ objects. It is also said that:

The term object b refers to a variable b which has been bound to a class. The terms visual class and display class will be used for class when the emphasis is useful.

Not all variables can be bound to the array class, and usually there is more than one class to which a variable can be bound (but it can be bound to only one class at a time). Each class has it own visual appearance. For example, a character vector can be in the array class:

     c"Visual Displays"
     `c is `array
     show `c
or in the label class:
     d"Visual Displays"
     `d is `label
     show `d
A numeric matrix, however, which can be bound to the array class, cannot be bound to the label class.
     m2 3
     `m is `label
     !!  .m: variable cannot be bound to label
There are several display classes of compound, or container, objects, by which complex displays can be created by arranging collections of objects of simpler classes. The layout display class is one of these.
     w()         Initialize a layout.
     `w is `layout
 `.w
     x"Array Format"
     `x is `label
 `.x
     ww,`x       Add label to layout; context assumed same as container's.
     y2 4
     `y is `array
 `.y
     ww,`y        Add an array to the layout.
     show `w
The display of w should suggest the potential power of the layout class, but it also reveals some characteristics of displays that one might like to modify. For instance, in this example the values of x and y may convey all the information needed, and so it is preferable to remove the variable names y and w from the display. The characteristics that can be modified are called the attributes of the display class. Each class has its own set of attributes; some are shared with other classes, while others are specific to that class.

Continuing with the above example, the variable names, y and w, are the default values of the attribute called title, which is an attribute of both the array and layout classes. A new title can be specified by modifying this attribute:

     `w has (`title;"New Title")
Or the title can be removed by specifying it to be empty:
     `w has (`title;"")
Similarly, the title can be removed from the object y:
     `y has (`title;"")
Just as attributes can be altered, their current values can be determined. For instance, to find out what background color is used for the label in the above example:
     `bg of `x
<  `grey
And to change it:
     `x has (`bg;`yellow)
Another way to alter the behavior of the screen management system is to redefine various global variables in the s context. These variables are discussed at the appropriate places throughout the chapters that follow; in particular, see "
The s Context".

There are now several displays on the screen, and in an application one might want to control potential screen clutter by specifying what is seen and unseen. A+ provides two functions for doing that. It is possible to hide one or more of the displays, perhaps replacing them with icons:

     hide `w
The hide function is an inverse of show. There is also an inverse to the class-binding function is, called free, that releases an object from its specified class and has the side effect of removing its display from the screen:
     free `d
Note that in the case of hide `w, the variable w is still bound to the display class layout; it is simply not visible. Showing w again and getting back the original display verifies this:
     show `w
However, in the case of free `d the variable d is no longer in the label display class; if the variable d is shown again it will be bound to the array class by default, and its display will look different from before:
     show `d
    .d: variable bound to `array
All the basic primitive functions for visual representation, namely is, free, show, hide, of, and has, have now been shown. (These functions are put in both the root and the s contexts by $load s.) Examples have been shown of several display classes, namely array, label, and layout, and two attributes, title and bg (background color). Complete definitions are given in "
Display Classes", and in the chapters that follow.

Controlling Which Screen the Windows Are Displayed On

A+ does not (yet, at least) provide a means to let a single process write to several X displays. You can, however, choose which display your s windows appear on. If, for example, your machine, "mymachine", say, has two displays and you want to show your s windows on the second of them, you can execute
     sys.setenv{"DISPLAY=mymachine:0.1"}
     $load s
Note that you must set DISPLAY before you load s.

Default Behavior

The primitives for visual display often report information in the A+ session when executed, such as binding class assumptions. This information has been mostly left out of the simulated terminal sessions shown here. Whether or not messages appear is controlled by the s context variable s.QUIET. The default value is 0, which causes all messages to be printed. The value 1 causes only error messages to be printed, suppressing any warning or informational messages, and 2 only messages for severe errors. No messages are printed if the value is -1. In addition, the behavior of s context functions when errors occur is controlled by the s context variable s.ABORT, whose default value is 1, meaning that error messages will be displayed (if s.QUIET allows) and function execution will simply end when error conditions arise. The value 0 means that errors will be signalled and execution suspended (see "Signal").

   Example 1. Manipulating Layouts

If you are executing the above layout example, at this point you should have on the screen a label object x and an array object y inside a layout object w. Now append a view object to w. (A view is simply a character matrix.)
     s`name `address `phone
     `s is `view
     ww,`s
     show `s
It is worthwhile to experiment with respecifying w as `s `y, then as `y `s `x, and so on, watching the changes on the screen. In particular, you will note that as objects are removed from w they remain on the screen. This behavior, which is the same for tables, graphs, and windows, is controlled by two s context variables, s.AUTOREPARENT and s.AUTORESHOW, both with default value 1. For example, if
     w1w
the object that was named in w[0], say x, is redisplayed outside the layout. However, if s.AUTORESHOW had been 0 then x would not have been redisplayed. It would, however, have remained bound to the label class. If s.AUTOREPARENT had been 0, then x would have been freed from the label class (and consequently not displayed).

As you proceed with the experiment you may notice that the relative positions of x, y, and s on the screen are not necessarily related to the arrangement of these names in the variable w. The geometry of the layout can be controlled by specifying w as a matrix or a nested array. For example, to put x on the left, y in the middle, and s on the right, set

     w1 3`x `y `s
Or, to put `y above `x and `x above `s, set
     w3 1`y `x `s
Or, to put y to the left of s and x above them both, set
     w`x,1 2`y `s
or
     w(`x;`y `s)
A layout can also be composed on the screen. Set w to Null, so that all the objects that were contained in it will be shown on the screen as independent displays:
     w()
Reset x, y, and s to their original display classes if necessary. Move the displays around so you can see them all, and then move them into an agreeable arrangement. For example, make s larger and x smaller, put x next to y, and s on top of them both. Now set
     `w has (`build;1)      Or s.AUTOBUILD1
     w`x `y `s
The independent objects have now been captured in a layout reflecting their independent arrangement on the screen, and their displays have been replaced with the layout display.

Once a display has been arranged to your liking, it can be saved and redisplayed later using the s context functions s.save and s.load. For example,

     s.save 'testfile.+'
will save all objects created in this session, including the layout w, in the file testfile. Then if the arrays x, y, and s are recreated in some future session, the layout can be recreated and redisplayed as follows:
     s.load 'testfile'
More details can be found in "
Layouts, Geometry, Constraints".

   Example 2. A Text-Graph Dependency

As a second example, consider devising a display with a text input area and a graph area, so that when an expression for a scalar function is entered in the text area, its graph appears. Set
     xc0.11000
Functional expressions can be captured on the screen by a scalar object. For example:
     fn20' '
     show `fn is `scalar
The value area of a scalar can be edited. Move the mouse onto the blank area to the right of the text fn, enter 1xc (i.e., sine of xc), and press the Enter key. Now examine fn and you will see that this expression has been captured.

A trace for a graph of this expression is a two-column matrix, with the domain to the left of the range. To make the trace a dependency on xc and fn (or rather the expression in it):

     tr:xc(,@0)fn
     tr
 1000 2
Now put the graph on the screen:
     g`tr
     show `g is `graph
Quite possibly the graph display has covered the scalar display. Move it so that you can see both. Now enter another expression in the slot. The graph will change automatically and immediately because it displays the value of the dependency tr on the screen. Finally, these two independent displays can be put in a layout:
     l(`fn;`g)
     show `l is `layout

   Default Class Binding

If a global variable or dependency is not bound to a display class and its name is used as the argument to show or as the left argument to the screen management primitive function has when the right argument contains a nonpersistent attribute (see "Persistent Attributes (key = p)"), it will be bound to its default display class1.

The default display class of an object is determined according to the following rules, which are applied in order:

  1. 0 is bound to class array and the Null to class scalar;

  2. if the variable is of type box, attempt to bind it to class radio, and if that fails try class check, and so on through the list of slot, layout, array, scalar (any object can be bound to class scalar);

  3. if it is of type function, bind it to class button;

  4. if it is a matrix, attempt to bind it, in order, to class layout, array, scalar;

  5. if it is a vector, attempt to bind it, in order, to class table, graph, layout, array, scalar;

  6. bind it to class scalar.
Children (see next section) of a table object are always bound to the tableField class and children of a graph are always bound to the graphTrace class.

Display Classes

The descriptions in the following chapters assume default values for the attributes of the display classes. In particular, whenever a display class has a title area, the name of the variable bound to the class is displayed there. Settings other than defaults are controlled by the attributes of the various classes, which are described in the several chapters on attributes as well as in the chapters on the various classes. The classes can be grouped in seven categories: The objects that can be bound to container classes are variables whose values consist of collections of other objects. For example, in this chapter the value of the layout object w ended up as `x `y, where x is a label object and y is an array object. An object bound to a container class is said to be a parent of the objects named in its value, and these objects are said to be children of the parent. The children of a child are also children of its parent. In A+, no object can be a child (or parent) of itself.

When a child is bound to a container class, its context need not be given if it is the same as the parent's, because the same context is assumed. Different contexts are accommodated by qualifying the child's name.

Entering and Editing Information on the Screen

A+ provides a very simple scheme for entering and editing on the screen. Not all display classes are designed for entering and editing, but those that are all use this same scheme. The way the area to be edited is identified does vary, however, and so this point is discussed first. (A more detailed account can be found in "User Interactions with Displays".)

   Traversal: Activating the Area for Input or Editing

The screen is composed of a series of windows, and to enable keyboard entry the one that holds the area for input or editing must be selected. The usual way to do this is simply to move the mouse pointer onto that window. The appearance of the window will change to indicate that it now has keyboard focus, such as by a change in the background of the title area at the very top of the window.

Assuming that this is a window created by the screen management system, it must have been the result of executing show `a for some object `a. Depending upon the display class of `a, the editing state will be different:

   Input and Editing

Once the row or cell you want has been selected, you can simply start typing to enter input; input mode begins automatically on the first key stroke. Edit mode is ended for that row or cell and the input is sent to the appropriate A+ variable when the Enter key is pressed, or the Tab key, Meta-Tab on a Sun keyboard or Alt-Tab on an IBM keyboard, or an arrow key is used to move to another row or cell.

There are actually three input modes: complete replacement, overwriting, and insertion. When input mode is entered by starting to type, anything previously displayed in the input area is lost; it is completely replaced. If the mouse pointer is on a character in the area to be edited and the middle or right mouse button is pressed, the area takes on the appearance of edit mode and the keyboard focus is on that character. If it is the middle button that is pressed, anything entered overwrites the existing text, starting at that character. If it is the right button that is pressed, anything entered is inserted just to the left of that character.

Windows

A+ windows are tree structures of bound variables. A bound variable is often referred to as an object. For example, in the window consisting of a layout m containing a scalar s, a table t of fields a, b, and c, and a button f:
     abc10
     t`a`b`c
     s101
     foo{s;c;v}:'pressed'
     f<{foo}
     `t is `table
     `s is `scalar
     `f is `button
     m(`s;`f;`t)
     `m is `layout
     show `m
there are seven objects:
       m           `layout
           s           `scalar
           f           `button
           t           `table
               a           `tableField
               b           `tableField
               c           `tableField

     `descendents of `m
<  `.t `.s `.f `.a `.b `.c

   Toplevels and Popups

An A+ process will usually contain several windows. The windows in a process also form a tree. For example, continue the example above by creating three more arrays:
     v1v2v310
     `v1 `v2 `v3 is `array
     show `v1 `v2 `v3
The first thing to note is that the control borders of m and the v-arrays are different. m is a top-level window and the v-arrays are popup windows. The default shelltitle of m is the value of _argv, if it is nonnull, or "A+", and those of the v-arrays are "v1", "v2", and "v3".

   There Is Normally at Most One Toplevel

An A+ process normally has at most one top-level window. By default, this property - top-level windowhood - is given to the first object bound. In the above example, it is t:
     `t is `table           First object bound.
Subsequently bound objects which do not contain t are created as popups. Finally, when a container for t is bound:
     `m is `layout          Layout containing t
the property of top-level windowhood is passed to m.

This can be demonstrated by creating a new layout n containing m.

     n`m `v1
     `n is `layout
     show `n
Top-level windowhood is given to n, since n contains m.

Note that this property can be lost:

     n()                   m reparented to popup.
     free `n                Toplevel freed.
All windows are now popups.

   The Workspace

When the A+ process has a top-level window x, that top-level window is called the workspace of that process. It usually has the shelltitle "A+".

   The Workspace Variable s.WS

The variable s.WS is either () (there is no workspace for the process) or `cx `var, where cx.var is the top-level window. s.WS can be assigned the symbol of any popup window in the process, and no matter what the form in the assignment, fully qualified name, context and unqualified name, or (for the root context) just unqualified name, the value of s.WS will be a two-element symbol vector. Continuing the above example, we can restore the property of top-level windowhood to m:
     s.WS`m
     s.WS
 ` `m

   The Screen Management Function hide{}

The function hide{} dismisses a popup window if its pin is in the 0 state. That is, hide{} will not dismiss a pinned window.

   The `exit Callback

Quitting from a top-level window causes a quit event; dismissing a popup window causes a dismiss event: generically, an `exit event. The default `exit callback function frees a toplevel and hides an unpinned popup. This callback can be reset:
     `popup has (`exit;my.dismiss)
     `top has (`exit;my.quit)

   Leaders and Followers

Construct a set of windows:
     abcde3
     show`a`b`c`d`e
The workspace is a:
     s.WS
 ` `a
b, c, d, and e are popup windows.

Now hide the workspace:

     hide `a
Notice that if they are unpinned the popups "follow" a into hiding.

Now, show the workspace:

     show `a
The popups follow a back into visibility.

Now pin one of the windows, say b, and hide the workspace again. This time, b remains visible.

By default, all popups follow the workspace:

     `followers of `a
< `.b `.c `.d `.e
     `leader of `b
< `.a
     `leader of `c
< `.a
Note that
     `x has `leader `x
is equivalent to
     s.WS`x
I.e., only the workspace follows itself.

Also note that

     `x has (`leader;)
causes x to follow the workspace if there is one. Otherwise, it has the same effect as
     `x has `leader `x

   Follower Trees

More complex hierarchies are possible:
     `c has `leader `b
     `followers of `a
< `.b `.d `.e
     `followers of `b
< `.c
A convenient form for describing the whole leader-follower tree of a process is a nested slotfiller:
     k0    `followertree of `a
<  `.a
< < <  `.e `.d `.b
  < <
    <
    < <  `.c
    < <
     `.a `.bk
<  `.c
< <

   The `iconic Attribute

Any popup window can be converted to a toplevel, and vice versa. For example:
     ab10
     show `a is `array     First object is toplevel.
     show `b is `array     Subsequent objects are popups.
     `b has (`iconic;1)    Convert to toplevel.
     `b has (`iconic;0)    Convert back to popup.

   s.WSNAME Contains the Workspace Shelltitle

By default, the shelltitle on the workspace is either 01_argv, or "A+" if _argv is Null. The default workspace shelltitle can be set to an arbitrary string:
     s.WSNAME"y Application"

   s.SHELL Determines whether Windows are Top-level or Popup.

The first object bound is a toplevel, the workspace; subsequent objects are popup windows. This behavior can be altered at any point by changing the value of the global variable s.SHELL:
     s.SHELL
 1                   1 means popup window.
     show 10         First object bound is a toplevel.
     show 10         Subsequent objects depend on s.SHELL
     s.SHELL0        0 means top-level window.
     show 10         This is a top-level window.

   The s.wstree{sym} Function

This function creates a nested slotfiller dependency which represents the window hierarchy:
     s.wstree{`my.var}
     show `my.var is `tree
The default assignment callback function on my.var shows and raises the selected window. (When a window is raised, wherever it overlaps another window it is shown and the other window is obscured.)

Layouts, Geometry, Constraints

   Containers

A+ windows are trees of screen objects. An A+ screen object is an A+ variable bound to a display class. E.g.,
     a10               A variable
     `a is `array        bound to array display class.
The A+ display classes are of two kinds: data and container. Typically, an A+ window will consist of a container with children, some of which may in turn be containers. All objects at the leaves are data objects, which are childless.

There are eight container classes in A+: table, graph, window, layout, hpane, vpane, hgrid, and vgrid. The following sections explain how to use layouts to create geometry - pictorial structure - in a window tree.

   Layouts

A layout m is a structure of virtual rows and columns. The position of an object p in a layout can be specified by four numbers, called the at of p (in m): E.g.,
     `at of `p
< 1 2 1 4
indicates that p is at row 1, column 2, and spans one row and four columns.

Ultimately, all methods of positioning objects in layouts reduce to that of specifying the at of the object.

   The Natural Size of Objects

Every A+ screen object has a natural size. The natural size of an object is a function of its data, font, and other attributes, which might be specific to the object. For example, the natural size of an array class object is a function of the shape of the data, the width of the formatted data, the font, and the settings of the rows and columns attributes for the object. The natural size of a container is a function of the natural sizes of its children and their arrangement within the container. Natural size can always be expressed in pixels (xs and ys).

   The Structure of a Layout

The Simple Symbolic Vector Form

The structure of an A+ layout, as it is manifested on the screen, is a function of the array which is bound to that layout. That is, a layout m containing children a, b, and c is a variable m containing the symbols `a, `b, and `c. In the simplest case, m is just:

example     

     m`a `b `c
which represents vertical stacking of a, b, and c, with at of:
     >0ء`at ofm
 0 0 1 1
 1 0 1 1
 2 0 1 1

Simple symbolic vectors are ambiguous representations. Two attributes control the interpretation of a simple symbolic vector:

  • build:
    if 1, approximate the current arrangement of a, b, and c on the screen;
    if 0, use the default arrangement for `a`b`c, taking the position attribute into account;

  • position (effective when build is 0):
    if 1, place `a`b`c in a vertical column (equivalent to (`a;`b;`c));
    if 0, position each of them at 0 0 for 1 1 (stacked one atop another).
That is, if build is set to 0, then m (i.e., `a`b`c) is taken to represent the geometry of the layout, with the orientation depending on the position attribute. To be effective, position must be set before the layout is built, when m is (), say.

If build is set to 1, a, b, and c retain the same relative positions and sizes when they are orphaned ("zero parented").

     `m has (`build;0; `position;0)  m()
     `m is `layout  m`a`b`c
     >0ء`at ofm
 0 0 1 1
 0 0 1 1
 0 0 1 1

The Nested Vector Form

The simplest method of representing geometry is confined to depth 1 vectors of symbolic vectors. Each symbolic vector in a variable of this form represents a new virtual row of the layout. E.g.,

example     
     m(`a `b;`c `d;`e `f)
     `m is `layout
     >0ء`at ofm
 0 0 1 1
 0 1 1 1
 1 0 1 1
 1 1 1 1
 2 0 1 1
 2 1 1 1
giving the representation shown. Each object spans exactly one row and column; each vector in m represents one virtual row of the layout. (Hence, the ";" in the expression above can be read as a sort of virtual "new line" character.)

     The Span of Objects
This nested vector form works fine in the case where the same number of objects is found on each virtual row of the layout, but what about the case where each row contains a different number of objects? For example,

example     
     m(`a `b;`c;`d `e `f)
In this case, c is meant to span the entire width of the structure, a and b half each, and d, e, and f a third each, as shown, with at of:
     >0ء`at ofm
 0 0 1 3
 0 3 1 3
 1 0 1 6
 2 0 1 2
 2 2 1 2
 2 4 1 2
The total number of virtual columns is the least common multiple of the number of objects in each row.

Note that symbols can appear exactly once in a form of this type, and that depth and rank must be less than or equal to 1, and that the simple symbolic vector v is treated as ۡv.

The Simple Matrix, or Canonical Form

Only certain geometries can be represented using vectors of vectors, namely, those in which row-span is one for all objects. Consider the case in which buttons a and b and table t have the following spatial arrangement:
          a  t
          b  t
a and b are located on successive rows, and t spans both rows. In order to describe this arrangement, we use the canonical A+ notation for layout geometry:
     m2 2`a `t `b `t
which precisely pictures the arrangement described above:
     !@1 m
 at
 bt
The rules of canonical representation are more intuitive than accurate formalization would suggest: where uunique{,m} is the vector of unique symbols in m, there must be a rank 2 subarray mim[j;k] for each u[i] in which all occurrences of u[i] in m are found and no occurrences of any other element of u, and the shape of mi specifies the number of virtual rows and columns spanned by u[i]. Hence, neither of the following counts as a canonical representation:
     2 2`a`t`t`b
     2 2`a`a`a`b
Note that any layout array in nested vector form can be translated into canonical form. E.g.,
     m(`a;`b `c;`d `e `f)
     n3 6`a`a`a`a`a`a`b`b`b`c`c`c`d`d`e`e`f`f
represent the same geometry.
     The Placeholder in Simple Matrix Layouts
Consider the geometry of three objects, a, b, and c, which are to be arranged in a rectangle whose lower left quadrant is intended to be "empty". That is:

example     
     >0ء`at of`a`b`c
 0 0 1 1
 0 1 1 1
 1 1 1 1
To obtain this geometry, use the "placeholder" symbol "`" in the layout variable:
     m(`a `b;` `c)
allowing the c object to span a single column, as shown.

Note that ` is not the symbol of any object.

The Nested Matrix Form

Consider the array m:
  abcdefgh3
  m2 2<()
  m[0;0]<2 1`a`b
  m[0;1]<1 2`c`d
  m[1;1]<4 1`e`f`g`h
m is a 2x2 array of symbolic matrices, where the shapes of the submatrices implicitly specify the relative spans of their children, as shown.
  `m is `layout
   >0ء`at of,,m
 0 0 1 1
 1 0 1 1
 0 1 2 1
 0 2 2 1
 2 1 1 2
 3 1 1 2
 4 1 1 2
 5 1 1 2

example

The ability to manipulate blocks of objects without the intervening step of assigning to a global variable can be extremely convenient in applications with dynamic structures. Thus:

     wxyz3
     r1 2(`w `x;2 1`y `z)
     m[1;0]<r

example

The display of m is modified as shown; note how () is replaced wholesale by the arbitrary structure containing w, x, y, and z.

     >0ء`at ofء,,,m
 0 0 1 3
 1 0 1 3
 0 3 2 1
 0 4 2 1
 2 0 4 1
 2 1 4 1
 2 2 2 1
 4 2 2 1
 2 3 1 2
 3 3 1 2
 4 3 1 2
 5 3 1 2
The placeholder can be restored or another variable inserted:
     m[1;0]<()

example

     v3
     m[1;0]<3 1`v

example

with the results shown. The compactness shown in previous figures can be obtained by freeing m and then rebinding it after inserting the placeholder or variable.

   Constraints on Resizing Objects

Objects can be resized in either of two ways: directly, by specifying values for certain size-dependent attributes (e.g., font, or rows or cols); or indirectly, by resizing the object's container. E.g.,

example     
     a10
     b`a
     show `b is `layout
The natural size of the array-class object a is determined by (among other things) the default number of rows displayed, which in this case is:
     `rows of `a
<  5

example      Now grab the frame of the window with the mouse and pull down until eight rows of a are exposed.
     `rows of `a
<  8
This should look pretty much the same as executing the code
     `a has (`rows;8)
Intuitively, however, they seem to be quite different. In the first case, the size of a is a function of the size of its parent b, while in the second case, the size of b is a function of the size of its child a.

Since the second case will only arise when the program explicitly sets an attribute, there is no real need to constrain or control that kind of resizing. But in the first case, you might want to keep a from being resized no matter what the user does to change the size of b. In this example, you will want to set the "don't grow in height" constraint on a:

example     
     `a has (`resize;'H')
As the window b is resized, a will hang on to its five-row display, centering itself in b-space.

What is actually going on, and what will help in understanding some of the examples to come, is this: suppose you stretch b to include 100 additional height pixels. The layout receives the message
"distribute 100 additional pixels over your rows"
in response to which it expands its only row by 100 pixels. The row of the layout then sends to each of its children - in this case, just a -
"grow by 100 additional pixels"
But a is H-constrained, and refuses to comply.

Consider the case:

example     
     ab10
     c<`a`b
     `a has (`resize;'H')
     show `c is `layout
In this example, only a is constrained. Resizing the layout c has the effect of giving extra space to the sole row of c, which in turn has the effect of giving extra space to each child in that row. Array a rejects the space while b accepts it.

In an example involving more than one row:

example     
     abc10
     d(`a;`b;`c)
     show `d is `layout
Resizing d 300 pixels larger will cause d to send three messages, one to each of its rows:
"grow by 3003 additional pixels"
which in turn causes each row to send to its child the message to grow by that amount. In this case, all three children will expand. But if we were to set the H-constraint on b:
     `b has (`resize;'H')
then we would see that a and c each take their share of the extra space, but b does not, as shown.

The "H" attribute is paired with one for constraining width, called, appropriately enough, "W".

   Constraints on Resizing Layout Structures

Where only constraints on objects are set, resizing the layout causes each of its rows to accept a share of the extra space. You will have noticed that while this method keeps constrained objects from changing size, it does not keep the rows or columns containing these objects from being resized. In the example given above, where b is constrained by "H", what you might have expected was that a and c would absorb all the extra space, leaving b the same size, and leaving the amount of space taken up by the second virtual row of the layout unchanged. Instead, b appears to be floating in empty layout-space. How do you keep b "glued" to a above and c below?

Recall that a resize causes the layout to send messages of the form
"grow/shrink by n pixels"
to each of its rows (or columns), which in turn send messages of the form
"grow/shrink by n pixels"
to each of their children. "H" and "W", set on objects, allow these objects to reject such requests, and remain the same size. We want a similar ability for the virtual rows and columns of a layout, which would enable them to reject resize requests they receive from the layout:

     abc10
     d`a`b`c
     `b has (`resize;'h')
     show `d is `layout
Now suppose you expand d by 300 height-pixels. d receives the message:
"grow in height by 300 pixels."
It then asks each of its rows,
"can you grow in height?"
to which rows 0 and 2 reply "yes" and 1 replies "no". d then sends the following messages:

to row 0: "grow by 3002 pixels"
to row 2: "grow by 3002 pixels"

which in turn send resize messages to their children. But note that row 1, having replied "no" to the message asking whether it could be resized, receives no such message from the layout. Consequently, no "H" constraint need be placed on any object in row 1.

The "h" attribute is similarly paired with one for constraining column width, "w".

   Constraining Layout Structure by Way of the Objects

While syntactically both forms of resize constraint are placed on objects in a layout, semantically they are quite distinct. "H" and "W" constrain the resizing of objects; "h" and "w" constrain the resizing of the structure containing the objects on which these constraints are placed. What this means is that "h" or "w" placed on an object will indirectly constrain all other objects on the same row or column. E.g.,
     abc10
     d(`a;`b `c)
     `b has (`resize;'h')
     `d is `layout
"h", set on b, propagates upward to row 1 of the layout, and hence downward in its effects to c. All extra space accumulates in row 0 of the layout.

Repositioning Objects in a Layout

Consider once again the example:

example     
     abc10
     d(`a;`b;`c)
     show `d is `layout
     `b has (`resize;'H')
Resizing the layout by 300 pixels causes row 1 to grow, but b is constrained. The extra space is allocated: half above b, half below. In other words, b is vertically centered in the row. Suppose we wanted b to stick to the top of its row as it grows:
     `b has (`resize;'t')
This causes the extra space given to row 1 to be allocated: all below b, as shown.

Other justification options are:

     `b has (`resize;'b')         Stick to the bottom.
     `b has (`resize;'l')         Stick to the left.
     `b has (`resize;'r')         Stick to the right.
Justification options can be paired in obvious ways:
     `b has (`resize;'lt')        Stick left and top.
but, of course, the following pair is inconsistent, and s will use the one it prefers, top, for:
     `b has (`resize;'tb')        Stick top and bottom.

Default Constraints

A+ classes such as table and array which come with the concepts of rows and columns are instantiated with no resize constraints. It is assumed that most uses of array will allow for resizing. Other classes, such as label and button, are constrained in the height direction, and are instantiated with both "h" and "H" constraints. No width constraints are set by default on any classes.

For default constraint information, enter, e.g.:

     `resize s.defaultOf `button
< hH

The resize Attribute

The resize attribute is cumulative in its settings unless a period is included somewhere in its value:
     `a is `button
     `resize of `a
<  hH                       Default constraints.
     `a has (`resize;'W')   Set 'W' cumulatively.
     `resize of `a
<  hHW
     `a has (`resize;'lt')  Set 'l' and 't' cumulatively.
     `resize of `a
<  lthHW                    Accumulated.
To set constraints noncumulatively:
     `a has (`resize;'lt.')  Set 'l' and 't'
                             noncumulatively, using a period.
     `resize of `a
<  lt                     Not accumulated: just 'l' and 't'
To reset the default constraints:
     `a has (`resize;)    Set to null.
     `resize of `a
<  hH                     Default constraints.
To eliminate all constraints:
     `a has (`resize;'')  Set to ''. Could have used
                          '.' with the same result.
     `resize of `a
<                         No constraints.

   The Geometry of Slotfiller Objects

A+ currently supports five slotfiller classes: slot, radio, choice, check, and action. Typically, the A+ variable of a slotfiller class has the form:
     (`sym ... `sym;(val;...;val))

Partition Vector Form

The method for controlling the geometry of slotfiller objects is strongly analogous to the one used to position objects in a layout. Conceptually, slotfillers possess row-column structure, within which components can be positioned and which they may span. For example, the slotfiller
     sf(`one `two `three;(10;20;30))
can be laid out as

example

through the geometry setting:

     `sf has (`geometry;1 2)
which specifies: one object on the first row, two objects on the second row. Implicitly, the first object spans two columns, each of the other two span one column.

Note that geometry settings of this form are partition vectors:

     1 2 0sf
<  `one
<  `two `three

Index Matrix, or Canonical Form

A more general method for controlling geometry is, once again, strongly analogous to the method of canonical representation for layouts. Consider the geometry for sf shown as

example

where the object one spans two rows, and two and three each span a single row. This is obtained by means of:

     `sf has (`geometry;2 20 1 0 2)
Note that
     !@ 1 (2 20 1 0 2)#0sf
 one  two
 one  three
     Placeholder Indices in the Canonical Form
Consider the geometry:
one: ___10    two: ___20
              three: ___30
where the lower left quadrant is empty. This is obtained by means of:
     `sf has (`geometry;2 20 1 1 2)
where -1 plays the same role in geometry arrays as ` does in layout variables.
     Symbolic Geometry
The s functions will translate symbolic matrices into geometry matrices:
     `sf has (`geometry;2 2 `one `two ` `three)
is equivalent to
     `sf has (`geometry;2 20 1 1 2)
for slotfillers containing the symbols `one, `two, and `three.
     Horizontal Geometry
The default geometry for a slot is 1; i.e., (#0sf)1. Horizontal geometry for any slot is #0sf. For convenience, this can be specified simply as
     `sf has (`geometry;2)

   Making Objects Equal Sizes

Consider
     a(`one `thirteen `twelve `seven;(;;;))
     `a has (`geometry;2)
     show `a is `action
The buttons have different widths, and grow and shrink proportionally as the object is resized. Objects with geometry - layouts and slotfillers - can be constrained by the R and C attributes:
     `a has (`C;1)
causes all buttons in a to assume the size of the button with maximum width, namely, thirteen.

The R constraint has the same effect on rows:

     a10
     b20
     c`a`b
     show `c is `layout
     `c has (`R;1)
now causes b to assume the height of a.

One consequence of making sizes equal is that virtual row or column ratios can be used to indicate the proportional sizing of objects. For example, compare

     a3 4
     b10
     c`a`b
     show `c is `layout
with
     aa3 4
     bb10
     cc1 4`aa`aa`aa`bb
     `cc is `layout
     `cc has (`C;1)
     show `cc
Both a and aa are at 0 0 1 3 and b and bb are at 0 3 1 1, but only in cc does virtual-columns span determine relative width.

   Locking and Zooming

Suppose you have a layout of four graphs:
     aabbccdd10
     (a;b;c;d)(`aa;`bb;`cc;`dd)
     `a`b`c`d is`graph
     m`a`b`c`d
     `m is `layout
     m has((`at;0 0);(`at;0 1);(`at;1 0);(`at;1 1))
     show `m
You might like a mechanism whereby one of the graphs can be "zoomed" to fill the entire layout and later restored to its original cell. Note that m is not representational: the graphs are explicitly positioned using `at.

     Z()      Symbol of the graph that has been zoomed.
Consider the function
zoom{x}:
      {
      if (x())
              {
              if (~Z())
                    {
                    imZ;
                    s2 2i;
                    yxs`ys `xs of 0#(mZ)/m;
                    hide Z;
                    Z has (`ys `xs `at;yxs,<s,1 1);
                    show Z;
                    }
              }
     else
              {
              if (~Z()) &{()};
              x has (`at `raise;(0 0 2 2;1));
              };
     .Zx;
     }
If the argument to zoom is nonnull, then (1) if something is zoomed, "unzoom" it (see below), and then (2) zoom the object named. To zoom an object, reposition it at 0 0 with a span of 2 2 and raise it to the front.

If the argument to zoom is null and something is zoomed, "unzoom" it by translating its index in m to a position in the layout, get the pixel extent of some other (arbitrary) child of m, hide the object, reset its position and extent, and reshow the object.

These roundabout procedures are necessary because an attempt to reposition the object that was zoomed will cause the layout to expand to accommodate the extent of the formerly zoomed object.

The attribute lock on layouts (and their subclasses) can be turned on, after showing, to force the downsizing of an object that is being positioned so that the object will fit into the cell. In other words, the layout will not expand to fit the object, but will force the object to fit the layout. Now consider the zoom function, rewritten to operate on locked layouts;

     `m has (`lock;1)

     zoom{x}:
            {
            if (x())
               {
               if (~Z()) Z has (`at;(2 2mZ),1 1);
               }
            else
               {
               if (~Z()) &{()};
               x has (`at `raise;(0 0 2 2;1));
               };
            .Zx;
            }

   Natural Size Action

Consider
     abc10
     d(`a;`b `c)
     show `d is `layout
Resize the layout d. Now execute
     `d has `naturalsize
The layout sends the naturalsize message to each of its children. A primitive object that receives the naturalsize message recomputes its natural size (see "
The Natural Size of Objects"). After each child recomputes its natural size and transmits it to the parent, the parent recomputes its own size.

doc@aplusdev.org© Copyright 1995–2008 Morgan Stanley Dean Witter & Co. All rights reserved.