squirrel32x32

An Introduction to Squirrel

Introduced the 6th of September, 1999, as a Logo dialect scripting language for BeOS, Squirrel has since been receiving continuous improvements in stability and performance. New built-in primitives and capabilities have also been added at each release.

One of the features introduced in the Developer Release 2 was the GUI Add-on. This Add-on was more a prototype than a fully working Graphical User Interface tool. In the upcoming release of Squirrel (DR4), this Add-on will be completely rewritten, to give Squirrel the ability to build user interface in a simple and elegant way.

Features of the GUI Add-on of Squirrel includes:

  • Several widgets which cover most of the Interface Kit elements
  • Gluing tools
  • Font support and widget font sensitivity
Further improvement will be made to this Add-on in post-DR4 releases, which will follow in the coming weeks and months.


What is Squirrel anyway ?

Squirrel is a scripting language developed for BeOS. Squirrel is a dialect of Logo and has a short and easy learning curve which makes it the perferred language for the beginning programmer. As well, it's power and good performance makes it a good alternative for the professional developer who is looking for an easier way to program under BeOS.

Squirrel is a functional programming language with primitive functions (also called primitives) defined as part of the language. Other functions can be defined and named by the programmer. As a functional programing language, Squirrel sees every task in terms of functions. For example, if we consider the C statement :
a = b + c;
Squirrel equivalent of it will be :
make "a :b+:c
Where make is a primitive, "a is a word and :b and :c are references to two variables.

And so, the syntax for Squirrel is simply an order name followed by some inputs. An order could be a primitive name or a user defined function.

As a high-level language, Squirrel does not require its variables to be declared or even be created before being used. The primitive make will create the variable if the variable doesn't exist. A variable value could change its datatype anywhere in the program.

Squirrel features several types of simple datatype like: string, word and number, as well as a data structure, like a list.

make "a 12
Stores to the variable a with the number 12
make "b 'John Doe'
Stores to the variable b with the string John Doe
make "c "hello
Stores the word hello in the variable c
make "d [1 2 3]
Stores the list of elements 1, 2 and 3 in the variable d

A list is a powerful data structure which can contain any kind of datatype, even other lists :

make "alist [1 'BeOS' "hello [4.56 'John Doe' "sunny]]

A set of instructions in Squirrel could also be contained in a special kind of list called a block. This data strcuture is most often used for control structures, although the next example illustrates another use:

make "ablock {
	make "c 12
	make "d :c**2
	make "e :c + :d	
}

run :ablock

Most of the control structures in Squirrel are similar to those featured in a procedural language like C. The table below summarizes these control strcutures with short examples :

Decision Structures
if Test whether an expression meets a criterion and if so executes a block
make "a 78
if :a>10 {print 'a is greater than 10'}
ifelse Test whether an expression meets a criterion and if so executes a block otherwise execute another block
make "a 78
if :a>10 {print 'a is greater than 10'} {print 'a is smaller than 10'}
test Test whether an expression meets a criterion and store the result of the test.
make "a 78
test :a>10
iftrue Use the result of the last test and execute the input if it's true
make "a 78
test :a>10
iftrue {print 'a is greater than 10'}
iffalse Use the result of the last test and execute the input if it's false
make "a 78
test :a>10
iffalse {print 'a is smaller than 10'}
Loop structures
for Execute a block of instruction(s) in a counter controlled repetition
for ["i 1 10] {
	print :i
}
repeat Execute a block of instruction(s) a specified number of time
repeat 10 {
	print 'Hello World!'
}
do.while Execute a block of instruction(s) while a condition is true
do.while {
	make "i foo :i
} :i>10
do.while Execute a block of instruction(s) while a condition is true. The block will be executed at least once since the expression is evaluated only after the first execution
do.while {
	make "i foo :i
} :i>10
do.until Execute a block of instruction(s) while a condition is false. The block will be executed at least once since the expression is evaluated only after the first execution
do.until {
	make "i foo :i
} :i=10
for.each Execute a block of instruction(s) in a counter controlled loop. The counter variable will assume the value of the elements of a list
for.each "i [1 2 3 4 5]
	print :i
}
until Execute a block of instruction(s) while a condition is false. The expression is always evaluated before executing the block
until :i=10 {
	make "i foo :i
}
while Execute a block of instruction(s) while a condition is true. The expression is always evaluated before executing the block
while :i>10 {
	make "i foo :i
}

In Squirrel, the programmer is capable of extending the language by creating new functions. To define a doubling function, for example, we could write:

to double :x
	output :x*2
end

The keyword to indicates that we're defining a new function. the function name and inputs follow on the same line. The keyword end terminates the function definition. In our example, we multiply the input by two and we return the result.

You may consult the User Manual of Squirrel for a more complete coverage of the language.


Why a GUI Add-on ?

BeOS features a well built Interface Kit containing several good GUI objects that offer the programmer the ability to build a powerful Graphical User Interface. But to succeed, you need to be well versed in the C++ programming language and have a good understanding of the Interface Kit, part of the Application Programming Interface (API) for BeOS. Also, building a font sensitive application requires a special attention to detail : When the size of a widget mat change, where does one put the other widgets relative to it ?

The result of your work will be worth the time and energy you put into it, but what if you don't have the time nor the knowledge to build the GUI ? What if you just want to build a prototype to validate an idea?

Using Squirrel allows you to build your user interface quickly and easily, without prior knowledge of C++ or the Be Interface Kit. This saves you time and energy for things that are more important.


The Basis

This section of the article will cover the basis of GUI design in Squirrel:

  • Positioning of widgets
  • Responding to the user action
  • Widget & Linked Variables

Positioning of widgets

While using Squirrel to design your GUI, you will not have to bother with the placement of your widget within a window or another widget, in contrast to the Be Interface Kit, where you have to specify the position in pixel of the widget. Instead, in Squirrel, you just define for each widget a set of rules which defines the way the widget is going to position itself according to what is around it, like the size of the window or the size and position of the neighbouring widgets.

For example, if we want to put on a window two buttons, one over the other, we will do something like :

make "button.1 Button 'Do that'
Create a new Button widget with the label Do that and store it in the variable button.1
make "button.2 Button 'Do this'
Create a new Button widget with the label Do this and store it in the variable button.2
Glue :mywindow "top [] :button.1 :button.2
The command Glue is one of the most important commands of the GUI Add-On. This command instructs the window contained in the variable mywindow to attach on itself the widgets button.1 and button.2. The second input of the command defines where the widget must be placed within the window. In this example, we instruct the window to place them on the top, that is above any other widgets that may be already glued on the window. The third input is not relevant for the gluing, it may be an empty list like in this example, or the pading in x-axis and y-axis for each widget that you glue in this command.


example1

In our example, we have glued the second button on top. What happens if we glue it on the bottom ?

make "button.1 Button 'Do that'
make "button.2 Button 'Do this'

Glue :mywindow "bottom [] :button.1 :button.2


example3

Now the button.1 is below button.2. When asking a window (or a container widget) to glue a set of widget like we do in this example, Squirrel understands that you want the widget to be glued from bottom to top, so button.2 will be glued on the window first, and then button.1 will be glued below it.

Maybe we want the widget to be at the same level, side-by-side. Instead of "top or "bottom: we will use "right or "left to glue the widgets the way we want them :

make "button.1 Button 'Do that'
make "button.2 Button 'Do this'

Glue :mywindow "left [] :button.1 :button.2


example4

In this situation, Squirrel understands that you wish button.1 placed before button.2.

If we had specified "right instead, we would have had :

make "button.1 Button 'Do that'
make "button.2 Button 'Do this'

Glue :mywindow "right [] :button.1 :button.2


example5

Where Squirrel understands that you want the last widget to be the most right widget.

Let us consider now the case where the label of each of our two buttons are not of the same size. For example, if we have :

make "button.1 Button 'Do something complicated'
make "button.2 Button 'Do something simple'

Glue :mywindow "top [] :button.1 :button.2


example6

The result is not very pleasing to the eye. There are two different approaches to solving this kind of problem. One is to set a expand parameter to the smaller button, the other is to set the width of the buttons with a fixed size of character places.

Let's see what's happens with the first approach :

make "button.1 Button 'Do something complicated'
make "button.2 Button 'Do something simple'

$button.2~config "expand.x "set true
We change the configuration of the widget in order to allow its width to expand whenever it is possible
Glue :mywindow "top [] :button.1 :button.2


example7

"expand.x" is one of the many properties found on all the widgets.

The second way to have a better asthetics of our buttons is to set both of them to a certain width in character places :

make "button.1 Button 'Do something complicated' [15 0]
make "button.2 Button 'Do something simple' [15 0]

Glue :mywindow "top [] :button.1 :button.2


example8

As you have noticed we have added to the instructions creating each of the buttons on the list [15 0], which indicates that we want the button to fit 15 characters in width and as much as needed in height (0 indicates that the widget is free to compute what height is needed). You are probably wonderiing why we ask for 15 when it should be 24 (The size of the label of button.1). This is due to the fact that we use a font where all the characters don't have the same width. In our example, we were using 'Comic Sans MS' which is a TrueType Font. Let's change the font and use for example Monospac821 BT which is distributed in standard with the BeOS:

make "font Font 'Monospac821 BT'

$font~size "set 9

$mywindow~config "font "set :font

make "button.1 Button 'Do something complicated' [15 0]
make "button.2 Button 'Do something simple' [15 0]

Glue :mywindow "top [] :button.1 :button.2


example9

Hmmm ... not quite what we wanted. Well, we have set the width of the buttons fixed to 15, maybe with the correct number of characters it will be better:

make "font Font 'Monospac821 BT'

$font~size "set 9

$mywindow~config "font "set :font

make "button.1 Button 'Do something complicated' [24 0]
make "button.2 Button 'Do something simple' [24 0]

Glue :mywindow "top [] :button.1 :button.2


example10

Here we are ... the font is less fancy of course, but the buttons have exactly the same size.

In conclusion, the best way to have the two buttons as the same width is to set there expand.x property to true.

In Squirrel, only a window and a special kind of widget allow you to glue in other widgets. A widget that accepts other widgets is called container, and one of them is a Frame. A container allows you to group several widgets together and sees them as one widget for the placement within another container or window.

make "button.1 Button 'Do something complicated'
make "button.2 Button 'Do something simple'

$button.1~config "expand.x "set true
$button.2~config "expand.x "set true

make "frame Frame
We are creating a Frame object to be stored in the variable frame
Glue :frame "top [] :button.1 :button.2
Gluing widgets on a container is exactly the same as that on a window
Glue :mywindow "top [] :frame


example11

Now that the window sees only a single widget, we could, for example, allow the window to be resized and set the frame to be centered all the time, like in the following example :

make "mywindow Window "titled 'Squirrel DR4' [100 100] "not.zoomable

$mywindow~config "font "set :font

make "button.1 Button 'Do something complicated'
make "button.2 Button 'Do something simple'

$button.1~config "expand.x "set true
$button.2~config "expand.x "set true

make "frame Frame

$frame~config "align "set "center "center

Glue :frame "top [] :button.1 :button.2

Glue :mywindow "top [] :frame


example12

Reponding to user actions

Now that our two buttons are glued on the window, we may want them to do something each time the user clicks on them. Squirrel allows you to define a set of hooks for a widget or a window. Each hook which could either be a block or a function, which will be called when an event occurs. The event could be generated by the user or by the system.

Let's add the following code to our example:

Hook :button.1 "invoked {
	Info "info 'Ok' 'Doing that ...'	
}
This command set-up the hook of the widget button.1 for the event invoked . The block given as the last argument will be executed each time the user clicks on the button.
Hook :button.2 "invoked {
	Info "info 'Ok' 'Doing this ...'	
}


example2

Each type of widget has its own set of events that you may or may not want to hook but they have all in common some events, like pulsed.

pulsed is a special event which is not generated by the user but by the system. It's a message that is sent by the system to the widget after a certain interval of time. By default, a widget doesn't receive this event unless it's specified by the programmer through a flag.

To illustrate this, let's build a window which displays a blinking text :

Font.init

make "font Font 'Comix Sans MS'

$font~size "set 14

The three previous lines defined and created a font and set its' size. We're going to use this font as the window default font.

make "mywindow Window "titled 'Squirrel DR4' [100 100] "not.zoomable "not.resizable

We create our window with the behavior to not be resizable or zoomable by the user.

$mywindow~config "font "set :font

We set the font for the window and for all widgets glued on the window.

make "text Text 'Merry Christmas!!' "center [] "pulsed

A Text widget is a very simple widget which just displays text. In our example, we want to display the string 'Merry Christmas!!'. The third input of the command specifies the justification of the text within the widget in case the widget is expanded. The fourth input in our example is an empty list since we don't want to fix any size for the widget, but rather have the widget compute it. The last input "pulsed is the flag which indicates that we want this widget to receive a repeated pulse from the system.

Glue :mywindow "top [] :text

$mywindow~show

The widget is glued on the window and then the window is shown.


example13

Now, let's set-up the hook for the pulse and make the text blink by adding the code :

make "text Text 'Merry Christmas!!' "center [] "pulsed

make "Red [255 0 0]
make "Green [0 167 0]
make "Blue [49 61 225]

$text~config "high.color "set :Red

We define the the three colors that we want to use, and set the initial color of the text displayed in the widget to the color red.

make "Colors list :Red :Green :Blue

make "Color 1

The variable Colors is a list which contains the 3 colors, and the variable Color is the index of the color to use in the Colors list.

to Blink :src

	$text~config "high.color "set lindex :Colors (:Color % 3) + 1
	incr "Color

end

We now define the function Blink which will be executed each time the event pulse is received by the widget. This function changes the color used by the widget to display the text.

Hook :text "pulse "Blink

Using the command Hook, we setup our function Blink as the hook for the event pulse.

Glue :mywindow "top [] :text


blink

Widgets & Linked Variables

Several widgets in Squirrel are linked to a variable. That means that the widget will display the value of the variable and that the variable value will be changed when the user modifies the widget. Also, the widget will be updated if the variable is changed. A variable is linked to the widget and the widget is linked to the variable. Let's consider the following example which uses RadioButton

make "w Window "titled 'OS?' [100 100]

We create a simple window

make "os "windows

We going to use the variable os as the linked variable to the widget.

make "r1 RadioButton 'Linux' "os "linux
make "r2 RadioButton 'BeOS' "os "beos
make "r3 RadioButton 'OS/2' "os "os2
make "r4 RadioButton 'Windows' "os "windows

We are creating 4 RadioButton widgets, one for each OS people are mostly likely to use and like to use. The first input of the command RadioButton is the label to be displayed on each radiobutton. The second input is shared by all widgets and it is the name of the linked variable. The last input is the value that the variable should take whenever the user selects the radiobutton

$r1~config "expand.x "set true
$r2~config "expand.x "set true
$r3~config "expand.x "set true
$r4~config "expand.x "set true

We want all of the buttons to have the same size, so we set their x-axis expand flag to true

make "change Button 'I like BeOS!'
make "get Button 'What do I like ?'

$change~config "expand.x "set true

Hook :change "invoked {
	make "os "beos
}

Hook :get "invoked {
	Info "info ['Yup!'] 'Well looks like you like' (stoupper :os) ' no ?' 
}

We also create two buttons to illustrate what happens when the user selects a radiobutton or changes the linked variable

Glue :w "top [] :r1 :r2 :r3 :r4 :change :get

$w~show


example14

If the user clicks on the I like BeOS button, the linked variable changes and the widgets reflect this change as shown below:

example15

Now, if the user changes the selection by cliking on, say, the Linux radiobutton and clicks on the What do I like ? button after that, we will have :

example16

In the second part of this article, we will build a fully working application with Squirrel.

To Be continued...