Back to Contents


The Interaction Model

Summary:

See also: Dynamic User Interface, Form Files, Windows and Forms.


The Model-View-Controller Paradigm

The Dynamic User Interface architecture is based on the Model-View-Controller (MVC) paradigm. The Model defines the object to be displayed (typically the application data that is stored in program variables). The View defines the decoration of the Model (how the Model must be displayed to the screen, this is typically the form). The Controller is the program code that implements the processing that manages the Model. Multiple Views can be associated to a Model and a Controller.

With BDL, you define the Views in the Abstract User Interface tree or through built-in classes designed for this (such as Window or Form). You store Models in the program variables, and you implement the Controllers with interactive instructions, such as DIALOG or  INPUT.

Normally the Controllers should not provide any decoration information, as that is the purpose of Views. Because of the history of the language, however, some interactive instructions such as MENU define both the Controller and some presentation information such as menu title, command labels, and comments. In this case, the runtime system automatically creates the View with that information; you can still associate other Views to the same controller.


Controlling User Actions

Binding Action Views to Action Controllers

In the user interface of the application, you can have interactive elements (such as buttons) that can trigger an event transmitted to the Runtime System for interpretation. To manage such events, the Action Views can produce Actions Events that will execute the code of the corresponding Action Handler in the current interactive instruction of the program.

Action Views (like buttons) are bound to an action by the 'name' attribute. For example, a Toolbar button with the name 'cancel' is bound to the Action Handler using the name 'cancel'. Action Handlers are defined in interactive instructions with ON ACTION clause or COMMAND / ON KEY clauses:

01 INPUT ARRAY custarr WITHOUT DEFAULTS FROM sr_cust.*
02   ON ACTION printrec
03     CALL PrintRecord(custarr[arr_curr()].*) 
04   ON ACTION showhelp
05     CALL ShowHelp()
06 END INPUT

The name of the action must be a valid identifier in the program.

Warning: In the Abstract User Interface tree (where the action views are defined), action names are case-sensitive (as they are standard DOM attribute values).  In BDL, however, identifiers are not case-sensitive. To avoid any confusion, the compiler automatically converts action identifiers to lowercase.

For singular dialogs such as INPUT or DISPLAY ARRAY, actions only have a simple name. Action names in a DIALOG instruction, however, have both simple names and prefixed names. When using a DIALOG instruction, one can define the same action (or have predefined actions such as insert, append, delete) in different sub-dialogs. These action names automatically get a prefix to identify actions properly across sub-dialogs. These  actions are prefixed with the sub-dialog identifier. For example, when defining an ON ACTION save handler in a DISPLAY ARRAY sub-dialog using a screen-array named "sa", the runtime system will identify that action with both the "save" name and the "sa.save" prefixed name. In the form files, you can then bind Action Views to sub-dialog Action Handlers by using the sub-dialog prefix.

For backward compatibility, the COMMAND / ON KEY clauses are still supported. However, it is strongly recommended that you use ON ACTION clauses instead, as the ON ACTION clauses identify user actions with an abstract name.

Decorating Action Views

A default decoration for action views can be centralized in an external file. This is strongly recommended, to separate the decoration of the action view from action usage (the action handler) as much as possible. See Action Defaults for more details.

Enabling and Disabling Actions

During an interactive instruction, you can enable or disable an action with the setActionActive() method of the ui.Dialog built-in class. This method takes the name of the action (in lowercase letters) and an boolean expression (0 or FALSE, 1 or TRUE) as arguments.

01 ...
02    BEFORE INPUT
03       CALL DIALOG.setActionActive("zoom",FALSE)
04 ...

Default Views for Actions

If no explicit action view is defined, such as a toolbar button or a topmenu command, the front end creates a default action view for each MENU COMMAND, ON KEY or ON ACTION clause used in the current interactive instruction (typically, the default action views appear as buttons in the action frame). You can control the presentation of these default views with  Window Styles.

If one or more action views are defined explicitly for a given action, the front end considers that the default view is no longer needed, and hides the corresponding button. Typically, if you define in the form a BUTTONEDIT field or a BUTTON that triggers an action, you do not need an additional button in the action frame.


Predefined Actions

Definition

The BDL language predefines some action names for common operations of interactive instructions. Predefined actions can be:

If you define your own ON ACTION handler with the name of an Automatic action, the default action processing is bypassed and the program code is executed instead. For more details, see Overwriting Automatic Actions in Interactive Instructions.

As for user-defined actions, if you design action views using predefined action names, they will automatically attach themselves to the actions of the interactive instructions. It is also possible to define default images, texts, comments and accelerator keys in the Action Defaults resource file for these predefined actions.

List of predefined actions

Action Name Description ON ACTION block Context
Runtime Controlled Automatically created by the runtime system (except for DIALOG)    
accept Validates the current interactive instruction possible (1)
cancel Cancels the current interactive instruction possible (1)
close Triggers a cancel key in the current interactive instruction (by default) possible (7)
insert Inserts a new row before current row possible (2)
append Appends a new row at the end of the list possible (2)
delete Deletes the current row possible (2)
nextrow Moves to the next row in a list possible (4)
prevrow Moves to the previous row in a list possible (4)
firstrow Moves to the first row in a list possible (4)
lastrow Moves to the last row in a list possible (4)
help Shows the help topic defined by the HELP clause possible (1)
Special Special behavior    
interrupt Sends an interruption request to the program when processing no (5)
dialogtouched Sent by the front end each time the user modifies the value of a field yes (7)
Front-end controlled Handled by the front end (the runtime does not know about these actions)    
editcopy Copies to the clipboard the current selected text no (1)
editcut Copies to the clipboard and removes the current selected text no (1)
editpaste Pastes the clipboard content to the current input widget no (1)
nextfield Moves to the next field in the form no (3)
prevfield Moves to the previous field in the form no (3)
nextrow Moves to the next row in the list (supported for backward compatibility - is a runtime action) no (4)
prevrow Moves to the previous row in the list (supported for backward compatibility - is a runtime action) no (4)
firstrow Moves to the first row in the list (supported for backward compatibility - is a runtime action) no (4)
lastrow Moves to the last row in the list (supported for backward compatibility - is a runtime action) no (4)
nextpage Moves to the next page in the list no (4)
prevpage Moves to the previous page in the list no (4)
nexttab Moves to the next page in the folder no (6)
prevtab Moves to the previous page in the folder no (6)
  1. CONSTRUCT, INPUT, PROMPT, INPUT ARRAY and DISPLAY ARRAY.
  2. INPUT ARRAY only.
  3. CONSTRUCT, INPUT and INPUT ARRAY.
  4. INPUT ARRAY and DISPLAY ARRAY.
  5. Only possible when no interactive instruction is active.
  6. Possible in any kind of interactive instruction (MENU included).
  7. DIALOG, CONSTRUCT, INPUT, PROMPT, INPUT ARRAY and DISPLAY ARRAY.

Overwriting Automatic Actions in Interactive Instructions

The ON ACTION clause can be used with a predefined action name in an interactive instruction :

01 INPUT BY NAME customer.*
02  ON ACTION accept
03     ...
04 END INPUT

In this case, the default behavior is not performed; the user code is executed instead.


Keyboard Accelerator Names

Accelerator keys

Some parts of the user interface can define accelerators keys. With Action Defaults, you can define up to four accelerator keys for the same action, by setting the acceleratorName, acceleratorName2, acceleratorName3 and acceleratorName4 attributes.

If no accelerators are defined in the Action Defaults, the runtime system sets default accelerators for predefined actions, according to the user interface mode. For example, the accept action will get the Return and Enter keys in GUI mode; the Escape key would be used in TUI mode.

If you want to force an action to have no accelerator, specify "none" as the accelerator name.

If one of the user-defined actions uses an accelerator that would normally be used for a predefined action, the runtime system does not set that accelerator for the predefined action. For example (in GUI mode), if you define an ON ACTION quit with an action default using the accelerator "Escape", the cancel predefined action will not get the "Escape" default accelerator. User settings take precedence over defaults.

Note that text edition and navigation accelerators such as Home and End are usually local to the widget. According to the context, such accelerators might be eaten by the widget and will not fire the action bound to the corresponding accelerator defined in the Action Defaults. For example, even if the Action Defaults for firstrow action defines the Home accelerator, when using an INPUT ARRAY, the Home keystroke will jump to the beginning of the edit field, not the the first row of the list. 

The following table lists all the keyboard accelerator names recognized by the runtime system:

Accelerator Name Description
none Special name indicating the the runtime system must not set any default accelerator for the action.
0-9 Decimal digits from 0 to 9
A-Z Letters from A to Z
F1-F35 The functions keys
Return The RETURN key (alphanumeric keypad, see Note)
Enter The ENTER key (numeric keypad, see Note)
Space The SPACE-BAR key
Escape The ESCAPE key
Tab The TABULATION Key
BackSpace The BACKSPACE key (do not confuse with DELETE key)
Up The UP key (arrow keyboard group)
Down The DOWN key (arrow keyboard group)
Left The LEFT key (arrow keyboard group)
Right The RIGHT key (arrow keyboard group)
Insert The INSERT key (navigation keyboard group)
Delete The DELETE key (navigation keyboard group)
Home The HOME key (navigation keyboard group)
End The END key (navigation keyboard group)
Next The NEXT PAGE key (navigation keyboard group)
Prior The PRIOR PAGE key (navigation keyboard group)

Note:

The "Enter" key represents the ENTER key available on the numeric keypad of standard keyboards, while "Return" represents the RETURN key of the alphanumeric keyboard. By default, the validation action is configured to accept both "Enter" and "Return" keys. See the Action Defaults file.

Accelerator key modifiers

All of the key names listed in the previous table can be combined with CONTROL / SHIFT / ALT modifiers, by adding "Control-", "Shift-", or "Alt-" to the name of the accelerator.

For example:

Control-P
Shift-Alt-F12
Control-Shift-Alt-Z

Interruption Handling

Why do we need interruption handling?

When the BDL program executes an interactive instruction, the front end can send action events based on user actions. When the program performs a long process like a loop, a report, or a database query, the front end has no control.  You might want to permit the user to stop a long-running process.

How to program the detection of user interruptions

To detect user interruptions, you define an action view with the name 'interrupt'. When the runtime system takes control to process program code, the front end automatically enables the local 'interrupt' action to let the user send an asynchronous interruption request to the program.

Warning: The front end can not handle interruption requests properly if the display generates a lot of network traffic. In this case, the front end has to process a lot of user interface modifications and has no time to detect a mouse click on the 'interrupt' action view. A typical example is a program doing a loop from 1 to 10000, just displaying the value of the counter to a field and doing a refresh. This would generate hundreds of AUI tree modifications in a short period of time. In such a case, we recommended that you calculate a modulo and display steps 10 by 10 or 100 by 100.

Interruption handling example:

Form file "f1.per":

01 LAYOUT
02  GRID
03  {
04   Step: [pb                    ]
05         [sb                    ]
06  }
07  END
08 END
09 ATTRIBUTES
10 PROGRESSBAR pb = FORMONLY.progress, VALUEMIN=0, VALUEMAX=100;
11 BUTTON sb : interrupt, TEXT="Stop";
12 END

Program:

01 MAIN
02  DEFINE i,j INTEGER
03  DEFER INTERRUPT
04  OPEN FORM f1 FROM "f1"
05  DISPLAY FORM f1
06  LET int_flag=FALSE
07  FOR i=1 TO 100
08    DISPLAY i TO progress 
09    CALL ui.Interface.refresh()
10    FOR j=1 TO 1000 -- Loop to emulate processing
11      DISPLAY j
12      IF int_flag THEN EXIT FOR END IF
13    END FOR
14    IF int_flag THEN EXIT FOR END IF
15  END FOR
16 END MAIN

Detecting data changes immediately

You can use a special predefined action to detect user changes immediately and execute code in the program to set up your interactive instruction. This special action has the name "dialogtouched" and must be declared with an ON ACTION clause to be enabled:

01  DIALOG
02    ...
03    ON ACTION dialogtouched
04       LET changing = TRUE
05       CALL DIALOG.setActionActive("dialogtouched", FALSE)
06    ...
07  END DIALOG

Each time the user modifies the value of a field (without leaving the field), the ON ACTION dialogtouched block will be executed; This can be triggered by typing characters in a text editor field, clicking a checkbox / radiogroup, or modifying a slider.

The dialogtouched action works for any field controlled by the current interactive instruction, and with any type of form field.

Note that the current field may contain some text that does not represent a value of the underlying field data type. For this reason, the target variable cannot hold the current text displayed on the screen when the ON ACTION dialogtouched code is executed, even when using the UNBUFFERED mode.

You may only want to detect the beginning of record modification, to enable a "save" action for example. To prevent further dialogtouched action events, just disable that action with a setActionActive() call.  If this action is enabled, the ON ACTION block will be fired each time the user modifies the value in the current field.

To avoid data validation, the dialogtouched action is defined with validate="no" in the default Action Defaults file. This is mandatory when using the UNBUFFERED mode; otherwise the runtime would try to copy the input buffer into the program variable when a dialogtouched action is fired.


Controlling data validation when an action is fired

When using the UNBUFFERED mode of interactive instructions such as INPUT or DIALOG, if the user triggers an action, the current field data is checked and loaded in the target variable bound to the form field. For example, if the user types a wrong date (or only a part of a date) in a field using a DATE variable and then clicks on a button to fire an action, the runtime system will throw an error and will not execute the ON ACTION block corresponding to the button.

If you want to prevent data validation for some actions, you can use the validate Action Default attribute. This attribute instructs the runtime not  to copy the input buffer text into the program variable (requiring input buffer text to match the target data type).

The validate Action Default attribute can be set in the global action default file, or at the form level, in a line of the ACTION DEFAULTS section.


Windows closed by the user

In graphical applications, windows can be closed by the user, for example by pressing ALT+F4 or by clicking the cross button in the upper-left corner of the window. A Predefined Action is dedicated to this specific event. When the user closes a graphical window, the program gets a close action.

By default, when the program is in a MENU instruction, the close action is converted to an INTERRUPT keypress (the key that cancels an interactive instruction). This means that a COMMAND KEY(INTERRUPT) block is invoked if it is defined in the MENU statement. If there is no COMMAND KEY(INTERRUPT), nothing happens. 

When executing an INPUT, INPUT ARRAY, CONSTRUCT or DISPLAY ARRAY, the close action acts by default the same as the cancel predefined action. So when the user clicks the X cross button, the interactive instruction stops and int_flag is set to 1. Note that if the CANCEL=FALSE option is set, no close action will be created, and you must write an ON ACTION close control block to create an explicit action. In this case, you define an action handler for close and the int_flag register will not be set.

When executing a DIALOG instruction, the close action executes the ON ACTION close block if defined. Otherwise, the close action is mapped to the cancel action and the ON ACTION cancel block is fired is one is defined. If neither ON ACTION close, nor ON ACTION cancel is defined, the window cannot be closed with the X cross button or an ALT+F4 keystroke.

You typically implement a close action handler as in the following example:

01 INPUT BY NAME cust_rec.*
02    ...
03    ON ACTION close
04      IF msg_box_yn("Are you sure you want to close this window?") == "y" THEN
05        EXIT INPUT
06      END IF
07    ...
08 END INPUT