Web Apps: GUI Component Interaction

Web Apps: GUI Component Interaction


3 min read


For a minimal "application", write a program that presents a form with three components to the user:

  • a numeric input field ("Value")
  • a button ("increment"
  • a button ("random")

The field is initialized to zero. The user may manually enter a new value into the field, or increment its value with the "increment" button. Entering a non-numeric value should be either impossible, or issue an error message.

Pressing the "random" button presents a confirmation dialog, and resets the field's value to a random value if the answer is "Yes".

This task is very similar to the previously discussed task "Simple Windowed Application", so we will use the script and modify it. This is how it will look like in the end:


A hosted version can be found here.

Defining the GUI elements

In this task, we need a numeric input field that can be modfied by the user, a button called "increment" and another button called "random". Let's define them without putting any logic inside:

(gui '(+NumField) 20 "Value: ")
(gui '(+Button) "increment")
(gui '(+Button) "random")


Define the logic

We want the +NumField to show us the value of the *Count variable, and also be able to modify it. We can do this by setting the prefix class +Var on the *Count variable:

(gui '(+Var +NumField) '*Count 20 "Value: ")

Secondly, we want the button to increase the '*Count value. So lets add a function to the button definition:

(gui '(+Button) "increment" '(inc '*Count))

And the prefix class +JS to avoid the reload of the complete page.

(gui '(+JS +Button) "increment" '(inc '*Count))

As last step, we need to define the random function. PicoLisp has a built in function called rand which can be used for that purpose:

(rand ['cnt1 'cnt2] | ['T]) -> cnt | flg

Returns a pseudo random number in the range of the positive short numbers cnt1 and cnt2 (or -2147483648 .. +2147483647 if no arguments are given). If the argument is T, a boolean value flg is returned.

The task description has no specification about the range, so let's use the rand function without any arguments. When the button is pressed, we set *Count to the random value.

(gui '(+Button) "random" '(setq *Count (rand)))


There is only one last specification to fulfill: The task description tells us that we need to open a dialog which needs to be confirmed by the user first. form.l provides a function ask for this purpose:

(ask Str . Prg)

Creates an alert dialog with a string, a 'Yes'- and a 'No'-button. Str is the text that will populate the alert, Prg is the action triggered by the 'Yes'- button.


As you can see, the pop-up moves the position of all the other components, because it's not an overlay dialog box but a normal <form> element that is inserted just above the form which created them.

<form enctype="multipart/form-data" ...> ...
   <fieldset class="alert"> ...
   <span class="ask">Reset to a random value?</span><br/>
   <input type="submit" name="*Gui:-1" value="Yes" onmousedown="inBtn(this,1)" onblur="inBtn(this,0)" class="submit" id="i0--1"/>
   <input type="submit" name="*Gui:-2" value="No" onmousedown="inBtn(this,1)" onblur="inBtn(this,0)" class="submit" id="i0--2"/>

There is not so much we can do about the shifting, but at least we can make sure that it is inserted above instead of next to our form GUI elements by wrapping these into their own <div> within the form function:

(<div> "d-flex flex-column bg-light text-center mx-5"

    <-- Dialog comes here! -->

   (form NIL
      (<div> "d-flex justify-content-center text-center my-5"
         (gui '...)   
         (gui '...)

Due to the text-center class in the <div> above the dialog it gets centered. Then it looks like this:


Now our form elements get pushed down when the dialog box appears, but not to the side. Finished!

The final source code can be downloaded here, css here.