With Kawa you can easily write compact programs for graphical interfaces, using nested expressions that match the structure of the display. This makes GUI programs easy to read, write, and maintain. Also, the programs are toolkit-independent, so the same program can use Swing, SWT, or execute in a Web Server. So it is just as easy to write an interactive Web Application as a normal application. The library can be used from different programming languages (it is written in pure Java), but the examples below will use the Scheme language.
This is an early prototype, little more than a proof-of-concept, so not a lot of functionality has been implemented.
(require 'gui) (run-application (Window title: "Hello World!" content: (Label "Hello world!")))
The first line is boiler-plate to make the GUI functions available.
The third and fourth lines create a new Window
object,
with the given title
, which is shown
in the window's title-bar. The content
is a text
Label
containing a single string.
The second line invokes the run-application
command,
passing it the newly-constructed Window
as an argument.
This makes the window visible and active.
If you put this program in a file named HelloWorld.scm
,
you can run it from a console by typing this command:
java kawa.repl HelloWorld.scm
You should see a window pop up looking like [[[image]]].
If you prefer to use the SWT toolkit, you can instead do:
java kawa.repl display:toolkit=swt HelloWorld.scm(You also need to make sure Java can find the SWT packages and libraries.) The
display:toolkit
parameter allows you
to specify the preferred toolkit, in this case using swt
rather than the default swing
.
You can also run the same program in a web server. See [[link coming soon]] for details and examples.
Here is a more interesting example. It displays a counter, which is incremented each time you click on a button.
(require 'gui) (define n 0) (define count-label (Label "Hello! This is a click counter.")) (define (handle-click event) (set! n (+ n 1)) (set! count-label:text (if (= n 1) "You have clicked once." (format #f "You have clicked ~d times." n)))) (run-application (Window title: "Hello counter!" content: (Column count-label (Button text: "click here" foreground: 'red action: handle-click))))
We first define the variable n
, which counts
the number of times the button has been clicked.
The count-label
is a Label
that
will be updated to show the number of clicks.
The handle-click
is an event handler:
It is a function that will get called each time the button is clicked;
we'll cover what it does later.
Then as before we create a Window
, and
pass it to run-application
.
The Window
we create is a little more complicated this time:
The content
of the window is a Column
,
which is a container of parts that are displayed in a vertical stack.
Above is the previously-defined count-label
,
and below it is a Button
.
The button shows a text
in a foreground
color,
but the most important property is the action
, which
is bound to the previously-defined handle-click
function.
When the button is clicked, the implementation calls the procedure bound
to the action
property, which is handle-click
.
The handle-click
procedure takes one parameter named
event
(providing information about the event) - which is ignored.
When it gets called, it first increments n
,
and then it updates the text
property of
the code-label
.
The functions Button
, Label
,
and Column
return fresh model descriptor
((is better terminology needed?)) objects.
People talk about model (application data) vs
view (a displayed component instance).
These descriptors have aspects of both:
A descriptor is model
because it isn't tied to a specific
display, window, or area of the screen.
The same descriptor can displayed on multiple
displays and multiple times on the same display.
Hence a descriptor doesn't have a parent
- just like
list doesn't have a parent.
To illustate, let's modify the previous example so the
count-label
is displayed twice, once above and once below
the button:
... unchanged except for following ... (run-application (Window title: "Hello counter!" content: (Column count-label (Button text: "click here" foreground: 'red action: handle-click) count-label)))
When the button is clicked, the action updates the count-label
's
text property - which causes the text to be updated in both places
in the window.
A descriptor object has a number of named properties.
A property is normally implemented as a private field which you access
with coventional get
and set
methods; however, in
Kawa you normally name the properties directly,
without the get
and set
prefixes.
Usually you initialize them using the colon notation shown above when the object is created:
(Constructor name1: value1 name1: value1 ...)
This creates a new instance of Constructor
,
initializing the property named name1
to value1
, and so on.
You can modify a property of an existing object in various ways, but the easiest may be using this syntax:
(set! object:name value)
This sets the property with the given name
of the given object
to the new value
.
This change will propagate to other objects that depend
on the object
, which will often update the display.
(The current prototype implementation does not propagate
all the updates it should.)
Currently, only some ad-hoc style propertioes can be associated with
a descriptor object, such the background
color of a button.
The plan is that a style will be a updatable collection
of name-value bindings, and you will be able to assocate a style
with a descriptor. The style is not used directly by the
descriptor, but it is used when the descripctor is used as the model
for a toolkit-specific view.