In the last post, we have shown how to create an extremely minimalistic HTML form without any styling. This was the code:
(form NIL (gui 'a '(+TextField) 30 "First Name") (<br>) (gui 'b '(+TextField) 30 "Last Name") (gui '(+Button) "Send" ) )
Today we will show how we can add styles and other things to our code using Prefix Classes.
What are Prefix Classes?
As explained in the Object-Oriented Programming posts, classes can inherit methods and properties of one or more other classes. This concept is called "multiple inheritance". If the same method or property appears several time within these classes, the first appearance is the valid one.
Technically, there is nothing special about prefix classes. They are just normal classes. They are called "prefix" because they are intended to be written before other classes in a class's or object's list of superclasses.
Prefix Classes can be used to add or modify characteristics that are defined within the GUI class. For example, the the prefix class
+Mono displays text in monospace, while
+Tinydisplays it in tiny font.
+Style Prefix Class
By using the
Style-prefix class, we can add CSS styles to our GUI classes. Let's modify our example so that it utilizes again the Bootstrap-classes from our previous example.
(action (html 0 "Simple Session" *Css NIL (<div> "d-flex flex-column" (<h1> "d-flex m-5 justify-content-center" "A little POST example with the GUI framework") ) (<div> "d-flex flex-column bg-light mx-5" (<div> "d-flex justify-content-center my-5" (form NIL (gui 'a '(+Style +TextField) "mx-5" 30 "First Name") (<br>) (gui 'b '(+Style +TextField) "mx-5 my-1" 30 "Last Name") (gui '(+Button) "Send" ) ) ) ) )
Then it looks like this:
To initialize a field with values, there are two possibilities:
+Initclass populates the field with a value. If the user clicks inside the field, the text will be preserved.
+Cueclass populates the field with the specified text inside angled brackets:
<my text>. When the user clicks inside the field, it disappears. Note that the text only appears after sending the form, i. e. if a mandatory field has been left blank.
Enabling and Disabling:
Sometimes we need to enable or disable forms or single elements. The easiest way to do that is by using the Prefix Class
+Lock, which doesn't take any further arguments:
(gui '(+Lock +Style +TextField) "mx-5 mb-2" 30 "Locked Textfield") (gui '(+Lock +Style +Button) "mb-5" "Locked Button" )
The user can't click on any of the fields, they are disabled.
+Able class is more flexible, as it takes an executable argument and disables the field if the argument evaluates to
(gui '(+Able +Style +TextField) '(< 3 5) "mx-5 mb-2" 30 "Enabled Textfield") (gui '(+Able +Style +Button) '(< 5 3) "mb-5" "Disabled Button" )
The text field is enabled because
(< 3 5) evalues to
T while the button is disabled because
(< 5 3) evaluates to
It's also possible to disable the form in total using the
disable function, which takes an executable argument. If the argument evaluates to non-
NIL`, all elements in the form are disabled **except** the ones with a prefix-class+Rid``:
(form NIL (disable (< 3 5)) (gui '(+Init +Style +TextField) "Disabled textfield" "mx-5 mb-2" 30 "Enter your text: ") (gui '(+Rid +Init +Style +TextField) "Enabled by +Rid" "mx-5 mb-2" 30 "Enter your text: ") )
Let's take another look at last post's example, our button that prints a text field's content to standard error.
(form NIL (gui '(+Style +TextField) "mb-2" 30 "Enter your text: ") (gui '(+Button) "Print" '(msg (val> (field -1))) )
When we press the button, the whole page is reloading. Now let's add another button with the same functionality, but prefix class
(gui '(+JS +Button) "Print" '(msg (val> (field -1))) )`
Now let's try both. When we press the button "Print", our page is reloaded and our text input is displayed on standard error. If the "+JS Print" button is pressed, our text input is also displayed on standard error, but without reloading! (The only exception is if there is no session running yet. In this case, the page will reload once to establish a session.)
How does it work? Comparing the HTML source code of both buttons, we can see that the
+JS has an additional
onclick="return doBtn(this)" tag and there is a
form.js script sheet included, which causes the browser to only re-evaluate the GUI elements without reloading the full page.
Defining variables with
Usually if we have a form sheet, we want to do something with the data. Say we want to write this data to a global variable called
*TextVariable. When the button
In order to do all this, we will use the prefix class
+Var, which takes a variable name as argument:
(setq *TextVariable "Initial Value") (gui '(+Style +Var +TextField) "mb-2" '*TextVariable 30 "Enter your text: ") (gui '(+Style +JS +Button) "mb-5" "+JS Print" '(msg (val> (field -1))))) (<p> (prinl "*TextVariable: " *TextVariable)))))
The page is initialized with a text field and text block content "Initial Value".
Now let's modify the value and press
+JS Print. Immediately we see our new value in the standard error of the server process. However, the
<p>-block remains unchanged, because the page has not been reloaded.
Modifying values with
We already heard that each gui object has a method
val> to return the value of a property, and
set> to set its value. These methods are inherited from the abstract top class
However, there might be situations where we want to customize these methods to our own needs. This can be done using the prefix classes
+Set respectively. Both take a function as argument that overwrites the methods
set> respectively. Let's look at the following example:
(gui '(+Val +NumField) '((N) (* N N)) 10 "Enter a number: ") (gui '(+Style +Button) "mb-3" "get square value" )
We have a number input field. However, the value of the field is not the content, but its square, because the value is multiplied with itself each time it is retrieved:
If we type
10 and press the button, the field content becomes
100, if we press again,
The complement to
+Set. It is called everytime a value is set to another value.
(gui '(+Set +NumField) '((N) (* N N)) 10 "Enter a number: ") (gui '(+Style +Button) "mb-3" "get square value" )
Look and feel of that function are exactly identical to the
+Val version, so what's the difference?
The difference is how the data is stored internally. Say we type
10 and press the button. In the
+Val case, the actually stored value is
10, although the displayed value is
100. In the
+Set case, the actually stored value is
The source code of the examples can be found here.
These were some of the most important prefix classes. In the next post, we will take a look at some further GUI elements.