Implementing a GUI with REBOL/View
In my overview of REBOL, I skimmed across the capabilities of the REBOL/Core package. In this article I will give you a quick tour of the graphical version: REBOL/View.
The main addition to the basic functionality of REBOL/Core is the inclusion of a graphics engine that allows the programmer to produce a graphical user interface (GUI) in REBOL. The graphics engine provides an array of GUI widgets and the capacity for some nifty effects. A standard feature of REBOL/View is the Visual Interface Dialect (VID), which gives the programmer easy access to the graphics engine.
Face up to some jargon
In VID, standard GUI widgets such as buttons, labels, and images are called “faces”. Attributes of faces like text, colour, size and actions are called “facets”. Each face may have one or many facets, or none. Specific “keywords” set the general layout attributes. All of this information is wrapped up in a block and fed into the layout function.
For example, in my previous article I included a one-line script that loads the Melb PC logo into a window:
view layout
[image http://www.melbpc.org.au/pict/mpclogox.gif]
The view function is responsible for the actual screen display, in this case taking input from the layout function. Everything inside the layout block is VID code. The image face displays an image, and in this example it has one facet - the url http://www.melbpc.org.au/pict/mpclogox.gif.
The result of the layout function can also be stored in a variable instead of being passed straight into view, so that the following operates identically to the previous example:
melbpc_logo: layout [image http://www.melbpc.org.au/pict/mpclogox.gif] view melbpc_logo
A basic interface design
The previous article included source code for a REBOL file renamer that operated from the command line. Now I will put that basic functionality behind the GUI interface shown below.
What is this interface going to do?
- display the current working folder across the top
- display available files/folders in the left-hand scroll pane
- update current directory and file list when a new folder is chosen
- accept user input in search and replace boxes
- trigger the search-and-replace operation using the Go button
- display results of the search-and-replace in the right-hand scroll panel
- terminate the program using the Quit button
The first task is to describe the interface itself. Download rebname1.r, listed here:
REBOL [Title: "REBOL Renamer"]
my_path: system/options/path
files_data: []
feedback_data: []
change-dir my_path
if exists? %../ [append files_data %../]
append files_data sort read my_path
view layout [
across
label "Current Folder:"
lab_path: label to-string my_path
return
list_files: text-list data files_data
list_feedback: text-list data feedback_data
return
label "Search pattern:"
fld_search: field
return
label "Replace pattern:"
fld_replace: field
return
button "Go"
button "Quit" [quit]
]
The REBOL header.
REBOL [Title: "REBOL Renamer"]
To save space I’ve kept the REBOL header to a minimum - just some text for the title bar of the window. Usually you should include more information here (see the example in my previous article).
Set a variable my_path to the startup folder.
my_path: system/options/path
Retrieve a copy of the startup folder from the system variable system/options/path.
Initialise some variables.
files_data: []
feedback_data: []
The variables files_data and feedback_data are used as facets providing data to the text-lists list_files and list_feedback respectively. Both are initialised to empty blocks.
Set files_data to hold a list of files in the current working folder.
First use the change-dir function to make the current working folder the same as the folder stored in my_path. Now if the current working folder has a parent folder (that is, if the folder ../ exists) then add ../ to files_data. Finally append a sorted list of files in the current working folder to the files_data.
Change layout direction.
across
By default, REBOL/View adds each new face vertically below the previous one. Using the across keyword changes the layout so that new faces are added horizontally, to the right of the previous.
Add a label face.
label "Current Folder:"
A label face simply displays some text.
Make a variable lab_path to hold a face.
lab_path: label to-string my_path
In the next version, I will need to refer to the value of some faces by name, so I can assign them to variable names now. Notice the other faces also have variable names: list_files, list_feedback, fld_search, and fld_replace.
Display the data.
Use my_path as a facet for the lab_path text label. The to-string function converts the file value in my_path into a string suitable for display.
Make a line break.
Think of return as similar in function to the return key on the keyboard. In this case it makes a new row and moves the insertion point to the left - the next face will sit at the left of a new row. But if the layout direction is set to vertical, return makes a new column and moves the insertion point to the top.
<p class="noteheading">Time to experiment! </p>
Change the across keyword to below and see what happens.
What happens when you take out one or two returns?
Add a text-list face.
list_files: text-list data files_data
The text-list is a scrolling text panel.
Enable some user input.
fld_search: field
The user can type text into input fields.
And it wouldn’t be a GUI with out a button or two.
button "Go"
button "Quit" [quit]
Slip in some interactivity.
I will come to full interactivity in the next version, but this one is so simple I might as well do it now. Adding a facet including the command quit in a block adds this action to the click action of the button. Now when you click the Quit button, the window closes and the script terminates.
How do you run the program?
You’ll need REBOL/View to run the example - download from http://www.rebol.com/view-platforms.html.
There’s a couple of options. Try double-clicking on the rebname1.r filename in Windows Explorer and if REBOL/View is associated with the .r extension it will start the script in REBOL automatically. Otherwise open a command (DOS) window, change to the folder where you saved rebname1.r and enter rebol -s rebname1.r.
All being well, the screen should show similar to the screen capture above.
I think you’ll agree that this is a very quick and easy GUI to make. Of course, there’s a catch - it doesn’t do much, so it’s time to add some interactivity.
Now make it interact
That’s about it for the purely VID part of the example, the rest is ordinary REBOL code used to provide actions to some of the faces. Actions are blocks of REBOL code added as a facet to a face, and they are triggered whenever the default action of that face is triggered - usually a mouse click. I have already shown this in the first version above where the action block for the Quit button is [quit]. To avoid cluttering the VID code, I prefer to put as much action code as possible into separate functions.
A quick look at functions in REBOL
In their simplest form, functions are declared in REBOL according to the following template:
function-name: func [specification block] [code block]
The specification lists the arguments to the function. You may remember this example from my previous article:
add-up: func [this that] [this + that]
Function arguments may also be specified with a data type:
add-up: func
[this [number!] that [number!]] [this + that]
When a data type is declared, the function will only attempt to execute the code block if the right types of arguments are passed to it.
<p class="noteheading">Experiment again </p>
Type the last example in at the REBOL command line then try add-up 5 10 then add-up “five” “ten”. Do they both work?
The final revision
The final working version is included in the next listing (Download rebname2.r). Note that it is considerably longer than the first version because I have added several new functions to provide the actions.
REBOL [Title: "REBOL Renamer"]
my_path: copy system/options/path
files_data: []
feedback_data: []
do-update-files-data: func []
[
change-dir my_path
if exists? %../ [append files_data %../]
append files_data sort read my_path
]
update-text-list: func [current_list [object!]]
[
current_list/sld/data: 0
current_list/sn: 0
current_list/sld/redrag
current_list/lc / max 1 length?
head current_list/lines
show current_list
]
do-change-folder: func [selected [file!]]
[
test_selection: to-file rejoin
[my_path selected]
if dir? test_selection
[
my_path: copy test_selection
lab_path/text: clean-path my_path
show lab_path
clear files_data
do-update-files-data
update-text-list list_files
]
]
do-rename: func []
[
messages: copy []
foreach file read my_path
[
if not dir? file
[
if not none? find file fld_search/text
[
new-file: to-file replace to-string
file fld_search/text fld_replace/text
either exists? new-file
[
append messages rejoin
["FILENAME " new-file " ALREADY IN USE"]
]
[
append messages rejoin
["Renamed " file " to " new-file]
rename file new-file
]
]
]
]
return messages
]
do-go-action: func []
[
clear feedback_data
insert feedback_data sort do-rename
update-text-list list_feedback
clear files_data
do-update-files-data
update-text-list list_files
]
do-update-files-data
view layout [
across
label "Current Folder:"
lab_path: label to-string my_path 300x50 top
return
list_files: text-list data files_data
[do-change-folder value]
list_feedback: text-list data feedback_data
return
label "Search pattern:"
fld_search: field
return
label "Replace pattern:"
fld_replace: field
return
btn_go: button "Go" [do-go-action]
btn_quit: button "Quit" [quit]
]
New function, old code.
do-update-files-data: func []
The group of code to change current working folder and set a value for files_data has been made into a function. This will be called every time the user changes folders. Note that it is acceptable to have an empty function specification block.
A kludge.
update-text-list: func [current_list [object!]]
Incredibly the text-list face does not update when its data source is changed. This function fiddles with some of the innards of the text-list declaration to reset it. I borrowed this function from www.codeconscious.com/rebol/vid-notes.html (thanks to Brett for his help).
When the user clicks on the file list…
do-change-folder: func [selected [file!]]
If the user has clicked on a folder name this function changes to that folder and updates the display. Note the call to update-text-list to clear the file list.
You may recognise this function.
do-rename: func []
The code here was lifted from the example in my previous article, with a few modifications - it doesn’t refer to the command line arguments anymore, it uses the text from fld_search and fld_replace.
Make everything happen.
do-go-action: func []
When the user clicks the Go button, the rename takes place and file and feedback boxes are updated.
Initialisation.
do-update-files-data
This function call initialises the file list data before the GUI is created.
Two extra facets.
label "Current Folder:"
lab_path: label to-string my_path 300x50 top
Because file paths can vary in length, I have allocated some extra space with a pre-set size for this label using a tuple 300×50 (that is width x height). The facet top ensures that text is always vertically aligned to the top in the space allocated.
Click in the file list.
list_files: text-list data files_data
This triggers a call to do-change-folder.
Click the Go button.
btn_go: button "Go" [do-go-action]
This triggers a call to do-go-action.
Run it with a double-click on the rebname2.r issue the command rebol -s rebname2.r at the DOS command line. Hopefully it will look like the program in action in the image below.
REBOL/View resources on the web
- REBOL/View.
- “A Beginner’s Guide to REBOL Visual Interfaces“
- “REBOL/View Developer’s Guide“
- “Overview of Graphical User Interfaces using REBOL/ View“
- “VID/View Notes” (the update-text-list function was borrowed from the section “Problems with updating text-lists”).
- REBOLforces has the “REBOL/View FAQ“.
- Look in the REBOL script library for “View related” and “VID related” examples.
- An old version of the “REBOL/View User Guide“. It’s outdated but it shows how much more complex GUI programming would be without VID, and gives a feel for what goes on ‘behind the scenes’.
First published: PC Update June 2003 (online version updated)