REBOL
Docs Blog Get-it

R3 GUI Faces

Technical documentation for developers.
Part of the R3 Graphical User Interface (GUI).
New users should read the R3 GUI - User's Guide.

Concepts

A face is an instance of a style.

A style holds the default attributes, variables, and functions of a GUI element, but a face object stores the specific values for that instance of the style.

For example, a button style may specify a default size, but its actual size as displayed on screen is stored in its face object. Its current position as well as its "clicked" state are also stored in its face object.

Panels are collections of related faces used for a specific part of the user interface. The faces within a panel are created during the layout stage of processing where the GUI language (a dialect) is interpreted and its styles create actual faces. A panel is itself a face, and a GUI is created from one or more layers of panels, each of which holds faces of its own.

This method of design helps minimize memory requirements. The static variables of a graphical element are kept in its style object to be shared by many instances of that style. Only the dynamic variables of the style need be stored in a face object. The style object also stores the code related to the face, which is reused for every face.

Using Faces

Faces are created during the layout of panel. The GUI dialect specifies the styles and attributes required for the GUI, and those are used to construct the faces of the panel.

For example, the GUI dialect code below uses the view function to layout a single panel that is displayed in a window.

view [
    title "Example GUI"
    field
    button "Submit" submit
]

The title, field, and button styles are specified, and the result will be a panel containing three faces, each associated with those style.

Faces can also be created with the make-face function. This function takes a style and an attribute block as its arguments, and returns a new face object. This provides a functional method of creating GUIs (but, it's rarely used, because the GUI dialect is more concise.)

b-face: make-face 'button [text: "Submit"]

In order to be useful, this new face must be inserted into a panel to be displayed. See the R3 GUI Panels and Layouts section for details.

Primary Functions

These functions are the most common for faces:

Function Description
view Create a panel from a dialect block, and display it in a window.
unview Close a window opened with view.
layout Layout a panel of faces. The panel can be displayed later.
make-face Creates a new face object based on a given style with the given attributes.
get-face Get the primary value of the face, or a specific state variable of the face.
set-face Set the primary value of the face, or a specific state variable of the face, then redraw the face.

Advanced Functions

These functions are used mainly for style implementation or to create special results within a GUI.

Function Description
get-facet Get a named facet of a face. If not present in the face, get the value from the style of the face. Multiple values can be fetch at the same time in a block.
set-facet Set a named facet of a face. If not defined, create it.
do-face Evaluate the reactor functions for a face. This is provided to allow reactor code from one face to relay actions to another face.
draw-face Generate the rendering block for a face, and refresh the face on screen.
extend-face Add a new field to the face. This is used by special styles during face creation.
do-style Call a style actor function for the face. Used to decouple the face, style, and actor.
has-actor? Return true if the face's style supports the given actor.
do-related If there are related faces within the same parent panel, call their related actors. This is how faces inter-connect.
find-face-actor Find the next (or prior) face that responds to a given actor. This is also used to inter-connect faces by nearness.

Face Names

Each face can have a name. Within a panel, it is useful to refer to a face by its name. For example, a panel that requests a user name and email address might need to refer to those faces within its code.

name:  field
email: field
button "Submit" do [
    unless verify get-face name [warn "invalid name"]
    unless verify get-face email [warn "invalid email address"]
]

The name and email words are panel-local variables that refer to their respective face objects.

Here's another example that attaches a slider to a progress bar. The prog name is used by the slider to refer to the progress bar face and change it as necessary:

text "Drag slider to see progress bar change:"
slider attach 'prog
prog: progress

The more about face names is provided below and in the R3 GUI Panels and Layouts page.

Face Values

Faces can have one or more values. In the example above, both the slider and the progress faces hold a percentage as their value.

The get-face and set-face functions get and set the values of faces. Because the majority of faces have only a single primary value of interest, get-face and set-face refer to the value by default.

For example, you can write:

slid: slider
button "Show" do [print get-face slid]
button "Full" do [set-face slid 100%]
button "Empty" do [set-face slid 0%]

However, it should be noted that there is an even easier way to set the slider that does not require the set-face function:

button "Full" set 'slid 100%
button "Empty" set 'slid 0%

These face reactors are shorthand for the set-face function.

Face Object

Users don't need to know much about the structure of the face object just to create GUI's. However, if you plan to implement your own styles, you'll need to know a few things about faces and how they store information related to the style.

Some styles may require more storage than others; therefore, face objects are extensible (vary in size.) This minimizes the memory required for faces that do not require extra fields.

Every face must have these standard fields:

Field Datatype Description
style word! The name of the style for this face.
facets object! Attributes that are unique to this face (from style/faced).
state object! Storage for state-related variables (not attributes).
gob gob! The graphical object used to display this face.
options object! Optional attributes specified for this instance.

Depending on how the face was defined, these fields may also be present:

Field Datatype Description
name word! A name used to refer to this specific face. (For example, to get or set its value.)
reactors block! A block of user actions for this face.

Some styles may add other fields. For example, a panel adds these fields:

Field Datatype Description
grid object! A table of panel layout parameters and limits.
faces block! A list of sub-faces of the panel.
triggers block! Triggers needed for the panel.

The next few sections are detailed descriptions of the fields above.

Style Field

The style field just holds the name of the style, not a direct reference to the style object itself. It's done this way (as a word) to avoid long listings during debugging.

Facets Field

The facets object stores attributes that are unique to the instance of the face. That is, it only holds attribute values that are different from the style itself or that may change based on variations in the state of the object (for example a change to a color when the mouse is hovering over a button.)

For example, a button might have these face facets:

area-size: 24x100      ; set by resize
pen-color: 100.50.50   ; set by on-draw
area-fill: 200.200.200 ; set by on-draw

Note that the face/facets field is merged with the style/facets field during the processing of the DRAW dialect. The face/facets always take precedence over the style/facets.

State Field

The state object provides storage for the non-attribute values of a style. For example, a button might keep track of its over and selected states. For a scroll-bar, the scroll value and delta percentages must be stored.

An example of the state object for a scroll-bar:

value: 0%   ; scroller offset
delta: 10%  ; page size increment

Note that the state object is normally small, but can be extended as necessary for specific styles that require extra fields. If you need to store your own internal, non-attribute variables for a face, it's best to store them in this location.

GOB Field

The gob field refers to the lower level graphical object that implements face rendering. It is normally a GOB of the draw type; however, for special text sections, it can be a text GOB (richtext).

Note that within a face gob the gob/data field is a reference back to the face object itself. This allows the display and event sub-systems to process gobs efficiently.

Some faces will use a face/gob/pane to holds sub-gobs to implement various parts of their display. For example, a scroll bar contains several such sub-gobs for its various components. These sub-gobs may or may not be actual faces, depending on how the style is implemented.

It should be noted that the gob/offset and gob/size fields are only approximations of the actual graphic image area. This is due to the anti-aliased graphics engine which requires that the gob be large enough to include pixels that are part of the anti-aliased edges of the face.(The same reason why the facet/size and facet/area-size have slightly different values.)

Options Field

The options field holds the optional attributes that were provided to the face within the GUI dialect when the face was defined. For example, in the line:

button "Submit"

The submit string is an option, and it will be stored in the face/options object:

text-body: "Submit"

What gets stored in the options object is determined by the options field of the style.

It is permitted for the face/options object to be used to reset a face back to its initial value. For example, when resetting the fields of a form. If a style requires this ability, it should always copy its option series values in order to be able to restore them later.

Note that face actions (reactors) are not stored in this options object.

Name Field

When a face is named, the name field holds that name (as a word). Such names can later be used to reference the face object from other faces and functions.

The name field comes from the set-word used in the dialect. For example, the area face created in this line:

summary: area

will have the name summary. The name can now be used to reset or clear the area:

button "Reset" reset 'summary
button "Clear" clear 'summary

For user coding convenience, the face name spans more than just the panel where it was defined. For example, this code works as you would expect, even though the buttons are part of a sub-panel, not the panel in which the field name was defined:

panel [
    user: field
    group [
        button "Reset" reset 'user
        button "Clear" clear 'user
    ]
]

In cases where it is necessary to open a new name-space, for example in unrelated sub-panels, this can be done with the style/facet/names field. See the panels document for more information.

Reactors Field

The reactors field is a block of names and values for special actions on a face, called reactors.

For example, the line below specifies a reset reactor with an argument of summary:

button "Reset" reset 'summary

This information is stored in the reactors block of the face. Multiple reactors may be specified.

Whenever specific events occur, the block is processed to perform various actions. For example, to evaluate code when a button is clicked.

Debugging Techniques

There will be times when you create a GUI but do not understand why it displays or acts a certain way. You can experiment around and try different methods, and often you'll find a good solution. At other times, it may get frustrating, and you're not sure what's going on.

Sometimes, you really want to know more about what's going on "under the hood". Fortunately, because the R3 GUI system is relatively simple. You can display more information about the GUI and gain an understanding what that information means.

Examining Faces

If you plan to write your own styles, you need to understand face objects fairly well. One easy way to learn about faces is to use the make-face function with different styles and options.

For example, here's the face that is created for a text style:

>> probe make-face 'text [text-body: "test"]
make object! [
    style: 'text
    facets: make object! [
        area-size: 0x0
        text-body: "test"
        text-color: none
        size: 200x20
    ]
    state: make object! [
        mode: 'up
        over: false
        value: none
    ]
    gob: make gob! [offset: 0x0 size: 100x100 alpha: 0]
    options: make object! [
        text-body: "test"
    ]
]

Face Debug

If you want to dump (display to the console) a face object created during a layout, just add the word debug on the line.

The GUI code:

button "Test" debug do [test: true]

will dump debug information to the console:

-- debug-face[button:make]: make object! [
    style: 'button
    facets: make object! [
        area-size: none
        size: 100x28
        area-color: 60.70.150
        pen-color: none
        area-fill: none
        text-body: "test"
        wide: none
    ]
    state: make object! [
        mode: 'up
        over: false
        value: none
    ]
    gob: make gob! [offset: 0x0 size: 100x100 alpha: 0]
    options: make object! [
        text-body: "test"
    ]
    debug: [make]
]

Now you can review it and determine all of the facets and other variables are set as you would expect.

Keep in mind that this dump occurs just after the face has been made and before any panel-related resizing occurs.

Red-line Mode

For face styles that render graphics (contain a DRAW), the face debug method above will also show up as a red box at the edges of the face's clip area (the GOB).

Example:

Missing image: /-code
Missing image: /-code
view [button "Test" debug do [test: true]]

Notes:

Style Debug

When defining new styles you can examine the object that is created in the processby adding a debug field to the style definition:

stylize [
    thing: [
        facets: [
            size: 100x100
            area-color: blue
        ]

        draw: [
            circle
        ]

        debug: [style]
    ]
]

When the "thing" style is defined, you will see:

-- debug-style [thing]: make object! [
    name: 'thing
    facets: make object! [
        size: 100x100
        area-color: 0.0.255
    ]
    draw: [
        circle
    ]
    actors: make map! [
        locate: make function! [[face arg /local][
            arg/offset
        ]]
        on-resize: make function! [[face arg /local][
            face/gob/size: arg
            face/facets/area-size: arg - 2x2
        ]]
        on-over: make function! [[face arg /local][
            face/state/mode: pick [over up] face/state/over: not not arg
            draw-face face
            none
        ]]
        on-get: make function! [[face arg /local][face/state/value]]
    ]
    options: make object! [
    ]
    parent: none
    state: none
    content: none
    faced: make object! [
        area-size: 0x0
    ]
    about: "Not documented."
    debug: [style]
]

Note that you can also use this same method to output the face object each time it is used in a panel. To do so change the debug line to:

debug: [style make]

Runtime Debug

You can globally enable debugging throughout the GUI system for monitoring various functions and events.

The GUI module object includes a debug field that can be set to a block of debug flag words. They are:

Flag Description
make-style when stylize creates a new style
do-style when a style actor is invoked
draw when a draw block is reduced
events to monitor events as they occur
dialect to watch panel GUI dialect

These can always be found simply by scanning source modules for "debug-gui" calls.

For example, the line:

guie/debug: [events draw]

will show all events and draws. Output will look like this:

-- draw tight
-- draw text-list
-- draw scroller
-- draw scroller
-- draw code-area
-- draw text-box
-- draw scroller
...
-- events move 380x113
-- draw text-box
-- events move 336x102
-- events move 320x93
-- events move 301x85
-- events move 285x83
-- events move 268x83

==Debug Functions==

Two internal debug functions can be called from your code:

Function Description
dump-face print information about a face
dump-panel print information about a panel

An example of dump-face is:

view [
    example: button "Test" do [dump-face face]
]

When you click on the button, you will see:

button: 10x10 size: 100x28 example "Test"

This shows the button style location, size, name, and data.

An example of dump-panel is:

view [
    pan: panel [
        button "Dump" do [dump-panel pan]
        button "Quit" quit
    ]
]

and it will output:

panel: 10x10 size: 120x81 pan "*"
   button: 10x10 size: 100x28 * "Dump"
   button: 10x43 size: 100x28 * "Quit"

It shows the panel face and all of its sub-faces, even if they are sub-panels.

The asterisk means that the field has not been set.

About | Contact | PrivacyREBOL Technologies 2024