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
Contents:
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:
- show events, when the reblet wants to display something to the user;
- request events, when the reblet wants to request input from the user;
- 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.
|