
Most of the examples below are dialogs to edit instances of the class employee whose definition is:
(defclass employee ()
((name :type string :initarg :name
:accessor name)
(email :type string :initarg :email
:accessor email)
(full-time? :type boolean :initarg :full-time?
:initform T :accessor full-time?)
(department :type keyword :initarg :department
:initform :shipping :accessor department)
(salary :type integer :initarg :salary
:initform 2000 :accessor salary)))
To obtain a minimal dialog to edit an instance of employee (*employee-1*):
(dialogue
`(name
department
full-time?
salary)
:data *employee-1*
:title "Minimal")
Notes
Dialogue makes use of the employee class definition:
In this example no layout is specified for the main shell. The default layout in Dialogue is :2c-grid, a 2 columns specific layout based on the SWT grid layout.
What is missing in the previous dialog is a combo widget to choose the departement. Here it is:
(dialogue
`(name
(:accessor department
:type :combo
:style :read_only
:range ,*departments*)
full-time?
(:accessor salary
:value-accepter ,(lambda (new control-data)
(and (integerp new)
(<= 0 new 5000)))))
:data *employee-1*
:title "Adequate")
Notes
The combo control is not editable. The value of the style argument is a keyword or a list of keywords that matches the SWT style constants (READ_ONLY, SIMPLE,...)
An accepter function for the salary slot will only accept values between 0 et 5000.
How to replace a check box by a group control with 2 radio buttons:
(dialogue
`(name
(:accessor department
:type :combo
:style :read_only
:range ,*departments*)
salary
(:group :prompt "Employment time"
(:column
((:accessor full-time?
:type :button :style :radio
:methods (:settext "Full"))
(:type :button :style :radio
:reader ,(lambda (data)
(not (full-time? data)))
:methods (:settext "Partial"))))))
:data *employee-1*
:title "Radio buttons")
Notes
The :column layout applies to the :group composite control that contains the radio buttons
The :reader option needed to initialize the 2nd radio button.
The :methods option to apply any of the SWT control methods.
(dialogue
`(:tabfolder
("Employee 1"
(:composite
(name
(:accessor department
:type :combo
:style :read_only
:range ,*departments*)
full-time?
salary)))
("Employee 2"
(:composite
((:accessor name
:data ,*employee-2*)
(:accessor department
:data ,*employee-2*
:type :combo
:style :read_only
:range ,*departments*)
(:accessor full-time?
:type :button :style :check
:data ,*employee-2*)
(:accessor email
:data ,*employee-2*)
(:accessor salary
:data ,*employee-2*))))
("report"
(:composite
(:column :fill t
((:type :button :style (:push)
:name :show
:initial-value "Show control names"
:layout-data (:width 300)
:event (:selection
,(lambda (event control-data)
(declare (ignore event))
(let ((text-control (find-control :report control-data)))
(do-controls (cd control-data)
(text.append
text-control
(format nil "~s~%" (cd-name cd))))
(text.append
text-control
(format nil " ~d controls"
(/ (length
(dd-control-data-list
(find-dialog-data control-data)))
2)))))))
(:type :button :style (:push)
:name :clear
:initial-value "Clear"
:event (:selection
,(lambda (event control-data)
(declare (ignore event))
(let ((text-control (find-control :report control-data)))
(settext text-control "")))))
(:type :text :style (:multi :border :v_scroll)
:name :report
:layout-data (:height 100)
:methods
(:setbackground ,(system-color :cyan)))
)))))
:data *employee-1*
:title "Tabfolder, controls report")
Notes
The employee instance edited in the first tab is the default one specified in the :data argument of the call to dialogue (*employee-1*).
In the third tab item, a :column layout that fills is specified for the composite in the tab (fill: the controls in the column will have the same width). In the first two tab items, no layout is specified for the composite: it defaults to :2c-grid.
To set the size of the text control in a layout you have to use layout-data. Positioning and sizing of the controls in a layout are the job of the layout algorithm only. Don't use the setSize method because the action of setSize will be overridden. The SWT layout algorithm computes the preferred size for each control, width and height based on the contents of the control. You can use a layout-data to override this behaviour.
The :event control specification option may be used to define SWT event listeners.
For each control (widget), Dialogue maintains a control-data structure instance that contains all the information necessary to its functioning. The do-controls macro allows loops in the control-data instances of a dialog.
(defvar *number-var* 1)
(defvar *string-var* "test") (dialogue
`((:item *string-var*
:prompt "&String variable"
:layout-data (:width 100))
*number-var*)
:edit :special-variable
:title "Variables")
Notes
The :edit argument of Dialogue is :special-variable (instead of :clos in the previous examples, the default). This is one of the arguments that are inherited by all the control specifications. Of course, it's possible to override this at the control level and to mix different kinds of edit (:clos, :special-variable, ...) in a dialog.
The default prompt of a variable is the name of the variable it self (*Number-Var*), capitalized. Prompts are in use in :2c-grid layout only.
Just for fun ! Recurses in the employees list, one employee by folder.
(labels ((nested-tabfolders (employees)
(if (null employees)
()
(let ((employee (first employees)))
`(:tabfolder
(,(string-capitalize (name employee))
(:composite
((:accessor salary :data ,employee)
(:accessor email :data ,employee)
(:accessor full-time? :data ,employee
:type :button :style :check)
(:accessor department :data ,employee
:type :combo :style :read_only
:range ,*departments*))))
("next"
,(nested-tabfolders (rest employees))))))))
(dialogue
(nested-tabfolders *employees*)
:title "Nested tabfolders"))
(dialogue
`(:grid
:horizontalSpacing 15
:verticalSpacing 10
,(cons ;; column titles
'((:type :label :initial-value "Name")
(:type :label :initial-value "Salary")
(:type :label :initial-value "Email")
(:type :label :initial-value "FT")
(:type :label :initial-value "Departement")) ;; column content
(mapcar (lambda (employee)
`((:accessor name :data ,employee
:layout-data (:horizontalalignment ,*swt.fill*))
,(if (hide-salary? employee)
()
`(:accessor salary :data ,employee
:layout-data (:horizontalalignment ,*swt.fill*)))
(:accessor email :data ,employee
:layout-data (:horizontalalignment ,*swt.fill*))
(:accessor full-time? :data ,employee
:type :button :style :check)
(:accessor department :data ,employee
:type :combo :style :read_only
:range ,*departments*)))
*employees*)))
:title "Grid")
Note
The empty list to specify a empty unvisible label control.
In all these examples, Dialogue provides 2 default exit buttons, OK and Cancel, whose specifications are :
`((:name :ok
:type :button
:style :push
:initial-value "OK" ; or :methods (:settext "OK")
:value-accepter nil
:event (:selection
,(lambda (event cd) ; cd: control-data
(declare (ignore event))
(update-data (cd-dialog-data cd))
(shell.close
(dd-dialog (cd-dialog-data cd))))))
(:name :cancel
:type :button
:style :push
:value-accepter nil
:event (:selection
,(lambda (event cd) ; cd: control-data
(declare (ignore event))
(shell.close
(dd-dialog (cd-dialog-data cd)))))
:methods (:settext "Cancel")))
Notes
Instead of :initial-value, the SWT method setText can be used to set the text of a push button.
The update-data function loops in the control-data instances, updating the Lisp data that have been changed in the dialog.
Note: there is a "revert-data" function to revert the Lisp data and basic controls to their very initial value.
The dialog-data slot of a control-data contains an instance of the dialog-data structure. This one maintains information about the dialog, especially a Foil reference to the SWT dialog, that permits the call to shell.close
Same dialog as the "Adequate" dialog above but the user is allowed to type new departments in the combo control. Code has been added to update the variable *departments* (list of departments).
(run-test
(dialogue
`(name
(:accessor department
:type :combo
:range ,*departments*
; :read-converter ,(lambda (value cd) (symbol-name value)) )
full-time?
salary)
:main t
:data *employee-1*
:title "Editable combo"
:exit-controls
(list (ok-control
:after-hook
(lambda (dialog-data)
;; instead of this code, you could just get the value of the
;; department of *employee-1* and pushnew it in *departments*
(let ((dept-cd (find-control-data :department dialog-data)))
(when (data-written-back? dept-cd)
(pushnew (cd-new-lisp-value dept-cd) *departments*)))))
(cancel-control))))
Notes
To get an editable combo control remove the :read_only style.
In the department control specification, use the :read-converter option to change how the values in the combo control look.
The :after-hook argument of the ok-control function to add code in the selection event handler of the ok button. This code is run after the edited employee instance has been updated. Instead of this code that shows what can be done whith the control-data, you could just get the value of the department of *employee-1* and pushnew it into the *departments* variable. Of interest in this place, the do-written-back-controls to loop in the controls whose value has been written back to the Lisp side.