VID User Guide
From DocBase
REBOL Visual Interface Dialect User GuideCopyright 2007 REBOL Technologies | |||||||||||||||||||||||||||||||||||||||||||||
|
[edit] WelcomeWelcome to the REBOL VID User Guide. This manual introduces the Visual Interface Dialect, or VID. As a dialect, VID represents the application writer's layer of a multi-layered graphics system. VID is used to write the Graphical User Interface, the GUI, in REBOL. [edit] Getting StartedFirst, you will need a REBOL executable for your computer and Operating System. REBOL is compiled on many different platforms, and the good people at REBOL Technologies have made it easy to download the correct version for your system. This all starts at http://www.rebol.com. Point your web browser to rebol.com and from there follow the Download link, click the downloader for your operating system and then install REBOL. Detailed instructions for installing can be found on the download web pages of rebol.com. [edit] Hello REBOLStarting small; from the REBOL console, try typing view [text "Hello World"] and you will have a graphical window of REBOL VID announcing itself to the world. You'll have to close the window using the system menu that comes with your windowing system, usually an × in the very top left or top right corner. So let's fix that, and add our own close button. view [
text "Hello World"
button "Goodbye" [unview none]
]
Press the goodbye button and the window will go away. [edit] Hello with button explainedSo what is going on?
[edit] CustomizationNow for an example of how VID simplfies some aspects of Graphical User Interface development. view [progress slider attach] While of no real use beyond demonstration; that short three word VID input block creates a functional window. You end up with a progress meter that is attached to a slider. Move the slider right and left, and the progress bar will fill in, to match where the slider is. And yet, it still needs a close button. view [
progress slider attach
button "Goodbye" [unview none]
]
The Goodbye button is fairly easy code to type, somewhat easy to remember, but there may be a better way. VID allows for certain customizations that enable consistent look and feel within and across applications. [edit] StylizeVID allows for the development of new styles. New styles can be
There is a whole section of this document on STYLIZE but for now; lets just create a simple button we can use for to close example windows. stylize [
goodbye: button [
[
on-init: override :on-init [
super face spec
spec/text: copy "Goodbye"
spec/action: [unview none]
]
]
[]
]
]
|
[edit] Style Quick ReferenceA B C D E F G H I J K L M N O P Q R S T U V W X Y Z
| ||||||||||||||||||||||||||||||||||||||||||||
Style Options | |||||||||||||||||||||||||||||||||||||||||||||
|
[edit] Style Option ReferenceVID offers a lot. There are many styles and each have a variety of options. Options are applied to styles using the options keyword and a block! holding the option specification. For example button "Quit" options [rounding: 10 material: 'sand] Would build a button in the layout with more rounded corners than the default, and a white beach sand look. [edit] button options
[edit] material gradients
| |||||||||||||||||||||||||||||||||||||||||||||
{{Links|
REBOL Visual Interface Dialect User Guide: Design OverviewCopyright 2007 REBOL Technologies | |
|
[edit] A VID Design OverviewThe Visual Interface Dialect has some very specific goals:
This allows for rapid development of REBOL VID applications while providing a great depth of design and control over finely tuned and professionally crafted complex systems. [edit] For the Intrepid ExplorerIf you are the curious type, REBOL is a highly reflective system. Most of the VID system is accessible as REBOL source code. From a console session the help, source, probe and mold functions provide the tools to satisfy the most inquisitive of sleuths. The main entry points for exploring into the realms of VID are view, layout and make-face. Other points for interest are the vid-dialect and For example; output subject to change, REBOL also evolves >> source make-face
make-face: make function! [[
"Create a VID face"
parent [object! none!] "The parent panel"
spec [block!] "Dialect spec for the face"
/local name command
][
if empty? spec [return none]
command: clear [] none 16x16 200.200.200]
spec: delect vid-dialect spec command
if command/1 = 'set [
name: command/2
clear command
spec: delect vid-dialect spec command
]
init-face make-face* command/1 parent name
next command [1 1] [1 1]
]]
If this is your first read through the VID documents the code listing above may seem a little technical; but know that the capability is there when you need to satisify a curiousity. |
[edit] VID Layout ConceptsThe REBOL Visual Interface Dialect builds up layout using a table approach. By default styles start at the top and then build downwards, taking as much space as needed to fill virtual columns. The group style changes this default to build styles from left to right, adding columns to the virtual table. This allows for conceptual grids while building up sophisticated layouts, without worrying about counting pixels for actual placement. VID keeps elements aligned during initial layout and when windows are resized, a very handy feature. Some examples; [edit] Top Down, One Column Row Layout view [
h2 "Example One"
button "Top"
btn "Bottom"
]
[edit] Left Right Grouping, multiple Columns view [
h2 "Example Two"
group [button "Left" btn "Right"]
]
[edit] Top Down with Left Right Grouping btn view [
h2 "Example Three"
button "Top"
group [button "Left" btn "Right"]
btn "Bottom"
]
[edit] Same thing using button view [
h2 "Example Four"
button "Top"
group [button "Left" button "Right"]
button "Bottom"
]
[edit] Some explanationsNotice how the button styles react a little differently than the btn style when it comes to sizing and layout. Also note how all of these simple layouts are nicely aligned and sized to take up exactly the right amount of space to keep things all lined up. In particular, note how the Right btn in Example Three takes up less space than the the Right button of Example Four. This is due to nature of the btn style initially being sized to hold the text label, where as a button style is initially sized to a larger default. Also note how the Top and Bottom buttons are larger in Example Four than in Example Three as they initially size to fill in the virtual grid created by VID. This feature may take those people that are used to forcing the size of graphical elements by surprise, perhaps with a little dismay. Not to worry; VID allows complete customization and creation of styles to suit any need or preference; more details are found in the Style Creation Guide. For most application developers these are not required efforts, but are available when desired. |
This page describes the R3 View system.
| This page is under construction. More content is needed. |
Layout commands for VID 3
- VID
- Visual Interface Dialect, also referred to as a style description dialect.
Along with VID: Styles the REBOL 3 VID dialect includes layout control and other commands.
set-word
set-words, a variable name followed immediately by a colon, allow faces to be named. These names can then be used in REBOL code blocks. Please bear in mind that the words defined inside layout blocks are bound to the layout. Gone are the days of global variables for faces. See set-word! or set-words explained.
view [ the-area: area button "show details" [help the-area] ]
The action block of the button will use the REBOL help function to display a high level view of the face created from the area style word. In this case the set-word the-area: defined the variable the-area. This allows for very powerful and convenient interaction between faces in a REBOL VID application.
The variable created with the set-word! is bound to the current layout and stored in an object! called faces. They do not propogate to the user namespace. These values can be accessed using a technique such as;
view win: layout [
the-area: area
button "show details" [help the-area]
]
>> help the-area
No information on the-area (word has no value)
>> help win/faces/the-area
WIN/FACES/THE-AREA is an object of value:
gob gob! make gob! [offset: 20x20 size: 200x100 alpha: 0 dr...
feel object! [on-init: default-options: on-set: on-set-actions:...
look object! [on-init: default-options: on-update: on-update-ac...
style word! area
...rest of output from help...
This is important to remember while exploring REBOL VID from the console or when developing multiple window applications that may need to communicate back and forth.
ATTACH
This command allows Styles to be connected. A primary example is attaching scrollbars and sliders to other styles. The attach command accepts optional numeric arguments that are the relative offsets to the faces to attach. Without arguments attach will connect the last two defined faces. This is equivalent to attach -1 -2.
Anyone that developed REBOL version 2 VID applications should appreciate the new simplicity.
Examples
view [progress slider attach]
Displays a progress bar and a slider. The ATTACH word, attaches the face to the previous face. When the slider is moved, so does the progress bar.
view [progress progress slider attach -2]
Will attach the slider to the second progress bar.
view [progress progress slider attach -3]
Will attach the slider to the first progress bar.
view [progress progrss slider attach attach -1 -3]
Will attach the slider to both progress bars. The first attach is equivalent to attach -1 -2.
rebol []
view [
h1 "Scrolling (SCROLL-PANEL and SCROLLER)"
group 2 [
tight bottom right ; stick the panel to the scrollers
scroll-panel 150x200 [image %Cat.jpg]
tight only bottom left
scroller 20x200
attach ; attach scroller and scroll-panel
tight only top right
scroller 150x20
attach -3 ; attach with scroll-panel
]
text "(Hope you like the cat. ;)"
]
The example above includes the tight, only, top, bottom, left and right VID commands. It also shows the h1, text, scroll-panel, scroller visible styles along with the invisible, powerful and critical group style.
Attaching face objects
The attach command also accepts face objects. This is perfect for use with the set-word feature of VID. This can help;
- alleviate counting faces
- ensure source code of a layout can be moved around without worrying about numeric attach arguments
but
- it means keeping track of extra words in the layout, which could lead to naming conflicts.
VID lets you choose what is best for you.
Examples
view [p1: progress p2: progress s1: slider attach s1 p1]
As with integer! arguments, if only one argument is given, the missing argument is assumed to be the previous face.
view [p1: progress p2: progress slider attach p1]
And arguments can be mixed.
view [progress p2: progress s1: slider attach s1 -3]
DO
During the creation of a VID layout, the do command allows for evaluation of any REBOL code. The uses are nearly endless, but keep in mind that the code will be evaluated during the layout, not as runtime actions during the GUI event handling.
do expects a 'block! of REBOL code.
TIGHT
The tight command controls how the layout flows margins (or space) around the faces that follow.
It can be used alone; to effectively remove all margins, or with a variety of control options.
Being REBOL; some options are datatype!, some options are keyword or you can use a combination of both.
- off
- resets margin control. It returns the layout flow to normal spacing around faces.
- This is actually a logic! value, so off, false and no would all work but off carries the most meaning.
- percent!
- reduces margins by percentage. tight 100% effectively removes margins. tight 50% makes things half as tight; 50% less margin. Whereas tight 10% will make faces only a little bit tighter together than normal; 10% less margin. For completeness, negative values are allowed. tight -50% will make for a looser layout in terms of spacing; 50% more margin.
positional by name
- left
- tight left controls the left margin and can include off or percent! options.
- right
- control the right margin. Example tight right off.
- top
- control the top margin. Example tight top 75%.
- bottom
- and the bottom margin. Example tight bottom.
- only
- restricts influence to the named margins; resetting the others. For example tight only left tightens the left margin and resets all the others to normal. only will come into play in those rare cases where cascade margin control is required. only will accept the other options as well. Examples tight only left 50% for 50% less left margin and all others back to normal. tight 50% ... tight only right ... would flow the layout with half margins on all sides of each face then change to tight layout only on the right and all other margins reset to normal.
tight options can be used in conjuction with each direction.
Examples
Removes spacing between two buttons in a group:
group [tight button button]
It works until you turn it off again with tight off:
group [tight button button button tight off button]
Notice here that VID finds the largest face in the group and scales other faces to that size. That face is the last button, since the button size added with its margin size makes for the biggest face in the group. VID scales the buttons without margins to fit the new size. Therefore the first three buttons are bigger than the last one.
BACKGROUND
The background command, influences the background of the current face and expects a tuple! in r.g.b.a format, or a block! which will be interpreted with the Draw Dialect.
EFFECT
The effect command applies an Effect Dialect block! to the current face.
ACTION
The action command determines what code will evaluate when the current face is triggered. The particular style defined in VID will influence what determines the action, but most often it will be a mouse-click, as with button styles.
The action command expects a block! of REBOL code.
Examples
view [
button "Hello" [print "Hello World!"]
]
and
view [ button "Hello" action [print "Hello World!"] ]
are equivalent. Some styles, such as group use an unqualified block! for purposes other than an action block, but these differences are usually obvious and self-evident.
SELECTED
The selected command accepts a block! that specifies which, if any, entries in a list will be highlighted. Here the term list would apply to any style that can have highlighted entries. For text-list and icon-list the values in the selected block are assumed to be indexes into the list.
Examples
view [
text-list [
["Header col1" "Header col2"]
["row1 col1" "row1 col2" "value-of row1"]
["row2 col1" "row2 col2" "value-of row2"]
["row3 col1" "row3 col2" "value-of row3"]
]
selected [1 3]
]
Would display the text-list with rows 1 and 3 initially selected and highlighted.
OPTIONS
All styles now support (but don't neccessarily use) a block! of options.
Options are additional adjustments or features you can enable for a style. The format for the block, is the same as for entering the block for an object, in the [word: value] format.
Note that when the options block has been entered, no other values can be entered for this face in the layout.
Please look at the Style Option Reference for each style to see, what can be altered for the style.
Examples
The BUTTON style by default supports the ROUNDING feature, to adjust the size of the rounding of the button.
view [button "Help!" options [rounding: 4]]
This will give this particular button a rounding of 4.
ACTION-HANDLER
The action-handler command allows for any-function! to be used for handling actions for a face.
Examples
More examples needed.
VID References
Summary of VID styles.
GROUP
GROUP is the basic kind of panel. It is the style that takes care of the layout of all faces.
Specs
columns: integer! pane: block! pane-values: object! background: tuple! background-draw: block! padding: pair!
- columns
- Sets the number of columns in the group. All faces are aligned (as in HTML table). Default: GROUP is one row, as many columns as necessary.
- pane
- VID block for the contents of the group.
- pane-values
- Initial values for named faces. (Mainly used for SET-FACE.)
- background
- Background color for the group.
- background-draw
- Drawing to be put in the background; in the block, you can use the words BOTTOM-RIGHT, BOTTOM-LEFT and TOP-RIGHT to refer to the corners of the group (top left is always 0x0).
- padding
- Padding for the group (space around the group, but considered to be part of the background).
Notes
- When using SET-FACE, you can't set the columns without also setting the pane.
- GET-FACE returns an object with the values of all the named faces in the group; however, this only works with named groups. GET-FACE/ALL returns the dialect block for the whole group.
Description of the layout/resize process
To Be Written.
Layout Examples
Produce three horizontal buttons:
group [button button button]
Produce a 2xN grid of elements:
group 2 [label "Name" field label "Address" field label "Post Code" field]
BLANK
BLANK is a "spacer". It is used to just "skip" a cell in a group. It is also the base style for a number of other styles.
Specs
size: pair! shrink: pair! stretch: pair! color: tuple! draw: block! image: image! source: file!
- size, shrink and stretch
- Specify custom values for FACE/SIZE, FACE/SHRINK and FACE/STRETCH.
- color
- Background color of the spacer.
- draw
- Background drawing for the spacer; BOTTOM-RIGHT, BOTTOM-LEFT and TOP-RIGHT available in the block.
- image
- Image to use in the spacer; this also forces the size etc. (see IMAGE style).
- source
- File to be loaded as the image.
Derived styles
- PAD
- FILLER
- DRAW
- IMAGE
PAD
PAD is based on BLANK. The difference is that it has a default size of 24x24, so it takes a little space in the layout. See BLANK for the specs etc.
Layout Examples
Button with a spacing to its left. When the window resizes, the button stretches:
group [pad button]
FILLER
FILLER is based on BLANK. The difference is that it has a default size of 24x24 and it is greedy when resizing, ie. it gets as much space as possible, so it's usually used to push other faces to the right or the left and so on. In some other user interface system, this is equivalent to a spring. See BLANK for the specs etc.
Layout Examples
Right aligned button:
group [filler button]
Centered button:
group [filler button filler]
Three buttons that don't stretch or shrink when the window size changes:
group [button filler button filler button]
Colored FILLER:
group [filler red button]
DRAW
DRAW is a fixed size "box" that you can put a draw dialect block in. It is based on BLANK. See BLANK for the specs. For using the DRAW dialect, see the DRAW documentation.
Layout Examples
Draws a box inside the draw area:
draw [pen black fill-pen red box 20x20 50x50]
IMAGE
IMAGE just shows an image, the size is fixed to the image's size. It is based on BLANK (see there for the specs etc.).
Layout Examples
Displays a built-in stock icon:
image stock-icons/info
Display an image directly loaded from the internet:
TEXT
TEXT is a simple style to show (rich) text. The size of the face is determined by the size of the supplied text. See Rich Text for how to use rich text formatting.
Specs
text: string! text-rich: block!
- text
- Text string to show. TEXT-RICH has priority when both are specified.
- text-rich
- Rich text block to show.
Derived styles
- H1
- H2
- LABEL
Layout Examples
Standard text:
text "REBOL is fun!"
Rich Text:
text ["REBOL " yellow "is " red bold "fun!"]
Mixed types cause Rich Text to be preferred:
text "REBOL is fun!" ["REBOL " yellow "is " red bold "fun!"]
H1
H1, based on TEXT, displays text with a big font; also, it does not stretch much vertically.
H2
H2, based on TEXT, displays text with a medium font; also, it does not stretch much vertically.
LABEL
LABEL, based on TEXT, displays right aligned and vertically centered text on one line, and it does not stretch much (mostly used for FIELD labels).
Layout Examples
group [label "Address" field]
BUTTON
BUTTON creates a clickable button.
Specs
text: string! action: block!
- text
- Text to show in the button.
- action
- Action to be DOne when button is clicked.
Notes for skin writers
(See VID: Skin creation guide.) FACE/STATE can be one of CLICKED or NORMAL and indicates if the button is "pressed" or not; you get the STATE change event (ON-UPDATE look function) when FACE/STATE changes.
The default LOOK object for BUTTON is structured to make it easy to create new button skins (eg. see TIGHT-BUTTON). The EXTRA-WORDS block defines the words added to the FACE object; the MAKE-GOB function is called to create FACE/GOB during ON-INIT:
make-gob: func [face] [
face/gob: ...
]
The CHANGE-STATE function is called by ON-UPDATE whenever FACE/STATE changes:
change-state: func [face] [
...
]
Derived styles
- BTN
- TIGHT-BUTTON
Layout Examples
Standard button with an action:
button "Hello World!" [print "Hello world!"]
BTN
BTN is just like BUTTON, except that the size of the button is determined by the button's text instead of being fixed.
TIGHT-BUTTON
TIGHT-BUTTON is an alternate skin for BUTTON, suitable for areas without spaces around the button.
Comment: Since this may be needed for a number of styles, what about BUTTON TIGHT where TIGHT is a keyword to indicate no spaces. Then, we can use it on other styles too. --Carl 23:13, 24 July 2007 (EDT)
This is not an option to BUTTON. It is a different skin for BUTTON. There is a TIGHT command in the dialect that works with all styles (we need to discuss it a bit). Gabriele 04:13, 26 July 2007 (EDT)
This either has been or will be removed, since TIGHT-BUTTON would be depending on it's appearance for when it's used. If you change the skin to something where it looks bad when lining up multiple TIGHT-BUTTONs with zero spacing, usage parameters are violated. For this, we'll devise a button panel style, where buttons are completely lined up against each other without any spacing, without depending on the skin for this purpose. --Henrikmk 19:45, 17 August 2007 (EDT)
TOGGLE
TOGGLE creates a clickable button that toggles between two states (on/off) when the mouse pressed and then released over it.
Specs
text: string! action: block!
- text
- Text to show in the button.
- action
- Action to be DOne when button is clicked.
Notes for skin writers
(See VID: Skin creation guide.) FACE/STATE can be one of PRESSED or NORMAL and indicates if the toggle is "pressed" or not; you get the STATE change event (ON-UPDATE look function) when FACE/STATE changes.
Layout Examples
Simple TOGGLE with an action, prints TRUE and FALSE on TOGGLE state:
toggle "Manual Override" [print value]
SLIDER
SLIDER allows changing a value from 0% to 100% using a "slider". It can be horizontal or vertical depending on the specified size.
Specs
action: block! value: percent! size: pair!
- action
- Action to be DOne when value changes.
- value
- Initial value of the slider.
- size
- Size of the slider (also determines slider orientation).
Notes for skin writers
FACE/STATE can be NORMAL or CLICKED (when the "knob" is clicked). You get the STATE change event when it changes.
MAP-OFFSET must return either the word KNOB or a value to be added to FACE/SPEC/VALUE. ON-DRAG must return a value between 0% and 100%.
Derived styles
- SCROLLER
- RANGE -- Possibly? --Henrikmk 19:46, 17 August 2007 (EDT)
Layout Examples
Horizontal slider at 40%:
slider 100x20 40%
Vertical slider at 20%:
slider 20x200 20%
FIELD
The FIELD style allows users to input text (one line only).
Specs
text: string! action: block! text-mask: string!
- text
- Text to be edited by the user.
- action
- Action to be DOne when user hits ENTER.
- text-mask
- Currently only used to determine the size of the field (otherwise, field takes as much horizontal space as possible - but less than a filler).
Notes for skin writers
FACE/CURSOR is FACE/SPEC/TEXT at the position of the editing cursor. You get the CURSOR change event when it changes or FACE/SPEC/TEXT changes. You should also handle the FOCUS and UNFOCUS change events.
MAP-OFFSET must return a position in FACE/SPEC/TEXT.
Layout Examples
Displays a field and prints the value when tabbing out of the field or pressing Enter:
field [print value]
SCROLL-PANEL
SCROLL-PANEL is basically like a GROUP, except that the contents are always rendered at their "normal" size, and scrolled.
Comment: What if SCROLL is added as a keyword that can be used with a variety of styles? --Carl 23:15, 24 July 2007 (EDT)
Once we add PANEL, maybe SCROLL-PANEL can be an option to PANEL, if they are not much different (and they probably aren't). For GROUP, it would make it much more complicated, and I don't like the idea because GROUP is "invisible" while with SCROLL it would become visible. Gabriele 04:17, 26 July 2007 (EDT)
Specs
columns: integer! pane: block! pane-values: object! background: tuple! background-draw: block! size: pair!
All specs except SIZE work exactly like in GROUP; SIZE sets the size of the panel (ie. the visible size). Default size is 800x600, minimum size is 50x50.
Notes for skin writers
FACE/SCROLL holds the amount of scroll as a pair. You get the SCROLL change event when it changes. You also get the AREA-SIZE change event when the size of the contents changes. You should send the SCROLL-CHANGE action event whenever the area-size or the visible size change.
Layout Examples
A simple scroll panel with some elements:
scroll-panel [button "Button" field "field text" label "Test"]
See SCROLLER for example on attaching scrollers to a scroll panel.
SCROLLER
SCROLLER is based on SLIDER. It is a style that you attach to other faces to scroll them. (It is not intended to interact with the application, only with other faces.) See SLIDER for the specs.
Notes for skin writers
You must set FACE/DIR to the direction of the scrolling (1x0 for horizontal scrollers, 0x1 for vertical ones). FACE/OFFSET, FACE/AREA-SIZE and FACE/VISIBLE-SIZE are the current scroll offset, the size of the area being scrolled, and how much of that area is visible. Eg. if the area size is 100 and the visible size is 10, offset can go from 0 to 90. FACE/STATE is CLICKED when the knob is clicked, NORMAL otherwise; you get the STATE change event when it changes. You get the OFFSET change event when FACE/OFFSET changes. You get the SCROLL-CHANGE event when area size, visible size or offset change (if just offset changed, you get OFFSET not SCROLL-CHANGE).
MAP-OFFSET must return either the word KNOB or a number to add to FACE/OFFSET. ON-DRAG must return the new value for FACE/OFFSET (from 0 to area-size - visible-size).
Layout Examples
Horizontal scroller:
scroller 200x20
A scroll panel with scrollers attached to the right and bottom side of the scroll panel:
group 2 [ scroll-panel [button "Button" field "field text" label "Test"] scroller 20x200 attach ; vertical scroller scroller 200x20 attach -2; horizontal scroller blank ]
ICON-LIST
ICON-LIST is used to display a list of icons.
Specs
size: pair! icons: block! selected: block! ; this way set-face will not work. need to solve this. action: block! background: tuple! background-draw: block!
(There are too many blocks in this spec, so we're waiting for changes in DELECT to allow using keywords rather than position to specify them.)
- size
- Size of the ICON-LIST face.
- icons
- List of icons. The format is: [any [ [file! | image!] any-string! skip] ], ie. triplets with the first value being the icon, the second value being the icon text, and the third value being the "value" for the icon (used in action events and for drag and drop).
- selected
- Selected icons (block of integers, each being the position of an icon in the list, eg. [1 2 3] means that the first three icons in the list are selected).
- action
- Action to be DOne on double click.
- background
- Background color for the icon-list.
- background-draw
- Background drawing for the icon-list; usual BOTTOM-RIGHT, BOTTOM-LEFT and TOP-RIGHT words available.
Notes for skin writers
ICON-LIST is intended to scroll only vertically. FACE/SCROLL is the amount of vertical scroll. You get the SCROLL change event when it changes; you should send out the usual SCROLL-CHANGE action event when area or visible size change. You get the SELECTION change event when face/spec/selected changes.
MAP-OFFSET must return the index of the icon at the offset (ie. 1 for first icon in the list, 2 for second, and so on).
Layout Example
Empty icon list:
icon-list
Display predefined list of icons:
icon-list [folder-image "Folder" 'my-folder file-image "File" 'my-file]
TEXT-LIST
TEXT-LIST is for multi-column text lists. The columns allow for resizing.
Specs
size: pair! rows: block! selected: block! ; this way set-face will not work. need to solve this. action: block! background: tuple! background-draw: block!
- size
- Size of the text list (default 400x400, min 100x100).
- rows
- Rows in the text list. The contents of the block is something like:
[
["Header col1" "Header col2"]
["row1 col1" "row1 col2" "value-of row1"]
["row2 col1" "row2 col2" "value-of row2"]
]
the value of a row is the value that is sent on double click
and on drag and drop for the row
- selected
- Sets the selected rows; block of integers, ie. [1 2 3] means that the first three rows are selected.
- action
- Action to be DOne on double click.
- background
- Background color for the text list.
- background-draw
- Background drawing for the text list; usual BOTTOM-RIGHT, BOTTOM-LEFT and TOP-RIGHT words available.
Notes for skin writers
To Be Written.
Layout Examples
Simple list:
text-list 300x300 [ ["Name" "Age"] ["Luke Lakeswimmer" "22"] ["Derth Veder" "47"] ["Jar Jar Bonks" "5"] ]
PROGRESS
PROGRESS is for displaying the progress of an operation.
Specs
size: pair! value: percent!
- size
- The size can be currently any value.
- value
- The value can be set from -1% to 100% in any decimal precision. Setting it to a value between 0% and 100% displays a normal progress bar, while setting it to -1% displays a general waiting bar.
Notes for skin writers
The default progress features sub-pixel display by altering the horizontal resolution to 10 times of normal, giving very accurate depiction of progress on very long operations.
Layout Examples
Progress bar at 40%
progress 40%
Waiting progress bar:
progress -1%
LED
LED is for displaying the state of a logic! variable. When TRUE, the LED lights up. When false, the LED is black.
Specs
state: logic! color: tuple!
- state
- The state can be TRUE or FALSE.
- color
- Color of the LED when it's set to TRUE.
Notes for skin writers
None.
Layout Examples
Simple LED that is turned off:
led
LED that is turned on:
led on
LED that turns red when turned on:
led red
Not Yet Implemented
- H3
- NOTE
- INFO (probably, should just be disabled field)
- AREA (should also allow rich text eventually)
- COMBO
- ICON
- ARROW
- ROTARY (really?)
- CHECK
- RADIO
- RANGE (maybe just slider skin?)
- BAR
- PANEL
- LIST
- TABLE
- MENU
- COLOR-SLIDER
- COLOR-WHEEL
- GRADIENT-SLIDER
VID References
This page will contain information about requesters that will be built using VID3 and will be part of VID3.
Screenshots of these requesters are prohibited. They must not appear in DocBase until after beta.
Current list (incomplete) is:
REQUEST
Displays a general request dialog with a 'Yes', 'No' and a 'Cancel' button. Returns TRUE for Yes, FALSE, for No and NONE for Cancel.
Usage
request "Delete this file?"
Refinements
- /ok
- Displays only an 'OK' button. Returns TRUE.
- /only
- No buttons are displayed.
- /confirm
- Displays 'Yes' and 'No' buttons. These buttons return TRUE or FALSE.
- /type icon [word!]
- Valid values are: alert, help, info, stop.
- /timeout time [time!]
- Closes the requester after a specific period of time.
REQUEST-VALUE
Displays a requester with a field and an OK and a Cancel button.
Usage
To be written.
REQUEST-PASS
Displays a requester with a username and password field and an OK and a Cancel button.
Usage
To be written.
REQUEST-DATE
Displays a requester with a month calendar and an OK and a Cancel button.
Usage
To be written.
REQUEST-COLOR
Displays a requester with RGB sliders, a color box and an OK and a Cancel button.
Usage
To be written.
REQUEST-FILE
Displays a requester with a field and an OK and a Cancel button. This is currently a native standard Windows file requester.
Usage
To be written.
REQUEST-PROGRESS
This may be removed or changed, as it does not fullfill the requirements for being a request-* function.
Displays a window with a message, a progress bar and an optional Cancel button. The purpose of this requester is to easily display the progress of an operation that you're doing; A calculation, a download process or a lengthy installation process.
The requirement is to use a callback function, which returns a percent! value to the progress bar and the progress bar will automatically refresh. This value can go from -1% to 100%.
Another feature is the ability to change the title text and small text without closing the window.
When using the /cancel refinement, you can add a Cancel button to the requester to cancel the operation. This button should have a function attached to it to cancel the operation.
Usage
When using it, you start the operation first and then call the REQUEST-PROGRESS function.
request-progress progress-var "Downloading file..."
Determine here how you pass a function to the requester, so we can show progress over time.
To display progress in the requester, the function should return a percent! value.
You can change the text in the requester, without closing and opening up a new progress window. To change the text in the requester, the function can instead of a percent! value, simply pass a key/value block. This will be interpreted as a change in the requester. The keys can be:
- TEXT - The title text of the progress bar, e.g. "Downloading files"
- SUBTEXT - The text below the title, e.g. "File 4 of 17 downloaded."
- PERCENT - The progress value. This can be useful at times where you want to keep the progress value synchronized with SUBTEXT. Otherwise, it's better to return just a percent! value alone.
The format is then:
[text "Downloading files" subtext "File 1 of 17 downloaded."]
You only need to provide the keyword for the item in the progress bar that you want to change:
[text "Unpacking files"]
Or:
[percent (to-percent 6 / 17) subtext "File 6 of 17 unpacked"]
ALERT
Displays a window with an icon, a message and an OK button.
Usage
alert "This is an ALERT!"
You can select from different icons to use from the stock icon list, using the /type refinement.
FLASH
Displays a simple window with a message and an icon from the stock icon list.
Usage
flash "Loading program"
You can select an icon from the current choices of stock icons.
To close it, you need to use:
unview none
To be written.
...And more?
VID References
REBOL 3 will contain an extensible range of stock icons for use in the VID 3 based graphical user interface. This design is not finalized, but there should be a method to store these icons and extend this list with new icons or manipulate the current icons for different GUI skins. Generally, stock icons are used in the requester functions and in a few other areas.
Each icon can be of 3 (possibly 4) different formats:
- You can provide a PNG binary, JPG, GIF or BMP such as one directly loaded from disk.
- An image!
- A block! that will be interpreted as a DRAW block. The advantage here is that you can create dynamic icons that correspond to the context in which they are used.
- It may be possible at some point to load SVG data files.
Stock Icons for Requesters
Final screenshots of these icons are prohibited until after the beta of R3 comes out.
These icons are used in requesters and should all be of roughly the same size.
INFO
An information "i" icon, used in for example, ALERT.
QUESTION
A question mark icon, used in requests, where you need to respond.
WARNING
A traditional warning sign icon, used in ALERT to depict that REBOL wants to warn you from something.
FORBIDDEN
An X icon, used in places, where you can't go to, or for operations that you are not permitted to do.
LOCKED
A locked padlock icon, as shown in REQUEST-PASSWORD.
UNLOCKED
An unlocked padlock icon.
NETWORK
A cartoon picture of Earth depicting computers that are networking. For internet operations, such as downloads and software updates.
OPERATION
A picture of gears, used for generic time-consuming operations. Used in REQUEST-PROGRESS.
Stock Icons for Other Places
R-ICON
The R icon.
REBOL-LOGO
The rectangular REBOL logo.
FOLDER
The folder icon as used in the desktop.
R-EXE
The .r executable icon used in the desktop.
PAPER
The paper icon used for depicting text files in the desktop.
MONITOR
The monitor icon with a .r logo inside used for depicting the console in the desktop.
Stock Pictograms
Pictograms are small icons used in various places in the GUI. These are depending on the skin, but are all going to be a part of the standard REBOL3 executable.
X
A DRAW block, used in search fields and as a delete button.
Glass
A DRAW block, depicting a magnifying glass used in search fields.
File
A DRAW block, depicting a small folder icon used in file requester faces to call up a file requester.
Folded
A DRAW block, depicting a triangle, pointing to the right. Used in tree menus and folded areas.
Unfolded
A DRAW block, depicting a triangle, pointing down. Used in tree menus and folded areas.
Using Stock Icons
To use a stock icon in your layouts, you can access them via (a method yet to be determined). The name of each icon is a word and this word is what you use, when wanting to using a specific icon. In a requester, you can use a stock icon like this:
request/type "An error has happened!" 'warning
Adding New Icons
Each new icon added to the stock icon list can be used in the same way as the icons that exist in the list from the start. In a sense, they are "citizens" equal to the original icons.
Need to describe the method for doing that here.
Replacing Icons
All icons can be fully replaced.
Need to describe the method for doing that here.
Handling Missing Icons
If a stock icon goes missing, it should be replaced with a question mark icon or a template icon. This can be generated purely in DRAW. The reason for this is to avoid such icons, loaded from disk at start up, stopping a program from working properly, if one of the icon files is missing. (Is this a good enough reason?) --Henrikmk 05:44, 18 August 2007 (EDT)
VID References
The Style Editor is an application for building styles for VID3. It has not yet been built, and probably won't be for a while, however some scripts are coming together for testing styles, that could be used as parts of the style editor.
Palletize.r
This script is available in VID/Style Editor/palletize.r.
This is a function to create a color palette of one style. To use it:
do %palletize.r palletize 'button
And a window is displayed with the button style in it.
More scripts will be added later.
List of Requirements
- List all functions in a list for a specific style.
- Select between multiple styles in a style list on which one to edit.
- Provide a standard template for a style.
- List on-* functions in the order they are executed.
- Duplicate and delete styles.
- Create new styles from a standard template.
- Indicate how to create a style from a minimum template.
- Export code on a style by style basis.
- Import code (difficult?)
- Real time feedback.
- Test of resizing.
- View multiple instances of the same style.
- Add a custom background to the style.
- View a palette of optional appearances for the style by adjusting a parameter (gradient, color, button rounding, etc.) within a specific range. This is important for evaluation of the style by another person.
- Screendump the style to a file.
- View the style at any scale.
- Possibly show how fast the style is to draw, e.g. "Drawn in 0.002 seconds".
- Show a sample layout with all styles in a specific set up, to see how many different styles look together.
- Check the source code for a style to catch early errors, such as unknown words in ON-SET-ACTIONS
VID References
VID FACE
This is the initial entry for the VID 3 face. Subject to change.
The new FACE of REBOL
REBOL 3 gets a whole new face.
- Bigger, stronger, faster? No.
- Smaller, stronger, faster? Yes.
Document Reference
Title: "REBOL VID 3: Definition of FACE prototype object" Rights: "Copyright REBOL Technologies 2001-2007" Date: 15-Aug-2007 Comment: "SUBJECT TO CHANGE"
Comparing REBOL 2
The REBOL 3 VID face is similar to the REBOL 2 face, but has been redesigned, pruned and optimized. One of the main separations is that of REBOL 3 gob!. All the graphical information is now stored in the gob! datatype, and face is at a slightly higher level. VID: Styles still create faces, but keep in mind that there are differences from REBOL 2. Differences for the better.
FACE objects
gob: none ; reference to the main gob used to render the face
feel: context [
; feel functions
; they handle events (from user and from app)
; they produce events (eg. lower level event can produce higher level one)
; they should never access/change the gobs directly
look: context [
; look functions
; they handle the gobs that render the face
style: 'face
name: none ; face name (when you use set-words in the layout)
spec: context [ ] ; style arguments (object)
action: none
attached: [ ] ; list of attached faces - faces that receive action events from this face
parent: none ; parent panel face
pane: [ ] ; child faces for panels
size: shrink: stretch: 0x0 ; determine how the face resizes
; size is the "natural" size of the face
; shrink determines how the face shrinks below the natural size
; face is not allowed to become smaller than face - shrink
; eg, if size is 100x100 and shrink is 10x10, min face size is 90x90
; stretch determines how the face stretches above the natural size
; there is no limit on how much a face can stretch; the bigger the
; stretch value, the faster the face grows
; (wrt other faces with smaller stretch values)
origin: 4x4
margin: 4x4 ; face margins
; they always shrink to 0
; they never stretch
; ... other style specific fields
face/gob
The REBOL 3 gob! is a datatype!. Features similar to an object!, but not an object!, gobs are a datatype!. Each face has one main gob!, which will be linked to all the graphical elements required to support the face through the old tried and true REBOL 2 concept of panes.
face/feel
The event handling functions. Now with event generation!.
on-init
Called during face creation, in this order
- face/feel/on-init face spec where spec is an object with style arguments
- face/look/on-init face spec same spec, will create gob!s
- face/look/on-resize face size where size is initial size
on-set
Called during set-face to update the face state with new spec. The on-set function must return a block of changes that will be used in a call to face/look/on-update. Developers will rarely need to redefine this function as the default code will handle most cases.
on-set-actions
Moved to the Style Creation Guide.
on-get
on-get is called from get-face. The default code will handle most cases, but this code must be overridden for panels.
on-get-value
on-get-value defines the default specification value to return for get-face.
on-click
on-click is called for mouse events. It is called with face event and where parameters. Must return the event or none. The event will bubble up to a parent's face on-click. where is determined by a call to look/map-offset, which maps the offset to a high-level value.
on-move
feel/on-move should only be redefined if really necessary. It should be a fast function if it is overridden. It is called with face event gob and offset parameters. The offset is low-level and developers will have to call look/map-offset to get high-level offsets. The designer suggests using look/on-over and look/on-away if the face needs highlighting during a move.
on-key
feel/on-key is called when the face receives a key event. If is called with face event and key parameters. This will only happen if the face has focus. If returned, the event will bubble up to a parent's key handler.
on-drop
on-drop opens up REBOL for Drag and Drop capabilities. It is called when something is dropped on the face. feel/on-drop is called with face where origin and value parameters. where is the look/map-offset high-level offset. origin is the face that the value comes from and will be none if the drop is dragged from another application. It is normally handled by the face/action handler.
on-action
on-action is called from higher level events. Called with face and event parameters. event/1 is the event type.
on-child-resize
on-child-resize is called with face and child parameters for a panel face when a child's face/size, face/shrink or face/stretch changes.
on-drag
on-drag is called during internal dragging, for example the knob of slider being dragged. feel/on-drag is called with face and value parameters, where the value is the result of look/on-drag.
on-drag-over
on-drag-over is called when a value is being dragged over the face. The function is called with face where origin and value parameters. where is the look/map-offset high-level offset, and origin is the face the the value comes from. If on-drag-over returns true the face will accept the value as a drop, otherwise the face will not accept the drop. The user may still drop it here, so on-drop will still have to check the value in on-drop. The function is used to signal the user that this face will the accept the drop and decide if look/on-drag-over must be called. By default this is handled by the face action handler.
on-focus
feel/on-focus is called with the face parameter when the face becomes the focal face for key events.
on-unfocus
feel/on-unfocus is called with the face parameter when it loses key focus.
face/look
The face visual events handlers.
on-init
look/on-init is called with the face and spec parameters. spec is an object with style arguments. The look/on-init should create all gob! data along with other initializations. Called during creation of the face after the feel/on-init.
on-update
look/on-update is called with face and a block! of changes when the internal state changes, or on set-face to pass new spec. The changes is a block of words from feel functions or resulting from on-set. The default look/on-update will handle most cases, using the on-update-actions block.
on-update-actions
Moved to the Skin Creation Guide.
child-resize?
on-child-resize? is called with face and code parameters. It is a useful support function allowing controlled resizing of sub-faces. The VID 3 resizing uses the concept of size limit, shrink and stretch ratios.
on-resize
look/on-resize is called with face' and size parameters when the face is resized and on initial creation. This function should update all the the gob! data sizes along with other calculations.
map-offset
look/map-offset is called with face gob and offset parameters. It maps gob! and offset data to higher-level values used by the feel functions. When the /is-drag? refinement is used, this function should return a block! with [where drag-gob drag-min drag-max], where
- where is the value to send to feel functions
- if drag-gob is not none, is the gob! to drag
- if drag-min and drag-max is not none then this is an internal drag such a slider knob. and drag-gob is already shown and drag-min and drag-max define the constraints on drag-gob/offset during dragging. Otherwise (drag-min and drag-max are none) it is a drag and drop operation. During drag and drop, drag-gob is the gob to drag and must not be already shown. drag-gob/data/1 is the value that will be sent on drop.
NOTE: due to event bubbling, a panel may get map-offset with a 'gob! that is from one of it's child faces.
on-over
look/on-over is called with face gob and offset when the mouse first passes over the gob!. This function is only called once on an over.
on-away
look/on-away is called with face and gob when the mouse pointer moves away from gob!.
on-drag
on-drag is called during internal dragging, for example the knob of a slider being dragged Developers should use this function if there is a need to update face visuals during drag. Note that knob offset is updated automatically (and knob is shown), so you don't need to update it. look/on-drag must return a value for feel/on-drag.
on-drag-over
look/on-drag-over is called with face and where when something is dragged over the face. where is the result of look/map-offset. This function should be used if there is a need to update visuals when something is dragged over the face.
on-drag-away
look/on-drag-away is called with face and where when a dragged object goes away from where. Another function to be used to update visuals.
Some notes from the designer
; General notes on FACE/FEEL and FACE/LOOK functions (see below for more details)
; * Initialization (face creation) ; when a face is created, face/feel/on-init is called. then, face/look/on-init is called. ; (before the face is shown, face/look/on-resize is also called with the initial size.)
; * SET-FACE / GET-FACE ; on get-face, face/feel/on-get is called. the result is returned by get-face. ; you don't usually need to change on-get, because the default code will work with ; almost all styles. you can just set face/feel/on-get-value to a word: for example, ; if you set on-get-value to 'value, face/spec/value is returned by get-face. ; on set-face, face/feel/on-set is called. this function must return a block of "changes". ; this block is then sent to face/look/on-update as the changes block. ; you don't need to change on-set often too, because the default code should be enough ; for all styles. it uses a block, face/feel/on-set-actions, which has the format ; [some [word action change]], where word is the word in face/spec that is being set ; (eg. if face/spec/value is being set, word would be 'value), action is an (optional) ; action to perform when that word is set, and change is an (optional) change word ; to put in the changes block for on-update. action can be a block, in which case it is ; done and the resulting value is used to set the word in face/spec; inside the block, ; the word 'value refers to the value the user is setting the spec to; otherwise, if ; action is not a block, no action is done, and face/spec is automatically set to ; the value given by the user. change is either a word, that is added to the changes ; block, or a block, whose result after evaluation is added to the changes block.
; * FACE/LOOK/ON-UPDATE ; this is the function that keeps the graphical representation of the face (the gobs) ; in sync with the state of the face (face/spec etc.). whenever something in the face ; changes, on-update is called; the changes block tells on-update what has changed, and thus, ; what needs to be updated. normally, the changes block is just a block of words (however, ; if you don't use the default code for feel/on-set and look/on-update, you can put what ; you want in the changes block). on-update is called automatically during set-face, ; and you usually call it from the feel functions when they alter the state of the face ; (eg. if a mouse click alters the state of the face, feel/on-click cannot change the gobs ; directly, so it calls look/on-update to sync the gobs with the new state). ; basically, the contents of the changes block is an agreed upon protocol between the feel ; object and the look object. this protocol defines how feel and look communicate with each ; other; normally, just words are used, with each word meaning that something specific in ; the face has changed (for eg., you could use the word 'value to mean that face/spec/value ; has changed for some reason). ; normally, you don't change on-update, because the default code is enough for most styles; ; instead, you change face/look/on-update-actions, which is used by on-update this way: ; foreach chg changes [ ; switch/all chg on-update-actions ; ]
; * Events ; ** Click ; a mouse click is composed by a DOWN event followed by an UP event (or an ALT-DOWN event ; followed by ALT-UP, or AUX-DOWN followed by AUX-UP). first of all, face/look/map-offset ; is called. the value returned by this function is then passed to face/feel/on-click. ; in the case of DOWN (or ALT-DOWN or AUX-DOWN), if on-click returns the event, ; it is sent to the face's parent face, that is, the event "bubbles up" until ; some on-click function does not return it or the window face is reached. in the case ; of UP (or ALT-UP or AUX-UP), the event is always sent to the face that catched the DOWN ; (or ALT-DOWN or AUX-DOWN) regardless of the face under the cursor. ; a DOWN event (etc.) can also initiate a drag operation: map-offset is actually called ; with the /is-drag? refinement indeed, and if it returns a drag-gob, and the on-click ; function catches the event, the drag operation is initiated (for more details, see the ; comments in the map-offset function). on UP (etc.), if it is actually a drop (user ; released something over the face), face/feel/on-drop is called for the face under ; the mouse (face/feel/on-click is still called for the face that received the DOWN, ; that is, the origin of the drag). by default, face/feel/on-drop delegates the handling ; of the event to the action handler (using the DROP action event). ; ** Move ; when the mouse is moved, there are many cases: ; - internal drag (see map-offset comments): the gob is moved within the boundaries, ; and face/feel/on-drag is called with the result of face/look/on-drag. ; - drag&drop: the gob being dragged is moved, face/look/map-offset is called for ; the face under the mouse, then face/feel/on-drag-over is called if necessary ; (ie only if face or where has changed); if it returns true, the user is ; notified that the face may accept the drop, and face/look/on-drag-over is called ; too. face/look/on-drag-away is called when mouse is moved away from the area ; that got the over event. by default, face/feel/on-drag-over delegates the handling ; to the action handler (using the DROP? action event). ; - any other case: face/feel/on-move is called for the face under the mouse. (this ; function, if needed, should be small and fast.) also, whenever the gob under the mouse ; changes, face/look/on-over and face/look/on-away are called. in 99% of the cases, ; on-move is not used and left empty, while you use look/on-over and look/on-away ; if you want to visually show that the mouse is over the face, or if you need to ; update the face state ; ** Resize ; whenever the window is resized, window/look/on-resize is called. (the on-resize for ; panel faces is responsible to call on-resize for child faces.) ; ** Key ; for KEY or KEY-UP events, if a focal-face is defined, focal-face/feel/on-key is called. ; if this function returns the event, it is bubbled up to the parent face, and so on. ; (this means that the window face gets all keys that are not catched.) ; ** Scroll wheel ; for SCROLL-LINE and SCROLL-PAGE, face/feel/on-key (with 'scroll-line or 'scroll-page ; as the key) is called for the face under the mouse (not the focal face!). usual event ; bubbling: if on-key returns the event, it is sent to the parent face.
; * Actions ; action events are higher level events. a face may receive action events from other faces: ; in that case, face/feel/on-action is called. a face may also send action events to its ; action handler and to the attached faces by calling the DO-FACE function. (see vid-funcs.r)
; * Child resize (panels) ; for panels, if a child face changes its face/size, face/shrink or face/stretch values, ; panel/feel/on-child-resize is called. this way the panel can relayout the child ; faces if necessary. when a face changes its size, shrink or stretch values, it must ; call the parent's feel/on-child-resize function. for this reason, a useful support function ; is provided in the look object (because size is handled by the look): face/look/child-resize? ; you can pass some code to this function, and if the face size changes after the code has been ; DOne, then the parent's on-child-resize is called automatically.
; * Focus ; when a face is focused (FOCUS function), face/feel/on-focus is called. when the focused ; face loses the focus, face/feel/on-unfocus is called. by default, they just call ; face/look/on-update with the 'focus or 'unfocus changes.
Notes for style and skin writers
To Be Written.
Not Yet Implemented
VID References
This document will guide you through the creation of a VID style. We'll create a simple BUTTON style.
VID 3 proto 2
STYLIZE
The STYLIZE function allows creating new styles. (Global only at this point.)
The general format for creating a style is:
stylize [
new-style: base-style [spec] [feel] [look]
]
where:
- new-style - the name of the style to create
- base-style - the style to begin with
- spec - an optional block to specify the contents
- feel - an optional block to describe the behavior
- look - an optional block to provide the visual details
The sequence VID evaluates optional blocks is:
- spec feel look
- feel look
- look
This means, if only a single block is specified, it's the LOOK block.
Together, these values completely describe the contents, look, and feel of the new style.
Example
For example, a new button style might be defined as:
stylize [
button: face [
; the spec ("args")
] [
; the feel (how it acts and reacts)
] [
; the look (visually what is displayed)
]
]
You can specify a base style for your new style; in this case, we'll use FACE, which basically means no base style. The style is specified by three blocks (they are not all mandatory, however when you start from FACE you'll want to specify all three; more on this later). The first block, specifies the arguments for the style in the dialect.
stylize [
button: face [
text: string!
action: block!
] [
; ...
] [
; ...
]
]
In the VID dialect, argument order is only important for arguments of the same datatype. So, in this case, argument order is not important. We'll accept one string and one block, and they will go in FACE/SPEC/TEXT and FACE/SPEC/ACTION.
The second block in the stylize spec is the FEEL object for the style. The feel object handles events for the style (both from the user and other faces or the application); it should always do so without being dependent on how the style is actually rendered. The visual rendering of the style (using GOBs) is done by the LOOK object, which is specified by the third block in the stylize spec. The functions in FEEL are allowed to call the functions in LOOK (usually, just the ON-UPDATE function) to update the visual rendering of the face when the face's state changes. The functions in LOOK create and manipulate the GOBs that render the face, but should never modify the face's state.
FEEL
The feel object, as said, contains the functions that handle the events for the style.
ON-INIT
The first function we'll write is ON-INIT, which is called to initialize the FACE when an instance of the style is being created.
on-init: func [face specs] [
; ...
]
The FACE argument is the face object being created. The specs argument, which is the same as FACE/SPEC, is the object with the arguments for the style from the dialect. For example, considering the argument spec we specified above, if the user says:
button "Click" [print "clicked"]
then SPECS will be:
make object! [
text: "Click"
action: [print "clicked"]
]
So, our ON-INIT could look like this:
on-init: func [face specs] [
; extend the face object. (Note that FACE is an object here; under R3,
; APPEND can extend the spec of an object.)
append face [state normal]
; we want to always have some text
specs/text: any [specs/text "Click me"]
; set up the action
if specs/action [face/action: func [face event] specs/action]
]
First, we extend the face object, adding face/state that we'll use to keep track of the state of the button. We also set up face/action using specs/action; face/action is the function that is called for "action" events, which are the higher level events; in the case of a button, these will be HIT events.
ON-CLICK
The ON-CLICK function is called when the user clicks on the face with the mouse.
on-click: func [face event where] [
; ...
]
EVENT/TYPE can be one of UP, DOWN, ALT-UP, ALT-DOWN, AUX-UP or AUX-DOWN. The WHERE argument abstracts the location of the click from the actual rendering of the face (which is defined by the LOOK functions); in this case, we don't care where the user clicked (so, more details on this argument will be given in the feel reference section).
on-click: func [face event where] [
switch event/type [
up [
if face/state = 'clicked [
face/state: 'normal
face/look/on-update face [state] none
do-face face [hit]
]
]
down [
face/state: 'clicked
face/look/on-update face [state] none
]
]
]
On DOWN, we set FACE/STATE to CLICKED and we call FACE/LOOK/ON-UPDATE to update the rendering of the button (so that it can be shown as clicked). (We tell the ON-UPDATE function that the only thing that changed is the button state. More on this in the skin creation guide.)
On UP, if we are in clicked state, we go back to NORMAL and update the rendering, then send the HIT event to the face (which means sending it to all faces attached to it, and calling FACE/ACTION with the event). This sequence is triggered by calling DO-FACE. The event HIT is user defined.
DO-FACE looks like this:
do-face: make function! [[
{Evaluate face action (and send action event to attached faces)}
face [object!]
event [block!] "The action event"
][
foreach face face/attached [
face/feel/on-action face event
]
face/action face event
]]
== function!
Handling SET-FACE and GET-FACE
To handle SET-FACE on our buttons, we need to define the ON-SET function in the FEEL.
on-set: func [face new-specs] [
if new-specs/text [face/spec/text: new-specs/text]
if new-specs/action [
face/spec/action: new-specs/action
face/action: func [face event] new-specs/action
]
]
ON-SET is called with the new specs from the user. So, if the user specified some new text, we set face/spec/text to it; if they specified a new action block, we change the action function (and so on if we had other arguments).
For GET-FACE, a default ON-GET function that does what we want for most faces is already defined; we just need to decide which spec value we want to return by default. We do so by setting ON-GET-VALUE:
on-get-value: 'text
ON-SET-ACTIONS
These are called in the on-set function and serve as a streamlined way to set up actions. From a VID developer's perspective, these actions are run during set-face. The syntax is, as seen from parsing:
some [word action change]
- word
- This indicates the change to be made on the face. This can be any word, or one that is in the specifications for the fac. If you want to change face/spec/value, the word to use is 'value.
- action
- This can either be a block with an action that is run, or simply a minus (-), indicating that no action is necessary here.
- change
- This is another word that indicates what should change in the LOOK object's on-update-actions block. Similarly to how the first word for each entry corresponds to changes with on-set, the first word for each entry in on-update-actions is coupled to the last word for each entry in on-set-actions.
Examples
value - value
This responds whenever value needs to be changed. This can be seen as value just "passing through". Nothing happens to the value.
value [face/spec/value: min max 10 face/spec/value 20] value
Here an action has been added to limit face/spec/value to between 10 and 20, regardless of the input.
color - color
The word color is passed through, so you can act on it in on-update-actions.
LOOK
For the definition of the look object, see VID: Skin creation guide.
FEEL function reference
; feel functions
; they handle events (from user and from app)
; they produce events (eg. lower level event can produce higher level one)
; they should never access/change the gobs directly
on-init: func [face specs] [
; specs is an object with style arguments
; look/on-init called after this, then look/on-resize with initial size
]
on-set: func [face new-specs] [
; called on set-face to update face state with new specs
; look/on-update called after this
]
on-get: func [face all?] [
; called on get-face
; you only need to override this for panels
; using copy on series to avoid problems if someone modifies returned values
either all? [
collect [
if face/name [emit face/name]
emit face/style
foreach value get face/spec [
unless none? :value [emit/only either series? :value [copy :value] [:value]]
]
]
] [
face: get in face/spec face/feel/on-get-value
either series? :face [copy :face] [:face]
]
]
on-get-value: 'value ; default spec value to return for get-face (see on-get)
on-click: func [face event where] [
; called on up, down, alt-up etc.
; where is the result of look/map-offset (used to map offset to high level value)
; must return event or none; if event is returned, the parent's on-click is called
]
on-move: func [face event gob offset] [
; you should use this function only if really necessary,
; and keep it as fast as possible
; for highlighting etc. use look/on-over and look/on-away
; you need to call look/map-offset on your own
]
on-key: func [face event key] [
; called when face is focused
; if event is returned, key is sent to window's global key handler
event
]
on-drop: func [face where value] [
; called when something is dropped on face
; where is the result of look/map-offset
]
on-action: func [face event] [
; called on higher level event
; event is a block, event/1 is the event type
]
on-child-resize: func [face child] [
; for panel faces, called when a child's face/size, face/shrink or face/stretch change
]
on-drag: func [face value] [
; called during internal dragging (eg. knob of slider being dragged)
; value is the result of look/on-drag
]
on-drag-over: func [face where origin value] [
; called when value is being dragged over face
; where is the result of look/map-offset
; origin is the face from which value comes from (may be none
; if user is dragging from some other app)
; if you return true, it means you can accept value as a drop,
; otherwise you don't accept it (user may still drop it here,
; so you have to check value in on-drop)
; this is only used to signal the user whether you accept the drop or not,
; and to decide if look/on-drag-over must be called
false
]
on-focus: func [face] [
; called when face becomes focal face for keys
face/look/on-update face [focus] none
]
on-unfocus: func [face] [
; called when face loses key focus
face/look/on-update face [unfocus] none
]
on-set-actions: [
; block used in ON-SET
]
VID References
This document will guide you through the creation of the "skin" or look of a style. We'll create the look for the simple BUTTON style created in VID: Style creation guide. (This look for button has been created by Henrik Mikael Kristensen.)
STYLIZE
Following VID: Style creation guide, we'll be defining the contents of the third block in the STYLIZE spec. If we were just changing the LOOK of an existing style, we would do so with:
stylize [
my-button: button [
; ...
]
]
In that case, since we are only specifying one block, the SPEC and FEEL objects are left as they are, and only the LOOK object is changed.
LOOK
ON-INIT
LOOK/ON-INIT is called just after FEEL/ON-INIT and should create the gobs that render the face (as well as do any other initialization necessary for the look of the face). This function is also responsible of setting FACE/SIZE, FACE/SHRINK and FACE/STRETCH. (See below)
First, we add some words to the FACE object; they are used by the look but mainly are used as variables in the draw block for the gob (we need many of them because evaluation is not allowed in the draw dialect). Of course, this is not the only way to do it, but it is a nice way because during resize (for e.g.) you need to just change these variables instead of having to recreate the draw block.
Then, we create FACE/GOB, and set the size, shrink and stretch for the face; we want buttons to be fixed size, so all buttons will have a "normal" size of 120x24. They can shrink to 100x24, and can stretch rather quickly (they get to 240x24 when resize ratio is 1), but only horizontally.
on-init: func [face specs] [
append face [
; helper words for draw block (needed for resize)
bottom-right 99x24
bottom-left 0x24
edge-right 99x23
draw-text none
text-offset 0x0
text-size 0x0
]
face/draw-text: specs/text
face/gob: make gob! bind compose/deep [
draw: [
translate 0x0
; shadow
pen 0.0.0.220 line-width 1
box 0x2 bottom-right 2
grad-pen
linear 0x0 1 23 90 1 1
(gradients/glossy)
; dark edge and background
pen 0.0.0.96 line-width 1
box 0x0 edge-right 2
text text-offset [anti-alias on font normal-font 0.0.0 draw-text]
]
data: face
] face
face/text-size: size-text make gob! bind [size: 600x30 text: [anti-alias on font normal-font draw-text]] face
face/size: 120x24
face/shrink: 20x0
face/stretch: 120x0
]
normal-font: make system/standard/font [
name: "Trebuchet MS"
size: 14
align: 'center
]
The GRADIENTS/GLOSSY block is:
[ 240.240.240 240.240.240 235.235.235 233.233.233 232.232.232 231.231.231 230.230.230 229.229.229 228.228.228 211.211.211 212.212.212 213.213.213 214.214.214 215.215.215 216.216.216 217.217.217 218.218.218 218.218.218 ]
SIZE, SHRINK and STRETCH
Little digression here to explain size, shrink, and stretch. (They will also be documented where we explain the layout/resize model.)
The three pair values SIZE, SHRINK, and STRETCH determine the "best" size of the face, and how the face resizes when the window is resized. SIZE is the "optimal" size of the face, and the layout process will try to use it as the initial face size if possible. SHRINK determines how much the face can shrink below this optimal size. In the above, we set FACE/SIZE to 120x24 and FACE/SHRINK to 20x0, which means that the face cannot shrink vertically, and can shrink by 20 horizontally, ie. it can go down to 100x24. (The face will never be smaller than 100x24.) The system determines how much to shrink the face based on the window size and the layout; basically, a ratio is computed: if this ratio is negative, then the face is shrinked. The ratio can never be lesser than -1. The face actual size is computed as:
face/size + (face/shrink * ratio)
FACE/STRETCH works in a similar way, except that the ratio can be greater than 1 (that is, there is no hard limit to the max size of the face). If the ratio is positive, the actual size is computed as:
face/size + (face/stretch * ratio)
so that FACE/STRETCH determines how fast the face will grow when the window grows. In the case above, since FACE/STRETCH is set to 120x0, the actual size will go to 240x24 when ratio is 1, or 360x24 when it is 2 and so on. If we had specified a bigger number for stretch, the face would grow faster; eg. with 240x0 as stretch, the actual size would be 360x24 for a ratio of 1 and 600x24 for a ratio of 2.
ON-RESIZE
The ON-RESIZE function is called just after ON-INIT to set the initial size, and afterwards every time the window is resized. The SIZE argument is the new size of the face. We use it to recompute the variables we use, such as BOTTOM-RIGHT and TEXT-OFFSET.
on-resize: func [face size] [
face/gob/size: size
face/bottom-right: size - 1x1
face/bottom-left: face/bottom-right * 0x1
face/edge-right: face/bottom-right - 0x1
face/text-offset: size - face/text-size / 2 - 2x3
]
ON-UPDATE
ON-UPDATE is called automatically just after FEEL/ON-SET when the user calls SET-FACE, or manually by the feel object. The CHANGES argument is a block of words that states what is changed in the face; it is an empty block for SET-FACE (what has changed has to be inferred from NEW-SPECS). NEW-SPECS is the object with the new arguments from SET-FACE, or usually NONE when called manually from the FEEL object.
In this case, if a new text has been set with SET-FACE, we update FACE/DRAW-TEXT, FACE/TEXT-SIZE and FACE/TEXT-OFFSET. Also, if face state has changed (STATE word in the CHANGES block) we update the rendering to indicate the clicked state.
on-update: func [face changes new-specs] [
if all [new-specs new-specs/text] [
face/draw-text: new-specs/text
face/text-size: size-text make gob! bind [size: 600x30 text: [anti-alias on font normal-font draw-text]] face
face/text-offset: face/gob/size - face/text-size / 2 - 2x3
show face/gob
]
foreach chg changes [
switch chg [
state [
face/gob/draw/2: pick [0x1 0x0] face/state = 'clicked
show face/gob
]
]
]
]
ON-OVER and ON-AWAY
These two functions get called when the mouse goes over or away from a gob in the face. We use them to go out of clicked state if the mouse goes away from the face while the button is clicked. As an exception, we update the face state here, since it would be too expensive to do this in FEEL/ON-MOVE (FEEL/ON-MOVE should only be used when really necessary, so LOOK/ON-OVER and LOOK/ON-AWAY are exceptionally allowed to change the face state for performance reasons).
on-over: func [face gob offset] [
if face/state = 'clicked-away [face/state: 'clicked face/look/on-update face [state] none]
]
on-away: func [face gob] [
if face/state = 'clicked [face/state: 'clicked-away face/look/on-update face [state] none]
]
ON-UPDATE-ACTIONS
This does not flow well with the rest of the tutorial. Examples should be like the rest of the tutorial.
This is a block of actions to perform, whenever there is a change in the face, set with on-set. The block is processed with switch inside the default on-update function, so it's parsed like:
some [word action]
For each word that is passed through the block used in on-set-actions, you can act on it here and change the visual appearance of the face. This is for example used, when the text changes in a field or the color of an LED changes.
Examples
on-update-actions: [
text [
face/draw-text: face/spec/text
queue-show face/gob
]
]
If text is output from the on-set-actions block, face/draw-text is set with the new value and the face is redrawn using queue-show.
Write more examples
LOOK functions reference
; look functions
; they handle the gobs that render the face
on-init: func [face specs] [
; specs is an object with style arguments
; should create all gobs and so on
]
on-update: func [face changes new-specs] [
; called when internal state changes, or on set-face (new specs)
; changes is a block of changes (usually block of words) from feel funcs
; new-specs can be none if it's just a state change (called from feel)
; feel/on-update is called before this on set-face so face state has been updated already
; in that case you must infer state changes on your own from new-specs (changes is empty)
]
; useful support function
child-resize?: func [face code /local old-sz old-shr old-str] [
old-sz: face/size
old-shr: face/shrink
old-str: face/stretch
do code
face/look/on-resize face max face/gob/size face/size - face/shrink
either all [face/parent any [old-sz <> face/size old-shr <> face/shrink old-str <> face/stretch]] [
face/parent/feel/on-child-resize face/parent face
] [
either face/parent [
show face/gob
] [
; window group, resize the window if necessary
;face/gob/parent/size: face/gob/size ;CRASH!!
;show face/gob/parent ;win offset changes on show!
show face/gob ; temp, while bugs are fixed
]
]
]
on-resize: func [face size] [
; called when face is resized (and for initial face size)
; should update gob sizes and so on
]
map-offset: func [face gob offset /is-drag? event] [
; maps gob+offset to higher level value used by feel funcs
; when /is-drag? is passed, should return a block with:
; [where drag-gob drag-min drag-max]
; where is the value to send to feel funcs
; drag-gob, if not none, is the gob to drag
; if drag-min and drag-max are not none, then it is an internal drag
; (eg slider knob); drag-gob is already shown, and drag-min/drag-max
; define the constraints on drag-gob/offset during dragging
; otherwise (drag-min and drag-max none) it is a drag&drop operation,
; and drag-gob is the gob to drag (must not be already shown)
; drag-gob/data is the value that will be sent on drop
none
]
on-over: func [face gob offset] [
; called when mouse is over a gob (only once)
]
on-away: func [face gob] [
; called when mouse goes away from gob
]
on-drag: func [face offset] [
; called during internal dragging (eg knob of slider being dragged)
; should use this if you need to update face visuals during drag
; note that knob offset is updated automatically (and knob is shown),
; so you don't need to update it
; must return a value for feel/on-drag
none
]
on-drag-over: func [face where] [
; called when something is being dragged over the face
; you should use this if you need to update visuals when
; something is being dragged over the face
; where is the result of map-offset
]
on-drag-away: func [face where] [
; called when dragged object goes away from "where"
; use this to update visuals
]
on-update-actions: [
; SWITCH block used in ON-UPDATE
]
VID References
| This page is under construction. More content is needed. |
Changes in VID
Highlighting some of the major changes in the REBOL 3 VID.
Terms and Timeline
- REBOL 1
- Any release of REBOL prior to 1.2.0, no longer supported.
- REBOL 2
- Any release of REBOL prior to 3.0, the current production releases being from 2006.
- REBOL/View 1.3.2
- REBOL/Core 2.6.2
- REBOL/SDK 2.5.8
- REBOL/Command 2.6.2
- REBOL/IOS 1.0.2
- REBOL 3
- The REBOL unveiled in 2007.
- VID
- Visual Interface Dialect
Overview
The REBOL 3 version of VID is brand new. Written from the ground up and based on over seven years of experience with the REBOL/View Graphical User Interface. The changes are many and varied, are consistent with the REBOL Way, are all for the better, but come at a price.
Some of the VID changes are necessary to keep up with other REBOL 3 modifications.
- REBOL now has a completely new Event System. On the upside, this new system will allow for programmer generated events.
- REBOL also has a new Graphics system based on gob! a graphical object datatype!.
- VID still supports and uses faces, but these are at a higher level than the faces of REBOL 2 and the graphical portions are held in the gob! datatype!. The gob! datatype makes for a lighter weight face than previous versions.
Major Changes
- Layout engine is entirely rewritten and is not compatible with VID under REBOL/View 1.3.2.
- Styles have been overhauled
- Skins now possible and encouraged
- view no longer requires layout, as block! parameter will automatically be passed to layout
- Brand new dialect engine based on the DELECT function, see also Delect notes.
This page is meant for explaining how to access certain internals in VID, if you want to list styles, or want to view the source code for a style.
For now it's a collection of notes, but should grow into a real document.
VID Sources
REBOL3 already contains a fairly capable version of VID3, but the bleeding edge sources must be included to take advantage of the latest features.
The bleeding edge VID sources are available for testing by R3 alpha testers. In order to add these sources, you must go to the VID directory and add the following files:
do %vid/vid-face.r do %vid/vid-funcs.r do %vid/vid-events.r do %vid/vid-styles.r
Now you are ready to work with the latest VID.
It's important that you do not alter these sources! Doing that causes confusion when Gabriele releases new versions of them.
Instead, when adding new or adjusting existing styles for inclusion into later official versions of VID, use your own source file and append that to the above shown include list.
When adding a new style, just do that with 'stylize, e.g.:
stylize [ my-new-style: scrollable [...] [...] [...] ]
When editing an existing style, you should not patch it, but recreate it from scratch, e.g.:
stylize [ button: clickable [...] [...] [...] ]
Patching styles is only for those who want to develop additional skins for VID. To work with skinning, see the Skin Creation Guide
Tips & Tricks
To see all styles, access style-protos.
To access the feel source code of a single style:
source style-protos/<style-name>/feel
To access the look source code of a single style:
source style-protos/<style-name>/look
To access which specifications and their datatypes are used for a single style:
probe style-protos/<style-name>/spec
More to come.
VID References
Currently various non-organized notes.
Reference links
FACE object
gob: ; reference to the main gob used to render the face.
feel: ; object containing the feel functions
; they handle events (from user and from app)
; they produce events (eg. lower level event can produce higher level one)
; they should never change the gobs directly
look: ; object containing the look functions
; they handle the gobs that render the face
none
style: 'face ; not sure if useful
action: none ; action function - receives higher level events
; maybe it should be a block of functions?
; or we can have lower level listeners block and action
; only gets higher level events that are not consumed by listeners
parent: none ; container face (?)
pane: [ ] ; contained faces
size: stretch: shrink: 0x0
spec: none ; spec object (style arguments)
attached: [ ] ; list of attached faces - faces that receive action events from this face
; other style specific fields
FEEL object
- high granularity (many small functions)
- change face state, then call look functions to update rendering
- functions will most likely get block of events instead of just a single event. more on this soon.
; from user
on-click: func [face event offset] [
; click events
; event/type one of: up down alt-up alt-down aux-up aux-down (?)
; offset within main gob? or should we pass gob and offset?
]
; on-double-click ??
on-move: func [face event offset] [
; move events
; mouse is being moved over one of the face gobs
; i think it should probably get it too if mouse was inside and is moved outside
]
; from app
on-init: func [face specs] [
; initialize face state based on VID specs
]
on-update: func [face new-specs] [
; update face data
; new-specs is like on-init specs
; eg change face text or state etc.
]
; from other ui elements
on-action: func [face event] [
; event is a block
; [word! ...] event/1 is the type of event
; common types: [hit] [value <x>] [scroll <offset>]
; [scroll-change <offset> <area-size> <visible-size>]
]
LOOK object
Render face or update face rendering based on face data/state, or events from other faces
; from app
on-init: func [face specs] [
; initialize (render gobs for the first time)
; size usually unknown at this time, will get resize later
; must set face/size, face/shrink and face/stretch appropriately
]
; from feel or apps
on-update: func [face new-specs] [
; face state changed, or new specs (new-specs can be none)
; may call on-child-resize on parent if size, shrink or stretch changed
; parent will call on-resize if necessary
]
on-resize: func [face size] [
; face size changed
; should try to be as fast as possible
]
; from other UI code
on-scroll: func [face offset] [
; scroll by offset
; eg scroll event from a scroller
]
on-child-resize: func [face min-size max-size] [
; for container styles
; one of the contained faces' size,shrink,stretch changed (after an update - eg new label text)
; may send same event to parent
]
; etc - need to think about this. maybe just one function instead of many.
Attaching faces
Face can be attached to another face; doing that means that it will send action events to the attached face. Eg. you can attach a scroller to a scrollable area.
Drag and drop
feel/on-click can return a gob! or a block! with [gob! opt [pair! pair!]]. The returned object will be dragged when mouse is moved. If two pairs are returned (min-offset and max-offset), the gob is constrained in that area (example: dragging the knob of a slider or scroller); also the gob is assumed to be already shown (ie added to a window etc.), and the system will only change its offset, and send call face/feel/on-drag on the face that was clicked. Dragging ends when on-click returns none (ie on mouse up). If only a gob is returned, it is added to the window gob (but only at the first mouse move - not immediately after the click) and the user can drag it freely (future: will be its own window and be draggable outside of the face's window). While user is dragging, faces under the cursor get face/feel/on-drag-over, which can return a logic! to indicate if it accepts a drop or not (cursor image will change based on this in the future). Dragging ends on any event that would cause on-click to be called; in this case, face/feel/on-drop is called on the face under the mouse at the time of the drop, with gob/data as argument.
Panels
A panel is a container, it holds one face (usually one column table) and may render a box with text around it, add a scrollbar, and so on.
Groups
A group establishes a relationship between faces. It can group panels together to form a tab panel; it can group faces together (eg mutually exclusive radio buttons); or it can just create a table that holds the contained faces. How to best implement all this not decided yet.
Layout process
Layout starts with a one column table. Styles (including groups and panels) are added to it. Then, the table is initialized. The table's on-init will call on-init on all subfaces. It computes the min and max sizes for itself and returns that. So, a size for the layout is determined, and the table's on-resize is called. The table computes the offset and size of each subface and calls on-resize on them.
Containers
Since all the layout is done by the containers, they become important styles. Containers should be able to not only justify their contents, but also left, center or right align them, in both directions.
Dialect Evaluation
Here are a few notes on VID dialect evaluation.
VID, like the draw, text, and effect dialects is keyword driven with position independent, datatype-based argument specification. That is, arguments of different datatypes can appear in any order and all arguments are optional. This characteristic is important because it removes the requirement that users memorize argument order - a virtual impossibility given the number of styles multiplied by the number of arguments.
The valid keywords for VID span several contexts:
- command words (like ORIGIN or RETURN)
- master style names
- local style names
- special keywords (like BOLD or CENTER)
- normal script variables and paths
In addition, style value variables must be handled (set words).
Using DELECT
One possible way to efficiently parse the VID dialect is to use the DELECT function - the same function that is used to parse draw, text, and effect blocks. However, we will want to make a few small changes to make VID a bit easier to process.
Multiple Contexts
The general method would be to specify the commands, styles, and keywords to allow DELECT to recognize them and validate their argument values. However, currently DELECT can only handle a single context, so that must be expanded to allow multiple contexts. This allows us to keep the commands, master styles, and local styles as separate blocks and eliminate a lot of garbage during the the parse.
delect/multi dialect [commands master-styles local-styles ...]
Argument Spec and Map
In addition, it would be good to specify arguments by name and allow access by name. This can be done either by mapping argument values to an object or by applying a function. The former gives us a single "spec" object that we can save, but the latter gives us a direct access method, eliminating the need to use paths to access the values.
For example a keyword entry (here as a style) for the DELECT dialect would become:
button: context [
name: string!
size: pair!
width: integer!
color: tuple!
alt-color: tuple!
action: block!
alt-action: block!
]
After the button style is parsed, the object can be cloned with the argument values mapped as provided.
Or an alternate method would be to use the same syntax as function definitions:
button: [
name [string!]
size [pair!]
width [integer!]
color [tuple!]
alt-color [tuple!]
action [block!]
alt-action [block!]
]
Then call a function (via APPLY) that would accept those arguments.
Callback Function
And finally, DELECT currently parses the entire dialect and returns a block of blocks, one for each command. However, in the interest of saving memory, it would be possible to allow DELECT to evaluate a callback function for each command/style. The advantage here is that the argument block can be reused each time (just as it is in the native draw, text, and effect dialects).
Do not forget...
- Accessibility
- Disabled state
- Keyboard
- Tooltips
- Data validation and constraints
We need...
- window options (eg min-size)
Open questions
1) Should face/gob be allowed to be a block of gobs?
It can have a gob/pane of gobs, but it is not a block of gobs. Carl 15:03, 28 June 2007 (EDT)
The reason is, if the "main" gob is completely transparent and is just used to hold the subgobs, would it not be better to NOT adding it to the hierarchy? Gabriele 14:59, 28 June 2007 (EDT)
Is there such a case of a collection of that type? Do you mean like a text box with scroll bar as an example? Perhaps. For me, the one that comes to mind is a panel or group, but you still want it in the hierarchy so it can be handled as a group. Carl 16:07, 28 June 2007 (EDT)
A simple example could be even a radio or check button with label (like R2 check-line) - you might have just two gobs (one that renders the button plus one that renders the text) for example (i know this could probably be done with draw on a single gob, but let's pretend that's not the case :), and the "container" gob would then just be a container. Maybe this can always be avoided in practice by having the main gob do something useful, so I don't know, it just seems something to keep in mind. Gabriele 03:11, 29 June 2007 (EDT)
Having one main gob that holds sub-gobs for a style make it easier to render 'disabled' or 'selected' state of that style. --rebolek 04:53, 29 June 2007 (EDT)
Rebolek, that is not necessarily true. First of all, such kind of rendering is to be handled by the look object, that will know every details of the gobs, and may be able to do any optimization - external code should never care about what's inside face/gob. Gabriele 16:08, 29 June 2007 (EDT)
Yes Gabriele, I'm not talking about external code - it would be easier for the look object when for example setting the disabled look to apply effect let say [luma -50 blur] on main gob than on every gob. Also you need to call SHOW on one gob instead a block of gobs (which may be faster, but I'm not sure on that). It will make style coding a bit easier I think. --rebolek 12:49, 2 July 2007 (EDT)
Except that if the main gob is already is use you can't set an /effect for it, and even if it's not in use I don't think /effect would apply to contained gobs. So, there is no much difference. It's possible that the block of gobs is not necessary because the main gob can always be used to something, but I don't know yet. Gabriele 15:52, 2 July 2007 (EDT)
2) Panel is a style, while group seems more like a dialect command. What do you think?
It is quite possible. As you implement it, you will be able to tell if that is true. The advantage of it being a style is that it can be accessed and operated on like a style. For instance: moved, scrolled, disabled, "get-contents", etc. Carl 15:04, 28 June 2007 (EDT)
Agreed - I'd say it's a command that generates a style, but the generated style changes with context. I'm still not sure if there are cases where an actual face should not be generated. Gabriele 14:59, 28 June 2007 (EDT)
Ah, now I understand your point. I always considered that it would create panel, not just change the layout. So, it would always be a face. - Carl 16:03, 28 June 2007 (EDT)
3) What happens if you put something that is not a panel in a tabbed group?
A good question. It can give an error, or in some cases there may be another use. But, perhaps we can ask that question of any group, such as radio buttons: what if there is a field in there too? Seems ok? Carl 15:07, 28 June 2007 (EDT)
I think field in radio group would be ok (basically field gets ignored, or more specifically, it would ignore the events coming from the radio buttons). Not sure what should happen if there are other mutually-exclusive styles though. We may need to see what happens in practice... Gabriele 15:15, 28 June 2007 (EDT)
It seems useful to allow it. For example, you can have radio "A" and radio "B" then some text (or even a field or image), then radio "C". For example, the text may just be additional comments on the option. Carl 15:56, 28 June 2007 (EDT)
4) Dynamic resizing of all styles?
The layout method allows it to be dynamic as well, correct? So if someone resizes a window or inner panel, the styles know what to do? Carl 15:04, 28 June 2007 (EDT)
Exactly - the same process happens during resize, except all the initialization has been done already so it should be much faster. Basically layout of content is delegated to container, so you can have different kinds of containers that work in different ways. (This is the way MUI etc. work, it is the "traditional" way of doing GUIs ;) Gabriele 15:15, 28 June 2007 (EDT)
Yes, but it is a bit tricky to determine priorities. For example, I might have a list that says it is 50% wide next to something that has no specified size. Anyway, I assume we can get this all figured out, as did MUI and so many others over the years. (Remember Sun "News" GUI? From Gosling. Now there was a spec. ;) Carl 15:59, 28 June 2007 (EDT)
Normally, it should be the container to determine the sizes, not the faces; the faces only determine how much they can stretch and shrink. (A model that may be interesting to use, instead of the "traditional" one, would be the one used by TeX of size, shrink and stretch. It is very flexible.) So, the 50% would be a weight not a size - how much does the face shrink or stretch when the container shrinks or stretches. Gabriele 03:16, 29 June 2007 (EDT)
5) Do we want a face/spec object?
To answer, let me define it this way: the face/spec would hold the attributes provided by the user to specify the look and feel of the face.
Generally, the spec is static and is not needed once the interface has been created. But, there are two reasons to keep the spec:
- Reflection - to be able to output the spec, in terms of a layout. This is useful for GUI building tools, but for most app GUI's it is not important information.
- Errors - to be able to generate a meaningful error message. The main thing: the error may want to refer to the name of the style. If we can do that some other way, then keeping the rest of the spec may not contribute that much to error messages.
Those are my current thoughts on the question. Carl 11:51, 9 July 2007 (EDT)
6) (Related to previous one) How do we handle specs?
DELECT will handle arguments by datatype (mainly). For different styles, they may be mapped to different things; how do we refer to them? DELECT will give us a block, and we could refer to them by position; however, this means that we need to change the code whenever we decide to change those positions for any reason. If we make an object out of the argument block, so that we refer to them by name, what names do we use? Something like specs/first-tuple, specs/second-tuple and so on? specs/color1, specs/color2? specs/first-block etc. or specs/action, specs/alt-action etc? For panels, specs/action would actually be the contents of the panel, so it would be confusing... I'm still not sure how to make this work well. Gabriele 03:09, 2 July 2007 (EDT)
I can never give a perfect answer, because I find the perfect answer during the coding process. However, what we use for the graphics engine in R3 is to refer to the args by position, but as you point out, it is unfriendly at higher levels. A better method would be to map it quickly to an intermediate object by using SET (eliminating normal object creation overhead). Then, what you must maintain is the 1to1 map between the DELECT and the object words. (Of course, you are right that the names may vary, such as action word above.) You could even make an object that is the definition that creates the DELECT block and the object name block. That would be a good method I think. And of course, the other approach may be to modify DELECT to collect the same datatypes into blocks. So, you would have all the INTEGERS, TUPLES, PAIRS, etc. We would need to think more about how to best do that to avoid a lot of GC. Carl 12:13, 9 July 2007 (EDT)
So, after some chat on AltME, a possible solution is (also answers previous question):
text-style/spec: [
text: string!
color: tuple!
color-back: tuple!
text-rich: block!
action: block!
; ...
]
delect-object: context [
; ...
text: get text-style/spec
; ...
]
; ...
; result from DELECT
set style/spec next delect-block
; ...
-Gabriele 01:36, 10 July 2007 (EDT)
7) Do we want to use the size, shrink, stretch model for resizing?
This is an interesting model used by TeX when doing justification. Applied to faces, it would be like this: a face has a "normal" size, that is considered the "best" size for that face. Then, it has an amount of "shrinkability" - how much smaller than that it can get. If size is 100 and shrink is 10, then it can go as low as 90, but not lower. Then, it has an amount of "stretchability" - how fast it can grow in size; the difference with the shrinkability is that stretchability is not limited: if size is 100 and stretch is 10, it can still go to 120. Faces with higher stretch grow faster than faces with lower stretch. TeX also introduce "infinite" stretchability, that is the ability of a face to take over all the space so that the other faces do not stretch at all. TeX has actually four levels of infinity, so that a higher level infinity takes over all the space of lower level infinities. At the same level they share the space. The computation for the resizing would be as follows:
; assume face/size, face/shrink, and face/stretch
; assume a sum-of function that is basically a foreach that does a sum,
; just to avoid cluttering the example
; let's pretend we are working on one axis only (which is mostly true in practice)
container/size: sum-of face container/pane [face/size]
container/shrink: sum-of face container/pane [face/shrink]
container/stretch: sum-of face container/pane [face/stretch]
; note, they don't need to be recomputed at each resize unless they change (update event)
either container/size > available-size [
; must shrink
ratio: available-size - container/size / container/shrink
; ratio should never be < -1. we assume available-size has been constrained already
; by the container's shrinkability
foreach face container/pane [face/actual-size: face/shrink * ratio + face/size]
] [
; must stretch
ratio: available-size - container/size / container/stretch
; ratio can be > 1
foreach face container/pane [face/actual-size: face/stretch * ratio + face/size]
]
I will make an example of how to handle infinity levels if you are interested (it gets a bit more complicated, but not much). Gabriele 03:35, 29 June 2007 (EDT)
The metric is: will average web-page authors be able to understand it? They know how HTML tables work - so if we can be close to that model (but more powerful), the more they will understand it. I want to be sure that most people can use VID. So, the best way to make decisions like this: we must write some examples and try it out. We will quickly draw conclusions and go from there. Carl 14:19, 9 July 2007 (EDT)
8) Do we want faces to have their own margins and padding?
After doing proto 1, I think it's probably a good idea to have at least per-face margins. Padding is probably not necessary (any padding can be done by the look object and does not influence the layout process). One big reason is that when you have a scrollable area and a scroller you usually don't want any space between them. What do you think? Gabriele 03:36, 16 July 2007 (EDT)
9) Do we want a "spacer" style that just eats space?
It probably does not need to be accessible as a face by users (although there may be uses for it...), mainly a way for the container to remember when resizing something like PAD command has been used in the dialect. But we can also make it a real style, then PAD command (etc) is not necessary anymore. Gabriele 04:14, 29 June 2007 (EDT)
That may be fine, especially if faces are efficient and minimized for such cases. Carl 14:22, 9 July 2007 (EDT)
10) How to call "spacer" styles?
In the current implementation, we have three different spacer styles: skip, space and filler. skip just skips the current cell, and it does not take space. space takes a small space and. filler is greedy and takes as much space as possible (not too much in the current implementation - we can change that), normally used to push things to the left or to the right and so on. Gabriele 14:28, 14 July 2007 (EDT)
This is a high level, internal specification document. It is intended only for the development team at this time. Please do not publicize it or publish links to it. See also VID Implementation Notes.
Introduction
We have learned a lot over the last 6 years of using VID. We have learned not only the strengths of the dialect approach to describing graphical user interfaces, but also some of the weaknesses when there is perhaps too much freedom for new users.
This project is to design a new VID for REBOL 3. Yes, we have limited time to build it, but I think we have a good idea what we want, and how best to construct it. That is the advantage of 6 years of experience.
It should be noted that there are a few alternative approaches to VID in the world today: RebGUI, GLayout, and others. We need to examine those designs closely to find ideas that may be of benefit in VID 3. For example, the RebGUI dialect is more regular, making it simpler for users. That may be a good thing, and we may want to do that.
I have no strict requirement as to how we implement VID 3, and if synergy or collaboration with other projects is possible, we should take that into account. However, in the end, it is us that must be satisfied. Only with that will VID 3 go far.
And finally, this is a draft document, and I am sure that I missed a number of important areas. If something is missing, don't make any assumptions about that. It could be a mistake. We can update the document to include it.
General Goals
Here is a short summary of the top level goals for VID 3. I know each of these sounds rather general, but each one is deeply significant to me. For this document, I will refrain from a deep analysis of the importance of each one.
VID 3 must be:
- Powerful - to create most types of GUIs used in modern applications.
- Clean - less cluttered and "more regular" dialect format.
- Simpler - less complex implementation method.
- Fast - very fast at layout, display, and update.
- Small - no program bloat and a smaller memory footprint (than R2).
- Pretty - we need an updated graphical look and some level of skins.
Of course, stated again for emphasis, the ultimate requirement is that we must be satisfied with the new VID design and implementation.
Dialect Syntax
Although there are a few small changes that we should make, I think the general syntax for VID has been good.
General Form
The syntax in pseudo PARSE format is:
ANY [ var: [ command | style ] ]
where command is:
command-word ANY args
and style is:
style-name ANY [ attribute | keyword-option ]
Command-Word
Commands are keywords of the VID dialect. They have the highest precedence in context (meaning you cannot use a style that has a command name). See the section below for more.
Style-Name
A style-name is also a word, but it is part of a different context then commands. A list of standard styles is provided later in this document.
Attributes
A attribute is a datatype that specifies a modification to a style.
In this example:
text "This is a line"
the quoted string is an attribute of the type STRING!, and the TEXT style understands its meaning.
This form is similar to that of functions, so it is quite natural for users. Where it differs from functions is that the datatype order is not position dependent, so users need not care about that detail.
You are free to write:
text "This is a line" bold
or
text bold "This is a line"
The datatypes allowed for attributes are:
- integer - width in pixels
- percent - width in terms of the bounding panel
- pair - size of style
- string - text of style
- tuple - a color for style
- logic - an initial state
- file - a file link or source location
- URL - a URL link or source location
- block - action code or sub-layout
- image - an image as style background
- word - a keyword such as BOLD or CENTER
- paren - a computation
There may be a few others that I missed. Let's add them.
Options
An option is a special keyword followed by a specification block.
For example:
image effect [colorize blue]
text-list data list-values
EFFECT and DATA are the option keywords, followed by their data.
Variables
Users can put set-words in front of commands and styles to use within their code to refer later.
n-list: text-list data names
In R3, variables will be bound into the context of the layout. This is method is similar to an object definition and helps users prevent leaky layouts where their internal variables change global values.
Expressions
In R2 VID allowed arbitrary in-line expressions like:
image i-size + 50x100
Advanced users may like this format, but for most beginners who are just learning to parse VID, it may make the dialect overly complicated.
An alternative is to require parens for such expressions, similar to the COMPOSE function:
image (i-size + 50x100)
We can certainly should discuss this matter soon.
If we elect to do it, then we can still allow words and paths to do their value lookup.
Commands
I'm not going to specify the commands in detail at this time. They are discussed in other documents.
However, I think it would be good to remove problematic or rarely-used commands, if at all possible.
Another choice would be to move general setup commands like SIZE into a special OPTIONS block.
options [size: 500x500 origin: 10x10]
This has the advantage of removing keywords from the dialect, which is normally a good thing.
Faces
The object created by a style is a FACE; however, the definition of a face is extensively modified from R2 (although it is likely that you will be able to emulate R2 if you need to.)
Similar to the reduction of the PORT object a FACE object is grouped into primary categories of sub-objects.
At this time, I do not want to specify the exact fields, because it is too early to accurately know which are needed, but here is a rough idea:
- gob - the graphics object (offset, size, type, panes)
- feel - object that holds engage, etc.
- look - object that describes the look of the style
- style - name of style
- state - holds the state variables for this instance
- access - accessor functions
Style Summary
I list the styles here because I want to be sure we agree on the range of styles that we need.
Of course, more styles can be added later, including advanced composite styles.
- Text Styles
- text - normal text
- h1-h3 - predefined headings
- note - text with border, optional icon
- label - special markers (field labels)
- info - boxed text (like a field but no input)
- Input Field Styles
- area - paragraph input (with wrap option)
- field - line input (with hide option)
- combo - field with dropdown choices
- Image Styles
- box - simple box with color or effect, borders
- image - simple image with effect, borders
- icon - image text (below, above, beside), borders
- arrow - directional choice, alone or boxed
- led - indicator
- Button Styles
- button - single action
- toggle - single action, dual state
- rotary - dual action, multi-state (up/down arrows), value ranges
- check - like check-line, optional text
- radio - link radio-line, optional text
- Range Styles
- slider - drag bar
- scroller - proportional slider with arrows
- range - arrow to bar with graduated markings
- progress - with optional % done number on top
- Divider Styles
- bar - simple divider
- Groupers
- panel - subpanel with optional name, tabs, borders, scrollbars
- group - auto-grouping for panel, fields, checks, radios, etc., row major
- List Styles
- list - multiple rows and columns of any type (e.g. icon, text, text)
- text-list - list of text lines (simple)
- icon-list - list or array of icons, optional text (desktop)
- table - list with cells, headings, dividers, etc.
- menu - menu lists
Note that any list can be made modal with DROP keyword. For example, a text drop list is:
text-list drop data [...]
Hot Styles
Most of the above styles can be made hot (react on an action) by specifying an action block.
text "Click Me" [click-me]
In this case, the text is allowed to change its appearance to indicate that it is hot.
Groupers
All of the above styles are obvious, but PANEL and GROUP require more explanation.
Panel
A panel is a collection of styles. It allows you to create a sub-layout within a layout, and then to use the panel as a container.
panel [
label "Name" field
label "Email" field
button "Submit"
]
If you provide a name, by default, you will get a border box with title (like HTML fieldset). You can also specify a scroll bar to easily create a scrolling pane of styles.
panel "Properties" scroll [
button "Faster"
...
]
See below for tab panels.
Group
A group is a way of automatically organizing a set of styles. It deals with more than just the layout, it can also form a relationship between the faces within it.
For example:
group [
radio "Stereo"
radio "Mono"
radio "Surround"
radio "Muted"
]
will layout the radio buttons, but also make them mutually exclusive relative to each other.
Groups can also be used to specify automatic return for repeating patterns:
i-form: group 2 [
label "Name:" field
label "Email:" field
label "Age:" field
]
Groups also provide another implied features. The values of faces can be kept together.
For example, in the above group, all of the values of the fields can be obtained or set by referring to the i-form variable:
i-data: get-face i-form
set-face i-form i-data
Here, the i-date contains only the field values, not the label values. This is an internal attribute of the FIELD style that specifies that its values can be "groupable".
Tabbed Panel
A tab panel is created by grouping a set of panels. You use the group style with a set of panels. For example:
group tabbed [
panel "Properties" [
button "Faster"
...
]
panel "Networking" [
...
]
panel "Sound" [
...
]
]
Context Layers
The handling VID code is mainly the process of identifying the meaning of the symbols in the dialect. In R2, this was done by scanning various lists to map a word to a command, keyword, or style. A better approach is to recognize that VID code is really just layers of contexts that can be identified directly if the block is bound the multiple contexts.
The contexts include:
- Commands - VID commands, such as SPACE, PAD, or RETURN.
- Master Styles - the master style sheet.
- Local Styles - temporary styles used just in this layout (or its children).
- Keywords - special attribute and property words, such as EFFECT or DATA.
- Variables - the names used to identify specific faces. In R3, these variables can be grouped into an object that is local to the layout (rather than always being global, as in R2).
The way R3 handles these layered contexts is unique. A new feature of BIND allows us to bind to multiple contexts at the same time. The precedence of the binding is determined by the order of the list.
This feature allows us to identify the commands, keywords, master styles, local styles, and variables all at the same time. This should reduce the code required to implement VID as well as make layout faster.
Event Levels
Events are handled at different levels. Here is the full range with some suggested names for each:
- Trigger - this is the lowest level of the event system where the user clicks on window at location of button, even system maps event to a face. This level is important in the fact that modes and filters can be applied to the event stream to control what events are sent to what faces.
- Reaction - the next level up activates the GUI behavior of the button, such as changing its up/down appearance. This is mainly done for visual feedback purposes. This is commonly handled by the face engage feel function.
- Action - based on the behavior of the button, it initiates an action that may apply at the GUI level alone (such as scroll a list), or any other function. In R2, this is the level most applications used to interact with application code, but in many cases the action is to simply store the current state of a GUI object into an application variable. In those cases, the next level is more suitable.
- Transaction - an action can collect and validate an entire "form" then submit the form to the application as an atomic transaction unit. This is the web model that has been proven to satisfy a large number of uses. The state of the display is relatively independent of the state of the application. For such displays, the individual actions for the GUI are not relevant to the application, and can be handled at the GUI level alone. Upon a key GUI event, such as clicking a submit button, the application is sent a submit event along with the necessary data. If in addition to layout information, the GUI is also provided with field validation information, even those details can be handled by the GUI and not the application itself.





