REBOL
Docs Blog Get-it

R3 GUI Actors

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

Concept

An actor implements a style function.

If you think of a style as an object class, its actors are like the methods, and they operate on the instance of that class, the face object. Essentially, they are functions that are reused for each instance of a style, each face.

We call them actors because they are are not true methods and are similar to the actor functions used in [?schemes?].

This document is is for advanced users who plan to implement their own styles and need to provide new functions for rendering, input, or other control. Casual GUI users do not need to know about actors.

Defining Actors

GUI styles define actors to provide functions that handle events, process input, modify attributes, and setup graphics rendering.

Within a style an actor block holds the actor definitions. The block is a collection of actors names followed by their function body blocks. No function creators or argument specifications are needed, the make-style function will create those.

The naming convention for actors begins with on- followed by a verb or verb-noun that best describes the action. Examples are: on-draw or on-view. Although not strictly required, using this convention helps make GUI code more clear.

Here's an example actors block as it would look within a style definintion:

actors: [
    on-make: [
        ...
    ]
    on-update: [
        ...
    ]
    on-resize: [
        ...
    ]
    on-scroll: [
        ...
    ]
    on-over: [
        ...
    ]
]

Here is a complete style definition of clicker, the base-style that implements buttons.

clicker: [

    about: "Single-action button without text. Basis of other styles."

    facets: [
        size: 28x28
        area-color: 60.70.150
        edge-color: 96.96.96
    ]

    options: [
        size: [pair!]
        area-color: [tuple!]
    ]

    faced: [
        area-size: ; set by resize
        pen-color: ; set by on-draw
        area-fill: ; set by on-draw
    ]

    draw: [
        pen pen-color
        line-width 1.5
        grad-pen cubic 1x1 0 40 area-fill
        box 1x1 area-size 3
    ]

    actors: [
        on-draw: [
            face/facets/area-fill: make-fill face/facets/area-color face/state/mode
            color: get-facet face 'edge-color
            if face/state/mode = 'over [color: color / 2]
            face/facets/pen-color: color
            arg ; return draw block
        ]

        on-click: [ ; arg: event
            face/state/mode: arg/type
            draw-face face
            if arg/type = 'up [do-face face]
            none
        ]
    ]
]

Actor Arguments

Actor arguments are fixed and standardized. The arguments are:

faceThe face upon which the actor acts.
argsA single value or block of multiple values.

When an actor is called, those values are passed to the body block. Normally, local variables are not required because the face object, or subobjects within it, hold most values. However, should a temporary be required, they can be created with use, foreach, or other such context creating functions.

Q: Should the FUNCT method of set-word locals be used?

Inherited Actors

When a style is derived from another style its actors are inherited.

It should be the derived actors are not bound to any particular style, but are simple functions. This allows actors to be efficiently reused in all instances of a face object.

For example, button uses the clicker actors here:

button: clicker [

    about: "Single action button with text."

    facets: [
        size: 100x28
        max-size: 200x28
        min-size: 50x24
        text-body: "Button"
        text-style: 'button
    ]

    options: [
        text-body: [string! block!]
        area-color: [tuple!]
        size: [pair!]
        wide: [percent!]
    ]
]

Notice, no actor definitions are needed.

Accessing Facets

For any given style a facet value may be stored in either the style object itself, or within the face instance. The location depends on whether the facet is static for all face instances, or changes for each instance.

Because you don't know and shouldn't care where it is stored, the get-facet function is provided to get the value and the set-facet function to set it.

For example, if the on-resize actor needs to know the size facet, it would use:

size: get-facet face 'size

If the size is found in the face/facets object, that will be used. Otherwise, the style/facets object will be used.

Standard Actor Names

A number of actor names are predefined for standard actions, and we recommend that you use these for their equivalent actors within your GUI styles:

Actor Description
on-make when face is first created to initialize special values
on-click when mouse button clicked on face
on-drag when dragging inside a face
on-drag-over when dragging and are over a target face
on-drop when drag has stopped or when a file is dropped
on-focus when we have been given or are losing focus
on-get to fetch state values
on-set when state values are set
on-clear when the state values are cleared
on-key when key has been pressed (for our focus)
on-move when mouse has moved
on-over when mouse passes over or away
on-reset when reset is needed
on-resize when size values have changed
on-draw when system starts to draw the face (create DRAW block)
on-scroll when scrolling is needed
on-scroll-event when the mouse wheel is used
on-init when face and its parent pane are ready (including initial sizing)
on-attach when a face is attached to another face

Default Actors

A small number of actors are defined by default to work for all styles. They are:

Style Description
on-resize Recompute the area-size facet.
on-over Force redraw on state change.
on-get Return the face/state of a given name.
locate A special actor to map an event to an offset position.

Style definitions are allowed to override these actors with their own definition specific to their operation.

Calling an Actor

There will be times when one actor function will call another. This is done with the do-style function. Actors are always called this way, never directly.

For example:

do-style face 'on-click true

It should be noted that the call will be ignored if the face has no on-click actor.

Actors for Initialization

on-make

The on-make actor is called when the face is created within the layout engine.

argsnone
returnnone

This actor can be used to setup face facet values such as blocks of data, colors, face orientation, etc. and anything else unique to the face. If you are defining a compound style, this is a good actor to set-up any sub-faces.

It is not necessary to compute the face size within on-make because on-resize will be called later to do that.

Example that sets colors unique to the new face:

on-make: [
    face/facets/colors: copy [255.255.255 128.128.128]
]

on-init

The on-init actor is called when a new panel is created.

argsnone
returnnone

When a new panel is created, the on-init actor will be called for each face within the panel. At this point the face objects exist and the panel has been initialized, including any special bindings.

It is also called for any trigger faces (special faces that act on when panel events.)

Resetting the face on initialization:

on-init: [
    do-style face 'on-reset
]

on-attach

The on-attach actor is called when this face gets attached to another face.

argsthe face requesting attachment
returnnon

For example, the on-init actor for a scroll bar will call the on-attach for a scrollable face that was defined earlier.

Here's how a scroller uses it:

on-attach: [ ; arg: scroller
    ; Called when scroll bar auto attaches:
    extend-face face 'attached arg
    update-scrollers face sub-gob? face
]

For the scroller, the code to attach adds a scroll reactor to the target style through its on-init:

on-init: [
    ; Find a prior face to attach the scroll bar to:
    if target: find-face-actor/reverse face 'on-scroll [
        append-face-act face reduce ['scroll target]
        do-style target 'on-attach face
    ]
]

Actors for Events

on-click

The on-click is called each time a mouse button press or release occurs.

argsthe event object for the mouse click.
returnon down, can be none or the drag object. On up, none.

This example prints the button event that occurred:

on-click: [
    probe arg/type
]

on-click is also the precursor to dragging (holding down a mouse button, while moving the mouse), so you can create a drag object in on-click using the init-drag function. If you return the drag object the on-drag actor will be invoked.

on-click: [
    if arg/type = 'down [
        return init-drag face arg/offset
    ]
    none ; return value
]

on-drag

The on-drag actor is called when the drag object is created or when the mouse is moving inside its target face.

argsdrag object (created earlier)
returnnone

on-drag usually comes after on-click and ceases to be used when the drag object is destroyed, which happens right after on-drop.

Note that when a drag object exists, the on-move and on-over actors are not called.

Here is an example used by a slider that changes a value constantly during drag, updates its graphics, and calls its reactors (if any).

on-drag: [
    do-style face 'on-offset arg/delta + arg/base
    draw-face face
    do-face face
]

on-drag-over

The on-drag-over actor is called when the mouse is moving over a foreign face (not the target of the drag.)

argsBlock of values related to the drag (!! define)
returnnone

Examples

on-drag-over: [
    ; write this example
]

on-drop

The on-drop actor is called when the drag operation is released.

argsthe drag object created earlier.
returnnone

At the end of a drag operation, when one of the mouse buttons is released, the on-click action is called with the event, then on-drop is called.

After on-drop, the drag object is automatically destroyed. The on-drop actor is caleld regardless of whether the drag operation was started over this face or a different face.

The on-drop actor is also called if a file is dragged and dropped from the system desktop over the face. This is a special case.

Example:

on-drop: [
    ; write an example here
]

on-focus

The on-focus actor is called every time the face gains focus using the focus or next-focus function or loses focus with the unfocus function.

argstrue for focus and none for unfocus.
returnnone

Nothing is called after on-focus, so if the face is changing appearance, a show-later should be called within the actor.

This example makes the background color yello (selection variation of yellow) when the face is focused and makes it white, when the face is unfocused:

on-focus: [
    face/facets/back-color: pick reduce [yello white] arg
    show-later face
]

on-key

The on-key actor is called when a face has focus and a key is pressed.

argsthe input event
returnthe same event

The on-key actor has a default value for all faces, to return the event argument.

Simple keyboard navigation in a street map face:

on-key: [
    dx: dy: 0
    switch arg/key [
        right [dx: 1]
        left  [dx: -1]
        up    [dy: 1]
        down  [dy: -1]
    ]
    if find arg/flags 'shift [
        dx: dx * 3
        dy: dy * 3
    ]
    move-map as-pair dx dy
    arg ; return same event
]

on-move

The on-move actor is called every time the mouse moves.

argsthe event value (with face-relative positions)
returnnone

Important notes:

on-over

The on-over actor is called every time the mouse moves over or away from the face.

argsthe face relative position or none
returnnone

If the face has the all-over value specified as true, this actor will be run continuously as long as the mouse is over the face. If all-over is not true, it does not report continuously, only on enter and exit.

The on-over actor has a default for all styles. It will set the face/state/mode to over or up and then redraw the face. The default on-over actor code is:

on-over: [
    face/state/mode: pick [over up] face/state/over not not arg
    draw-face
    none
]

on-resize

The on-resize actor is called every time the layout is resized.

argsnew size (pair! value)
returnnone

Normally, you use this actor to modify the size fields of the draw block and any GOBs used within it. If your style is a compound style, the faces inside may also need to be resized, and can often be done with the resize-panel function.

Resizing a single face:

on-resize: [
    face/gob/size: arg
    set-facet face 'size arg
    set-facet face 'area-size arg - 2   
]

Resizing a compound face with scrollers:

on-resize: [
    resize-panel face arg
    update-scrollers face face/gob
]

on-scroll

The on-scroll actor is called from one face to scroll another face.

argsthe face object of the scroller
returntrue if the attach was successful

When a scroll-bar is moved it will call this function to tell its target face to scroll. This is how a scroll-bar informs the other face that it's time to update. So, this actor must be defined for styles that are scrollable.

When a face is scrolled, this actor can be called directly by the scroll reactor or by a prior attachment that occurs when a scrollable face is defined before a scrolling style.

Do not confuse this with on-scroll-event which takes an event to perform.

Scroll a text area face:

on-scroll: [ ; arg: scroller
    gob: sub-gob? face

    ; subtract view area size from total text size:
    size: negate max 0x0 gob/size - face/gob/size

    ; multiply by scroll amount:
    offset: size * arg/state/value

    ; change only the axis
    axis: face-axis? arg
    xy: gob/offset
    xy/:axis: offset/:axis
    gob/offset: xy

    show-later face

    true ; we got it
]

on-scroll-event

The on-scroll-event actor is called when a scroll event occurs.

argsthe event
argsthen same event

Scroll events can be caused by the mouse-wheel or other such devices.

Do not confuse this actor with on-scroll which is used to scroll a face from another face, such as a scroller style.

Here's an example:

on-scroll-event: [
    dy: none
    switch arg/type [
        scroll-line [dy: arg/offset/y / -30]
        scroll-page [dy: negate arg/offset/y]
    ]
    if dy [bump-scroll face dy]
    none
]

Note that bump-scroll is a function specific to this style.

Actors for Value Control

on-get

The on-get actor returns values stored in the face object and is normally called by get-face. Normally, the default actor is all you need.

argsthe name of the variable to fetch (default is value)
returnThe value fetched.

The default definition of on-get is:

on-get: [ ; arg: the field to get
    select face/state arg
]

on-set

The on-set actor is called by set-face for setting the state value of a face or any other value in the face.

argsA block containing the name and its value.
returnnone

The default name is value, which means set the primary value of the face.

Here's an example of on-set used by a clock style to set its time:

on-set: [
    if arg/1 = 'value [
        if date? time: arg/2 [time: time/time]
        face/state/value: time
        face/facets/clock/set-clock time
        show-later face
    ]
]

on-clear

The on-clear actor is called when a face needs to be cleared.

argsnone
returnnone

This actor is called by the clear reactor. It is also called when a panel that contains input fields is cleared with the clear-panel function.

on-clear: [
    clear face/facets/text-edit
    show-later face
]

on-reset

The on-reset actor is called when the face needs to be reset to a predefined initial value.

argsnone
returnnone

This actor is mainly used for the reset reactor.

on-reset: [
    do-style face 'on-set 0
]

Other Actors

on-draw

The on-draw actor allows you modify a draw block immediately before it is rendered.

argsthe current draw block for the face
returnthe modified draw block

This function may be called often, every time a draw-face is needed. You should make this actor as efficient as possible. If it is possible for computations can be performed in other actors, such as on-click, that should be done.

Here is an example of the on-draw used by buttons (from the clicker style):

on-draw: [
    face/facets/area-fill: make-fill face/facets/area-color face/state/mode
    color: get-facet face 'edge-color
    if face/state/mode = 'over [color: color / 2]
    face/facets/pen-color: color
    arg ; return draw block
]
About | Contact | PrivacyREBOL Technologies 2024