VID/View Notes

VID/View Notes

Please note

Since I wrote this there have been changes that may make some of the statements you find in here just plain wrong. I intend to rectify that situation in due course, but in the meantime, instead of removing this resource I figure it is better to leave it here because most of it could be useful.

Introduction

I decided to create this document while learning the capabilities of Rebol/View. It resembles a bunch of notes. I figured the best time to record my "ahas" was when they occurred.

You can read this document as HTML on the codeconscious.com website or as a text file or much better than both of those as a Easy* reader document from the codeconscious.com rebsite. The benefit of the rebsite Easy* version is that most code examples within the document are executable - click on them to see them work. If they don't work in the reader they will be copied to the clipboard.

To use the interative Easy* reader version, start REBOL/View, ensure you are connected to the internet by clicking on "local" at bottom left if necessary, click on the "REBOL.com" icon, then on "Sites", then on "Code C.", then finally "Docs".

Also have a look on the website or rebsite for related documents "Overview of GUIs using REBOL/View" and "View Notes".

Confusion Alert

!You DON'T need this document to produce VID programs.

This is not a tutorial nor a manual on VID. If you want a VID tutorial or documentation then you should have a look in the Docs folder of the Rebol/View desktop.

Because this goes into the internals of VID it has the danger that it will confuse someone who has not already started using VID. So Only look at this AFTER you have read the offical documentation and have confidently programmed in VID.

!And one more warning. I add to this document incrementally as I pick up something. Therefore I could have quite easily missed some simpler information for a given topic or plain just got it wrong.

Let me know if I did - please :^)

Panel Style

Give panel a layout specification block for its data and it will create a subpanel. Now the various members of that panel can be treated as a group. For example,

view lyo: layout [
    panel-face: panel [
        box red 20x20
        box green 20x20
    ] edge [size: 1x1 color: black]
    return
    button "Show" [show lyo/pane/1]
    button "Hide" [hide lyo/pane/1]
]

Bug in LAYOUT

You might ask why I used "show lyo/pane/1" instead of "show panel-face" well the reason is that one works the other does not. The explanation is that there is a bug in the LAYOUT function that sets the panel-face variable incorrectly for PANELs. So I've shown one type of work around.

I have a patch for layout that fixes this problem. See patches.r script in my script library.

List Style

This style is so often needed yet I suspect for many people, myself included, it remains intimidating. I think the reason for this is one hopes to just give it some data and it display like text-list does. Though when you go to use this style you realise that is just a little more complex. In effect the complexity comes because this style is like the Layout function. It lays out faces in a list format - but you have to supply the faces it should lay out.

So with this style you need to supply data and the instructions for how things should appear.

I think there is a need for another style that satisfies the need for easy list usage - probably a grid style but that is getting more complex :)

An under-the-hood understanding of LIST comes if you can learn about what I call "calculated panes" (described later).

Text-List Style

This style is really useful because it is so easy to set up and use. However the behaviour you get with it [for View 1.2] does not support additions or removals. If you want to modify them on the fly you can, but you have a little more work to do.

Note that this:

view layout [text-list "red" "green" "blue"]

is equivalent to

view layout [text-list data ["red" "green" "blue"] ]

As far as I've seen the TEXT field of a text list is not used. TEXTS is the field that contains a reference to a block of strings to be displayed. The DATA field and the TEXTS field end up with the same reference.

Modifying text-lists

Because TEXTS refers to a block it can be manipulated as a normal series.

view layout [
    tl: text-list data copy system/locale/months
    button "Delete first" [remove tl/data show tl]
]

When you have add or subtracted lines to a text-list you need to modify the text-list's slider so that it reflects the new data.

Here's one method to do it:

view layout [
    style updatable-text-list text-list
    with [
        update-slider: does [
            sld/redrag lc / max 1 length? head lines
        ]
    ]
    tl: updatable-text-list 300x100
        data copy system/locale/months
    button "Delete first" [
        remove tl/data
        tl/update-slider
        show tl
    ]
    button "Append now" [
        append tl/data mold now
        tl/update-slider
        show tl
    ]
]

The following changes the strings in a block using a text-list.

view layout [
    do [block-of-strings: COPY/DEEP system/locale/months]
    tlist1: text-list data block-of-strings
    button 150x25 "Append *" [
        foreach string tlist1/picked [append string "*"]
        show tlist1
        info1/text: mold block-of-strings
        show info1
    ]
    info1: info 600x100
]

Here's one way to change a line in a text-list. The CLEAR of picked below is used to ensure that picked does not contain any keys that may no longer exist. Also note that you can select multiple lines that share the same string but only the first of these lines will be changed by this code.

view layout [
    do [block-of-strings: ["one" "two" "three" "four" "five"]]
    txtlst: text-list data block-of-strings
    button 150x25 "Change picked" [
        foreach string txtlst/picked [
            change find/only txtlst/texts string mold now/time
        ]
        clear txtlst/picked
        show txtlst
    ]
]

Scrolling text-lists

In the text-list style (TL) there is a field TL/LC ("line count") that holds the number of lines that can be displayed. Another field TL/SN (perhaps "scroll number") that is equals the number of lines scrolled off the top.

To be consistent with the default text-list functionality, TL/SN should be less than or equal to (1 + length? head TL/LINES)

Finally to change the slider position modify TL/SLD/DATA.

This example code will scroll the line you click on to the top and synchronise the slider to match.

tl-scroll-to: function [
    face line-num
] [max-scroll-num] [
    max-scroll-num: (1 + length? head face/lines) - face/lc
    face/sn: line-num - 1
    face/sld/data: face/sn / max-scroll-num
    show face
]

view layout [
    text-list 200x50 data copy system/locale/months [
        if not empty? face/picked [
            num: index? find face/lines first face/picked
            tl-scroll-to face num
        ]
    ]
]

Problems with updating text-lists

Some people have reported problems when updating text-lists.

The following example code and solution is taken directly from a message sent by Philip Bevan (I hope that it is ok Phil!) and is relevant to View 1.2.1 at least.

After clicking the button on this example, scroll down (you'll see that there is a strange scrolling artefact at the top of the list).

tl-data: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]

view layout [
    tl: text-list data tl-data 100x100
    button "Update it" [
        clear tl-data
        insert tl-data [1 2 3 4]
    ]
]

The following code demonstrates a solution to the above problem.

; fix slider when text list is updated
fix-slider: func [faces [object! block!]] [
    foreach lv-list to-block faces [
        ; print [length? head lv-list/lines lv-list/lc]
        lv-list/sld/data: 0
        lv-list/sn: 0 ; this is the Romano's change
        lv-list/sld/redrag lv-list/lc / max 1 length? head lv-list/lines
        show lv-list
    ]
]

tl-data: [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]

view layout [
    tl: text-list data tl-data 100x100
    button "Update it" [
        clear tl-data
        insert tl-data [1 2 3 4]
        fix-slider tl
    ]
]

Actions

When you give a style an action block - VID uses that block to create a Rebol function.

How actions are fired

Behind the scenes when an action needs to be performed the VID code uses DO-FACE and DO-FACE-ALT to perform your actions.

Logging actions

DO-FACE and DO-FACE-ALT appear to have been used consistently through VID, so if we redefine them, we can log all actions:

identify-face: func[face][
    any [
        if face/var [join "face refered to by " mold face/var]
        if face/style [join "element with style " mold face/style]
        "Unidentifable face"
    ]
]
do-face: func [face value][
    print ["Action fired on " identify-face face]
    do get in face 'action face either value [value] [face/data]
]
do-face-alt: func [face value][
    print ["ALT Action fired on " identify-face face]
    do get in face 'alt-action face either value [value] [face/data]
]

do load-thru http://www.rebol.com/view/tools/../demos/effect-lab.r

Radio style

The radio style gives you the functionality you expect of radio buttons - mutually exclusive options.

Radio button by default are grouped into a single group, if you want to group according to a custom grouping use the OF keyword. For example:

view layout [
    across
    label "Colours:"
    r: radio of 'colours l: label "Red" 
    radio of 'colours label "Green" 
    radio of 'colours label "Blue"
    return
    label "Fruits:"
    radio of 'fruits label "Apples"
    radio of 'fruits label "Oranges"
]

Lining the radio button with their labels is a different problem :)

Interestingly the OF keyword creates mutually exclusive check boxes as well:

view layout [
    across
    label "Colours:"
    r: check of 'colours l: label "Red" 
    check of 'colours label "Green" 
    check of 'colours label "Blue"
    return
    label "Fruits:"
    check of 'fruits label "Apples"
    check of 'fruits label "Oranges"
]

Which leads to the thought - I wonder what happens when I mix check boxes and radios in the same group?:

view layout [
    across
    radio of 'experiment
    radio of 'experiment
    return
    check of 'experiment
    check of 'experiment
]

Which shows that the style matters.

Specific VID fields

VAR

VAR contains the name of a word which was used as a variable in the VID layout specification.

For example, below I create a word "field-1" to refer to the field. Then when one of the button is press I get the value of VAR for the field.

view layout [
    field-1: field
    button "Direct route" [
        inform layout [
            text "Name of the variable:"
            text (mold field-1/var)
        ]
    ]
    button "Indirect route" [
        inform layout [
            text "Name of the variable:"
            text (mold face/parent-face/pane/1/var)
        ]
    ]
]

Customising VID

New styles can be created in VID. Styles can be created as part of the vid-spec given to the layout function by using the STYLE keyword. Alternatively a seperate function STYLIZE creates a stylesheet or modifies the master stylesheet.

WITH

Have a look section "6.7. Face Facet Blocks" of the View Developer's guide. It says of "with":

"The with block allows you to specify any other type of face characteristic using standard REBOL object format."

What this is saying is that WITH allows you to extend the definition of a style for your own purposes. That is, you can add more fields (facets). These fields could contain strings, numbers, functions or any other type of value you want. You can use it in a style definition (probably more useful) or use it like it did, directly on the thing (field, box, etc) you are creating.

Some examples. First WITH allows you to set existing fields using the normal Rebol object format:

view layout [  box with [color: green]  ]

Now to extend a style. Here I will add my own field "special-value":

view layout [

    style named-box box green with [
        special-value: none
    ]

    Text "The box has a special value."
    Text "Press the buttons to set and print it."
    the-box: named-box
    button "Set" [the-box/special-value: now]
    button "Print" [print the-box/special-value]
]

I did not realise before now just how powerful this extending feature is. It could be used to help build more sophisticated frameworks or reduce the complexity of your code.

For example, when you create styles you can base one style upon another in order to share common features. But what if you are using someone else's styles? Here is a contrived example. Instead of writing:

view layout [
    box "click me" red [
        print ["box has been activated"]
    ]
    button "click me" [
        print ["button has been activated"]
    ]
]

We can use common code:

a-common-action: [
    action: func[face value][
        print [face/style "has been activated"]
    ]
]
view layout [
    box "click me" red with a-common-action
    button "click me" with a-common-action
]

Actually here's another way to use common code

common-behaviour: [
    print [face/style "has been activated"]
]
view layout [
    box "click me" red common-behaviour
    button "click me" common-behaviour
]

This is simpler but creates two action functions (behind the scenes), whereas the previous example create on action function and shared it using WITH.

Style definitions

In all VID styles there is a field called FACETS. This field contains a block which in essence is the definition of the specifics for the style. All styles are based on another other style. FACETS holds the differences.

My program VID-Ancestry.r (on the rebsite) displays the dependencies of styles as a tree and allows you to easily view the style definitions for all the styles in the master stylesheet by clicking on the style name.

Note that while FACETS holds the definition, I think you should consider it as documentation only. I suspect that it is the definition copied directly from the STYLIZE that set up the style in the first place.

Flags

VID Faces can use flags.

The rest of this section is an edited email by Sterling of RT.

Change the flags in the face:

view layout [
    field 
    area with [flags: [field]] 
    field
]

The two fields will tab between each other but the area will not and the tabs will go into the area.

The flags you can use are:

field -- behave like a standard field meaning that text is highlited upon selection and the clear-fields function will clear the text in this field

tabbed -- tab and return will move the focus to the next valid face

return -- return key activiates the action block of the face

Style specific keywords

Some predefined styles in VID have particular keywords that they respond to. Consider the LIST style. It takes a SUPPLY argument that other VID styles do not have.

I went searching for where these style specific keywords are held. It appears to be the field (facet) of a VID style called WORDS. Words refers to a block that itself could be considered a small dialect the purpose of which is to provide a mapping of keywords to functions that know how to process the keywords. These functions are called by the do-facets function to create the facets of the face.

Each function takes two arguments NEW and ARGS. NEW would refer to the face being created during LAYOUT and ARGS is a block which holds the keyword and it's arguments from the original vid spec given to LAYOUT. The result of the function becomes a facet (field) of the face - though I'm unsure how as yet.

An additional field (facet) to note is INIT which is called once when the face is being created by layout.

Thus VID can be quite effectively extended by (a) creating new styles (b) giving those styles a WORDS block that provide style specific keywords and (c) possibly a INIT block that initialises the face on creation.

Special INIT processing

During INIT if you set the parent-face field, LAYOUT will apply some special processing. This is used by the PANEL style. Unfortunately there is a some bug associated with this processing but I have a patch see my patches.r script.

Essentially if you set parent-face within INIT you are telling LAYOUT that you want the face in PARENT-FACE to be the actual face to layed out - it might be a composite face.

Using INIT in Stylize

Here's an example provided by Romano. Text-list is a complex style in the sense that it contains nested faces. In this example Romano shows how one can create a new style based on text-list and at the same time modify one of text-list's internal components.

Using "insert tail init" Romano shows how he inserts new code into the init block so that he does not affect what is already defined for text-list. Note that the code he inserts *copies* the edge object so that he doesn't affect other styles that might share this same edge object.

stylize/master [
    new-text-list: text-list with [
        insert tail init [
            sld/edge: make sld/edge [color:1.2.200]
        ]
    ]
]

Custom Style Example

The script below creates a new style that has a style specific keyword called BARS. BARS takes a block as an argument and creates a colour bar for each colour in the block. The result is a bunch of horizontal colour bars.

color-bar-styles: stylize [
    color-bars: face with [
        bars: none
        words: [ bars [new/bars: second args next args] ]
        init: [
            use [temp-spec temp-lo] [
                temp-spec: copy [origin 0x0 space 0x0]
                foreach color bars [
                    insert tail temp-spec compose [
                        box 100x20 (color)
                    ]
                ]
                pane: get in temp-lo: layout temp-spec 'pane
                size: temp-lo/size
            ]
        ]
    ]
]
view layout [
    styles color-bar-styles
    color-bars bars [red green blue] edge [size: 2x2]
]

Making VID Faces

When making a VID face directly use the MAKE-FACE function. Here is how to add checkbox to a precreated layout by making the checkbox directly and then appending it to the pane of the layout:

view lay: layout [
    button "Add checkbox" [
        append lay/pane make-face 'check
        show lay
    ]
]

Shortcut keys

Shortcut keys allow a face's action to be associated with a keystroke.

This behaviour is provided by system/view/window-feel (see that section for the mechanics).

As the example below shows a window level event handler function can trap a key before it gets to the shortcut key functionality.

view layout [
    button "press" #"a" [print "button pressed - a"]
    button "press" #"b" [print "button pressed - b"]
    across label "Trap shortcut key b" trap-b: check false
    do [
        print "Installing event function"
        evtfunc: insert-event-func [
            if all [
                equal? event/type 'key
                equal? event/key #"b"
                trap-b/data
            ] [
                print "trapped key b"
                RETURN none
            ]
            if equal? event/type 'close [
                print "Removing event function"
                remove-event-func :evtfunc
            ]
            RETURN event
        ]
    ]
]

Another way to trap the shortcut key functionality is by redefining the window-feel detect function like the example below. Much of this code is a supporting framework for installing the change and removing it again.

This example shows how you could turn of shortcut keys when in a field or other text entry type styles. In this particular example I turn them off just for fields so that the area is still affected. "a" is the shortcut.

install-nofieldshortcuts: does [
    system/view/window-feel: make system/view/window-feel [
        detect: func [face event] [
            either all [
                event/type = 'key
                system/view/focal-face
                system/view/focal-face/style
                equal? system/view/focal-face/style 'field
            ] [event] [old-detect face event]
        ]
        old-detect: get in system/view/window-feel 'detect
    ]
]
uninstall-nofieldshortcuts: does [
    use [wf] [
        wf: system/view/window-feel
        set in wf 'detect get in wf 'old-detect
    ]
]

view layout [
    text {#"a" is a shortcut for the button}
    text {trying typing in the field and the area}
    button "press" #"a" [print "button pressed - a"]
    field
    area 300x75
    do [
        print "Installing shortcut trap"
        install-nofieldshortcuts

        print "Installing event function"
        evtfunc: insert-event-func [
            if equal? event/type 'close [
                print "Removing shortcut trap"
                uninstall-nofieldshortcuts
                print "Removing event function"
                remove-event-func :evtfunc
            ]
            RETURN event
        ]
    ]
]

Function Summary

I've popped this in. It deserves elaboration. Just click on the code.

[Website readers: You are definitely missing out on some info here. Copy and paste the code into Rebol/View to see it.]

function-description: function [
    f [any-function!]
] [result] [
    if any [none? result: pick third :f 1 not string? result] [
        result: copy {Undocumented.}
    ]
    return result
]
function-classifications: [
    "VID Main" [
        stylize layout
    ]
    "VID Flagging faces" [
        deflag-face flag-face flag-face?
    ]
    "VID Performing face actions" [
        do-face do-face-alt
    ]
    "VID Setting attributes" [
        set-font set-para
    ]
    "VID Styles" [
        set-style get-style
    ]
    "VID Popups" [
        hide-popup show-popup
        flash choose inform
    ]
    "VID utility" [
        clear-fields scroll-para
    ]
    "Face Debugging" [
        dump-face dump-pane
    ]
    "Requestors" [
        request request-color request-date
        request-download request-file request-list
        request-pass request-text
    ]
    "Face display" [
        unfocus focus
        hide show
        viewed? unview view
    ]
    "Face handling" [
        center-face find-window
        find-key-face make-face
    ]
    "Informative" [
        textinfo size-text
        in-window? screen-offset? win-offset?
        offset-to-caret caret-to-offset
    ]
    "Calculations" [
        confine inside? within? span?
    ]
    "Global events" [
        insert-event-func remove-event-func
    ]
]
do load-thru http://www.codeconscious.com/rebsite/rebol-library/standard-guis.r
vid-spec: copy [space 0x0 origin 0x0 across tabs 170]
foreach [t fs] function-classifications [
    append vid-spec compose [h2 (t) return]
    foreach f fs [
        append vid-spec compose [
            label (form f) tab
            text (function-description get to-lit-word f)
            return
        ]
    ]
]
vid-spec: append/only copy [
    styles utility-styles
    space 0x0 origin 0x0
    title "VID/View functions of note"
    text "Use the scroll bar to see all the functions."
    scrollpanel (system/view/screen-face/size - 50x150) subface
] vid-spec
view layout vid-spec

VID Dialect Implementation

Just notes as I discover stuff.

Layout WITH

Layout implements the WITH keyword. It will add any new facets defined in the WITH

system/view/vid/facet-words

Default VID Facet Keywords.

system/view/vid/do-facets

Used during processing of VID specification. Gets facets for current style from VID specification, also returns index to as yet unprocessed VID spec.

system/view/vid/expand-specs

Modifies the supplied specification to handle override of the face's edge font para subobjects.

system/view/vid/grow-facets

Sets a face object's facets using the facet spec supplied.

Code structuring

Sometimes it is good to wrap up all your GUI type words into an object so they don't "clutter" your global namespace.

This sort of style would see you write something like (it is not a working example in the vid-notes view though):

ctx-gui: context [
    field-1: none
    main-face: layout [
        title "Code structuring 1"
        field-1: field "test text"
    ]
]
view ctx-gui/main-face

I though of an interesting use for bind for this type of code structuring:

ctx-gui: context [
    the-fields: context [
        field-1: none
    ]
    main-face: layout bind [
        title "Code structuring 1"
        field-1: field "test text"
    ] in the-fields 'self
]
view ctx-gui/main-face

This seperates the field references into a new context without losing ease of expression. The same can be done with actions bound to a "controller" object. Just a thought.

Curios

Click a face

This is from a post by Anton Rolls on a potentially useful function:

click-face: func [face][face/feel/engage face 'down none]

view layout [
    chk: check
    text "click me" [click-face chk]
]

But sometimes buttons or some other style get stuck in the down position. It could then be useful to have

release-face: func [face][face/feel/engage face 'up none]

Then you can reset them easily.

Duplicating LAYOUT

This solution by Romano Paolo Tenca

Lay: do bind load mold :layout in svv 'self

Abbreviations

svv = system/view/vid svvc = system/view/vid/vid-colors svvf = system/view/vid/vid-feel