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.
Go here to view the hosted demo page.
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 +Tiny
displays it in tiny font.
The +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:
Initializing: +Init
and +Cue
To initialize a field with values, there are two possibilities:
- the
+Init
class populates the field with a value. If the user clicks inside the field, the text will be preserved.
- the
+Cue
class 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: Able
, Lock
, (disable)
and +Rid
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.
The +Able
class is more flexible, as it takes an executable argument and disables the field if the argument evaluates to NIL
.
(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 NIL
.
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: ") )
The +JS
class
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 +JS
:
(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 +Var
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 Print
is pressed, this data should be written to standard output. Also, we want to print the content of the variable as normal text block.
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 +Val
and +Set
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 +gui
.
However, there might be situations where we want to customize these methods to our own needs. This can be done using the prefix classes +Val
and +Set
respectively. Both take a function as argument that overwrites the methods val>
and 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, 10000
.
The complement to +Val
is +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 100
.
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.
Sources:
software-lab.de/doc/form/form.html
software-lab.de/doc/app.html