Editing callback interactions
Hello again, esteemed CAPI experts.
I have a dialog something like the example below: there is a text input box, which has an editing-callback that verifies the input, and if invalid, beeps and resets it to last valid value. There are buttons in the dialog that do things with the text.
I would like to arrange it so that if the user enters an invalid value in the input box and then clicks on a button (without otherwise switching focus first), then the normal action of the button is NOT executed. In other words, I'd like to arrange for the error in the input to somehow abort the action that caused the editing callback.
In experimenting with this, I run the test function below, type "bad" in the input box and click on the button. I found the following strange behavior:
1. On Windows, if I do (test nil), i.e. beep, then first the editing callback is called, then the button selection callback is called.
2. On Windows, if I do (test t), i.e. do display-message, then the editing callback is called, but the button selection callback is NOT called.
3. On the Mac, in either case, the editing callback is not called at all, only the selection callback.
The behavior in 2. is what I want, and I'm perfectly willing to put up a message dialog instead of just beeping, but I don't want to rely on whatever strange set of circumstances makes it happen in that case, since it might be a bug that will get fixed... Is there a way to get the behavior I want reliably? (I'm mostly concerned about Windows, I can deal with the Mac behavior)
FWIW, I tried an approach where the editing callback sets an "error occured" flag that the selection callback can look at, but I have not found a way to arrange for the flag to be reset in the case where the editing callback is invoked by an ordinary focus change not involving a button press (e.g. tab).
Perhaps editing-callback is not the right callback for error checking the text. Is there some other callback I should be using?
Any help/advice would be greatly appreciated.
Gail
(defun test (dialog-p)
(capi:contain
(let* ((text
(make-instance 'capi:text-input-pane
:editing-callback
#'(lambda (pane how)
(when (eq how :end)
(when (equalp (capi:text-input-pane-text pane) "bad")
(if DIALOG-P
(capi:display-message "Don't be bad!")
(progn
(format *trace-output* "Don't be bad!~%")
(capi:beep-pane)))
(setf (capi:text-input-pane-text pane) "good"))))))
(button (make-instance 'capi:push-button
:text "Do Something With Input"
:selection-callback
#'(lambda (item interface)
item interface
(capi:display-message
"Doing something with ~s"
(capi:text-input-pane-text text))))))
(make-instance 'capi:column-layout
:description (list text button)))))
Re: Editing callback interactions
On Tuesday 19 February 2008 1:22:50 pm Gail Zacharias wrote:
> Hello again, esteemed CAPI experts.
....
> I would like to arrange it so that if the user enters an invalid value in
Here's an orthogonal opinion...
Any sufficiently complicated UI (i.e. more than a few buttons) is a perfect
candidate for being designed and implemented as a consolidated state machine
(or more than one state machine). This is especially true with gui's because
the user can do anything / push anything at any time - you need a central
coordinator to track the users' actions.
If you try to do everything with callbacks, you end up with a bunch of state
variables anyway, but the result becomes unstructured and tough to maintain.
What I do, is to point every callback in an interface to the same routine
(which I usually call "transition" or "machine") which implements the state
machine and tracks all of the inter-related behaviour of the UI components in
one place. Each callback provides an "event" symbol to denote what action
occured,
e.g. :mouse-move, :left-pressed, :left-released, :toolbar-button-xyz-pushed,
etc. The state machine decides whether the event causes some action in the
current state or is ignored.
I recently posted a state machine macro to comp.lang.lisp. It implements
state machines with entry, transition and exit code - something which I know,
from decades of experience using state machines - is the kind of state
machine one should use in this problem space. (Contact me directly if you
can't find it, or want more info on how to use it).
You don't *need* the macro, you can implement the state machine by hand (but
you probably lose entry and exit code). A state machine is just a function
that looks at the incoming event and the current state and executes
one "step", and possibly changes the state of the machine. In CAPI, the
state variable can conveniently be an instance variable of the interface,
declared at the top of capi:define-interface.
pt
(and if Kenny were here, he would suggest that Cells be used :-).