REBOL/View Graphics - Face Hierarchy
How faces are layered and composited into windows and panels.
REBOL/View provides a system for the construction of graphical displays and graphical user interfaces in a straightforward manner. Such displays normally consist of much more than just one face. Typical interfaces may require dozens, if not hundreds, of faces that are layered or composited together within one or more windows.
To reduce the complexity of the implementation of a user interface, faces are normally organized into a hierarchy. The hierarchy defines the relationship between faces, such as the way the faces overlay each other or how they are positioned relative to each other. The hierarchy also allows the efficient update of areas of the screen as faces change, and helps speed the propagation of events to their target faces.
In REBOL, the view system hierarchy is implemented in a direct and easy-to-understand manner. It is simple to construct the layers of faces and achieve the desired results. The system design also supports dynamic changes in the hierarchy, allowing it to be changed on the fly according to the requirements of your application.
The graphical hierarchy is constructed using the pane field that is part of every face. A pane is simply a block of faces (or a single face) that are graphically related, or event related, to a given parent face. The faces within the pane are subfaces of the parent face.
For example, a display face may be constructed from many other faces. When you view it, the result may look like this:
but, under the hood, this display is built from many separate faces as shown here:
Using the pane field of faces, the graphical hierarchy of objects is created. The top level face provides a pane that lists the objects of the next level, and each of those objects includes panes that list the objects of the next level beyond that.
Only faces that appear within the pane blocks of the hierarchy will be displayed on the screen. If a face is not in one of these panes, it will not appear.
The offset field of a face always specifies its location relative to its parent face. If a face has an offset of 0x0 it will appear at the same upper left coordinate as its parent face. This relative positioning of faces is used for all of the faces in a pane, and it makes it easy to position or move entire sections of the graphical display by simply changing the offset location of a single parent face.
The order of faces in a pane determines which faces are on top of others. The first face of the pane block is in the back; the last is in the front.
For example, if a pane is has this order:
pane: [city flower dance]
The faces will overlay like this:
However, if you reverse the pane:
pane: [dance flower city]
You will see this order:
Note that the positions of the faces did not change. They are at the same coordinates, but appear in a different order.
Here is an actual example you can try:
the-pane: reduce [ make face [ text: "Face 1" color: red ] make face [ offset: 50x50 text: "Face 2" color: green ] ] window: make face [ offset: 100x100 size: 150x150 pane: the-pane ] view window reverse the-pane view window
A special face object is predefined to represent the screen of the computer. This face is system/view/screen-face. You can view its field values with the line:
The main fields of interest are the size and the pane. The size indicates the width and height of the computer screen in pixels. The pane holds a block of the REBOL windows that are open on the screen.
You don't normally need to be concerned about the screen-face unless you want the screen size for your application. Functions like view isolate the need to access the screen-face directly, and it is recommended that you use them.
Every REBOL window is also a face. The face appears on screen whenever it is added to the screen-face pane (above).
The depth arrangement of windows is the same as it is for any face. The last face is the topmost window.
The fields of a window face have the following meanings, fairly consistent with faces in general:
|offset||location of the window on the screen, not accounting for its border or title bar (which is operating system dependent).|
|size||the size of the window, not counting its border or title bar.|
|text||the title text (caption) for the window.|
|color||the backdrop color for the window.|
|image||the backdrop image for the window.|
|pane||the top level faces to be displayed in the window.|
A window will be displayed when it is added to the screen-face pane and the show function is called on the screen-face. This is normally handled by the view function and you do not need to do it yourself manually.
Similarly, the unview function removes windows from the screen by removing them from the screen-face pane and calling show on the screen-face.
When creating more complex user interfaces, there are times when you need to group several user interface elements together. The pane of a face can be used to create such panels, even if the face itself contains no graphics of its own (no image or color backdrop, etc.)
The advantage of such panels is that they allow you to move all the elements within them together as a single unit. You do not need to update the positions of each element.
In addition, these panels are clipped in the same manner as all faces, allowing you to easily create scrolling subpanels that hold more user interface elements than can be shown in the window at one time. The user preferences panel of REBOL/View is a good example of such a panel.
To move or scroll a panel, just change the offset value of its face and call show on the face.
There are just a few areas where problems occur that may be difficult to debug, so we want to mention them here...
- Face not in a pane.
You do a show on a face, but it is not part of any pane. The face will not be shown.
- Face in more than one pane.
It is not valid to add the same face object to more than one pane. If you need to do that, make a clone of the object, and use that in the other pane.
- Negative face sizes.
A face size can never be negative (either in x or in y). Trying to do that is considered an error.