Proximal User Interface - Now What?
Steven Solie sent me the following email:
After reading your well written article
(on The Proximal
User Interface)
I am left with the question,
"Now what?"
I like developing software and enjoy applying software engineering
principles to my work. GUI's have always been a problem for me with my
users often saying "what the?" when they are attempting to use my
software.
Your ideas about a more proximal computer interface makes a lot of sense
and I'd like to start applying it. I already know that I will run into
problems when I try to make Intuition, BOOPSI and ClassAct perform in a
more proximal way. I'm going to end up writing a lot of code because the
original programmers did not think the same way.
I hope, in the future, there will be extensions to Intuition which allow
it to be much more proximal and take a load off of the developers.
and I sent him a brief reply telling him how I went about implementing
some of the ideas. An email discussion then ensued and this web page
is the collation of that discussion. Steven's words are indented in
italics as the above is.
Now what?
The problem we have is that Intuition etc. are built out of the
conventional
paradigm. So it is very difficult to build proximal
UIs on top of them: we not only have to ignore many of the useful
facilities they offer us but we have to work against others.
We also find they impose oveheads that make proximal UIs only
feasible with fast machines - overheads we do not need.
Windoze is even worse. Though *maybe* BOOPSI might be a bit
better; I don't know.
I've built a software foundation for proximal UI from scratch
in C. (I could let you have it if you wish, though I should warn
you that it is not really yet ready for public release to the
timid!) It is modular and comprises a number of independent
modules arranged in three sets, approximalely corresponding to the
time-honoured model-view-controller triple.
There are data-structuring modules, which
set up complex data structures (the 'model'):
- RB - rings and blocks (the lowest, written in
assembler)
- SI - simple items
- IR - full items and relationships (many-to-many)
- GR - graphs (Directed acyclic etc.) and searching
- ES - expert system inference (the highest in this set so far)
Then there are modules that operate the 'view':
- EA - easels and easelpieces (graphics entities and
their drawing)
Includes import, export e.g. postscript, iff, html
- TX - text handling
Then there are modules that operate the 'controller' and which are most
concerned with proximal user interfaces:
- UA - basic user actions, getting IDCMP into shape
needed
- SK - sketching - pulling lines to draw things etc.
- DW - drawing, using SK, and endig up with easelpieces
- BA - boxes and arrows - using DW for this
The plan is to have several parallel to BA for specific styles
of drawing. These modules embody:
- # not only the ability to draw, but also
- # modifying the drawing according to the special reuirements of the
style (box and arrows or whatever)
- # maintaining checks on the syntax onf the diagrams (e.g. links
must always end on boxes, not on empty space)
- # providing hooks for other modules to take action at various
parts of the drawing; e.g. once a link is drawn we might
call an IR routine to create a relationship.
There is also one management module, which manages the lot:
Then there are two 'whole program' modules so far which integrate
these three sets of modules:
- KT, Knowledge Base Tools, which has become Istar,
which
draws boxes and arrows and from that creates knowledge
bases.
- Ann: Annotator, which links DW to GR to dual-playfield
screens (in EA) to allow such things as historical prints
to be annotated.
I have found that the controller set seem to be reasonably easy to
use for creating program-layer things, though one always goes through
a frustrating couple of days when one finds things you *want* to happen
do not happen (e.g. DW takes its default action rather than the
one you have tried to modify it to take) because one has not set
up all the inter-module links necessary. But after that
it seems to go OK. But I suppose even I am a learner with
using this stuff.
I suspect that others in computer science somewhere are far ahead
of this type of program structure, and it could do with a huge
revamp - but that is not what my research and other work is all
about.
Start with the concept of a knowledge base, implemented as a
single piece of memory of any size needed. Blocks of memory are
allocated from this that have
- a) a data part, in which data of your choice is stored
- b) a rings part, comprising pointers that form rings
- as they run through a number of blocks. Allows relationships.
- c) a typeword that shows the format of the block.
I'm confused on what you mean by grouping to any level. Are you meaning
that you may have as many rings of blocks as you want? That brings up
the question of whether a block is allowed to be in many rings or not
Yes and yes. It is designed for flexibility.
The rings part of a block comprises:
- # ringstarts, which make this block the 'parent' of these rings
- # ringlinks, which make this block a 'child' of thes rings
and there can be any number of each, up to a total of 8000 - though around
3-6 each is normal.
Each ring (start and link) (normally) has a purpose. e.g.:
- # 'Contains' Ring that links easel to its easelpieces and
a group EP to its members. Nested to any level
as an hierarchy. (Normally) all easelpieces have
both a Contains Ringstart and a Containts Ringlink
so that they can be both child of a higher thing
and parent of others - forming an hierarchy.
- # 'Expressed' ring, which links an item or relatinship etc.
to easelpieces that expresses it. The Expresses
ringstart resides in the item's block; the Expresses
ringlink resides in an Easelpiece block. In this way
several different EPs can express one item.
Thus, so far, we have the following ring words in an Easelpiece block:
- # 'Contains' RL
- # 'Expressed' RL
- # 'Contains' RS
but in actual practice there are also a few more. But that hopefully
gives an example to clarify.
Use some blocks to hold details of what to draw (and therefore
what has been drawn) on an easel (as I call it; from your point of
view treat it as a glorified Rastport). The holding details of
what has been drawn (EaselPieces) is important since it allows
you to find out
what the mouse is over. Now, all easelpieces in a given easel are
linked by a single ring of pointers, which terminates on a block
used for the easel itself. Note that grouping to any level is
allowed.
One major design feature on EA module is that the EaselPieces each
hold information about what type of graphic they are - line,
closed figure, whether poly or simple, bitmap, group, textpiece, etc.
This means that you can hav any mix of them in a ring. It also
offers a kind of polymorphism, so beloved of the OO community.
Now take IDCMP messages and process them in a more convenient
manner, detecting gestures like clicks, multiclicks, and complex
mouse operations. While any of this is proceeding, an optional
routine will tell you what easelpieces are affected by what types
of operation. e.g. click over an easelpiece, a mousemove over
the background, etc. (Also integrates keyboard with mouse.)
One large class of user actions is that of clicking on certain
things - and indeed this is common in WB etc. Another is that of
picking up something and dragging it - such as an icon in WB or
the handle of a slider gadget. Yet another class of user actions is
to draw or manipulate things, such as drawing a line, as in many
drawing packages. Now, for each class of user actions we need to
be able to do the appropriate things in response to mouse gestures.
Take the latter: drawing. As mouse moves various lines stretch
with the mouse, and/or things move. But what? So in the SK layer,
(sketching) we create a block for each 'sketchpiece' - a graphic
primitive such a line or rectangle that moves with the mouse.
These sketchpieces are created at the start of the mouse operation,
normally, and are deleted at the end, or when it aborts.
A sketchpiece holds data to say:
- # what shape it is
- # colour and render mode (usually COMP)
- # whether it moves with mouse or is stretched
- # and whether it does so symmetrically or not
- # and its purpose
Thus in a box and arrows diagram, to move a box and all its arrows
we create a skp for the box itself (rectangle) and one for
each arrow (lines) with purposes 'box' and 'arrow-end'.
What skpieces are created depends on the next outer layer.
This layer deals with the moving and then deleting of whatever
skpieces are created, and other administrative matters. It is
designed to allow complex multi-piece drawings.
That leads me to a question about the mouse pointer itself. Is the pointer
not a SketchPiece? You may want the pointer to change shape as it is
moved over EaselPieces or sections of EaselPieces. For example, near the
end points of a line you may want it to look like a rubber band for
"stretching" the line. After it leaves one EaselPiece, it is destroyed
and a new SketchPiece is created. These actions would take place just
before and just after an operation.
1. No, the mouse poiniter is not itself a sketchpiece. Two benefits
of this. a) It actually allows the mouse pointer to move slightly offset
from the 'active' point, so as not to obscure it such as when doing fine
drawing and positioning. b) allows the outer layers to supply whatever
mouse pointer is appropriate.
2. But you can make up sets of skps of arbitrary complexity. At present
they can be lines and rectangles, but it would be easy to (and has always
been planned to, but no time to do so) they could be a Bob as well.
Next layer: DW, drawing. Knowledge of what easelpieces an operation
started
over, and where in the easelpiece is started (in the middle, on an
edge, on a vertex, etc.) can be used, depending on routines supplied
by an outer layer, to decide what type of visual action is to be
carried out. e.g. in B+A, press LMB over box edge and we start
drawing a link (one skp: a line for the new arrow). Press LMB
over middle of box and we start to move it and its arrows as above.
What happens at end of operation? In DW, if we are drawing a new
thing then an easelpiece block is created for the new thing and
given data of position and size derived from the latest data
found in the relevant sketchpiece. If moving a box and its arrows
then at the end, all the relevant easelpieces are updated re. their
positions and end points according to the latest data in the
corresponding sketchpieces. the DW layer preforms almost all
the functions of a standard vector drawing program during the act
of drawing. It allows grouping, duplicating, as well as sizing,
moving, creating new poly vertices, etc. But it has a certain style
that is proximal, which means the user does not have to select the
easelpiece to operate upon first (as when normal drawing software
gives you handles) and then perform the operation. Rather, it
'intelligently' detects what part of an easelpiece is pulled
with the mouse and acts accordingly. So, to bend a line, simply
pull it in its middle. To stretch a box simply pull one ot its
edges or corners. To move it simply pull it in the middle, etc.
Next, two important questions:
- a) what styles of drawing do we want?
- b) what is the purpose of drawing?
By styles we mean e.g. box and arrows drawing is different from
contour lines, and each has its own distinct semantics.
This is where a user interface style guide is a must. The context would
also be very important for the user (eg. drawing versus erasing).
Exactly. But since there, at my last count, aroudn 15 different
styles of drawing, we need 15 parts to the style guide, and all
different.
I'm not sure what you mean by styles. A picture of the famous march
you referred to may help. I understand the need for distinct semantics but
I don't see how you fit that in.
Here are some examples:
- 1. style = simple drawing of diagrams as in normal draw package:
Need to just draw shapes, stretch them, rotate them, and maybe group them.
- 2. style = painting, as in DPaint. Need to 'paint' brush down onto
canvas as spot, line, etc. Need to smear and blur. Need magnification
for fine control. Etc.
- 3. style = box and arrows diagram. Need to place ready-made box
shapes
in empty space. Need to draw links between boxes, but error if end of
link is released over empty space. Need to move box with arrows
attached. Need to pull lines to bend them. Little need for grouping,
rotate, smear, etc. (except when treating the diagram as a painting!)
No need to move lines separately - cos this would detach them from
their boxes.
- 4. style = bar charts. Need to draw lengths of bars easily (just as
the 'sculpt' action in Music-X, in which you just moved the mouse along
and all bars went to where the mouse was over it - nice; only an Amiga
culture could have thought of that?). Need to be able to modify single
bars. But no need for rotate, smear, nor for dragging arrows with them
- 5. style = contour lines on map. Need to be able to insert new
conttour
quickly, and then to pull it to correct shape. And so that nearby contours
will move a bit as you pull this one. Must avoid contour lines crossing.
No need for rotate, smear, etc.
- 6. style = route map. Like box and arrows diagram except that the
boxes become points. But we still need
How then do you solve the problem of giving the user the correct context?
I usually handle it now by poping up a requester full of buttons with
the title indicating the context. Perhaps a row of indicators which tell
the user what is going on? For example, when I'm "drawing" in my paint
package I see a paint brush icon highlighted. When I'm "image processing"
I see the corresponding icon highlighted. I'm thinking about modes.
If one allows several 'styles' in a single diagram or easel (e.g. a
map with contour lines and also network of roads, rivers, railways, etc -
then we have two styles (5,6). If also we have little shapes to show
churches,
etc. then we have style 1 too. We might also have a wee bar chart
in each area of map to show proportions of pupolations etc. - style 4.
Now, how to combine them? Requester (or toolbar)? That is needed for
adding new elements to the drawing. But not for modifying existing
elements. Each Easelpiece (a) holds an indication of its purpose and
(b) has links ('Expression') to the item or relationship or attribute
it expresses, and by following that link we can find out what it is.
So, by either (a) or (b) we can find out what style of drawing
action to start on this thing.
So for instance one next layer out combines simple drawing actions
into the style of boxes and arrows - such as ensuring that all
arrows move with the box to which they are attached.
There are planned to be a number of modules, each for one style.
And note that the whole system is designed to allow several
styles in a single easel, rather than each easel having
a single style. The aim is to allow us to return to the complex
types of diagram that our forefathers used, such as the famous
one about Napoleon's march to and from Russia.
The next layer out is perhaps the most important one.
It deals with the purpose of drawing, in whatever
style we use. We can simply draw things, and print them out - but what
then? To be useful we must attach each easelpiece to some meaningful
object that it expresses. Each such object occupies one or more
memory blocks, and is linked to the easelpiece by ring of pointers.
In this way any user action on an easelpiece can be interpreted
semantically. e.g. in a B+A diagram start drawing a new line
from edge of one box and release over another, and once the
operation finished we create a block for a relationship and attach
it to the two item blocks expressed by the two boxes. Or, in a
different style, if we
draw a wall across a field this forces the creation of two fields
where there had been one.
Next layer out uses the results of layer 7. And so on.
But as you can see we are nearly at the outermost layer,
that of the main program.
Then there is a module-management module that keeps
all the above working just fine, and also allows flexibility.
The flexibility is provided by structures for each layer (=module).
e.g. xxOpControl usually holds, for layer xx, parameters to
define the precise shape of the operation and also optional
routines to be called at certain key points such as just after
it has started and just before it ends. It does get
fairly complex, but there is I hope an overall standrad
structure to it.
I'm not entirely convinced that the idea of a proximal interface is
that far of a leap forward. I'm beginning to see it as a methodology
for redesigning your GUI and extending it to be more proximal. Add a
layer onto Intuition to handle the vector objects versus the bitmapped
ones. But how far ahead is this idea?
Exactly. A methodology. Or, rather an approach. Not so much a
technically different thing. Except that the GUI APIs we have at present
are not flexible enough to fully and efficiently support it.
How far ahead? Well, CLI people in 1980 asked that about GUI.
The main difference is that standard GUI focuses on the BL-SL
(lexical-syntactic) mapping while proximal UI focuses on the BL-KL
(lexical-semantic) mapping. So, to those who are used to thinking
and discussing and researching mainly the syntax (SL) the semantics
(KL) seems of little concern to them. It is, they feel, merely
a concern for the users themselves to sort out. PUI says "No,
there are good and bad ways, and we must link in the KL demantics
directly.)
Andrew Basden.