\chapter{Dialog (controller) windows} \label{sec:controller} \index{controllers}% \product{} Dialog windows are normally used to display a number of controllers, named \class{dialog_item}{\em s} in \product{}'s jargon. Class \class{dialog} is a subclass of \class{window} with specialised methods for positioning controllers. Dialog items are graphical objects specialised for displaying and/or editing particular data. \Figref{dialoghierarchy} illustrates the inheritance relations relevant to dialog windows and the locations of the most important methods. \postscriptfig[width=4in]{dialoghierarchy}{Dialog Inheritance Hierarchy} Dialogs can be created both by using the new/2 and send/[2-12] operations as well as by using the Dialog Editor which is described in \appref{dialogeditor}. This section describes the first mechanism. Reading this chapter will help you understanding the dialog editor. \section{An example} Before diving into the complexities we will illustrate normal usage through an example. The following Prolog predicate creates a dialog for entering information on an employee. The result, running on Windows-NT, is shown in \figref{employee}. \begin{pcecode} ask_employee :- new(Dialog, dialog('Define employee')), send_list(Dialog, append, [ new(N1, text_item(first_name)), new(N2, text_item(family_name)), new(S, menu(sex)), new(A, int_item(age, low := 18, high := 65)), new(D, menu(department, cycle)), button(cancel, message(Dialog, destroy)), button(enter, and(message(@prolog, assert_employee, N1?selection, N2?selection, S?selection, A?selection, D?selection), message(Dialog, destroy))) ]), send_list(S, append, [male, female]), send_list(D, append, [research, development, marketing]), send(Dialog, default_button, enter), send(Dialog, open). assert_employee(FirstName, FamilyName, Sex, Age, Depth) :- format('Adding ~w ~w ~w, age ~w, working at ~w~n', [ Sex, FirstName, FamilyName, Age, Depth]). \end{pcecode} This example shows the layout capabilities of \class{dialog} and its \class{dialog_item} objects. Simply appending items will place items vertically and group buttons in rows. Labels are properly aligned. The {\sf enter} button defines a call-back on the predicate assert_employee/5 using the values from the various controllers. \Secref{exeobjects} explains the use of \class{message} objects in detail. \postscriptfig{employee}{Enter employee} \section{Built-in dialog items} \index{controllers,built-in}\index{dialog_item,built-in types}% \Tabref{diaitems} provides an overview of the built-in dialog items. The \productpl{} library defines various additional items as Prolog classes. See the file Overview in the library directory. \begin{table} \begin{center} \begin{tabularlp}{\class{dialog_group}} \hline \class{button} & Simple \idx{push-button}. Executes <-message when pressed. \\ \hline \class{text_item} & A \idx{text-entry field}. Editable or non-editable, built-in type conversion (for example to enter a numerical value), completion using the space-bar if a value-set is provided. \\ \hline \class{int_item} & Like a \class{text_item}, but providing properly sized field, buttons for one-up/down, type- and range-checking. \\ \hline \class{slider} & Select numerical value in a range. Handles both integers and floating point values. \\ \hline \class{menu} & Implements various styles of menus with different visual feedback. Realises \idx{radio-button}, \idx{tick-box}, \idx{combo-box} and much more. \\ \hline \class{menu_bar} & Row of pulldown (\class{popup}) menus. Normally displayed in a small dialog above the other windows in the frame. \\ \hline \class{label} & Image or textual label. Normally not sensitive to user actions. \\ \hline \class{list_browser} & Shows a list of items. List-browsers have been designed to handle lists with many items. Class \class{browser} is a window-based version. \\ \hline \class{editor} & Powerful text-editor. Handles multiple and proportional fonts, text-attributes, fragment marking, etc. Class \class{view} is a window based version. \\ \hline \class{tab} & Tagged sub-dialog, that may be combined with other \classs{tab} into a \class{tab_stack}, realising a tabbed controller-window. Often seen in modern applications to deal with many setting options. \\ \class{tab_stack} & Stack of \class{tab} objects. \\ \class{dialog_group} & Group of dialog items, possible with border and label. \\ \hline \end{tabularlp} \end{center} \caption{Built-in dialog items} \label{tab:diaitems} \end{table} \section{Layout in dialog windows} \index{layout, in dialog}\index{dialog,layout in}% The layout inside a dialog window may be specified by two means, either using pixel-coordinates or using symbolic layout descriptions. The latter is strongly encouraged, both because it is generally much easier and because the layout will work properly if the end-user uses different preferences (see \chapref{classvar}) than the application developer. The following methods can be used to define the layout of a dialog. All methods actually have both send- and get-versions. The methods listed only as `->send' methods are unlikely to be used as get-methods in application code. \begin{description} \sendmethod{dialog_item}{above}{dialog_item}% \sendmethod*{dialog_item}{below}{dialog_item}% \sendmethod*{dialog_item}{left}{dialog_item}% \sendmethod*{dialog_item}{rigth}{dialog_item}% These relations built a two-dimensional grid of dialog-items and determine the relative positioning of the dialog items. It suffices to relate each dialog item to one other item. \sendmethod{device}{append_dialog_item}{graphical, [\{below,right,next_row\}]} \sendmethod*{dialog}{append}{graphical, [\{below,right,next_row\}]} Append a dialog item relative to the last one appended. This method is the principal methods used to fill dialog windows. For the first item, the last argument is ignored. If the last argument is {\tt below}, this item is placed below the previous one. If the argument is {\tt right}, it is placed right of the previous one and if the argument is {\tt next_row}, the item is placed below the first one of the current row of dialog items. If the last argument is @default, dialog objects are placed {\tt next_row}, except for buttons, which are placed in rows, left to right. \sendmethod{dialog}{gap}{size} Defines the distance between rows and columns of items as well as the distance between the bounding box of all items and the window border. \bothmethod{dialog_item}{reference}{point} Point relative to the top-left corner that defines the {\em reference-point} of the dialog item. If two items are aligned horizontally or vertically, it are actually their reference points that are aligned. \sendmethod{dialog_item}{alignment}{\{column,left,center,right\}} This attribute controls how items are aligned left-to-right in their row. An item with ->alignment: {\tt column} will be alignment horizontally using the references of its upper or lower neighbour. Horizontally adjacent items with the same alignment will be flushed left, centered or flushed right if the alignment is one of {\tt left}, {\tt center} or {\tt right}. The alignment value is normally specified as a class-variable and used to determine the layout of rows of \class{button} objects. \sendmethod{dialog_item}{hor_stretch}{}{0..100} After completing the initial layout, possibly remaining horizontal space is distributed proportionally over items that return a non-zero value for this attribute. By default, class \class{text_item} yields 100 for this value, normally extending text_items as far as possible to the right. \end{description} \noindent The methods above deal with the placement of items relative to each other. The methods below ensure that columns of items have properly aligned labels and values. \begin{description} \bothmethod{dialog_item}{label_width}{[0..]} If the item has a visible label, the label_width is the width of the box in which the label is printed. The dialog layout mechanism will align the labels of items that are placed above each other if <-auto_label_align is @on. The argument @default assigns the minimum width of the label, the width required by the text of the label. \bothmethod{dialog_item}{label_format}{\{left,center,right\}} Determines how the label is aligned in its box. The values are {\tt left}, {\tt center} and {\tt right}. This value is normally defined by the look and feel. \bothmethod{dialog_item}{value_width}{[0..]} If the item displays multiple values left-to-right (only class \class{menu} at the moment), `dialog_item ->value_width' is used to negotiate equal width of the value-boxes similar to ->label_width if <-auto_value_align is @on. \end{description} \noindent The methods listed below activate the layout mechanism. Normally, only `device ->layout_dialog' needs to be called by the user. \begin{description} \sendmethod{dialog}{layout}{[size]} \sendmethod*{device}{layout_dialog}{gap=[size], size=[size], border=[size]} Implements the dialog layout mechanism. `Dialog->layout' simply calls `device ->layout_dialog' using `dialog <-gap'. `Device->layout_dialog' first uses the <-above, etc.\ attributes to build a two-dimensional array of items. Next, it will align the labels and value of items placed in the same column. Then it will determine the size and reference point for each of the items and determine the cell-size. It will then align all items vertically and afterwards horizontally, while considering the `dialog_item <-alignment'. \sendmethod{dialog}{_compute_desired_size}{} Sent from `frame ->fit' to each of the member windows. For class \class{dialog}, this activates ->layout and then computes the desired size of the window. \end{description} \subsection{Practical usage and problems} Most of the above methods are only used rarely for fine-tuning the layout. Almost all dialog windows used in the development environment, demo applications and Prolog library simply use `dialog ->append', sometimes specifying the last argument. Two problems are currently not taken care of very well. Aligning multiple objects with a single third object can only be achieved using a sub-dialog in the form of a device and often requires some additional messages. The dialog of \figref{layoutdemo1} is created using the following code: \begin{pcecode} layoutdemo1 :- new(D, dialog('Layout Demo 1')), send(D, append, new(BTS, dialog_group(buttons, group))), send(BTS, gap, size(0, 30)), send(BTS, append, button(add)), send(BTS, append, button(rename), below), send(BTS, append, button(delete), below), send(BTS, layout_dialog), send(D, append, new(LB, list_browser), right), send(D, append, new(TI, text_item(name, ''))), send(LB, alignment, left), send(D, layout), send(LB, bottom_side, BTS?bottom_side), send(LB, right_side, TI?right_side), send(D, open). \end{pcecode} \postscriptfig{layoutdemo1}{Aligning multiple items} In line~3, a \class{device} is added to the dialog to contain the stack of buttons. This device is sent an explicit ->layout_dialog to position the buttons. Next, the list_browser is placed to the right of this stack and the text_item on the next row. If you try this layout, the first column will hold the device and the text_item and the list_browser will be placed right of this column and thus right of the text_item. Using `dialog_item ->alignment: left' enforces the list_browser to flush left towards the device. Now we enforce the layout and adjust the bottom and right sides of the list_browser to the device and text_item. \index{dialog,resizing}% Dialog windows do not reposition their contents if the window is resized in the current implementation. If the window is enlarged, the items stay in the top-left corner. If the window is made smaller, part of the items may become invisible. Resizing can be implemented by the user by trapping the `window ->resize_message'. \section{Modal dialogs: prompting for answers} \label{sec:modal} \index{modal,dialog}\index{ask questions}\index{prompt}% A {\em modal} dialog is a dialog that is displayed and blocks the application until the user has finished answering the questions posed in the dialog. Modal dialogs are often used to prompt for values needed to create a new entity in the application or for changing settings. Modal windows are implemented using the methods `frame <-confirm' and `frame ->return'. `Frame <-confirm' invokes `frame ->open' if the frame is not visible and then starts reading events and processing them. `Frame ->return: value' causes `frame <-confirm' to return with the value passed as argument to ->return. The following code is a very simple example opening a dialog and waiting for the user to enter a name and press {\sc return} or the {\sf Ok} button. \begin{code} ask_name(Name) :- new(D, dialog('Prompting for name')), send(D, append, new(TI, text_item(name, ''))), send(D, append, button(ok, message(D, return, TI?selection))), send(D, append, button(cancel, message(D, return, @nil))), send(D, default_button, ok), % Ok: default button get(D, confirm, Answer), % This blocks! send(D, destroy), Answer \== @nil, % canceled Name = Answer. ?- ask_name(X). X = 'Bob Worker' \end{code} See also \secref{application} for a discussion on how frames may be related, as well as alternatives for keeping control in Prolog. \subsection{Example: a simple editor for multiple fonts} The following example allows the user to select text in an editor and change its appearance. The application is shown in \figref{wysiwyg}. \postscriptfig[width=\textwidth]{wysiwyg}{Very simple WYSIWYG editor} \hr \input{wysiwyg.tex} \section{Editing attributes} \index{attribute,editing}% In the previous section, we discussed dialogs for entering values. Another typical use of dialog windows is to modify setting of the application, or more in general, edit attributes of existing entities in the application. These entities may both be represented as \product{} objects or as constructs in the host language (dynamic predicates or the recorded database in Prolog). Such dialog windows first show the current settings. It allows for modifying the controls showing the various aspects of the current state and three buttons providing the following functions: \begin{itemize} \tick{\sf Apply} Apply the current controls, which implies invoking some behaviour on the application to realise the setting of the---modified--- controls. \tick{\sf Restore} Reset the controls to the current status of the application. \tick{\sf Cancel} Destroy the dialog and do not modify the current settings of the application. \end{itemize} The following methods are defined on all primitive controls as well as on the dialog window faciliate the implementations of dialog windows as described above. \begin{description} \sendmethod{dialog_item}{default}{any$|$function} \sendmethod*{dialog_item}{restore}{} For most dialog items, the <->default value is the second initialisation argument. Instead of a plain value, this can be a \class{function} object. The initial <-selection is set by evaluating this function. In addition, ->restore will evaluate the function again and reset the selection. \sendmethod{dialog_item}{apply}{always:bool} Execute the ->message of each dialog item for which `dialog_item <-modified' yields @on. If the argument is @on, the modified flag is not checked. \sendmethod{dialog}{apply}{} \sendmethod*{dialog}{restore}{} Broadcasts ->apply or ->restore to each item in the dialog. \end{description} \subsection{Example: editing attributes of a graphical} We will illustrate these methods described above in this example, which implements a dialog for editing the colour of the interior and thickness of the line around a graphical. Double-clicking on a graphical pops up a dialog window for changing these values. The result is show in \figref{editgraphical}. \begin{pcecode} colour(white). colour(red). colour(green). colour(blue). colour(black). append_colour(M, C) :- new(Img, pixmap(@nil, white, black, 32, 16)), send(Img, fill, colour(C)), send(M, append, menu_item(colour(C), label := Img)). edit_graphical(Gr) :- new(D, dialog(string('Edit graphical %s', Gr?name))), send(D, append, new(M, menu(colour, choice, message(Gr, fill_pattern, @arg1)))), send(M, layout, horizontal), forall(colour(C), append_colour(M, C)), send(M, default, Gr?fill_pattern), send(D, append, slider(pen, 0, 10, Gr?pen, message(Gr, pen, @arg1))), send(D, append, button(apply)), send(D, append, button(restore)), send(D, append, button(quit, message(D, destroy))), send(D, default_button, apply), send(D, open). attributedemo :- send(new(P, picture('Attribute Demo')), open), send(P, display, new(B, box(100, 100)), point(20, 20)), send(P, display, new(E, ellipse(100, 50)), point(150, 20)), send_list([B, E], fill_pattern, colour(white)), new(C, click_gesture(left, '', double, message(@prolog, edit_graphical, @receiver))), send(B, recogniser, C), send(E, recogniser, C). \end{pcecode} \postscriptfig[width=4in]{editgraphical}{Attribute editor for graphical objects}