What is the design pattern for GUI menu/toolbar activation

T

timasmith

So suppose you have a complex frame with multiple panels and depending
on the state of specific controls on the panels - you should enable or
disable menus and toolbars accordingly.

Menus have a workaround in that you could perform logic on 'click' -
but what about toolbars.

If I select text so copy and cut should be enabled. If a click on a
node so my delete button should enable.

I want to avoid polling the entire frames state every second of course.


Perhaps I could have some kind of property listener for the relevant
controls that on change changes (de)activates the toolbar - but the
cut/copy example shows the impracticality of that.

There must be a design pattern for this...

Tim
 
P

Phlip

timasmith said:
I want to avoid polling the entire frames state every second of course.

Observer Pattern, from the book /Design Patterns/. It decouples the target
from the observer, so neither knows the other's type.

When you discover that you typically have a triangle of observers, then you
have Model-View-Controller.
 
C

Chris Uppal

Phlip said:
Observer Pattern, from the book /Design Patterns/. It decouples the target
from the observer, so neither knows the other's type.

In particular, since you are posting in Java groups, look into the buttons'
Actions.

-- chris
 
W

wizofaus

Chris said:
In particular, since you are posting in Java groups, look into the buttons'
Actions.
Even within what Java/Swing supplies, there still seems to be a good
deal of room for various implementations. For instance, if you have
cut & copy buttons in a toolbar, the chances are they can apply to more
than one control in your application: you may have text-editing
controls (JTextComponent derived), and you may also have some other
sort of object manipulation editor. Usually then the buttons need to
enabled if and only if some control is focused that can currently
support the relevant action. Problem is, there isn't really a
consistent interface to determine when that state may change and how to
query it. For a JTextComponent, usually you'd want to add a
CaretListener that sets the Actions' enable status depending on whether
there is text selected. But there may be other events that cause
whether text is selected to change, for instance, programmatically
changing the text on the underlying document, that are not trapped by
this. For some other sort of control, there may be a rather different
way of trapping whether the current selection has changed. And then
you need to way of trapping every time focus is moved from one control
to another, just in order to determine which control should be
determining whether the commands are enabled.
Further more, while there is built-in support for controlling the
enable status of buttons, the same is not true for the "selected"
state, i.e. for toggle-style buttons that may reflect attributes of the
current selection (e.g. the "Bold" button in a word processor).

I have to say, after spending some time trying to get all this to work
for me, MFC's command update mechanism, which basically constantly
refreshes the status of all toolbar buttons every time there is a lull
in the windows message queue, is a good deal simpler to work with. You
never have to worry about which listeners to install, and 90% of the
time it correctly takes care of automatically querying the currently
focused window/control. I'm actually curious if someone has attempted
to emulate this in Java - if not, I'm extremely tempted to do so
myself. I doubt there would be a way to trap "idle" message queue
events, but even if it works by polling at a fixed interval, it would
do the job.
 
C

Chris Uppal

Problem is, there isn't really a
consistent interface to determine when that state may change and how to
query it. For a JTextComponent, usually you'd want to add a
CaretListener that sets the Actions' enable status depending on whether
there is text selected. But there may be other events that cause
whether text is selected to change, for instance, programmatically
changing the text on the underlying document, that are not trapped by
this.

I think the underlying problem here is trying to tie the activation state of
the Action to overly low-level implementation state. If the Action, say, is a
Listener itself (which is how I would probably want to set it up) then it
wouldn't listen to the state of the text component, but to the state of the
/application/ -- since it's the application which knows whether the
corresponding operation is applicable at <some instant in time>. Granted that
that is more work to set up, and may also be foreign to the way some
programmers think about application structure.

I have to say, after spending some time trying to get all this to work
for me, MFC's command update mechanism, which basically constantly
refreshes the status of all toolbar buttons every time there is a lull
in the windows message queue, is a good deal simpler to work with.

The system (not Java or MFC) I work with most uses that implementation
technique too. It uses the idea of an "action" (which talks to the
application, not the low-level components) as above, but doesn't use the
Observer pattern. It just updates the enabledness (and related aspects) of
command widgets whenever idle time starts. It's a hack, but it does work well
in practice -- being simple, reliable, and requiring little maintenance.

-- chris
 
W

wizofaus

Chris said:
The system (not Java or MFC) I work with most uses that implementation
technique too. It uses the idea of an "action" (which talks to the
application, not the low-level components) as above, but doesn't use the
Observer pattern. It just updates the enabledness (and related aspects) of
command widgets whenever idle time starts. It's a hack, but it does work well
in practice -- being simple, reliable, and requiring little maintenance.
I actually don't see it as a hack at all...it seems exactly the right
sort of thing to use idle processing for.
Unfortunately I can't see anyway of doing it portably using standard
Java - although the AWT toolkit (or more specifically, the singleton
AWTAutoShutdown object) is notified every time the Windows message
queue goes idle, there's no way of overriding what happens at this
point (you can't override creation of the AWTAutoShutdown object to do
the necessary subclassing). And as for what happens on other platforms,
who knows.
Which probably leaves as the only realistic option using a timer, which
is definitely slightly hackish, but trying to manually keep track of
every single event that can modify toolbar button state is really not
feasible (for instance, if someone copies something to the system
clipboard in another application, your own app's Paste button might get
disabled if the data format is not supported).
 
C

Chris Uppal

So suppose you have a complex frame with multiple panels and depending
on the state of specific controls on the panels - you should enable or
disable menus and toolbars accordingly.

I happened across this article today while looking for something completely
unrelated:

http://www.jot.fm/issues/issue_2004_05/column6

I have only skimmed it very quickly, but it may have some useful ideas.

-- chris
 
W

wizofaus

Chris said:
I happened across this article today while looking for something completely
unrelated:

http://www.jot.fm/issues/issue_2004_05/column6

I have only skimmed it very quickly, but it may have some useful ideas.

Nothing to do with updating the state of toolbar buttons/menu items
depending on the state of the currently active control etc.

One comment interests me
"...what if many components are interested in a text-field action? I
suggest that if this occurs, we are too tied to the components. That
text-field should be treated as a controller in a model-view-controller
design pattern. A control should alter a model. Views that are
interested in the model should be observing the model, not the
controller."

But that doesn't strike me as realistic. For instance, my
application's primary view allows manipulation of objects such as
boxes, lines and text-boxes. The "cut", "copy", "paste" and even
"undo" and "redo" actions while you are working in that view applies to
whole objects. But you can also click on a text-box object to edit the
underlying text. Once that becomes the current control, then "cut",
"copy", "paste", "undo" and "redo" are all handled by the text-editing
control. I don't see how it could be handled by any sort of "model" in
the traditional M-V-C pattern. So instead I have to supply code to
route the actions that each toolbar button fires down to the currently
active control. And note that detecting the currently active control
is not completely trivial - the only way I've found of doing it
reliably is to add a FocusListener to each button, and store a
reference to the component that previously had the focus. Using the
ActionEvent source or the KeyboardFocusManager's focus owner don't
help, as they both point to the actual toolbar button itself.
 
C

Chris Uppal

(e-mail address removed) wrote:

[me:]
I actually don't see it as a hack at all...it seems exactly the right
sort of thing to use idle processing for.

Well, hack or not, I agree that there doesn't seem to be a way to hook idle
time in Java.

Which probably leaves as the only realistic option using a timer, which
is definitely slightly hackish,

<nods sadly/>

-- chris
 
C

Chris Uppal

(e-mail address removed) wrote:

[me:]
Nothing to do with updating the state of toolbar buttons/menu items
depending on the state of the currently active control etc.

Ah, well... Apologies for the misdirect, I thought it looked relevant, but
you are right, it is not.

One comment interests me
"...what if many components are interested in a text-field action? I
suggest that if this occurs, we are too tied to the components. That
text-field should be treated as a controller in a model-view-controller
design pattern. A control should alter a model. Views that are
interested in the model should be observing the model, not the
controller."

But that doesn't strike me as realistic. For instance, my
application's primary view allows manipulation of objects such as
boxes, lines and text-boxes. The "cut", "copy", "paste" and even
"undo" and "redo" actions while you are working in that view applies to
whole objects. But you can also click on a text-box object to edit the
underlying text. Once that becomes the current control, then "cut",
"copy", "paste", "undo" and "redo" are all handled by the text-editing
control. I don't see how it could be handled by any sort of "model" in
the traditional M-V-C pattern.

I agree, but I don't think that sort of event was what the quoted passage was
referring to. I take it to mean that if we are interested in, say, whether the
text field contains a valid number, then we should be Observing the state of
the text field's Model -- if that is a String (or some sort of value holder
containing a String) which parses as a valid number then other UI elements will
update themselves accordingly.

But the specific actions you mention are not Model-related in the MVC sense --
they are, I think, the responsibility of the Controller. What you have is a
pattern where you have a sort of generic controller-like object, installed near
the application's top level, which responds to user input by finding another
contoller and invoking that controllers' specific cut, paste, or whatever code.
And....
So instead I have to supply code to
route the actions that each toolbar button fires down to the currently
active control. And note that detecting the currently active control
is not completely trivial

..... as you note, that isn't too simple.

(BTW, the non-Java UI system I mentioned before has exactly the same problem
with buttons and toolbar buttons with invoke actions "for" some other
component.)

-- chris

-- chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top