Guidelines for Clicker GUI programming 




The GUI server

The task of refreshing the screen according to the program's display is managed by the GUI server. This server will usually run in a separate process from the applications for improved safety and a better independence of application from the rendering subsystem.
The GUI server is also responsible from catching user inputs, translating them into actions  and delivering them to the applications. Learning from the past, the GUI server will have to be easy to update and to extend (new objects must be possible without exporting bitmaps to the server all the time), easy to develop for and deal easily with remote connections. Because the responsiveness of the GUI server will have a huge importance on the global responsiveness of the system to the user, the action of updating the display in response to a user event must be fast.

Y-window system : what are renderers.

Most of the redrawing job will be handled at server-side. Under a X-window system, when a REDRAW event is issued, it is forwarded to the application through a socket, then the application will send back to the server a list of commands (fill window, draw line, draw text, etc.) to define the new aspect of the graphical component. Our proposal - Y-Window will try to move the front end thread right into the server by the mean of pluggable renderers components and of abstract visual component description.

For instance, GuY might receive a plug-in that will accept html text directly as an input and render it in a screen buffer. This means that the application developer only needs to send HTML documents to the renderer to see them on screen and might also receive URL-based queries rather than mouse-click(x,y) that must be translated in text coordinates and resolved against a list of active regions by the applications.
Other type of datas (post-scripts files, vector graphics, true-type fonts, ...) can also be seen as a renderer feature.

Because all the renderers executes in the server's address space, it is easy to have a very short response time in case of a user input: most of the events are handled directly by the renderer rather than by the application.

At the programming level:

A renderer is made of a server-side part that will handle commands issued by the client-side components and will perform the drawing in on/off-screen buffers. The client-side components (user stubs) are some shared classes that are imported in the user program on demand and that will provide the user interface.

GuY general architecture

The user-stub object is a proxy for the real graphical object managed by the renderer and the GuY server. It can cache some of the server's state for application requests and optionnally pack some requests together to reduce the cross-domain traffic. The user stub will usually carry a handle that is used by the renderer to locate the referred object. A efficient mapping mechanism will be needed to ensure that every issued request comes from the appropriated application (i.e. the one which requested the object creation).

Dynamic loading

Both renderers and stubs are not part of the applications or of the server itself. They are dynamically loaded with an on-demand basis: a default language used for windows composition is always shipped with the server and allows the application to load new renderers (provided that they're loadable by the server).
For instance we could have
window help-browser(
  props (title<-"Help ...")
  holds (search: bars.search-bar(tip<-"Enter keywords here")@ north
         display:html.view(url<-"app://help/index.html")@south#both
  )
)

which will tell the GuY server that we have a horizontally-split window with a search-bar on the top. This search bar is also a compound object (a label, a text input and a "clear" button) that happens to be in the "bars" name space at the server. GuY will lookup for "search-bar" in "GuY.factories.bars.*" and retrieve the method to be called. Once the object is created, it will be registered as "GuY.windows.<winid>.labels.search" so that the application will be able to fill the bar, or clear it remotely by just giving its name, or receive a handle to the object from its name.

When GuY analyzes the "display:" line, it will fail to open "GuY.factories.html.*" (which doesn't exist already) and will lookup for a renderer called "html". Once this renderer is found, it is registered as the provider for "GuY.factories.html.*", which means it will have to be able to enumerate all the objects it can create and return a  factory to this object when GuY will pass it "<object-name>".
Once the factory has been generated, everything happens as in the previous case. However, if the user requests a handle to "display", we will not be able to use a generic proxy object, but instead, we will ask GuY to return informations needed by the client application to load the user stub and link it to the renderer.

Custom renderers

An application that refers to non-standard (i.e. widely distributed) renderer should provide its own distribution of that renderer. In such a case, GuY will still detect the renderer, but it might not have enough security credits to run inside of the server. If this occurs, the renderer will be started as a separated thread of the requestor application and will receive an asyncrhonous server that will listen/reply to the GuY server (then falling back in a X-window like responsiveness).
An alternative is to fork a slave server for the requestor application (so that the renderer still can execute at fast speed, especially if the application is the only component to run on the screen) and to load the renderer in the slave server as usual. This slave-server will have its private KDS "GuY.*" state, so that it cannot interfere with some other applications, and will usually be limitated to off-screen buffers rendering (no direct access to the hardware) unless the user explicitely turns-it in full-screen.

GuIDL -- The Graphical User Interface Description Language

In X-window system, the task of redrawing a scene is based on the emission of the "redraw" event (or window_exposed) to the client, then waiting for drawing commands from that client (which may be long if the client is busy with something else). Redrawing commands usually include things like filling the background, drawing lines, rendering text, drawing pixmaps, etc. which are enqueued in the X client before being transmitted to the server.

Y-window model is to delegate the redrawing job to the server (which now has *much* more processing power as in the time of X-terminals as it's usually a desktop computer ;). Rather than enqueueing redraw commands, we will send a description of the graphical interface to the server (either as plain text or byte-coded). This description will explain the server how to compose the window image from some  primitives commands or library-rendered widgets (which may themselves be described with the GuIDL).

Thus when the server needs to repaint a window, it only need to interprete the "window-tree" and execute each primitive command. Compared to the X-window approach, this is like having the widgets code at the server side... But it is even more! Because you can compose sub-windows at will, you can send the server the complete design of your SaveAs dialog box and have it completely handled by the server!

Another great feature that GuIDL brings is that it allows the client to send simple events response procedures to the server and have them processed on server-side too. As the user interfaces becomes more and more dynamic (buttons that are animated "on mouse over", auto-popup menus, tooltips, etc.), this will greatly save the amount of communications that must leave the server space (not only on remote display but also on single-machine because client & server execute in separate address space and thus must use the microkernel for every callback that is processed by the client!) and therefore gives faster responsiveness.

uses gui.colors.*;
uses gui.widgets.primitive.*;

imports external constants & classes such as grey, flattext, fillbox, hline etc.
object textbutton (
declare a new widget
properties: (string: text)
public variables: can be set and modified by the owner
registers: ( boolean: down, focused )
private variables : used for insternal state management
content: (
      background: fillbox (color <= grey) @center, #stacked
      corner_a: (
        hline (color<=lightgrey) @north, #expandx
        vline (color<=lightgrey) @east, #expandy
      ) @stacked
      corner_b: (
        hline (color<=darkgrey) @south, #expandx
        vline (color<=darkgrey) @west, #expandy
      ) @stacked
      txt: flattext (str<=text, color<=black) @center
  )

describes the content of the widget itself (and how it should be redrawn). sub-component can be named (<label> : <building>) for events-catching or dynamic modification.
Modifiers (@position and #expansion) explain what part of the drawing context they use. packers (or widget groups) are simply described by parenthesis with a list of widgets in it.

#expandx : the widget has the full width of the current drawing context.
#expandy : the widget is as high as the context
#expandxy : the widget uses the whole context (default)


the @north, @south, etc. options are quite obvious. @stacked allows to put a drawing object somewhere and define the whole context it had to be still available - thus enabling layering in the drawing ...

on GET_FOCUS: if (down) do {
      corner_a.color<=darkgrey;
      corner_b.color<=lightgrey;

      focused = true;
  } else background.color<=grey80;

on FOCUS_LOST: if (down) do {
     
corner_a.color<=lightgrey;
      corner_b.color<=darkgrey;

      focused = false;
  } else background.color<=grey;

an event-handler : when the object will get the focus (pointer falls within its coordinates), the code will be evaluated, the color of the background fill box will be modified, which will make this object emit the internal "redraw_request" event towards the upper layers.

the property modification is a possible way to send an event to a child widget: it can be caught with
on changed(<property-name>):
the keyword rvalue can be used to access the value to be affected to the property.
on MOUSE_DOWN: do {
      corner_a.color<=darkgrey;
      corner_b.color<=lightgrey;
      down=true;
  }

button pressing will be handled locally: no  costly client-server communication is required here ;)
You'll notice that the widget group corner_a automatically broadcast the modification to all appliable sub-widget.
on MOUSE_UP: do {
      corner_a.color<=lightgrey;
      corner_b.color<=darkgrey;
      down=false;
      if (focused) emit "clicked";
  }

now if the button is released while the component is focused (and have been pressed when in-focus), the button is clicked: this event is emitted towards the root, just as a Java exception. If  no widget hooks it, it is forwarded to the client-side
)


What can be done in the event handlers

Of course, we need to restrict the behaviour of the events handler (among others, it must not be able to loop freely or access arbitrary memory addresses). Only the properties, registers and local variables can be used. By the way, we'd like that those handlers have the opportunity of:
  1. perform expression evaluation and function composition
  2. process a list of datas coming from the client application (like a list of items in a combo list, etc) through a "foreach" loop.
  3. generate a data structure to be returned to the client application giving state update (encoding schemes like XML or DXI should be common)
(<var>|<register>) = <expression>
do { (<local declaration>;)* (<instruction>;)* }
if (<boolean-expression>) <instruction> [else <intruction>]
foreach <var> in <list> <instruction>

basic programming: affectations, finite list iteration, conditionnal execution, etc.
<object> = new <widget class>
emit <event-class>(<event args>)
<object>.<event>(<event args>)
<object>.<property> <= <expression>

objects management, including creation of new widgets (for dynamic content), emission of an event towards the root (emit) or to a child (<object>.<event>), which looks like function call.



An exemple of performances improvement by local high-level events handling


+-----------------------+
| Pick a color ...      |
+-----------------------+
| R ====[]==== 50% ####
 |
| G =[]======= 10% #### |
| B =======[]= 80% #### |
| <reset>  <ok>         |
+-----------------------+

properties: (default : RGBcolor)
register: (current : RGBcolor=default)
content: (
 (  red: colorslider(color<="R", val<=default.red) @north #expandx
    green: colorslider(color<="G", val<=default.green) @north #expandx
    blue: colorslider(color<="B", val<=default.blue) @north #expandx
  )@west
  color: fillbox(color<=default) @east #expandxy
  ( reset: textbutton("reset")@west
    ok: textbutton("ok")@east
  )@south
)



the colorsliders are slidebars with a coloured label that match the colour they'll be used for. they emit "value-changed(v)" event. For convenience, we assume that a RGBcolor type has been defined and that it can be used to transfer a color from the GUI and the application. It is editable through its red, green and blue properties. We assume it performs the needed conversion so that the color field of RGBcolor can be used as argument, etc.

The idea here is to have a real-time update of the fillbox color with low overhead (i.e. few communication between GUI and app) through local handling of the dialog box:

event color_selected {
RGBcolor col;

}

describe an event that could be emitted by the widget. If the system knows how to encode a RGBcolor, then it will learn that a color_selected event is just a wrapping on it.
on red.value_change(RGBcolor.field_type v) do {
     current.red <= v;
     color.color <= current;
}
tells the GuY server what to do when one of the components (red, green or blue) emits the "value_changed" event. Note that this time, it is a high-level event, not a mouse event!

in this case, we just store the value (notice the <RGBcolor. field_type> which allows a class to store types so that you don't worry about the actual type)

we also modify the actual color of the small box on the right, so that it reflects the actual selected color, which will make the box emit a "redraw" event that will propagate towards the root window
on green.value_change(RGBcolor.field_type v) do {
     current.green <= v;
     color.color <= current;
}
on blue.value_change(RGBcolor.field_type v) do {
     current.blue <= v;
     color.color <= current;
}

on ok.clicked() do {
     emit color_selected(current);
}

sends the actual datas to the application (or to something else if needed). the event class will be applied to a GuY-Yapp communication scheme to format the datas appropriately, for instance:
<event type="color_selected">
    <col type="RGBcolor" value="#eeeeff">
</event>

on reset.clicked() do {
  current=default
  color.color <= default;
}





In order to have an interresting language, it could be interresting that the basic items include a "one of" widget that could be used to implement pannels, etc.