A new GUI system and dialect for REBOL 3.0

Current status: Request For Comments
Version: 0.4
Last update: 26-Jun-2006
Author: Gabriele Santilli


1. Abstract
2. Goals
2.1 Short term goals
2.2 Mid term goals
2.3 Long term goals
3. Architecture
3.1 Separation of function from look
3.2 Dynamic behavior
4. Three dialects

1. Abstract

This documents outlines the short term, mid term and long term goals of the GUI system for the REBOL 3.0 project; it also outlines the architecture of the system and the dialect(s) that the system will use. It comments on how these dialects could be used for the Quilt project, too.

You should read this document if you are interested in this project.

2. Goals

In this section I will outline the design goals of the project.

2.1 Short term goals

In the short term, we want better "widgets" or styles; the commonly used controls should be already available, and it should be easy to "compose" them to create more powerful styles. This means that styles should be much more "componentized".

We want to create a better dialect for expressing UIs and for expressing new styles. (I see them as two separate dialects, for two different sets of users.)

This all leads to less work for creating UIs for applications or reblets.

Resizing and scaling

The GUI should handle both resizing and scaling. With resizing we refer to the act of changing the size of elements; with scaling we refer to the act of rendering an element of a given size to a pixel device with a given resolution. Handling scaling thus means being able to render the elements on devices with different resolutions, or even changing the resolution on the fly (magnifying). This implies that the GUI should work in two coordinate spaces, a device independent one, where the size of elements is defined, and a device coordinate space wich directly maps to the display device. All this should be transparent to the reblet; if ever the application code has to specify a size, it should refer to the device independent space; the application should never need to be aware of the actual device coordinate space.

2.2 Mid term goals

In the mid term, we want to:

  • Promote separation of the UI from the application (i.e. separate function from look/presentation);
  • Enable programmers to quickly write GUI reblets without having to write GUI code;
  • Enable graphic and UI designers to design and improve the UI of an application or reblet without having to know too many details of the application's code.

2.3 Long term goals

In the long term, our new UI architecture is meant to change the way applications are created; application or reblet writers should not worry about the details of the user interface design and look, if they don't want to; they should be able to focus on the function instead of wasting time with the presentation. At the same time, UI designers should be able to do their work without learning the details of the application code; sometimes it is desirable that UI designers are enabled to do their work without knowing how to write programs in REBOL.

It is also meant to change the way users interact with applications; users should not only be able to talk to the application via the user interface, they should also be able to talk to the user interface via itself; changing the user interface for a reblet should be as natural as using the user interface is. Of course, users should not be required to create or change their user interfaces; user interfaces should be designed so that no user will ever need to change them. However, should a user want to do so, she must be able to. (For example, a user may want to hide unused interface elements, or move often used elements in more comfortable places. The way a program is used changes from user to user, so it is not possible to anticipate all use cases in advance; in particular, usage may change over time as the environment around the user, and the user herself, change. Applications should be able to evolve as quickly as the environment does; the less users have to wait for programmer intervention, the better.)

It is also advisable to have a user model in the UI of a program, so that the UI can adapt itself automatically to the user that is using it. This can be as simple as "user levels" like "beginner", "intermediate" or "advanced", and UI elements that are shown or not depending on the user level. ("Optional", i.e. not strictly required for program operation, elements could also be automatically hidden when a user resizes the window to a size too small to hold everything.)

Another long term goal is allowing multiple UIs for the same application (e.g., web browser, desktop application, mobile application...).

3. Architecture

We need to define an architecture that allows us to reach our short term goals immediately, but that also will allow us to reach the mid term and long term goals in the future without having to redesign things.

Especially for reaching the long term goals, our design should be guided by these key ideas:

  • Separation of the application code (function) from the user interface (look); programming the function is a different task from defining the look. Also, a given program may need to be usable across different mediums, using different user interfaces (web browser, desktop application, mobile application...).
  • Dynamic behavior (visual editing); whenever the user interface is visual, it should visually represent any changes happening. It is desirable to avoid delays between a change and a visual change in the interface: when moving the knob in a scroller, the page should scroll at the same time; similarily, changing the font size should change the text on screen dynamically. This applies to changes in the user interface too: for example, changing the size of an element.

We need to keep these ideas under consideration while designing the first version, even though the first version will probably be far from reaching our end goals. This is an interesting design challenge: keep things simple while at the same time aiming for a greater goal.

Here I'm actually going to present some architecture ideas from the point of view of the long term goals; a design for reaching the short term goals and a path to the long term ones will follow.

3.1 Separation of function from look

The code for a reblet will need to be separated from the code for its UI; however, it's the reblet that defines what needs to be displayed to the user (i.e. "The current time is 5:42 PM"), and what the user needs to input (i.e. a web page URL). What we don't want the reblet to do is define how things are displayed or requested to the user. The reblet only understands data: it will request data input, and will display data as output.

   +--------+         +-----+
   |        |  data   |     |  gfx
   |        | ------> |     | ------>
   | reblet |         | GUI |  keys,   USER
   |        | <------ |     | <------
   |        |         |     |  mouse
   +--------+         +-----+

We have three domains: the reblet, which only deals with data (sends and receives data); the GUI, which receives and sends data on one side (talking with the application), while sends graphics and receives keyboard or mouse (etc.) input on the other side; and the user, which of course can only deal with the visual representation she sees on the screen, and with the input devices she has available.

Currently, the GUI domain is part of the reblets; they define not only what to show, but also how to show it. We need to separate the what from the how, and define a standard way for the reblet and the GUI to talk to each other.

3.1.1 Events

While a lot of simple reblets will be synchronous (for simplicity), generally speaking we need an asynchronous design (on top of which we can always define a synchronous API). This is already the case with VID; reblets receive "events" from the user interface through callback functions (such as face/action, or the face feels). This is how the reblet will receive data from the GUI in REBOL 3 too. A reblet will have its "feels", in the low level, that will be able to receive different kind of data events; we can think of higher level action blocks too, like in VID.

This might be done with tasks in REBOL 3.0; however, simple things should stay simple to do.

The same principle will be extended to apply to the other way around. The reblet will send data to the GUI by sending "events" to the GUI, which will basically mean calling callbacks in the GUI code. This means that the GUI will not only have the face feels from which it receives events from the user or the system, but it will also have application feels from which it receives events from the application code.

Events will be dialect blocks (like /Services messages). (Note: this design allows for easily having the reblet code and the GUI on different machines, with events being sent and received via /Services.) This bidirectional communication dialect needs to be defined independently from the actual GUI implementations, so that reblet code can be completely agnostic of the GUI code, and treat it as a black box. This is even more important for the GUI side, since the same GUI code is expected to be used for many kind of applications.

This dialect is yet to be defined (but see notes below). About the messages from a reblet to a GUI, we can imagine the following kinds of events:

  1. show events, when the reblet wants to display something to the user;
  2. request events, when the reblet wants to request input from the user;
  3. update events, when the reblet wants to update the display of data on the screen. (This might be considered a sub-kind of the show events.)

We may want to leverage the concept of "style" from VID, by making it abstract from the reblet side, and concrete on the GUI side. We'll have higher level styles with respect to the current VID (things like InputForm, Table, and so on); their name must reflect the "what" and not the "how".

3.2 Dynamic behavior

The fundamental idea about dynamic behavior is that if something is being represented visually, any changes happening to that something must be reflected visually too, so that the representation is correct at any time.

But, there is another meaning we want to give to dynamic behavior: the ability of the GUI to be modified and adapted at will. Users should be allowed, if they want to, to rearrange, move or resize any UI element. When a element can be represented in many ways visually, users should be allowed to change the representation (for e.g., changing from a drop down to a list, or back to a set of radio buttons, and so on).

Note though, that we don't want users to change the UI by mistake; nor we want UIs to be so poor that users will want to change them. We are simply stating that, should a user want to change the GUI to better fit with her preferences, she should be able to. We want reflective user interfaces, that is, ones that are able to edit themselves.

4. Three dialects

To achieve the long term goals, while being able to use our work for Quilt too, we need to create three different dialects; one dialect for creating "widgets" (like with the current STYLIZE dialect; this one won't be useful for Quilt), one for creating GUI layouts (like with the current LAYOUT dialect; this could be used for Quilt too, at least for static UIs), and the third to interface applications with the layouts.

Since we want to abstract the application logic from the GUI code, applications should not use the layout dialect directly; they should instead use the third dialect, which we can call "UI definition dialect". Apps will not specify the layout or the kind of user interface; they will just specify what data (events) they need. For example, if an app needs to ask the user to input her first name, last name and email address, it could use something along the lines of:

    request 'personal-data [
        first-name: "First name:" string!
        last-name: "Last name:" string!
        email: "EMail address:" email!

The app does not care about how the information is being requested to the user: this is to be defined by the "UI layout dialect". If no layout has been defined, the system could create a default one, using simple rules (fields for strings and emails, OK and Cancel buttons, and so on; the strings provided could in this case be used for labels); this way the above is sufficient to get the app running. (And, it could also easily fall back to a text only input if View capability is not available.)

To define the UI layout, it would be possible to use something along the lines of:

    define-layout 'personal-data [
        text "Please enter the following information:" return
        group [
            label "First name:" first-name: field return
            label "Last name:" last-name: field return
            label "EMail address:" email: field return
        ] return
        group [ok-button: button "OK" cancel-button: button "Cancel"]

or, for the buttons, maybe something like:

        group [button "OK" [ok-action] button "Cancel" [cancel-action]]

The layout model that seems the most suitable both for View and Quilt is a table layout model, similar to the HTML table model. Cells are created from left to right, with the RETURN keyword ending a row. Cells are always aligned; the dialect should allow specifying the alignment of the contents of each cell (in case it can't resize to fit), as well as the resizing weight of each row and column. Cells can contain subtables, like the GROUP keyword above indicates: so the above layout is made of three rows containing one cell each; the first cell contains some text, the second contains a subtable of three rows with two cells each, and the third contains a subtable of one row with two cells. (If the buttons were in the same group as the fields, the OK button would have been aligned with the labels and the Cancel button would have been aligned with the fields, which is probably not what you want; also, you may not want the labels to resize, while the OK button should.)

Note that, in View, there is no reason for GROUP to create a face (or GOB); it's just needed to define the position and size of the elements. In case the subtable should be rendered in its own face, it would be possible to use the PANEL keyword (so you can define borders, background, and so on).

The "widget definition dialect", that for obvious reasons can't be shared with Quilt, should be able to define how to render each widget (what faces/GOBs need to be created and so on), and how it should respond to events and so on. We're not entering into the details now, because many details of View 3.0 are not defined yet.

MakeDoc3 by REBOL - 12-Jul-2006