VID Extension Kit - User Manual (unfinished)
Author: Henrik Mikael Kristensen Version: 0.0.5 Date: 10-Oct-2009 Revised: 10-Jul-2010
This document only fits build 005 and upwards.
This manual is under construction. Please wait for it to be completed before reading it. Thanks.
The VID Extension Kit extends the basic capabilities of VID for REBOL 2, while fixing some style bugs. Some concepts are added and replaced, which makes it not 100% compatible with VID.
This kit only works with REBOL 2, not REBOL 3.
There are two famous alternatives to VID:
|RebGUI||by Ashley Trüter, it is a complete rewrite from the ground up to provide a new layout engine and a new style system. It can be found here.|
|GLayout||by Maxim Olivier-Adlhoch, it replaces the VID dialect layout while keeping its basic feature set. It can be found here.|
In this document, certain typefaces are used:
FACE-STYLE-NAME are written in uppercased fixed width font.
function-name are written in lowercased fixed width font.
Program code is written in green boxes in fixed width font.
All screenshots are grabbed from Windows XP.
To use the latest build of the VID Extension Kit, use the following line:
Note that the URL might change, so you may want to download it locally.
And you are ready to go! Be ware that you can't 'do the file again in the same console session.
This URL will change in the future. When using it a lot, please download it for local use.
Creating a window happens with the make-window function. This creates a resizable layout with tab navigation and various other features.
main: make-window [ across h3 "Main Window" return bar return label "Enter Name" field return ok-cancel ]
To view the window:
The use of LAYOUT provides a standard old-style VID window without keyboard navigation, resizing and popup menus won't work.
There are a few additions to the dialect to facilitate easy access to resizing, alignment, face event handling (actors) and face validation. For basic usage, refer to the original VID Manual.
The are now words that are available per style for resizing, face setup, actors and defaults:
Some faces have settings that are complex enough to warrant a setup word in addition to setting colors, size, content, etc. This for example counts for lists and panels with multiple panes.
Setup can vary between styles, as some are simple data blocks, where others are complete dialects, which describe complex aspects of the face behavior. This is wholly defined by the style in the setup-face* accessor and can in some cases be set using the setup-face function.
view make-window [ panel setup [ general [ h1 "General" ; ... General setup layout here... ] network [ h1 "Network" ; ... network setup layout here... ] printer [ h1 "Printer" ; ... printer setup layout here... ] ] ]
This list style has not yet been implemented but will be in the near future.
people: [ "Luke Lakeswimmer" 24 "Han Sulu" 31 "Anakin Lakeswimmer" 46 ] view make-window [ list-view setup [ source people header [name age] sort age descending scrollers [vertical horizontal] ] ]
A face which can have a value set, can also have a separate default value, using the default keyword.
When using the reset-face function, the default value is set.
The value you set in layout can be seen as an initial value, one that only occurs when the layout has been created.
This will not set an initial value in the field, but when the button is clicked, the field is set to its default value.
view make-window [ field default "Default value" button "Set default" [reset-face back-face face] ]
This example shows that an initial value is different from a default value.
view make-window [ field "Initial value" default "Default value" button "Set default" [reset-face back-face face] ]
Defaults can be applied both to single faces and whole panels.
Align occurs at layout time and at no other time. It determines how a face should be aligned inside its parent face. Since it only aligns at layout time, a face that is right aligned right when the window is displayed, may no longer be right aligned, when the parent face or the window is resized.
Not using the align keyword results in using the default alignment for the style. If there is no align information at all, then the layout engine will not attempt any alignment. The default for VID-FACE is none.
Alignment happens with a block of up to four words, made up of five words:
|TOP||This is default and places the top edge of the face near the top edge of the parent face, minus origin spacing.|
|BOTTOM||This places the bottom edge of the face near the bottom edge of the parent face, minus origin spacing.|
|LEFT||This is default and places the left edge of the face near the left edge of the parent face, minus origin spacing.|
|RIGHT||This places the right edge of the face near the right side of the parent face, minus origin spacing.|
|CENTER||This places the face at the center of the parent face. It's the equivalent of using top, bottom, left and right at the same time.|
At no time will the alignment attempt to place the face edges outside the parent face, unless there is too little room for the face.
You can freely combine the words. A combination of top and bottom or left and right will respectively place the face at the vertical or horizontal middle of the parent face.
The align word is also used for aligning text. Giving a word instead of a block, will align the face text instead of the face itself.
button align [right bottom]
button align [left right]
button align [top bottom]
button align [center]
Fill occurs at layout time and at no other time. It determines whether to fill the remaining space from the current offset to the edge of the parent face minus origin spacing. This is given as a pair! value of either 0 or 1 in the specified direction. For example 0x1 means fill remaining space vertically.
When using a negative value, the fill happens in the opposite direction. For example 0x-1 means fill from beginning to bottom edge of this face vertically.
Not using the fill keyword results in using the default fill for the style. The default fill for VID-FACE is 0x0.
In these examples, a standard BUTTON of size 100x24 pixels is used.
button fill 0x0
button fill 1x0
button fill 0x1
button fill 1x1
This example shows that filling takes place from the offset point of the face to the right and/or bottom sides of the parent face.
at 30x0 button fill 1x0
Examples using negative fill:
button fill -1x0
button fill 0x-1
button fill -1x-1
The alignment process does a few things to automate alignment of faces, but some alignments are not possible to do, since faces are generally not aware of the positions of each other in this resizing system.
When using align on two faces that are right next to each other, both faces will ignore each other and simply align to the parent face:
view make-window [ across button 150 align [right] button align [right] ]
Also, the layout engine will still apply the normally necessary cursor movement in preparation for placing the next face, even if the specified alignment will not place the face near that position at all. This can be seen here, where the second button should be at the location of the first button:
view make-window [ panel 200x200 [ across button align [bottom] button ] ]
The way to solve this, is to use the at keyword, to place the face that was unintentionally moved:
view make-window [ panel 200x200 [ across button align [bottom] at 0x0 button ] ]
When using fill, the face will be filled unconditionally to the edge of its parent, and thus may layout on top of other faces:
view make-window [ across box "Red Box" red fill 1x0 box "Yellow Box" yellow fill 0x1 return button 150 ]
As seen in the two examples above, it's also sometimes necessary to specify the size of the face. This goes for cases where you can't use fill to get the size you want.
You can overcome some of the previously mentioned problems by purposely describing two faces in the reverse order. This is useful, if you want to layout a variable sized face before a fixed size face without having to worry about adapting the faces to the parent face size. With alignment information, the faces will be swapped:
view make-window [ across panel 500 [ ; panel size is larger than the default size of button and field button align [right] ; fixed size face field fill 1x0 align [left] ; variable sized face ] ]
This reverses the tab order, but you can fix that by reversing the objects in the panel PANE block.
I would consider this a hack, as there is currently no official method to do this directly in layout.
Spring has to do with resizing only and is processed every time the layout is resized. It is not used during layout.
Springs are atomic: They are either there or they are not. There are no inbetweens or weighting for springs. There are four springs, one for each side of the face, you want to apply springs to.
When applying a spring, the distance between the face and the parent face varies linearly 1:1 with the parent face edge on the same side. When two springs on opposite sides are applied, the distance varies linearly 1:2.
Not using springs, causes the use of the springs set by default for the style. The default springs for VID-FACE (the overall base face for all styles) is [right bottom].
|TOP||This places a spring above the face. It's useful in cases where you want a face to stay near the bottom of the parent face.|
|LEFT||This places a spring to the left of the face.|
|RIGHT||This places a spring to the right of the face. This is default for VID-FACE.|
|BOTTOM||This places a spring below the face. This is default for VID-FACE.|
When using the top and bottom springs or left and right springs in combination, similarly to align, the face can be kept near the center of the window. More accurately, what happens is that the spring adjustment is divided evenly in half for both sides.
This causes the buttom to vary its distance to the top of its parent:
button spring [top]
This is default for VID-FACE:
button spring [bottom right]
This varies the distance between the spring and the left side of the parent face:
button spring [left]
This causes the button never to resize:
button spring [top bottom left right]
This causes the button to follow the parent face size on all sides:
button spring none
Combining fill, align and spring provides the full subset of features necessary to create a fully resizable layout.
This is a base layout that works well with putting a list in the left side and content selected from the list in the right side, such as what the style browser uses. The layout consists of a left panel that only adjusts vertically and a right panel that adjusts vertically and horizontally without moving its offset:
view make-window [ across panel spring [right] [ box 100x200 spring none blue "Left" ] panel spring none [ box 200x170 spring none green "Right" box 200x28 spring [top] red "Bottom" at 0x0 ; to avoid blank space box 40x30 orange align [top right] spring [left bottom] ] return box 0x30 fill 1x0 spring [top] gray "Button Area" ]
- The first panel has only a right spring, to keep it from resizing up horizontally. Since there are no other springs, the three other sides will follow the size of the parent sides respectively.
- The second panel has no springs in order to follow all four sides of the parent. Note that this means the distance to the parent sides is therefore always constant.
- The blue box has no springs, so it wholly follows the size of the parent panel. There is no alignment, since it sits in the upper left corner of the parent face and there is no fill, as the size of the box defines the size of the parent face, so it automatically fills the whole parent face.
- The green box must follow all sides of the parent face.
- The orange box will align to the top right on top of the green box. It will not resize due to the left and bottom springs. Instead the springs will cause it to stay at the top right of the green box. Note the use of at 0x0 before the orange box is declared; This is to avoid the box creating a space between the bottom of the green box and the top of the gray box as the layout engine normally would. Placing the orange box at 0x0 prior to alignment will not cause the problem to occur.
- The gray box has a fill to calculate the horizontal size in relation to its parent face, which is the window. It has a specific vertical size, and the horizontal size is simply ignored (here set to 0). The top spring makes sure it stays at the bottom. There is no alignment, because the initial position happens to be correct for the box in this layout.
After resizing it down:
After resizing it up:
The use of actors makes it possible to perform an action or a function on a particular event. All actors are prefixed with ON-*. A quick example:
view make-window [ field on-resize [probe face/size] ]
This will print the size of the face in the console, every time the face is resized.
There are many actors being run all the time in many different places. Note that all actors are run right after the action the actor is relevant for, has completed. This is so you can use the resulting values in the actor, such as the new face size after the face has been resized.
|on-setup||When using setup-face.|
|on-key||When pressing a key while the face is in focus.|
|on-tab||When pressing Tab, while the face is in focus.|
|on-return||When pressing Return, while the face is in focus.|
|on-click||When clicking on the face with the left mouse button.|
|on-alt-click||When clicking on the face with the right mouse button.|
|on-set||When using the set-face function.|
|on-clear||When using the clear-face function.|
|on-reset||When using the reset-face function.|
|on-escape||When using the Escape key, while the face is in focus.|
|on-focus||When the face is focused using the focus function.|
|on-unfocus||When the face is unfocused using the unfocus function.|
|on-scroll||When the face is scrolled using the scroll-face function.|
|on-resize||When the face has been resized.|
|on-align||When the face has been aligned.|
|on-time||When a face/rate time event has occurred.|
|on-search||When using the search-face function.|
|on-validate||When using the validate-face function.|
|on-init-validate||When using the init-validate-face function.|
|on-enable||When using the enable-face function.|
|on-disable||When using the disable-face function.|
|on-freeze||When using the freeze-face function.|
|on-thaw||When using the thaw-face function.|
Actors are stored in an object inside the face in face/actors. When a particular actor does not have any functions attached, the value is none.
When functions are attached, they are stored inside a block for the actor. This is similar to event functions attached to a window using the insert-event-func or remove-event-func functions.
To attach a new actor to a face, use the insert-actor-func function:
get-face-size: func [face] [ probe face/size ] win: make-window [ f: field ] insert-actor-func f :get-face-size view win
This method is often used inside face/init to quickly add specific actors to surfaces of a specific style.
Note that the function is appended to the tail of the block of functions. This means that successive uses of insert-actor-func will cause the functions to be run in the order they were appended.
You are free to add as many functions to a particular actor for a face as you like.
Similarly to remove the actor:
remove-actor-func f :get-face-size
Face actor functions may have up to four arguments in a specific order:
face ; the face object event ; if an event! was generated during the use of <tt>act-face</tt>, otherwise this is <tt>none</tt>. value ; the value that will be passed by using <tt>get-face</tt> on the face actor ; the word of the actor being run, e.g. <tt>'on-resize</tt>.
The validate keyword has a block argument that holds the code to determine what the correct validation for the face is. You can return any value, but it's processed as logic! internally. When logically true, the face is valid. If false, the face is invalid.
Validation occurs on unfocusing the face, or forcibly when using the validate-face function.
field validate [not empty? get-face face]
Validation makes the most sense, when using the VALID-INDICATOR style for validation as well as the presence of a TRUE-BUTTON face, such as that seen in the USE-CANCEL style. This allows the VID Extension Kit to perform validation as automatically as possible.
Whenever a window is opened, initial condition validation is performed. This ensures that faces that are required for validation have their VALID-INDICATORs properly marked up. Pre-filled faces are validated according to validation rules and will be marked up as valid or invalid.
This makes the form ready for initial use.
Initial condition validation can also be forced by using the init-validate-face function.
This uses the VALID-INDICATOR style to indicate whether the face is valid or not. Note that the VALID-INDICATOR must be positioned immediately after the face to be validated.
view make-window [ across field validate [not empty? get-face face] valid-indicator ]
When initial condition validation is performed, before the fields are touched by the user:
After validation (by pressing tab):
The required keyword is part of validation. When used, a validation failure will cause an overall error, that considers the form not to be valid for submission and any attempt at submission will be denied, when clicking a TRUE-BUTTON. If not used, validation may fail, but is not critical and the form can still be submitted.
When the initial validation occurs, the indicator will show a black dot instead of a transparent dot.
The required keyword does not work without a validate block.
Field may not be empty:
field required validate [not empty? get-face face]
When used in combination with the VALID-INDICATOR style, you get more substantial results:
view make-window [ across field required validate [not empty? get-face face] valid-indicator ]
When initial condition validation is performed, before the fields are touched by the user:
After validation (by pressing tab):
After validation failure (by deleting content and pressing tab):
Whenever the validate keyword is used, a validation object, called face/valid is created for the face:
make object! [ action: ; the block to be run for validation result: none ; the result of the validation required: false ; whether the face must pass validation ]
face/valid/result can be one of 4 values:
|NOT-REQUIRED||The face is not required for validation. This is used if the face fails validation or does not require validation, because it's disabled. Failure is not critical. This would be displayed in the VALID-INDICATOR.|
|REQUIRED||The face is required for validation, but has not yet been validated. This would show after opening the window. In this case face/valid/required would be true.|
|INVALID||The validation failed critically for the face and this would block closing the window.|
|VALID||The face has been validated and is valid.|
The submission process is manually handled by you, but in order to get there, the VID Extension Kit can use specific submission buttons to let you know in a simple way whether it's OK to submit the form.
This takes place in the form of a TRUE-BUTTON. You can use the TRUE-BUTTON directly, but a derivative located inside one of the standard button panels, such as USE-CANCEL or OK-CANCEL, offers a more complete solution.
view make-window [ across field required validate [not empty? get-face face] valid-indicator return true-button "Submit" ]
Before touching the form, the initial condition validation has been done:
When clicking the submit button without entering anything in the field, the window remains open and the error is indicated:
When entering a string in the field and then pressing the submit button, the window is closed, because the form is now valid.
To see, how the submitted result is handled, please visit the Returning from View Windows and Informs in the Special Style Features section below.
This example shows a simple form:
empty-field: [not empty? get-face face] numeric-field: [attempt [to-integer get-face face]] view make-window [ across label "First Name" field required validate empty-field valid-indicator return label "Last Name" field required validate empty-field valid-indicator return label "Age" field validate numeric-field valid-indicator return label "Comment" field validate empty-field valid-indicator return use-cancel ]
If you click "Use" without entering data, the window will stay open and indicate which fields are invalid and focus the first invalid field:
This example is the same, only there are ENABLER faces in the layout:
view make-window [ across label "First Name" enabler true field required validate empty-field valid-indicator return label "Last Name" enabler field required validate empty-field valid-indicator return label "Age" enabler field validate numeric-field valid-indicator return label "Comment" enabler field validate empty-field valid-indicator return use-cancel ]
Read more about the ENABLER style in the Special Style Features section below.
Due to limitations in next-face, back-face and traverse-face, it's not possible to validate across several panes in a PANEL or TAB-PANEL. You can only validate the currently visible pane.
The origin keyword is always a pair and is usually given during layout time, either by you or internally by the LAYOUT function. This allows alignment to use the correct distance to the bottom and right edges of the parent face.
Origin is set for the parent face and then used to place faces inside that parent face. The fill and align keywords take origin information into account.
view make-window [origin 4 button button]
view make-window [origin 8 button button]
This defines the spacing between faces during a normal layout situation. The standard size is 4x4 pixels. It can be defined both as an integer!, used in both directions, or as a pair!.
Space is set for the individual panel levels. This means when defining a new panel, the spacing inside it, will be the default 4x4 pixels.
Space has no effect on spring, align or fill.
view make-window [space 4 button button return button button]
view make-window [space 12 button button return button button]
The at keyword is used to place faces at specific locations during layout time. The input is a pair!. When this happens, layout is temporarily moved to that face until the next face is to be layed out.
This affects fill, but is overridden by align. It does not affect spring.
The aspect keywords sets fixed aspect for the face. In combination with fill, the face will be resized with a fixed aspect ratio. Fixed aspect ratio is usable in styles where you need to display a scalable print preview or a picture and you don't want to stretch it unintentionally during resize.
The aspect ratio itself is derived from the original size of the face. This is useful in cases where you would set the face to a specific image and want to keep the aspect ratio fixed throughout the time this image is displayed. When switching to a different image, the aspect ratio would change, but again remain fixed there during resizing.
There are 4 modes for fill when aspect is enabled:
|0x0||Fixed aspect resizing is ignored.|
|0x1||Fixed aspect calculation is done according to vertical size of parent-face as reference. The result is a face that stays vertically in the confines of the parent face. If the horizontal size of the face is bigger than the parent face, it will go outside the parent face.|
|1x0||Fixed aspect calculation is done according to horizontal size of parent-face as reference. The result is a face that stays horizontally in the confines of the parent face. If the vertical size of the face is bigger than the parent face, it will go outside the parent face.|
|1x1||Fixed aspect calculation is done according to both vertical and horizontal size of parent-face as reference. The result is a fixed aspect face that stays within the confines of the parent face no matter the aspect of the parent face.|
This will make the box center and scale between the top and bottom and the horizontal size is then scaled for aspect. This means if the aspect ratio is lower than that of the box, the box will be clipped in the sides, but stay centered.
view make-window [ box "+" red edge [size: 2x2] ; for visibility spring [left right] ; horizontal center fill 0x1 ; fill to top and bottom in parent-face aspect ; fixed aspect for this face ]
Tab navigation involves simply using the Tab key or Shift-Tab key combination to tab to a specific face. If the face is an editable field, it's focused for editing. If the face does not contain editable text, you can still use the Space key to invoke the action of the face through a simulated mouse-click, or whatever key navigation is possible through the key-face* accessor for the face.
- If a face normally can be tabbed to, but is disabled, the face is skipped.
- If a face that is already tabbed to and then disabled, the face will be skipped, the next time you tab over it.
- If a face that is already tabbed to and then hidden, depending on where it exists, a different face will be tabbed to. This depends on the style used.
Tab navigation is possible thanks to these features:
- make-window installs a set of 4 faces that works as a blue focus ring. This focus ring sits at the tail of the window pane, and it's moved, shown and hidden by internal functions. It also stores the tab-face for the window. Each window has its own tab-face and moving between several tab navigated faces should not cause focus to be lost.
- Global window detect function that serves as the single place to handle the Tab and Space key. There is no need for you to make special arrangements to get tab navigation working.
- next-face and back-face functions help determine which face to focus next. This is done using their /deep refinements.
- Tabbed flag helps only selecting faces that are suitable for tab navigation. You want, for example, to skip the BAR face or text labels, when tabbing.
- The key-face* accessor allows you to provide custom navigation options for a single face. DATA-LIST for example, allows the use of cursor keys to navigate up and down in a focused list.
- tab-face in each window face to store the current tab-face. Multiple windows have each their own tab-face.
- Various rules come into play, helping to determine which face should be focused on window open using the init-window function, the form validation process or panel change.
The order of tabbing is decided from the order of faces in the face tree. If the face pane contains 4 faces, the first one is focused first, then the second one and then the third and so on.
If any face has a sub pane, each sub pane is investigated for focusable faces and it will focus those. The pane level for tabbing can be infinitely deep.
Tabbing backward goes exactly in reverse of tabbing forward.
Tabbing is done using the next-face/deep and back-face/deep functions.
At this time, it's not possible to set a custom tabbing order.
The tab-face for a given window can be reached through the window root face, either by accessing the window face directly, or by using get-tab-face on a face in the window, if you don't know the window face. The tab-face is simply the face currently in focus by the focus ring.
The focus ring consists of four separate faces, both for speed, as four small faces are faster to draw than one big face, and also due to limitations in the event model in that you can't click to focus through a face with the mouse.
The focus ring color is determined from system/view/vid/vid-colors/focus-ring-color and the focus ring is two pixels wide on all sides and sits exactly around the face that is tabbed to, not covering it.
Use Tab to navigate between button and fields. Use Space on button to clear the fields.
view make-window [ panel [ across label "First Name" field return label "Last Name" field return ] button "Clear" [clear-face back-face face] ]
Currently the focus ring will draw on top of other faces, if the face that is tabbed to, is partially or entirely obscured.
Dialogs are simple windows that display information and do not pass important information back, when closed. All dialogs are blocking, which means the program does not continue until the dialog is closed.
|notify||Notifies with a message and a close button.|
|alert||Notifies with a message and a close button, but stronger emphasis.|
|warn||Notifies with a message and a close button, but even stronger emphasis.|
|about-program||Shows a dialog with information about the current script, grabbed from the script header.|
There are various types of requesters. All requesters are blocking, which means the program does not continue, until the requester is closed. When closing the requester, a value can be returned, depending on the type of requester.
|question||Asks a question and allows for a true or false response.|
|important-question||same as question but with more emphasizing layout.|
|request-file||This requests a file using the OS native file requester.|
|request-dir||This requests a path and allows for creation of a new directory.|
|request-color||This requests a color from a set of RGBA sliders.|
|request-user||This requests a user name and password.|
|request-pass||This requests a password.|
|request-date||This requests a date from a monthly calendar view.|
|request-download||This requests one or more downloads.|
|request-value||This requests a value returned as a string.|
|request-rename||This requests a value returned as a string and displays the original value.|
|request-email||This displays an email send requester.|
|request-message||This displays a message send requester.|
|request-item||This displays a list of items to select. One item can be selected at a time.|
|request-items||This displays a list of items to select. Multiple items can be selected at a time.|
|request-find||This displays a search requester for searching text in a text-face|
|request-replace||This displays a search/replace requester for searching and replacing text in a text-face.|
All requesters are built using super styles, which are complex layouts that store their content in exactly one panel. These layouts are defined using stylize/master so they can be directly used in another layout.
The layouts are then passed to the request function, which through minimal code then builds the requester, passes content to it, attaches a button panel to it, performs required validation, etc. before it's opened.
When it's opened, it behaves like any other layout, created using make-window and using the attached button panel, based on what button style is clicked, one of several things can happen:
|true-button||This will pass true to window-face/data unconditionally and close the window. This button is always placed to the left in the button group layout.|
|yes-button||This does the same as TRUE-BUTTON, except has the name "Yes".|
|retry-button||This does the same as TRUE-BUTTON, except has the name "Retry".|
|validate-button||This will validate the content and return the content to window-face/data if valid. If not, the window stays open.|
|send-button||This does the same as VALIDATE-BUTTON, except has the name "Send".|
|save-button||This does the same as VALIDATE-BUTTON, except has the name "Save".|
|use-button||This does the same as VALIDATE-BUTTON, except has the name "Use".|
|false-button||This will pass false to window-face/data and unconditionally close the window, while storing the content in the data word of the window face. This button is always placed to the right in the button group layout.|
|cancel-button||This does the same as FALSE-BUTTON, except has the name "Cancel".|
|no-button||This does the same as FALSE-BUTTON, except has the name "No".|
|close-button||This does the same as FALSE-BUTTON, except has the name "Close" and is always placed in the center of the button group layout.|
Certain button combinations exist in groups:
close cancel ok-cancel save-cancel use-cancel send-cancel yes-no yes-no-cancel retry-cancel
Tool windows are similar to requesters, but don't require that you must close them to obtain a result. Typically a tool window is tied to a face, which you are performing some kind of adjustment on.
An example of this is the COLOR-BUTTON style, that when clicked will open a color tool window with RGB sliders. When moving the sliders, the color will change in the COLOR-BUTTON in real-time. When closing the tool window, the value remains.
If multiple COLOR-BUTTON faces are present, clicking them while the color tool window is open will redirect the adjustment to the open color tool window, thus not allowing more than one color tool window open at a time.
A tool window is opened using the tool function, which accepts four parameters:
title ; the title of the tool window type ; the type as a word, referring to a specific super style content ; the content as a block or single value, which must match the type call-back ; a call-back function, which is run, when a change occurs inside the tool-window
Let's build our own variation of the COLOR-BUTTON to see how it works:
view make-window [ button [tool-color face] ]
|tool-color||This opens an RGBA palette.|
Tool Window construction is very similar to building requesters. All layouts are super styles.
A tool window will work, only when a call-back is described in one of its faces, so you have to decide when the callback needs to be used. The callback is bound to the calling face.
First you build a super style. It's possible to create standard layout windows and use those, but there is no guarantee in the future that those will work.
The VID Extension Kit does more to collect all information about colors, fonts, gradients in a single place for easy editing. This could be referred to as a skin system, but at this time, it globally affects all windows, and there is no system in place to handle multiple skins.
However there are formal descriptions of edges, colors and effects. These are made from a purpose driven scheme ("action color"), rather than a descriptive scheme ("red color"), to associate them with the styles in a way to allow higher level descriptions of a window face.
The color list exists in system/view/vid/vid-colors and is as follows:
vid-colors: context [ focus-ring-color: 20.120.230 font-color: reduce [black focus-ring-color - 75] important-font-color: reduce [white focus-ring-color + 75] body-text-color: black title-text-color: black field-color: snow field-select-color: yello window-background-color: 180.180.180 menu-color: window-background-color + 20 frame-background-color: window-background-color - 20 line-color: window-background-color - 100 menu-text-color: [0.0.0 0.75.150 255.255.255] important-color: 180.40.0 manipulator-color: 200.200.200 action-color: 200.200.200 true-color: 80.180.80 false-color: 180.180.180 action-colors: reduce [action-color action-color] disabled-action-colors: reduce [action-color - 50 action-color - 50] ]
A global shortcut to these colors is in ctx-colors/colors.
This concept is not entirely followed through. For example, much of the design of a button is stored in the BUTTON style itself. This is due to its nature of heavy use of face/feel/redraw, which constantly manipulates the colors and gradients directly.
Also certain styles may directly retrieve the calculated color when a face is created in layout, losing the reference to system/view/vid/vid-colors which prevents us from changing the color in real-time.
Many new functions have been created for easing the finding of faces in a big layout and managing windows and panes. They are the back bone for many of the new capabilities of VID, such as tab navigation. Most of these functions also end in *-face, but are not related to accessor functions.
The total list of functions with detailed descriptions can be found in the VID Extension Kit Function Reference.
There are many new styles, which can be seen in the VID Extension Kit Style Reference.
Also, there is a style browser, which can be invoked here:
Some styles provide special features that require an in-depth explanation.
Panels are capable of providing multiple panes. You can specify the panes directly in layout:
view make-window [ p: panel setup [ general [ h1 "General" ... General setup layout here... ] network [ h1 "Network" ... network setup layout here... ] printer [ h1 "Printer" ... printer setup layout here... ] ] ]
When using multiple panes, the panel is resized after the largest pane. When you want to switch the pane, you use SET-FACE on the panel, along with a word that matches the pane name:
set-face p 'printer
When only one panel is defined like normally, the pane name is 'default.
When getting and setting data, you most likely want to do it in large chunks, for example when doing it for many fields in a form.
Lists are much more capable and come in several levels of usage. Here is a brief list:
|LIST||Basic vertically iterated face|
|DATA-LIST||Provides a list with selectable rows, but no scroller|
|PARAMETER-LIST||Two-column list setup, meant for displaying object contents.|
Scrollers can automatically attach to faces that contain the SCROLLABLE flag.
This example demonstrates automatic attachment:
letters: [a 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] view make-window [ across data-list 100x300 with [source: :letters] scroller ]
You can then attach a second scroller, which will then control the horizontal movement for the list. We will then need to provide a more complex source.
view make-window [ across data-list 100x300 with [source: :letters] scroller with [fill: none] 20x300 return scroller 100x20 ]
Under normal circumstances, you should not need to customize scrolling, but it's possible to do.
Scrolling occurs internally using the SCROLL-FACE function. The SCROLL-FACE function calls the scroll-face* accessor in the face to be scrolled. Parameters for direction and step size are given.
The scroll-face* accessor then provides instructions for how the face to scroll should change, scrolling vertically or horizontally. For a DATA-LIST, scrolling vertically results in the data list starting index to be moved, while scrolling horizontally results in the subface for the data list to be moved to a new offset.
As a new concept, face interaction is the consolidation of handling all programmatic interactions with a face through their accessors. This is a rule that should be followed. Some faces generally expose three different attributes:
|Setup||The setup of the face. For a SELECTOR, that would be the buttons used. This only counts for faces that need to construct a specific layout inside themselves. PANELs and FACE-CONSTRUCTs do this too. This is handled through the setup-face function. The face would hold the value internally in setup.|
|Default||The default value of the face. For any face this is handled with RESET-FACE.|
|Value||This is the value set for the face. This is handled with SET-FACE and GET-FACE.|
A typical style setup for general interaction:
MY-FACE: FACE with [ access: make object! [ set-face*: func [face value] [ face/data: value ] get-face*: func [face] [ face/data ] setup-face*: func [face value] [ face/pane: layout/tight process value ] reset-face*: func [face] [ face/set-face* face/default ] ] ]
There are two new faces that provide balancing between two panels of faces. Given how they work, it's necessary to explain them here.
The balancer style is usually placed between two panels, but any resizable face can be used. When you pull it up or down, left or right, depending on the intended resize direction, which is autodetected, the panel before and after are resized so that the space that all three faces occupy remain the same. The balancer will not go beyond the edges of both panels.
The balancer only works properly, when the panels before and after it are placed logically before and after the balancer in the pane.
view make-window [ across box red "1" balancer box orange "2" ]
As calculated by the layout:
When moving the balancer to the right:
The resizer is also placed between two faces, but where it will resize the panel before it, the remaining faces that come after it, are simply moved. This style is extensively used in the list-header style to resize columns.
Like balancer, resizer only works properly when situated between the faces that need to be resized and moved.
view make-window [ across box red "1" resizer box blue "2" resizer box orange "3" ]
As calculated by the layout:
When moving the first resizer to the right:
When moving the second resizer as far to the left as possible:
The second face is not lost. It was simply horizontally sized to zero:
An ENABLER is a checkbox like face, that enables or disables a face that comes right after it. This is used in cases where a face needs to represent many values, such as in a form that allows mass-editing of table rows.
When a window is opened, all faces which have an enabler right before it, are disabled, requiring you to click the enabler to enable the face.
When an enabler is enabled, the following happens:
- The next face is enabled and focused.
- Initial condition for validation is set.
When an enabler is disabled, the following happens:
- The next face is disabled and unfocused.
- Validation is turned off for this face. If the form is validated, this face is skipped.
- The face is cleared.
view make-window [across enabler field validate [empty? get-face face]]
When the enabler is clicked, the field is enabled, becomes focused and you can type in it:
Typing in the field and then disabling the enabler again clears the field and disables it. The enabler is not tab focused, because it was clicked with the mouse in this screenshot:
Some styles are built to be used singularly in a specific type of window, such as dialogs and requesters. They are therefore entire layouts, put into a style, so they can be used in other layouts. Certain rules apply to super styles:
- They must consist of a single panel.
- They may not contain button panels to dismiss a window.
- Their content must be settable or gettable via a single set-face and get-face call.
The VID Extension Kit and its documentation is very incomplete, and so the work must continue on:
- Making it simpler, more consistent and cleaner
- Making more styles
- Fix a lot of bugs
- Finish documentation
Thanks must go to:
- Anton Rolls
- Brian Hawley
- Maxim Olivier-Adlhoch
- Graham Chiu
- James Nakakihara
- Carl Sassenrath
For their gracious help and assistance.