All Manuals > CAPI User Guide and Reference Manual > 10 Dialogs: Prompting for Input

10.5 Creating your own dialogs

The CAPI provides a number of built-in dialogs which should cover the majority of most programmers' needs. However, there is always the occasional need to create custom built dialogs, and the CAPI makes this very simple, using the function popup-confirmer which displays any CAPI interface as a dialog, and the functions exit-confirmer to return from such a dialog.

10.5.1 Using popup-confirmer

The function popup-confirmer is a higher level function provided to add the standard buttons to dialogs. In order to create a dialog using popup-confirmer, all you need to do is to supply a pane to be placed inside the dialog along with the buttons and the title. The function also expects a title, like all of the prompter functions described earlier.

  :callback-type :data
  :callback 'exit-dialog)
 "Enter a string")

Since interfaces and layouts are panes too, the pane argument to popup-confirmer can be a layout or an interface, and often it is. Layouts are used for simple combinations of panes, and interfaces are used for complex dialogs. All the dialogs in the LispWorks IDE which are not either native, just a message or asking for a single item of input are interfaces displayed by popup-confirmer. As an example, you can load the Othello example file:

(example-edit-file "capi/applications/othello")

which defines an interface othello-board. You can then run this as a dialog:

 (make-instance 'othello-board) "Play Othello")

Note that it works as usual, except that the menubar is not displayed.

Here is a simple example using a layout to ask the user for five strings:

(let* ((panes 
        (loop repeat 5
              (make-instance 'capi:text-input-pane)))
       (layout (make-instance 'capi:column-layout
                              :description panes)))
  (multiple-value-bind (res okp)
      (capi:popup-confirmer layout
                            "Enter some strings")
    (declare (ignore res))
    (when okp
      (loop for pane in panes
            (capi:text-input-pane-text pane)))))

An interface intended for display by popup-confirmer can also be displayed by display (not at the same time), in which case it is just another window. That is especially useful during development of your dialog code, because you can then work on the callbacks while the interface is displayed.

A common thing to want to do with a dialog is to get the return value from some state in the pane specified. For instance, in order to create a dialog that prompts for an integer the string entered into the text-input-pane would need to be converted into an integer. It is possible to do this once the dialog has returned, but popup-confirmer has a more convenient mechanism. The function provides a keyword argument, :value-function, which gets passed the pane, and this function should return the value to return from the dialog. It can also indicate that the dialog cannot return by returning a second value which is non-nil.

In order to do this conversion, popup-confirmer provides an alternative exit function to the usual exit-dialog. This is called exit-confirmer, and it does all of the necessary work on exiting.

You now have enough information to write a primitive version of prompt-for-integer.

(defun text-input-pane-integer (pane)
 (let* ((text
         (text-input-pane-text pane))
          :junk-allowed t)))
   (or (and (integerp integer) integer)
       (values nil t))))
   :callback 'exit-confirmer)
  "Enter an integer:"
  :value-function 'text-input-pane-integer)

A example using popup-confirmer

Note that the dialog's OK button never becomes activated, yet pressing Return once you have entered a valid integer will return the correct value. This is because the OK button is not being dynamically updated on each keystroke in the text-input-pane so that it activates when the pane contains a valid integer. The activation of the OK button is recalculated by the function redisplay-interface, and the CAPI provides a standard callback, :redisplay-interface, which calls this as appropriate.

Thus, to have an OK button that becomes activated and deactivated dynamically, you need to specify the change-callback for the text-input-pane to be :redisplay-interface.

   :change-callback :redisplay-interface
  :callback 'exit-confirmer)
 "Enter an integer:"
 :value-function 'text-input-pane-integer)

Note that the OK button now changes dynamically so that it is only ever active when the text in the text-input-pane is a valid integer.

Note that the Escape key activates the Cancel button - this too was set up by popup-confirmer.

The next thing that you might want to do with your integer prompter is to make it accept only certain values. For instance, you may only want to accept negative numbers. This can be specified to popup-confirmer by providing a validation function with the keyword argument :ok-check. This function receives the potential return value (the value returned by the value function) and it must return non-nil if that value is valid. Thus to accept only negative numbers we could pass minusp as the :ok-check.

  :change-callback :redisplay-interface
  :callback 'exit-confirmer)
 "Enter an integer:"
 :value-function 'text-input-pane-integer
 :ok-check 'minusp)

10.5.2 Using display-dialog

popup-confirmer creates an interface (of an internal class) around the pane that you give it which displays the pane and the buttons it adds, and then calls display-dialog to actually display it. If you have an interface and do not want any of the buttons, you can call display-dialog directly.

display-dialog takes an interface (unlike popup-confirmer, which can take any pane) and displays it as a dialog. The keyword arguments can be used to control the exact behavior. You can use exit-dialog and abort-dialog to dismiss the dialog programmatically.

10.5.3 Modal and non-modal dialogs

By default popup-confirmer and display-dialog create modal dialog windows which prevent input to other application windows until they are dismissed by the user clicking on a button or another appropriate gesture. You can change this behavior by passing the modal keyword argument.

10.5.4 Getting the current dialog

The function current-popup can be used to find the current popup pane, if there is any, and is useful inside callbacks.

The function current-dialog-handle returns the "handle" of the dialog in the underlying GUI system, which may be useful in some circumstances.

CAPI User Guide and Reference Manual (Macintosh version) - 01 Dec 2021 19:31:21