Pliant UI basic instructions set

The samples provided in this document cannot be run in the interpreter. They need to take place within the framework of a graphical Pliant application as introduced in  'a first graphic mode application'.
Moreover, most of these instruction must be used only within a 'window' instruction.

Simple text and paragraphs

text "Hello"

The two following sample provide the same: move cursor to the next line within existing paragraph:

eol

text "[lf]"

A paragraph is introduced with 'para':

para
  text "The first"
  text " line"
  eol
  text "The second"

The default is to have some empty space at the top and bottom of a paragraph. If you want to remove them, just use 'stick' option:

para stick
  text "line 1"
para stick
  text "line 2"

Para accepts two other options, 'cursor' and 'edit' that we will see later.

Title and headers are also paragraphs, with different looking:

title
  text "The main title of the document"
header
  text "A header"
para
  text "The content"

Also 'title' and 'header' accept a shorter writing if the content of the title or header is a single string:

title "The main title of the document"
header "A header"
para
  text "The content"

Simple text attributs

Usage is rather trivial:

para
  text "A sentence with a "
  bold
    text "bold word"
  text ", an "
  italic
    text "italic one"
  text ", and even "
  fixed
    text "fixed font"
  text "."

These attributes provide entry level styling. See 'Good looking using Pliant UI' article for doing the same, and more, using 'style' instruction.

Tables

Pliant tables are mostly moddled on HTML:

table
  row
    cell header
      text "Name"
    cell header
      text "City"
    cell header
      text "Phone number"
  row
    cell
      text "John Smith"
    cell
      text "New york"
    cell
      text "871 8272 2626"
  row
    cell
      text "God"
    cell span 2 1
      text "unknown"
  row
    cell
      text "Bob Lorenzo"
    cell span 1 2
      text "San Francisco"
    cell span 1 2
      text "821 2524 2541"
  row
    cell
      text "Marie Lorenzo"

Windows

As stated in the introduction to Pliant UI, a Pliant UI application displays it's content in up to four windows. The content of each window can be provided, and later changed using 'window' instruction:

window top
  text "some title"
window left
  text "some menu"
window main
  text "here we can display the content of the document, the database form, etc"
window bottom
  text "some status report"

The size of the side windows will be automatically adjusted according to their content size, within the limit of roughly 1/4 of the screen area. So, be wise when you send content to side windows in order to keep as screen space as possible for your main window.

Sections

A section is a way to identify some part of the document rendered on client side, most of the time in order to be abble to later change it:

section "one"
  text "hello"

Then you can change the content of the section with:

section_overwrite "one"
  text "good bye."

There are four other keywords enabling to extend the section content:

section_before "one"
  text "will be append just before the beginning of the section, so outside the section"
section_head "one"
  text "at the append at the head of the section, so inside the section"
section_tail "one"
  text "guess it"
section_after "one"
  text "same"

Another way to change the content of a section is to execute again the code that created it. A section that can be recreated through rexecuting at any time must be created with 'dynamic' attribute:

section "time" dynamic
  text string:datetime

Then recreation is ordered using 'section_replay' instruction:

section_replay "time"

Real applications mostly either use 'section', then 'section_overwrite' in a button code, or 'section dynamic', then 'section_replay' in a button code. See bellow in this document for an example.

I found many applications to contain:

window bottom
  section "help"
    void

then, in various places of the code:

section_overwrite "help"
  text "Do this, do that, or it failed because ..."

so that in the end, I created a 'help' instruction that does it all at once. The next code sample produces exactly the same as the previous one:

help "Do this, do that, or it failed because ..."

Forms

Let's start with an input field:

ovar Float s_value := undefined
input "Value: " s_value

If the default value is the right one, we can use more compact writing:

input "What is your name ? " (ovar Str name)

A help message can be automatically displayed in the "help" section when the mouse pointer is over the input field:

input "Value: " s_value help "Expected range is 1 to 1000."

The focus can also be set to the input field at time the field is created:

input "Value: " s_value focus true

More details about focus handling will be provided in 'High interactivity applications using Pliant UI' article.

We also have select boxes:

select "Source unit: " (ovar Str s_unit)
  option "" ""
  option "inch" "inch"
  option "millimeter" "mm"
  option "centimeter" "cm"
  option "meter" "m"

And finally, the button control:

para
  ovar Float s_value := undefined
  input "Value: " s_value
  select "Source unit: " (ovar Str s_unit)
    option "" ""
    option "inch" "inch"
    option "millimeter" "mm"
    option "centimeter" "cm"
    option "meter" "m"
  select "Target unit: " (ovar Str t_unit)
    option "" ""
    option "inch" "inch"
    option "millimeter" "mm"
    option "centimeter" "cm"
    option "meter" "m"
  button "Compute"
    var Float mm := s_value*(shunt s_unit="inch" 25.4 s_unit="mm" 1 s_unit="cm" 10 s_unit="m" 1000 undefined)
    var Float t_value := mm/(shunt t_unit="inch" 25.4 t_unit="mm" 1 t_unit="cm" 10 t_unit="m" 1000 undefined)
    section_overwrite "result"
      text string:value+" "+s_unit+" is "+string:t_value+" "+t_unit
para
  section "result"
    void

When using Pliant UI, a link is just a button with different looking:

link "clear"
  section_overwrite "result"
    void

'button' and 'link' instructions accept many options:

If the button contains a help option, a help instruction will overwrite the help section with the provided text each time the mouse passes over the button, and will clear the area when the mouse goes away:

button "Exit" help "Use this button to leave the myapp application"
  url_return

'key' option enables to bind the button to a keyword shortcut. It is highly recommanded the shortcut to be ATL plus a letter:

button "Exit" key "alt x"
  url_return

'stretch' option is a styling option that instructs the client drawing engine to extend the button horizontal size as much as possible. It is mostly used in the left window to get buttons that have all the same size.

button "Exit" stretch
  url_return

'active' option defauts to true and tells if the button is usable in the current state of the software:

ovar Bool saved <- false
button "Exit" active saved
  url_return

'selected' option defaults to false and is used to change the color of the button when it is intended to work as a check box:

ovar Str mode
section "menu" dynamic
  button "Edit" selected mode="edit"
    mode := "edit"
    section_replay "menu"

'layout' is the last option and it is used to ask the UI client to send informations about the screen layout (each window size) to the server just before the button clic instruction. It will be used by some very specific application to adjust the content sent to the client according to the effective size of the screen:

button "Info" layout
  text "Screen resolution is "+string:client_size_x+" x "+string:client_size_y
  eol
  var Pointer:UIServerWindow w :> client_window first "main"
  if exists:w
    text "Main window size is "+(string w:size_x)+" x "+(string w:size_y)

- explain why we use 'ovar' instead of 'var' -

URLs

As explained in the introduction, Pliant UI client uses the URLs and history notion it picked from the web terrific idea.
So, all Pliant UI applications are mapped to URL namespace subtrees using 'ui_path' instruction.

If we load a module containing the following code, or name the module '/myorg/url_demonstration.ui' to enable automatic loading on request:

ui_path "/myorg/url_demonstration"
  window main
    title "URL demonstation"
    text "Subpath is '"+subpath+"'"

then any client request to '/myorg/url_demonstration3/foo' will start a session of the demonstration applet on the server with subpath = "3/foo"

The application can use 'url_jump' to force the client to close the current session, then switch to another URL so open a new session, maybe on a different server:

button "test me"
  url_jump "url_demonstrationjump sample"

It can also use 'url_call' to ask the browser to push the current URL to it's history, then issue 'url_jump':

button "A submenu"
  url_call "/myorg/appli3/a_submenu"

and finaly use 'url_return' to ask the browser to pull the URL from the history.

button "Exit"
  url_return

With 'url_call' and 'url_return' we have a very convienient way to enter and leave a complex application submenus. 'url_jump' that we introduced first is in fact the non standard behaviour, as opposed to 'url_call', and you will use it only in very specific circonstances.

It is very important to understand that the Pliant UI session notion is tightly bound to the URL notion: each time the URL is changed through issuing 'url_jump', 'url_call' or 'url_return', the current session on the server is closed and a new one is open. It might seem strange at first if you use 'url_call' to provide access to submenus in your large application.

Let's image your application is a search engine (See /pliant/appli/file.ui for a real example). As a result of pressing 'search' button, the main window get's filled with search result, full of links to the various documents. Now, if you clic on one of the links, the application will issue 'url_call' to start the application dedicated to viewing this kind of document. When terminated, the user will press 'Exit' button and the document viewer will issue 'url_return' bringing back to the search engine application. The problem is that you expect not to get back an empty content, but again the result of your previous search, so that you can select another link if the first one you followed prooved not to be the right one, but it hurts the close connection between URLs and sessions. Your initial search session has been closed, so it's internal state is lost. We could work around through saying that 'url_call' does not close the current session, but keep it open and suspended until 'url_return' brings the user back to it, but then we would get servers full of suspended sessions, so that servers would have to timeout close them anyway.
So, the selected solution has been to specify that the UI client contains a dictionary (set of keyword -> value) associated with each URL. The application can fill or read it an any time. This is a bit like cookies with the limit that dictionaries are not site wide, but strictly bound to one URL in the browser history bound to a single browser session.
Let's now see how it works:

ui_path "/myorg/search"
  url_get "pattern" (ovar Str pattern)
  window main
    input "What to search: " pattern
    button "start searching"
      url_set "pattern" pattern
      ...

Now, in some situation, you would like the keyword -> value setting to cross the URL boundary so that you can use it within your all complex application, even if it is spitted in several URLs..
In such a case, just use 'cross' option:

url_set cross "pattern" pattern

But be warned about the potencial security related issue: the value can be changed or retreived by any application that knows the key. It is not site bounded like web cookies.
On the other hand, it is not stored on disk, so will be lost with any restart of the UI client.

Threading

Threading is a very powefull mechanism the server can use to track some event, and change content on the client accordingly.
Let's start with an example:

section "date"
  void
ui_thread
  while connection=success
    section_overwrite "date"
      text string:datetime
    flush
    sleep 1

'flush' is used to force the server to client communication channel cache to be flushed. 'flush' can be used in an UI thread, or just to display a message on the client while some long computation is appening on the server:

window top
  text "copying files ..."
flush

The 'sync' instruction is more rarely used. Not only does it flushes the cache, but it also waits for the client to have processed it. It will be used in situations like this:

var Sem sem
sem request
ui_thread
  share sem
  ...
  sync
  sem release
...
sem request ; sem release
# starting from here, we are granted that the changes applied by the thread in the client session are processed
...

Now, the big problem with threading is that Pliant standard variables are low level, so are not thread safe. As a result, you have to protect access to some of them with semaphores:

ovar Sem sem
ovar Str mode
button "emergency"
  sem request
  mode := "emergency"
  sem release 
ui_thread
  while connection=success
    sem rd_request
    var Str m := mode
    sem rd_release
    if m="emergency"
      ...
    sleep 1

If you had written your thread with the simpler:

ui_thread
  while connection=success
    if mode="emergency"
      ...
    sleep 1

then your application would be unreliable because 'mode' string variable might have an inconsistent value (since mode := "emergency" is executing at the exact same time) leading to a server crash even if the probability of such a situation is low. The semaphore prevents both actions to append at the same time.

In order to make things a little bit easier, 'critical' instruction is provided that grants that the current thread is the only one currently running in this session, so that no semaphore is required:

ovar Str mode
button "emergency"
  mode := "emergency"
ui_thread
  while connection=success
    critical
      if mode="emergency"
        ...
    sleep 1

All buttons code are always executed with the critical section single semaphore reserved, so that in facts, you need to use 'critical' only in the 'ui_thread' code. Be warned, that while one of your threads is executing code in a critical section, others in the same session cannot, and events from the user are not processed either, so it can be wise to use several small critical sections instead of a single big one if execution time can be long.