Newbie questions about Pliant

Newbie questions about Pliant

Writing app under new UI

How to arrange notification messages, and write a basic
form that submits for a long time.
Message posted by maybe Boris Reitman on 2006/05/08 17:52:21
I have come up with the following,

---8<--------------
    para
      ...
      button "Add link"
        section_tail "log"
          para stick
            text string:datetime + " adding link: " + sitename
        brfl_add_bypass_link mywebsite category url reciprocal_page sitename description email
        section_tail "log"
          para stick
            text string:datetime + " completed: " + sitename
      button "Clear Log"
        section_overwrite "log"
          para stick
            text ""
    section "log"
      para stick
        text ""
    ...
---->8--------------

The brfl_add_bypass_link operation take 5 seconds to finish. I would like
the message "adding link" to appear to the user before the procesing,
and "completed" after.  However, what happens is that both appear together, after 5 seconds.

Second question. If I move declaration of 'section "log"' one indent level in,
such that it appears inside the 'para', the application crashes. Why does it 
happen ?

Can you suggest a better way to organize such an application. Its basically a form,
which must be properly filled in (error checking here), and submitted.
Processing takes about 5 to 10 seconds.  Then the user would change some values
on the form and submit again.  How would I better organize error reporting 
on the values of the form, and tell the user that he has to wait for each
transaction to complete before trying to submit another link.  

Or better yet, how could I setup a queue, so that the user can submit links
and they would be processed in a separate thread, with status reported in a log
"window" (or section).

Thanks,
Boris
Message posted by hubert.tonneau on 2006/05/08 19:17:43
> I would like the message "adding link" to appear to the user before the procesing,

Add a 'flush' instruction:
        ...
        section_tail "log"
          para stick
            text string:datetime + " adding link: " + sitename
        flush
        brfl_add_bypass_link mywebsite category url reciprocal_page sitename description email
        ...

> Second question. If I move declaration of 'section "log"' one indent level in,
> such that it appears inside the 'para', the application crashes.

This is a serious problem:
many UI widgets assume to be inside a paragraph.
So, I could require the application to provide explicit paragrah instructions,
but it would be boring:
cell
  para
    text "hello"
instead of the simpler:
cell
  text "hello"

The current implementation is silently adding the 'para' when needed.
The drawback is that a section inside a paragraph must be explicit because
the following would be ambigious:
cell
  section "foo"
    void
Here we cannot safely decide if a 'para' muse be silently added in front of
'section'

So, a section inside a paragraph must use 'inside' parameter:
   para stick
     section inside "log"
        text ""

> Can you suggest a better way to organize such an application.

Well, displaying a message while the server is computing seems resonable
to me as a compromise between simple code and convienient user interface.

> Or better yet, how could I setup a queue, so that the user can submit links
> and they would be processed in a separate thread, with status reported in a log

Use 'ui_thread' instruction. It's very powerfull.

You could try something like:
      ui_thread
        brfl_add_bypass_link mywebsite category url reciprocal_page sitename description email
        section_tail "log"
          para stick
            text string:datetime + " completed: " + sitename
Message posted by maybe Boris Reitman on 2006/05/09 05:41:05
More questions:
- What is the significance of using "ovar" instead of "var" to declare the variables?
- What is the scope of variables. Will variable declared and filled-up 
outside of ui_thread be available inside ui_thread ?  In your ui_thread example above,
how would I communicated the values of all the variable in the form to the thread body?
If I create a datastructure RequestData, and then instantiate a queue List:RequestData outside ui_thread,
will the ui_thread see it ? I would like to enqueue an entry when submit button is pressed, and dequeue inside the thread.
- To what extent are aggregate variables supported ? (list, array, dictionary)
- Is multi-select box available ?
- Is checkhbox available ? 

I needed a feature that works like a multi-select. Since I couldn't find
it, I was trying to work around it.  One idea was to create an array
of checkboxes, and each checkdbox whould be simulated by a ("yes","no") select 
field.  I tried something along these lines,

---8<---------------
ovar Array:Str checkboxes
var List:Str mylist := get_mylist
checkboxes size := mylist size
each list_element mylist
  select "" checkboxes:count
    option "yes" "yes"
    option "no" "no"
  text list_element
----->8-----------------

This crashed, and my guess is because I am using checkboxes:count to remember
the state of each checkbox. How the proper way to do such thing ? Basically,
I would like to generate some form elements from a datastructure, and not
hard-code each element explicitely. So I have to use an aggreate data structure
to keep track of the values selected on the form.  If am not allowed to do such
thing, its a big problem.

So I tried something else.  I have arranged for a button [+] to add 
a selected entry is a standard select box, into a "selected-so-far" container.
Then I arranged a dynamic section "selected_items" to print out the contents of the selected 
container.  When the [+] button was pressed, the container would be updated, 
and the section "selected_items" whould be _replayed_.  This again didn't work
and crashed.  The error was something along the lines: 
"attempt to access frozen data".

So, I have tried something else, and that works, but it is not pretty.
 http://archimedes.hypervolume.com/~boris/pliant/flowerlinks_admin.pli.txt

Please tell me what I was doing wrong, and if you want code examples that fail, 
I will try to reconstruct them.




Message posted by maybe Boris Reitman on 2006/05/09 18:18:49
Also, flush instructions stopped working as expected when I put it inside a loop,
---8<-------------
          section_overwrite "error"
            text ""
          each mywebsite_ websites
            if (selected_websites exists mywebsite_)
              if selected_websites:mywebsite_ 
                section_tail "log"
                  text string:datetime + " adding link to: " + mywebsite_
                flush
                brfl_add_bypass_link mywebsite_ category url reciprocal_page sitename description email
                section_tail "log"
                  text string:datetime + " completed: " + mywebsite_
--->8-------------

Full code is ath the url in the previous message.  
So, again the message "adding link to" and "completed" appear together, instead
of "adding link" appearing first, and "completed" when the job is finished.
Message posted by maybe Boris Reitman on 2006/05/10 06:43:04
I tried to wriet the code with a queue and ui_thread. The ui_thread doesn't
notice changes to the queue, made in the main thread.  
The ui_thread always sees the queue as empty, even if has stuff in the main thread.
The code is at

http://archimedes.hypervolume.com/~boris/pliant/flowerlinks_admin-withqueue.pli.txt

Thanks,
Boris
Message posted by maybe Boris Reitman on 2006/05/10 21:19:37
The queue didn't work because I declared the List structure as "var" and not "ovar".
Message posted by hubert.tonneau on 2006/05/10 21:37:23
Thanks to the brave that experiment with the Pliant UI at a so early stage.

For the bugs you reported, I will probably wait next week to dig back
in the code.

> What is the significance of using "ovar" instead of "var" to declare the variables?

A Pliant input var must be a true Pliant object.
If you write:
  ovar Int i
It's basically the same as:
  var Link:Int i
  if not exists:i
    i :> new Int
and using 'ovar' is just a convienient way to get the object.

It is also very convienient to use 'ovar' so that when some 'button'
'section_replay' and many other functions are called, truely the same
variable content is used since what is copied is the link rather than
have the variable value copied.

> To what extent are aggregate variables supported ? (list, array, dictionary)

There should be no restriction.

> Is multi-select box available ?
> Is checkhbox available ? 

No. The only reason I can provide is: I don't like them.
This is a weak answer, so it may improve at some time, but at the moment
the Pliant widgets set tend to be minimal.

> This crashed,
>   ovar Array:Str checkboxes
>   ...
>     select "" checkboxes:count

Anything you want to use in 'input' or 'select' must probably be a true Pliant
object, so you have to do:
  
  ovar (Array Link:Str) checkboxes
  ...
  for (var Int i) 0 checkboxes:size-1
    checkboxes i :> new Str
  ...
    select "" checkboxes:count

> I tried to wriet the code with a queue and ui_thread. The ui_thread doesn't
> notice changes to the queue, made in the main thread.  

'ui_thread' is expected to behave just like 'thread'
So, if you specify noting, any variable should be copied to a new instance,
so that accessing or modifying it will not distrub the other thread.

If you want a variable to be shared (the new threads get's a pointer to
the value in the initial one) then you have to use the 'share' instruction.

But ... if the variable is an ovar; let's say
  ovar Int i
then it truely is
  var Link:Int i
so what is copied is the link, so that the variable is automatically shared.

Now, you have to remember that if the variable is shared, then you are
responsible to protect access through semaphores.
This is very important if you expect your server to be truely reliable.

Finally, if 'ui_thread' currently does not behave as discribed here,
then it is a bug I have to correct.
Message posted by maybe Boris Reitman on 2006/05/11 15:15:16
I tried using dictionary to remember which entry is checked.
However, when I select one of them to "yes", all of them turn to "yes".
And, if I select of them to "no" then all turn to "no".
At least, thats how the loop at the bottom of the code reports.

------8<-------------------------
      ovar List:Str websites := brfl_get_swapware_websites
      ovar (Dictionary Str Link:Str) selected_websites
      each website websites
        ovar Str initial := "no"
        selected_websites insert website initial
        select "" selected_websites:website
          option "yes" "yes"
          option "no" "no"
        text website
        eol
      eol
      button "Add link"
        each website websites
          console "Got " website " = " selected_websites:website eol
-------->8-------------------------
Message posted by maybe Boris Reitman on 2006/05/11 15:37:37
Ok, I figured it out.
I had to replace (ovar Link:Str initial := no) with

--8<--------------------
        var Link:Str initial
        initial :> new Str
        initial := "no"
-------->8--------------
Message posted by maybe Boris Reitman on 2006/05/12 18:35:59
I have a question about how to organize UI code. I would like to encapsulate/group 
the layout instructions in some functions.

- Suppose I have two blocks of code that are the same, how do I factor them out into a function.
The blocks of code are calling "section_tail" on some section.

- Suppose I want to write red text many times, and I don't want to write
use style "highlight"
  para
    text "hello"
everytime.

- how can I write a function "my_style1" that I can use as,
my_style1
  text "hello"
  ...

- How can I stretch out something like
AAA
  BBB
  CCC
    DDD
into one line ? Like this: (AAA BBB (CCC DDD)) ? 

I like the idea of a template, or a Resource to control the layout of things.
It would be good if I could setup some kind of structure, with the "para" 
and "highligh" and "table" instructions, but in the code I would just 
want to set the value of some of these subareas in the general template.
I guess that using the present system, I should use "section_overwrite" 
to set those areas ? How would I write this template in a function, 
so that I don't mix it with my main code.
  
- The moment that I exit an application, the running threads are killed.
Suppose that I would like to have many screens ("windows") to my application,
that I can switch between with a menu.  How would I do entirely different layouts
of the main window, and while I switch between them, so that the ui_thread that is running 
in the background and processing jobs is not going to be killed, and each screen
would remember its state? (for example, one one screen is a form with values that are
filled in), and on the other screen is a log of all the jobs being completed.

Thanks,
Boris
Message posted by hubert.tonneau on 2006/05/13 13:09:22
> Suppose I have two blocks of code that are the same, how do I factor them out into a function.
> The blocks of code are calling "section_tail" on some section.

Just put the shared code in an external function.
You can define the functions using 'ui_function' instead of 'function' keyword
to get automatic passing of the UI context.

> Suppose I want to write red text many times, and I don't want to write
> use style "highlight"

'style use' can be used on a large bloc of code.

> how can I write a function "my_style1" that I can use as,
> my_style1
>    text "hello"

Just define 'my_style1' as a meta (use 'compile_as' to make it easy).

> How can I stretch out something like
> AAA
>   BBB
>   CCC
>     DDD
> into one line

AAA { BBB ; CCC { DDD } }

> I like the idea of a template, or a Resource to control the layout of things.
> It would be good if I could setup some kind of structure, with the "para" 
> and "highligh" and "table" instructions, but in the code I would just 
> want to set the value of some of these subareas in the general template.
> I guess that using the present system, I should use "section_overwrite" 
> to set those areas ? How would I write this template in a function, 
> so that I don't mix it with my main code.

Once again, you can use meta programming with 'compile_as'.
When you use 'compile_as', you basically write and advanced macro instruction
(advanced means that it can verify arguments data types).
  
> The moment that I exit an application, the running threads are killed.
> Suppose that I would like to have many screens ("windows") to my application,
> that I can switch between with a menu.  How would I do entirely different layouts
> of the main window, and while I switch between them, so that the ui_thread that is running 
> in the background and processing jobs is not going to be killed, and each screen
> would remember its state? (for example, one one screen is a form with values that are
> filled in), and on the other screen is a log of all the jobs being completed.

Define you application context using something like:

type MyContext c
  ... # all various session datas

Then you have a set of (one or more per layout):

ui_function foo c
  arg_rw MyContext c
  implicit c
     ...

Finally, your switching menu on the left window of the UI is calling them
to get the general refresh.