The AlMuist's meta-gui system

To allow gui specifications to be independent of the used gui-system, I designed a "meta-gui", ie a generic specification to which all amool implementations of a gui-system should comply. This allows you to design your gui without needing to think about what particular gui-system you're going to use.

You find four types of objects in AlMuist's user interfaces, which are:

Windows
that rectangular thing you can move on your screen. You can put one gadget inside a window (group or gadget)
Gadgets
A gadget is something that is inside a window, which can either be a group (below) or something visible, like a button or a label. Every gadget must be attached to a group or a window.
Groups
a rectangular area inside a window, that has itself no visible part, but that can contain other gadgets. You use groups to decide how the gadgets should be laid out in a window.
Invisibles
programmatically-speaking, an invisible object is like an object in the sense that you can put notification to it, but it can't contain any other gadget, is not contained in a group and has no visible counterpart. Invisible objects are used to make the gui communicate with the core. I'll explain that in more detail when I introduce notifications.

Concerning the gui class hierarchy:

  1. The class hierarchy of your guiBlocks must exactely match the actual guiBlock hierarchy you are describing. This means that if a guiBlock is a parent of another (ie can directely create instances of it), then its class must be the parent class of it. Note that you can declare them in the way you want, it's the parental relationships that has to be respected.
  2. The class hierarchy of a guiblock description should exactely match the gui-hierarchy it is describing. (There are exceptions I will talk about later, namely when a gadget in a guiBlock has to be attached to something in another block). A gui hierarchy is something like that: At the top you will always have some windows. Inside a window there should be exactely one group (unless you are putting only one gadget in the window), as a window should not contain more than one direct child.

Existing classes

[I'll make this enumeration a bit more detailed later]

The different gadget types are quite limited for now.. I'll add more later... Here's a list of available gadgets (note that they are not defined in guiBlock itself, but its a naming convention to which all subclasses of guiBlock must comply):

@guiBlock.window
A standard window. Can (should) only contain one gadget, typically a group.
@guiBlock.vgroup
A vertical group, that arranges all contained gadgets on a vertical line.
@guiBlock.hgroup
An horizontal group, that arranges all contained gadgets on an horizontal line.
@guiBlock.label
a read-only label. Has integer input port.
@guiBlock.textField
a read-write text field. Has integer input port and action output port.
@guiBlock.button
a simple button, with an action output port.
@guiBlock.check
an unconnectable checkbox (completely useless until I add boolean in/output ports! ;-)
@guiBlock.slide
an horizontal slider, taking values between zero and one hundred (guess what? I'll allow you to change that, in the future). Has integer input and output ports.
@guiBlock.sMethod
an invisible object with an integer input port. You can make this object do whatever you want when an event occurs.

Here is a little example of a gui strcture declared with that. It defines a login window, with two fields that each have a label (note that the password field does not hide its contents, as this API does not yet allow it...)


&=myGui &=super @( some gui-system here &) && @super
  &=name MyGui &&
  &=theWindow &=super @'guiBlock.window &&
    &=name win &&
    &=vertical &=super @'guiBlock.vgroup &&
      &=name vertical &&
      &=login &=super @'guiBlock.hgroup &&
        &=name lg &&
        &=login: &=super @'guiBlock.label &&
          &=name ll &&
          &=text "login:" &&
        &&
        &=box &=super @'guiBlock.textField &&
          &=name lb &&
        &&
      &&
      &=password &=super @'guiBlock.hgroup &&
        &=name pg &&
        &=password: &=super @'guiBlock.label &&
          &=name pl &&
          &=text "password:" &&
        &&
        &=box &=super @'guiBlock.textField &&
          &=name pb &&
        &&
      &&
    &&
  &&
&&

   This is a partial view of the class hierarchy...

myGui
theWindow
vertical
login password
login: box password: box

And this is what you get (with swing on windows)

@login : @vertical
@password :

The names of the amool classes for you gadgets and guiBlocks are completely free. When you put a subclass of some gadget inside your gui-hierarchy, then the superclasses will propagate all needed variables, methods, initialization code in the correct place.

For details about the name of the fields (@name, @text, ...), you should have a look at the API reference

Notifications

I already explained what a notification is in the introduction, but I'll tell it again.. ;-)

Some of the objects have the ability to hold some values. For instance a string gadget holds a string value, a checkbox holds a boolean value. Now you may want to synchronize the values of two different gadgets. For instance you have a program that displays a list of objects. For each object you can open a setting window. Now you have a field in the window that allows to modify the name of the object. To have the name displayed in the objectlist synchronized with the text field in the setting window, you will put a notification.

I implemented that with the concept of input and output ports.
An input port for an object means that it can be programmatically modified, and can therefore be the target of a notification.
An output port means that the object can trigger a notification.

Now, ports can be of several types:

Action ports
The simplest port type. It does actually not contain any information, it just means that some event associated with the source object occurred. A typical example is a button. A window will also have an action output port, triggered when the user clicks on the closing gadget.
Boolean ports
Contains one true/false value. Note that this is different from action, in the sense that it contains both the fact that something happenned, and the current boolean value.
Typical is the checkbox gadget, but you could imagine a boolean port for the visibility of a window (visible/hidden)
Integer ports
Contain a numerical value. Typical is a slider gadget. Note that it does not specify the range of the value. I am not sure whether I will check that. It means that it would allow you to connect a [0;10] ranged slider to a [-5;5] ranged slider, which will in some cases lead to an error.
String ports
The most complex port, that can hold any character string. Labels are typical input port gadgets, and string gadgets are typical string output gadgets.

You can only connect an input port to an output port if they have the same type.

Implementation of notifications

A little code for a start:


&=theGroup &=super @'guiBlock.vgroup &&
  &=sliderOne &=super @'guiBlock.slider &&
    &=name one &&
    &+speakTo @parent.sliderTwo &&
  &&
  &=sliderTwo &=super @'guiBlock.slider &&
    &=name two &&
    &+speakTo @parent.sliderOne &&
  &&
&&

When you want an object @A to be connected to an object @B, you will simply put a pointer to the object @B in a field that has a name like @speakTo. You can put as many definitions of this symbol as you want, one for each notification starting from @A.

The superclass will then be responsible for setting up the notification (the way it is doing it depends on the particular gui-system; for Swing, your gadgets will set the guiBlock class as a (...)Listener, and put code in the corresponding method, that reads the data from the gadget, and so on)

Circularities (A speaksTo B speaksTo A, in the above example) is handled that way: when a notification is started, a flag is raised, that forbids any other notification to occur on the source. This means that changing A will lock A and change B, which will lock B and change A, and stop here because A is locked.

This was for output ports. Now, each gadget class contains an Amool class named @copyFrom, for each port. When you call that class with some argument, it will generate the code that writes the supplied arguments in the port.

Gui blocks

Everything above was valid inside a single guiblock (on in a gui that has only one guiblock).

When you want more than one guiBlock, then first build your guiBlock hierarchy (with a standard Amool class hierarchy), and then put your gadgets inside the guiBlock, in the same way as above.

There is one subtelty with guiBlock, which is that you might want to connect a (either by notification or attachment, ie where the gadget should be located) things accross guiBlocks.

As going between several guiBlocks might require some additional code to be generated, I decided to put a severe restriction on connections that are allowed between guiBlocks: each guiBlock has two special childs, named stepUp and stepDown. these children provide some kind of gateways between guiBlocks. When you do some connection, you can do it anywhere within the source guiBlock, or to the stepUp child of the parent guiBlock, or to the stepDown child of a child guiBlock.

Then, once you got to the guiBlock you wanted to reach, give as argument to the stepUp/Down class your actual target.

A little example to make things clear:


&=mainGui
(...)
  &=theWindow (...)
            &=source
              (...)
              &+speakTo @'guiBlock.subGui.stepDown( @'guiBlock.subGui.win.group.target &) &&
            &&
      (...)
  &&
  &=subGui
    (...)
       &=target (...)
       &&
   (...)
  &&
&&

I explain: We wanted the source class to speak to the target class, which happens to be in another guiBlock. As we can't refer to the target class directely, we need to refer to the stepDown child of the guiBlock (inherited from the guiblock class). Now, as argument, we can give anything that is accessible from the guiBlock. It might be another stepUp/Down class, or, like in this case, a gadget inside the guiBlock. Note that the argument is still relative to the source target, because arguments are evaluated relative to the caller, in Amool.

When you put a notification to a sub-guiBlock (like in the above example), the notification will be transfered to all existing instances of the guiBlock.

Attaching gadgets accross guiBlocks

One last thing I haven't yet told you about is how you can have a guiBlock attach stuff to another guiBlock. For instance, you might want to have a menu (they do not yet exist in AlMuist's but it won't last long!) that allows to display the windows of the opened projects. The menu will obviously be part of the main user interface (as you don't want as many menus as there are projects), but its items will be part of the project guiBlock. So this means that the menu items, part of the project guiBlock, must be attached to the menu, that is part of the main guiBlock.

Each gadget defines a child named @attachedTo, that specifies the container to which the gadget should be attached. Its default value is:
&=attachedTo @'gadget.parent &&
(which means that a gadget should be attached to the gadget defined by its Amool parent class)

It's now easy to put a pointer to a @stepUp class, and attach the pointer wherever you want it to go... (note that you can't point to a stepDown class, as this would mean that you want to attach your gadget to all existing subBlocks, which would imply that your gadget would be attached at several places at the same time! (If it is what you want to do, then directely define it in the sub-guiBlock itself...)


Maxime Gamboni
Last modified: Fri Jun 29 11:38:43 MET DST 2001