ViewRecDoc.tioga
Mike Spreitzer August 26, 1986 4:47:18 pm PDT
ViewRec
CEDAR 6.0 — FOR INTERNAL XEROX USE ONLY
ViewRec
Mike Spreitzer
© Copyright 1985 Xerox Corporation. All rights reserved.
Abstract: ViewRec is a tool for constructing pieces of user interfaces. It provides a combination of extended menus (the procedures can take arguments) and forms fill-in.
Keywords: User Interface, Viewer, Record
XEROX  Xerox Corporation
   Palo Alto Research Center
   3333 Coyote Hill Road
   Palo Alto, California 94304

For Internal Xerox Use Only
0. Contents
0.0 Contents
0.1. Introduction
0.2. The User Interface Provided by ViewRec
0.2.1. Format of a Record Viewer
0.2.2. How a Record Viewer interacts
0.2.3. What Working Directory ?
0.3. The Client Interface to ViewRec
0.3.0. Introduction
0.3.1. Typed Variables vs. REFs
0.3.2. Creating RecordViewers
0.3.3. A Record Viewer by Any Other Name ...
0.3.4. What Kinds of Things Can be Displayed with ViewRec
0.3.5. Initial Values
0.3.6. Non-PROCEDUREs at the Top Level
0.3.7. Keeping Up-To-Date
0.3.8. Abortion!
0.3.9. Introducing New Ways of Displaying Things
0.3.10. Layout
0.3.11. Some Random Customizations
0.3.12. Examples
0.3.13. The Working Directory
1. Introduction
1.1 What is it?
ViewRec is a Cedar tool that provides a quick and easy way to make user interfaces. It includes a procedure which puts up a viewer, called a RecordViewer, on a given aggregate (a record, sequence, or array). ViewRec can access aggregates through refs to records, and through names of interfaces. All of the components of the aggregate that are "simple enough" are displayed, and may be invoked (if they are procedures) or edited (if they are not). Arguments are retained from one invocation to the next, and results are displayed. The displayed values may be continuously kept up to date by a background process.
1.2 Organization of the Documentation
The documentation on ViewRec comes in two pieces: an introduction (ViewRecIntroduction.Tioga), and this reference manual (ViewRecDoc.Tioga). The introduction gives a quick description of the essential features of ViewRec. This reference manual gives a detailed description of everything. Read the introduction first; it won't take long, and it provides an overview. This reference manual dives straight into all the gory details. They are divided into two groups. Those in the first describe the interfaces produced by ViewRec. Those in the second describe how to get ViewRec to produce such interfaces.
1.3 What You Need to Know
This document assumes a general familiarity with Cedar. The first section, on the interfaces produced by ViewRec, references concepts from the Cedar user environment (screen, keyboard, and mouse; Viewers). The second, on programming ViewRec, is intended for those familiar with the Cedar programming environment. The packages most relevant to ViewRec are Viewers and the Abstract Machine. Simple usage of ViewRec requires little knowledge of either. ViewRec does, however, have options, the use of which requires deeper knowledge of one or the other.
2. The User Interface Provided by ViewRec
An example should help to make this clear. Bringover all the public stuff in ViewRec.df, then go to a nearby Commander and:
% @ViewRec.Load
% ← ViewRec.ViewInterface[name: "Rope", otherStuff: NIL]
2.1. Format of a Record Viewer
The client area of a viewer produced by ViewRec may be divided into three main parts; they are listed below:
2.1.1. Main Body
This part is always present (the other two may be absent), and its layout never changes. This part includes the name: value pairs for the variables, and the screen buttons (i.e. Buttons.Button) for the procedures, of the aggregate being viewed. There are a few other things that can go here as well. The things which can go in this part always appear in a certain order; that is the order in which they are described here:
2.1.1.1. Label: A descriptive string.
2.1.1.2. OtherStuff. The Client may have arbitrary viewers included between the label and the component areas.
2.1.1.3. Component Areas. Each component that is displayed gets a rectangular area here. That area is of one of three kinds:
Name-Value Pair:
A name-value pair has a screen button, whose name is the name of the component followed by a colon, and some kind of Viewer displaying the current value of the component. Some components may not be edited by the user. In this case, the name is displayed in black letters against a light gray background; normally it is against a white background. For scalar components, the value viewer is usually a Tioga text viewer, showing a textual representation of the value. For aggregate components, it is usually a RecordViewer.
Procedure:
A screen button named after the procedure.
Special:
This is an escape mechanism through which Clients may provide their own kind of component area. Thus, the layout and behavior of each is determined by the Client.
2.1.2. Arguments and Returns
This part is used to show the arguments under preparation for, and returns last produced by, a prepared simple enough procedure. At most one procedure at a time has arguments and returns displayed here. ViewRec indicates which one, if any, is using this part by displaying the name of that procedure black on a gray background (the normal display of procedure names is black on a white background). When arguments and returns are displayed here, this part is subdivided further into three smaller parts:
2.1.2.1. Control
This part has a screen button for invoking the procedure, and a text string describing the relationship of the argument and return values displayed to the invocations.
The name of the screen button is the name of the procedure, with "Do " prepended.
The relationship of the argument and return values displayed with to the invocations is summarized by one of the following strings:
"working":
The procedure has been called, and has not yet returned. The arguments have not been edited since it was called.
"working on old":
The procedure has been called, and has not yet returned. The arguments have been edited since it was called.
"done":
The procedure has been called, and has returned. The arguments have not been edited since it was called.
"aborted":
The procedure has been called, and raised the signal ABORTED, which ViewRec caught (using BugBane to abort from an uncaught signal is one way to get this behavior). The arguments have not been edited since it was called.
"":
All other cases.
2.1.2.2. Arguments
The Cedar/Mesa programming language considers each procedure to take one argument and return one result, each of which are records (or nil). If the argument type of the procedure under preparation is not nil, this part of the Arguments and Returns area contains a RecordViewer on an instance of that argument type. This instance is the one used when the procedure is invoked. The components of the argument instance are not changed except when the user requests it, so the arguments to the procedure are naturally sticky.
2.1.2.3. Returns
This is used to display the values returned by the most recent invocation of the procedure. It has a RecordViewer on an instance of the return type of the procedure.
2.1.3. Feedback
This is a Tioga text viewer, in which messages are displayed.
2.2. How a Record Viewer Interacts
ViewRec places some restrictions on the concurrency of user-initiated actions. Each RecordViewer may or may not allow more than one procedure activation at one time. No RecordViewers allow more than one concurrent activation of the same procedure. At most one component among all the aggregates being subjected to ViewRec is being edited.
The component which is being edited is said to be opened, and it's name is displayed white on black, instead of the normal black on white. If there is an opened component when any of the screen buttons in any RecordViewer is clicked, ViewRec closes that component before doing whatever else the button would do. To close a component means to parse the string in its value viewer. If the parse is successful, the component is updated to the value from the parse. If the parse is not successful, the user is given a message that says so and describes the type of the component.
2.2.1. How Procedures Interact
Here is the encoding for the screen buttons for immediate procedures:
Immediate Procs: Left Middle Right
Ctl Increment Inquire Decrement
-Ctl Invoke Invoke Invoke
Here is the encoding for the screen buttons for prepared procedures:
Immediate Procs: Left Middle Right
Ctl Increment Inquire Decrement
-Ctl Prepare Prepare Prepare
2.2.1.1. Invoking and Preparing to Invoke
Clicking the name of a procedure component without control held down will have one of two different effects depending on whether the procedure is immediate or prepared (recall that an immediate procedure takes no arguments and gives no returns; a prepared one does one or the other, or both). If the procedure is immediate, then it is invoked, and the Arguments and Returns area is cleared. If the procedure is prepared, the RecordViewer adopts the display format for that procedure, in which arguments and returns for that procedure are displayed. In the Control region of the Arguments and Returns area there appears a screen button that is used for invoking the procedure.
While either kind of procedure is executing, the procedure's name in the main body will be displayed white on black, instead of the usual black on white.
2.2.1.2. Revoking
There is a protocol by which users may request that the execution of a procedure be aborted. This request may be made with varying degrees of urgency. The procedure is responsible for polling the urgency and taking abortive action.
A separate abort urgency is maintained for every procedure. It starts out 0, and is reset to 0 every time the procedure terminates. The user can alter and inquire about the abort urgency by control-clicking the procedure's name in the main body:
Left: Increment by 100
Adds 100 to the abort urgency (unless it reaches 65,000). If it reaches or passes 300, a further action will be taken: Process.Abort will be called on the running process (if any). This may raise the signal ABORTED within that process (it might not, also, depending on how it's coded).
Middle: Inquire
Prints the abort urgency into the feedback area.
Right: Decrement by 100
Subtracts 100 from the abort urgency (unless it reaches 0).
2.2.2. How Name-Value Pairs Interact
2.2.2.1. Text Values
Here are the clicks for the name button of a Name-Value Pair for which the value viewer is a Tioga text viewer:
Name Button: Left Middle  Right
  Shift Type Error  Previous
 -Shift Open Selection Next
Shift-left-clicking the name button gives a description of the type of that component.
Clicking a Name any other way initiates editing of it's Value. There are three different ways of editing, selected according to which mouse button was clicked:
Left:
This opens the value viewer for user edits (value viewers normally do not allow user editing of the text therein). The contents of the value viewer will not be parsed until the next ViewRec screen button is clicked, or until certain keystrokes are made in the value viewer (which is a Tioga text editing viewer). The keystrokes are described later.
Middle:
This causes the current Tioga Selection to be used as the candidate string. It will be parsed, and if successful, the component value will be updated. If unsuccessful, the user will be told that, and the type of the component.
Right:
Increment or decrement the variable. Of course, this only works for variables of discreet types (and only some of them).
Here are the keystrokes that ViewRec treats specially in value viewers (the "Next" key is the middle unmarked one):
Value Viewer: Next  Return
Ctl & Shift Give Up  ??
Ctl & -Shift Close  Doit
-Ctl & Shift Goto Previous ??
-Ctl & -Shift Goto Next Close
Here is what each of those operations means:
Give Up
No attempt to parse the characters in the value viewer is made, and the component is not updated. A character representation of the value of the component is put in the value viewer. The message "OK, I give up!" is displayed.
Close
This component is closed.
Goto Next
This component is closed, and then the next component is opened. This is equivalent to -Ctl-Shift Red clicking the next component's name. The next component is the next editable one in a left-to-right top-to-bottom traversal of the outermost RecordViewer containing this component. If there is no next component (i.e. this component is the last one in the traversal), instead of opening it, the message "Ran off end!" is displayed.
Goto Previous
This is like Goto Next, except it traverses in the opposite direction.
Doit
This component is closed, and, if the enclosing RecordViewer has a Doit ButtonClick, it is done. A ButtonClick is the operation of clicking a certain screen button with a certain mouse button with the control and shift keys in certain states. Every RecordViewer has at most one Doit ButtonClick associated with it. This association is made by the client at create time. An example is the RecordViewer used for procedure arguments; its Doit ButtonClick is one that invokes that procedure.
2.2.2.2. RecordViewer Values
For aggregate components, the value viewer is usually a RecordViewer on that component. The name viewer for that component is not a screen button, and cannot be used to initiate editing. Instead, the components inside the aggregate component are edited.
2.2.3. Messages
Every RecordViewer has one place in which it displays messages. If the RecordViewer has a FeedBack area, that is it. If it doesn't, but is enclosed in a RecordViewer that does, then that is it. And so on, untill the top is ran off. In that case, the system window is it.
When one of a RecordViewer's screen buttons is clicked, the first thing done is to clear the place where that RecordViewer displays its messages. All messages caused by that button click accumulate, separated by semicolons.
2.3. What Working Directory ?
ViewRec associates with each RecordViewer is a working directory. It is expected that each application will have some way of letting the user know and manipulate that working directory.
3. The Client Interface to ViewRec
3.0. Introduction
ViewRec is simple to use: create, in any way you wish, an aggregate of the procedures and variables you want in your user interface, invoke a ViewRec create proc (ViewRef, ViewInterface, or ViewTV) on it, and you have a ready-made user interface. A number of whistles and bells have evolved, but they are just secondary attachements.
For a small, if frivolous, example, see ViewRecExampleClient.Mesa, included in the DF file.
3.1. Typed Variables vs. REFs
ViewRec really uses the "Abstract Machine" (TypedVariables, Types, and so on) inside. It is (or, at least, it is intended to be) not necessary to understand AMTypes (very much) in order to use ViewRec. An effort has been made to provide a convenient set of "sugar" routines that convert from the more familiar world into the world of AMTypes; if you find a piece of sugar coating missing, complain, and it should get fixed.
TypedVariables are like POINTERs and REFs (they "point to" Cedar/Mesa values), and Types are like TYPEs. The difference is that there are ways, at runtime, to explore the structure through TypedVariables and Types, and this is why ViewRec uses them.
3.2. Creating RecordViewers
ViewTV is the procedure that really does it. ViewRef and ViewInterface are sugared versions for dealing with common sources of aggregate TypedVariables: refs to records, and names of definitions modules.
ViewRef passes to ViewTV a TypedVariable for the referent of the first argument (agg: REF ANY). The rest of the arguments are passed on directly.
ViewInterface passes to ViewTV an Interface Instance Record, obtained from the current memory state, for the definitions module whose name is the first argument (name: ROPE). An Interface Instance Record looks like any other record to ViewRec, and it contains as fields the variables and procedures declared in the interface. Again, the rest of the arguments are passed on directly.
ViewTV Creates a RecordViewer for the given TypedVariable. Most of the arguments can be ignored for vanilla usage; their defaults produce ordinary behavior.
agg: TypedVariable
This is what the RecordViewer displays. It must be a TypedVariable for a Cedar/Mesa value of an aggregate type. The aggregate types are: all record types (including structures, which are unpainted records; if you don't know what that means, don't worry), all sequence types, and all array types.
specs: BindingList ← NIL
This is for special instructions to ViewRec about each component of the aggregate. It will be explained later.
label: ROPENIL
The Viewer created may optionally have, as the first thing in its main body, a Labels.Label. This option is taken if and only if the label argument is non-empty. Furthermore, if label ends with a carriage-return, that is removed from the name of the Labels.Label, and the Label is forced to be the only thing on the first row.
otherStuff: OtherStuffProc ← NIL
This is how the Client may put abritrary Viewers inside the main body of the RecordViewer. It is usually not needed, and will be explained later.
toButt: Closure ← []
This allows the Client to specify an action to be taken when control-return is typed in any textual value viewer in the RecordViewer created. It is usually not needed, and will be explained later.
parent: RecordViewer ← NIL
This is the "upward" link used for finding a FeedBack area. It need not be related to the Viewers hierarchy, or anything else, for all that ViewRec cares. Vanilla flavored usage just leaves it nil, since vanilla flavored usage also provides a feedback area in the RecordViewer created, thus precluding the need to look further.
asElement: EltHandle ← NIL
When the aggregate being viewed is an element of another aggregate also being viewed by ViewRec, this argument should give the EltHandle (gotten by GetEltHandle) of that element. This completes some internal data structure linkages that make things like GetEltHandle, and moving to the next and previous value viewers, work.
sample: BOOLEANTRUE
This controls whether the sampling process considers the RecordViewer being created to be a root in its forest of things to update. Explained later.
createOptions: CreateOptions ← []
This gives an assortment of parameters that are only meaningful at create time. Most are layout parameters: fonts, spacings, sizes, and so on. All will be discussed later.
viewerInit: ViewerClasses.ViewerRec ← []
This is used to tell Viewers the things it needs to know to create a Viewer. If you are familiar with Viewers coding, it is what is usually called "info" in create procs (ViewerOps.CreateViewer, Containers.Create, etc.). It is passed almost unchanged into Containers.Create to make the outermost Viewer. The fields of most interest are:
name: ROPENIL
This tells Viewers what to name the resulting viewer. If you default this, ViewRec will try to fill it in by looking for a field named "name" in agg.
parent: Viewer ← NIL
This tells Viewers whether to make a new top level Viewer (if NIL), or embed it in another. You must fill this in correctly.
wx, wy: INTEGER ← 0
If the Viewer created is to be embedded, this is how you specify where it goes in the parent.
ww, wh: INTEGER ← 0
In most Viewer Create procs, these determine the size of the created Viewer. ViewRec treats them differently. ViewRec ignores wh, and sets the viewer's height after creating it and laying it out. The ww field has its usual significance, and may additionally parameterize layout. See the section on layout.
icon: Icons.IconFlavor ← unInit
This is the only other field ViewRec alters before passing to Containers.Create. If left unInit, ViewRec will choose an IconFlavor of its own.
iconic: BOOLEANTRUE
This controls whether the Viewer starts out life iconic or full-blown; it is only meaningful for top level Viewers.
wDir: ROPENIL
If non-NIL, this specifies the working directory for the RecordViewer; if NIL, the working directory current at the time ViewTV (or ViewRef, or ViewInterface) is called will be used.
paint: BOOLEANTRUE
This determines whether ViewRec will paint the new viewer.
3.3. A Record Viewer by Any Other Name ...
ViewRec thinks of RecordViewers as a sub-class of Viewers. There are procedures to:
treat a RecordViewer like any other Viewer (RVQuaViewer),
determine whether a Viewer is a RecordViewer (ViewerIsRV), and
let the Cedar/Mesa type system know it is (ViewerQuaRV).
ViewRec also thinks of RecordViewers as a sub-class of REF ANY. Analogs of the above three functions are also provided along this relation. There are Cedar/Mesa language features intended to do this, but they don't all work in all cases (because RecordViewerRep is an opaque type). NarrowToRV is provided to fix that deficiency.
3.4. What Kinds of Things Can be Displayed with ViewRec
Given an instance of an aggregate type, ViewRec will decide, for each component of that aggregate, whether or not to display it. That decision can roughly be characterized as: "if the Client has told me what to do, I will obey; otherwise, if the component is simple enough, I will display it". ViewRec knows how to display numbers and enumerations (and subranges thereof), strings, procedures with "simple" arguments, and aggregates of those things.
There are two ways the Client can tell ViewRec what to do. First, it can say: "don't display the component". This is done with the BindingList argument (specs) to ViewTV. If there is a Value Binding for the component, with visible=false, the component will not be displayed. Don't be confused by the other things that Value Bindings can do as well, such as providing initial values. The switches in Value Bindings are set up to default to the commonnest case, which is making data structure linkages that you don't want the user to see.
The other way the Client can tell ViewRec what to do is to say "this is how to display the component". A Handler describes how to display the component and handle its User interactions. There is a later section that explains how to make Handlers and associate them with components.
Here is a precise statement of how ViewRec decides whether or not to display a component:
If there is a Value Binding that says the component is to be invisible, it will be invisible; otherwise:
Handlers for the component are sought. If one is found, the component is displayed according to the Handler. ViewRec starts out with standard Handlers for numbers, enumerations, strings, atoms, arrays, sequences, and "suitable" records; Clients may add more. A suitable record is "simple enough", and would display a non-zero number of components. The test for simplicity can be bypassed to always yield TRUE by setting doAllRecords in the CreateOptions. If no Handler is found:
If the component is a procedure, its argument and return records are examined to see if they're simple enough. The procedure is displayed if and only if they are. Again, the simplicity test can be short-circuited by doAllRecords in CreateOptions.
Otherwise, the component is not displayed.
The criteria for "simple enough" are similar to those for being displayed, except that there are ways something can be simple, but not displayed. An aggregate is simple enough if and only if, for every component:
There is a Value Binding for that component that says to be invisible. Otherwise:
A Handler is found for that component. Otherwise:
The component is a record (or structure) which is simple enough.
3.5. Initial Values
Since ViewTV can only be called on already existing aggregates, this is almost not a problem.
The problem comes in with procedure arguments. ViewRec must cons up an argument record for any procedure it is going to invoke. From the TypedVariable for the procedure it can do this. This is also where it gets initial values for that record.
In addition to that, ViewRec offers an additional complication: Value Bindings. These are effectively assignments performed in ViewTV.
3.6. Non-Procedures at the Top Level
ViewRec may be thought of as providing a generalization of Menus, where the procedures can take arguments. What, then, is the meaning of components that are not procedures?
One thing such components may be used for is to give some information to the user. You could think of them as showing a state variable of the user's model of your application. As such, you may not want the user to be able to directly edit that variable. This can be done with a Value Binding. Setting "editable" to FALSE makes ViewRec disallow user edits of that component.
Another use for such components could be to parameterize your application in some way. This could be accomplished with a state variable and procedure-to-set-it pair. Alternatively, ViewRec allows the Client to specify a procedure to be called whenever the user edits a component. This is done with a Notify Binding.
3.7. Keeping Up-To-Date
ViewRec will periodically review some components and update their displays if they have changed. This is done by a special process whose sole purpose in life this is. Exactly what gets reviewed, and how often the whole loop is performed, are adjustable.
3.7.1. What gets Reviewed
A list of root RecordViewers is kept. On each iteration of the update loop, each root RecordViewer is reviewed. Reviewing a RecordViewer consists of reviewing all of its visible components. Reviewing a visible component:
Is up to the Handler, if that is how it was made (see "What Kinds of Things can be Displayed with ViewRec").
For the standard Handlers for the scalar types, this consists of comparing the current value with the one which is displayed, and updating the display if they are different.
For the standard Handlers for aggregate types, this is a no-op. This looks wrong, but is not: these components were displayed with a recursive call on ViewTV with sample=TRUE, so they are roots on their own.
If the Component is a record (or structure), it has been displayed with a recursive call on ViewTV. So, it recursively reviews the RecordViewer thus produced.
For procedures, is a no-op.
3.7.2. Tweaking
There are procedures you can call to explicitly review a RecordViewer or component. The review is done in the process you call from (there are interlocks to prevent ViewRec from confusing itself).
SampleRV will review a RecordViewer.
RedisplayElt will review a component. The component must be accessed through the procedure GetEltHandle.
GetEltHandle, given a RecordViewer and a path, will traverse the tree of containment, rooted at the aggregate of the RecordViewer, along the given path, and return the component found.
3.7.3 Frequency of Review
The review loop is controlled by a delay in each iteration. The elapsed time to do one review of each root RecordViewer is measured, and the delay is some coefficient times that time (within limits, also settable). The coefficient is ViewRec.behaviorOptions.delayParms.dMicroseconds * 1000. Feel free to adjust this number to your liking.
3.8. Abortion!
There is a protocol by which a user can request, with varying degrees of urgency, that the executing procedure be aborted (see section 2.2.1.2). The procedure is responsible for occasionally checking the urgency of the user's abortion request, and taking abortive action. There is a procedure, TestAndMaybeResetUserAbort, that, given a threshold, will check whether the urgency has reached or passed that threshold. If it has, that urgency is what's returned, and the urgency is reset to 0. If it hasn't, 0 is returned, and the urgency is unchanged.
3.9. Introducing New Ways of Displaying Things
It is possible for the Client to enrich ViewRec's repertoire. A Handler determines how a given component will be displayed, and how it will interact. A Recognizer is a procedure, which, given a Type, can decide what Handler to use (it can also decide to pass, and let someone else decide). All Handlers are "guarded" by Recognizers. ViewRec has an elaborate mechanism for locating the Recognizers to apply in the right order to get a useful variety of effects. ViewRec starts out with a standard set of Recognizers, and clients may add more.
The search for the Handler for a given component is conducted by applying Recognizers in a particular order, until one of them succeeds. The order is as follows:
First, all of the Recognizers specified in TryRecognizer Bindings are tried. There are no guarantees about the relative ordering among them.
Second, a special list of Recognizers, whose only distinction is that they come before the third group, are tried, in order.
Third, the list of Recognizers associated with the equivalence class of the type of the component is tried, in order. The equivalence class of a type is that set of types that may freely be assigned to one another. For instance, if you code "rope: TYPE = Rope.ROPE", rope and Rope.ROPE are in the same equivalence class. For another instance, INTEGER and [-3 .. 47] are not (although the compiler will insert implicit NARROWs, so your code looks like you were freely assigning).
Fourth is another special list, whose only distinction is that it comes between the third group and the fifth.
The fifth list is like the third. If the type of the component was a subrange, the first step to get the fifth list is to find what it is a subrange of. And if that is also a subrange, then find what that is a subrange of. And so on, untill you've got something that isn't a subrange. With the equivalence class of the non-subrange type is associated (a different association from the one in the third step) the list of Recognizers that gets used here. This is the standard way ViewRec handles subranges of numbers.
Next comes another special list, whose only distinction is to come here.
To get the seventh list, the first step is to strip off subranges, as in the fifth step. Then, the AMTypes.Class of the non-subrange type is taken. A Class is a broad category of types. Some Classes are: record, array, enumerated, and procedure. The seventh list is found associated with the Class.
Finally, there is one more special list, whose only distinction is that it is tried last.
The last seven lists are each added to by one of three procedures:
RegisterRecognizerByType will add a Recognizer to one of: lists 3, 5, or 7. The Reductions argument selects which one.
RegisterRecognizerBeforeReductions will add a Recognizer to one of: lists 2, 4, or 6. Again, the Reductions argument selects which one.
RegisterRecognizerToApplyAfterAll adds a Recognizer to the eighth list.
Each of the last seven lists is maintained in a particular order, and each of the above three procedures offers (via the AddPlace argument) the option of adding to the beginning or the end of the designated list.
It can be easiest to just write one recognizer per Handler. Note that by carefully choosing which list to put it in, the task of actually deciding whether or not the Handler is appropriate can be obviated.
Handlers come in two flavors: Simple, and Complex. Simple Handlers are restricted to do the Name-Value pair style of display and interaction. Complex Handlers are less restricted.
To implement a Simple Handler, you need (almost) only provide procedures to parse and print. Here, exactly, is what you need to provide:
Parse: ParseProc
Parse is given a rope to parse, and a TypedVariable to put the parsed value into. It has to be a TypedVariable, rather than a ref, becuase you can't make a ref to something embedded in an aggregate. A general way to relate a TypedVariable to something more familiar is to: make a thing of TYPE = REF Familiar, then get a TypedVariable for the Familiar via AMBridge.TVForReferent. Now parse into the Familiar, then copy from the TypedVariable for the Familiar into the TypedVariable given to Parse. Simple, no?
UnParse: UnParseProc
This guy's job is the inverse of Parse's: given the TypedVariable, produce the rope.
Max: MaxProc
This procedure is called when doing layout to decide how much area to give to the value text viewer. The height of the Tioga viewer created is the font height times the "lines" answer, plus the vertical padding from createOptions; the width is the "maxWidthNeeded" answer plus the horizontal padding. Someting like VFonts.StringWidth[the biggest string likely] will give a good maxWidthNeeded. The padding is to give Tioga some extra room; it seems to need it. But, since Tioga will make do with just about anything you give it, there's no need to sweat too much over producing these numbers.
Butt: ButtProc
This is for implementing Increment and Decrement. The TypedVariable returned is copied into the component, and the message is given (if it isn't empty, that is).
Complex Handlers are even simpler to describe. You need to provide only two procedures:
producer: ComplexProducer
Given the TypedVariable, and some contextual data, this procedure is free to produce any kind of viewer. However, the viewer produced should follow the conventions in the rest of ViewRec. There are two to pay attention to:
Messages accumulate per user action
Messages to the user generally accumulate. The message place should be cleared out once at the beginning of any sequence of events triggered by a user action (mouse click or keystroke interpreted by ViewRec or cooperating client). The procedure for displaying messages, and clearing the message place, is DisplayMessage.
One thing at a time
Upon reciept of a mouse click or keystroke, before doing any work (but after clearing the message place), check to see if something else was pending. This is done by calling FinishPendingBusiness. Some protocols may decide not to terminate now, and the boolean returned by FinishPendingBusiness will indicate this.
Conversely, you may leave an interaction suspended for later attention with a call to SetPendingBusiness. For example, consider how Simple Handlers use this. When the User wants to edit a field with a Simple Handler (textual representation), the Tioga text viewer for that value is made user-editable, and SetPendingBusiness stashes a finalization procedure. Text editing keystrokes and mouse clicks are not seen by ViewRec (except for the few that are explicity trapped, like RETURN and NEXT). When the user goes to do something else in ViewRec, she is presumed to be done editing, and that something else first calls FinishPendingBusiness. FinishPendingBusiness then calls that stashed procedure, which parses the edited text and makes the update.
updater: Updater
This is the procedure that ViewRec calls when it has detected that the current value is different from the displayed one. Of course, since ViewRec has no control over (or even knowledge of) what is being shown, it can only assume that the ComplexProducer initially draws, and the Updater updates, representations of the values seen at the time these procedures are called. If your Handler has its own way of keeping its display up to date, then it can ignore this mechanism (provide a null Updater).
elementGiver: ElementGiver
If the element being handled by this ComplexHandler is itself an aggregate, this procedure should be supplied. This procedure is for accessing the elements of the aggregate.
3.10. Layout
A RecordViewer lays itself out as follows. First, it identifies a target width. Then, for each viewer it will contain, it creates that viewer, measures its size, and places it so as not to exceed the target width (if possible). Consecutive contained viewers are placed left to right, top to bottom (as words are in English). The height of the main body is constrained by the maxEltsHeight field in the CreateOptions; if the contained viewers stretch over more vertical distance, the main body is made scrollable. Next the containers for procedure arguments and returns are dealt with. For each prepared procedure, the vertical space allotted to its arguments and returns is again limited, by createOptions.maxArgsHeight; the container will be scrollable if necessary. Next, the FeedBack viewer is added on (unless the CreateOptions say its height is to be 0, in which case it is elided). Finally, the height of the whole Viewer is set accordingly (unless it is a top-level Viewer and the height is excessive: greater than about 80% of the screen).
The target width is identified as follows. If the RecordViewer is a top level Viewer, and the viewerInit.ww given is not zero, that is taken as the target width. Otherwise, if either (1: the RecordViewer is a top level Viewer that is not iconic) or (2: the viewerInit.ww given is not zero, and the Record Viewer is embedded), the inner width of the main body of the RecordViewer (cw in its ViewerRec) just after its creation according to viewerInit is used. Otherwise, the createOptions.defaultTargetWidth is used.
Some RecordViewers (according to CreateOptions) will change their layout when the width of their containing viewer changes.
3.11. Some Random Customizations
When creating a RecordViewer, the client may consider the RecordViewer to be for "forms fillout" preperatory to doing some task. For example, such is the case when ViewRec uses itself to prepare the arguments to a procedure. Here are two "reserved for future expansion" slots that can be used to signal completion of the form and readiness to do the task.
3.11.1. otherStuff
The client can put Viewers of its own design, not associated with any components, in the body of a RecordViewer. The OtherStuffProc passed to the create procs does this. It is given the RecordViewer seen as a Viewer, and creates and returns a list of Viewers to embed in it. They are placed after the label, if any, and before the component areas. Again, they should obey the conventions from the rest of ViewRec (see section 3.9).
3.11.2. toButt
Also an argument passed to the create procs. This is something to be done when CONTROL-RETURN is typed in a Value Text Viewer.
3.12. Examples
Some small examples can be found in ViewRecExampleClient.Mesa, and in the implementation of ViewRec.ViewSelf in ViewRecOther.Mesa. Also, try invoking ViewSelf and using the resulting viewer to view the Rope interface. For more information, see the implementor.
3.13. The Working Directory
A RecordViewer has a working directory. It will be current whenever ViewRec calls client procedures (notify procedures or procedures in aggregates).
The working directory may sampled and modified via GetWorkingDirectory and SetWorkingDirectory. It is suggested that the client have some way the user can at least see, and maybe modify, the working directory of a RecordViewer. One possibility is to include the working directory in the name of the viewer.