A Swing question about readable control-flow

L

Leo Breebaart

I have a control-flow issue I was hoping to get some advice on.

In my Swing GUI, I have a button. If I click on that button, I
want, via its associated Action, but, and this is the kicker:
*depending on a certain condition*, to show and process the
results of a modal JDialog *before* I perform the actual action.
But the action should always run. It's just the showing of the
dialog that is dependent on outside context.

How I would *like* to implement this:


public class MyAction extends AbstractAction
{

public void actionPerformed(ActionEvent e)
{
if (hasQuestionnaire)
showQuestionnaire();

doStuff();
}

private void showQuestionnaire()
{
final QuestionnaireDialog dialog = new QuestionnaireDialog();
dialog.setVisible(true);
dialog.getCancelButton().addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
dialog.setVisible(false);
// process cancel
}
});
dialog.getSubmitButton().addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
dialog.setVisible(false);
// process submit
}
});
}
}

This does not work, because the showQuestionnaire method will put
up the dialog without blocking, causing 'doStuff' to be executed
immediately afterwards, without waiting for the dialog to be
dismissed.

I can solve the issue by moving the doStuff() call into both of
the dialog's registered ActionListeners, *and* by placing the
MyAction's doStuff() call into an else-branch of the
if-statement, but all that multiplication of code (even if it's
just a single line) smells very wrong to me. It makes the actual
control flow a lot more difficult to follow.

Am I missing something simple? Does anybody have any advice on
how they would implement this cleanly?

Many thanks in advance,
 
D

Daniele Futtorovic

I have a control-flow issue I was hoping to get some advice on.

In my Swing GUI, I have a button. If I click on that button, I
want, via its associated Action, but, and this is the kicker:
*depending on a certain condition*, to show and process the
results of a modal JDialog *before* I perform the actual action.
But the action should always run. It's just the showing of the
dialog that is dependent on outside context.

How I would *like* to implement this:


public class MyAction extends AbstractAction
{

public void actionPerformed(ActionEvent e)
{
if (hasQuestionnaire)
showQuestionnaire();

doStuff();
}

private void showQuestionnaire()
{
final QuestionnaireDialog dialog = new QuestionnaireDialog();
dialog.setVisible(true);
dialog.getCancelButton().addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
dialog.setVisible(false);
// process cancel
}
});
dialog.getSubmitButton().addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
dialog.setVisible(false);
// process submit
}
});
}
}

This does not work, because the showQuestionnaire method will put
up the dialog without blocking, causing 'doStuff' to be executed
immediately afterwards, without waiting for the dialog to be
dismissed.

I can solve the issue by moving the doStuff() call into both of
the dialog's registered ActionListeners, *and* by placing the
MyAction's doStuff() call into an else-branch of the
if-statement, but all that multiplication of code (even if it's
just a single line) smells very wrong to me. It makes the actual
control flow a lot more difficult to follow.

Am I missing something simple? Does anybody have any advice on
how they would implement this cleanly?

Many thanks in advance,

Unless I'm mistaken, and if QuestionnaireDialog extends JDialog, then
you simply have to specify the dialog as /modal/ (see the Javadoc --
it's done in the c'tor). That way, the call to setVisible(true) will
*block* until the dialog is hidden; which means, in turn, you'll have to
add the listeners /prior/ to showing it.
 
L

Leo Breebaart

Daniele Futtorovic said:
Unless I'm mistaken, and if QuestionnaireDialog extends
JDialog, then you simply have to specify the dialog as /modal/

I apologise, I simplified my use case a little bit too much in
order to present it here concisely.

Making the JDialog (which is indeed the QuestionnaireDialog's
base class) modal indeed goes a long way towards what I want --
but I want a bit more.

In reality, my application has a number of these Actions. I want
the user to be able to continue to select other Actions, even if
he or she is not yet finished with the current one.

If I make the dialog modal, the main window becomes inaccessible
and no other buttons can be clicked. If I set the ModalityType of
my QuestionnaireDialog to DOCUMENT_TYPE, I *almost* get what I
want. Now I can indeed start multiple actions (and I take care to
disable the originating Action as long as its Questionnaire is
showing, so you can not start the same action twice), but
unfortunately the last-started Questionnaire will always be the
only available to user input. You cannot select any of the
earlier Questionnaire dialogs -- they are visible, but disabled.
You can start multiple actions, but you have to unwind the dialog
stack one by one.

If I could solve that one remaining problem, life would be
perfect...

Either way, thanks for your response -- much appreciated.
 
J

John B. Matthews

Leo Breebaart said:
I apologise, I simplified my use case a little bit too much in
order to present it here concisely.

Making the JDialog (which is indeed the QuestionnaireDialog's
base class) modal indeed goes a long way towards what I want --
but I want a bit more.

In reality, my application has a number of these Actions. I want
the user to be able to continue to select other Actions, even if
he or she is not yet finished with the current one.

If I make the dialog modal, the main window becomes inaccessible
and no other buttons can be clicked. If I set the ModalityType of
my QuestionnaireDialog to DOCUMENT_TYPE, I *almost* get what I
want. Now I can indeed start multiple actions (and I take care to
disable the originating Action as long as its Questionnaire is
showing, so you can not start the same action twice), but
unfortunately the last-started Questionnaire will always be the
only available to user input. You cannot select any of the
earlier Questionnaire dialogs -- they are visible, but disabled.
You can start multiple actions, but you have to unwind the dialog
stack one by one.

If I could solve that one remaining problem, life would be
perfect...

Sorry if I misunderstand, but would a JTabbedPane with each Action in a
tab be useful?
 
L

Leo Breebaart

John B. Matthews said:
Sorry if I misunderstand, but would a JTabbedPane with each Action in a
tab be useful?

Yes, I think that could work in general. In this case I fear a
TabbedPane GUI is not really an option for various annoying other
reasons I won't bore you with here, but it's a good thing to keep
in mind if I encounter this problem again in a different context.
Thanks!
 
D

Daniele Futtorovic

I apologise, I simplified my use case a little bit too much in
order to present it here concisely.

Making the JDialog (which is indeed the QuestionnaireDialog's
base class) modal indeed goes a long way towards what I want --
but I want a bit more.

In reality, my application has a number of these Actions. I want
the user to be able to continue to select other Actions, even if
he or she is not yet finished with the current one.

If I make the dialog modal, the main window becomes inaccessible
and no other buttons can be clicked. If I set the ModalityType of
my QuestionnaireDialog to DOCUMENT_TYPE, I *almost* get what I
want. Now I can indeed start multiple actions (and I take care to
disable the originating Action as long as its Questionnaire is
showing, so you can not start the same action twice), but
unfortunately the last-started Questionnaire will always be the
only available to user input. You cannot select any of the
earlier Questionnaire dialogs -- they are visible, but disabled.
You can start multiple actions, but you have to unwind the dialog
stack one by one.

If I could solve that one remaining problem, life would be
perfect...

Either way, thanks for your response -- much appreciated.

I see. I might note that your design may be a bit awkward (in terms of
"good UI design"), and may confuse some users. You might want to look
for better ways to visually represent the flow you envision.

Anyway, making the Dialog modal would have been the legacy way of having
the code wait until it's closed for the processing to resume, but the
basic idea is very simple anyway:

public interface ResultHolder<E extends Object> {
void setResult(E e);

E getResult();
}

public static class BlockingDialogWithResult<E extends Object>
extends JDialog
{
private final ResultHolder<E> resultHolder;

protected BlockingDialogWithReturnValue(Frame parent,
ResultHolder<E> rh){
super(parent);

if( rh == null )
throw new IllegalArgumentException(new NullPointerException());

resultHolder = rh;

init0();
}

private void init0(){
addWindowListener(
new WindowAdapter(){
public void windowClosed(WindowEvent e){
synchronized( resultHolder ){
resultHolder.setResult( getResultValue() );
resultHolder.notifyAll();
}
}
}
);
}

public abstract E getResultValue();
}

public static Object showBlockingDialogAndGetResult(final Frame parent){
final ResultHolder<Object> rh = new
SomeResultHolderImplementation<Object>();

EventQueue.invokeLater(
new Runnable() {
public void run(){
BlockingDialogWithResult<Object> d = new
SomeBlockingDialogWithResultImplementation<Object>(parent, rh);

d.setVisible(true);
}
}
);

synchronized( rh ){
rh.wait();
}

return rh.getResult();
}


Something like that, anyway (code is from scratch, untested,
uncompiled). Note: you'll have to invoke the static method above from
elsewhere than the EDT.
 
L

Leo Breebaart

Daniele Futtorovic said:
In reality, my application has a number of these Actions. I
want the user to be able to continue to select other Actions,
even if he or she is not yet finished with the current one.

If I make the dialog modal, the main window becomes
inaccessible and no other buttons can be clicked. If I set
the ModalityType of my QuestionnaireDialog to DOCUMENT_TYPE,
I *almost* get what I want. Now I can indeed start multiple
actions (and I take care to disable the originating Action as
long as its Questionnaire is showing, so you can not start
the same action twice), but unfortunately the last-started
Questionnaire will always be the only available to user
input. You cannot select any of the earlier Questionnaire
dialogs -- they are visible, but disabled. You can start
multiple actions, but you have to unwind the dialog stack one
by one. [...]

I see. I might note that your design may be a bit awkward (in
terms of "good UI design"), and may confuse some users. You
might want to look for better ways to visually represent the
flow you envision.

Hmmm. I don't want to abuse the newsgroup and anyone's time, so
if nobody is up for this discussion I will completely understand,
but let me explain the context in a bit more detail. I am always
interested in suggestions to improve the UI.

My application has to present the user with a main console from
which various "experiments" can be chosen by clicking on a
button/link/whatever. Users have to be free to run these
experiments at random and simultaneously (and even multiple
instances of the same one), hence my desire to not block the
entire application with one experiment's modal dialogs.

These non-modal experiments all take the form of third-party
"black box" applications that I am starting with ProcessBuilder.
So they all have their own GUIs that are *not* under my control
at all. This is why I feel I can't really use the suggestion made
earlier of giving every experiment its own tab pane to run in.
the tab pane would be empty -- there will be windows and frames
all over the place anyway (and I know *that* may end up confusing
the user, but the 'multiple simultaneous apps' is a basic
requirement I cannot escape from),

Now some, not all, of these experiments, need to be run bookended
by pre- and post-run questionnaires the user has to fill in. The
user *is* allowed to cancel the questionnaire, after which the
experiment app will be launched regardless (but in 'play' mode --
the experiment will not be logged in the experiment database).

And so that is why I want to be able to execute multiple
simultaneous Actions, with every Action in effect being a modal
wrapper around an application I have no further control over
during its run.

Does my UI approach make a little more sense now?
 
N

Nigel Wade

Leo said:
Daniele Futtorovic said:
In reality, my application has a number of these Actions. I
want the user to be able to continue to select other Actions,
even if he or she is not yet finished with the current one.

If I make the dialog modal, the main window becomes
inaccessible and no other buttons can be clicked. If I set
the ModalityType of my QuestionnaireDialog to DOCUMENT_TYPE,
I *almost* get what I want. Now I can indeed start multiple
actions (and I take care to disable the originating Action as
long as its Questionnaire is showing, so you can not start
the same action twice), but unfortunately the last-started
Questionnaire will always be the only available to user
input. You cannot select any of the earlier Questionnaire
dialogs -- they are visible, but disabled. You can start
multiple actions, but you have to unwind the dialog stack one
by one. [...]

I see. I might note that your design may be a bit awkward (in
terms of "good UI design"), and may confuse some users. You
might want to look for better ways to visually represent the
flow you envision.

Hmmm. I don't want to abuse the newsgroup and anyone's time, so
if nobody is up for this discussion I will completely understand,
but let me explain the context in a bit more detail. I am always
interested in suggestions to improve the UI.

My application has to present the user with a main console from
which various "experiments" can be chosen by clicking on a
button/link/whatever. Users have to be free to run these
experiments at random and simultaneously (and even multiple
instances of the same one), hence my desire to not block the
entire application with one experiment's modal dialogs.

These non-modal experiments all take the form of third-party
"black box" applications that I am starting with ProcessBuilder.
So they all have their own GUIs that are *not* under my control
at all. This is why I feel I can't really use the suggestion made
earlier of giving every experiment its own tab pane to run in.
the tab pane would be empty -- there will be windows and frames
all over the place anyway (and I know *that* may end up confusing
the user, but the 'multiple simultaneous apps' is a basic
requirement I cannot escape from),

Now some, not all, of these experiments, need to be run bookended
by pre- and post-run questionnaires the user has to fill in. The
user *is* allowed to cancel the questionnaire, after which the
experiment app will be launched regardless (but in 'play' mode --
the experiment will not be logged in the experiment database).

And so that is why I want to be able to execute multiple
simultaneous Actions, with every Action in effect being a modal
wrapper around an application I have no further control over
during its run.

Does my UI approach make a little more sense now?

Java 6 has extended the concept of modality so you should be able to do what you
want in Java 6. In previous versions the modality was application modality,
which blocked everything. There are now application, document and toolkit
modalities, and you can have excluded dialogs which are not blocked even by
application modal dialogs.

Have you looked at the modality examples in the Java Tutorial no Swing? This one
looks as though it might form the basis of what you are looking for:
http://java.sun.com/docs/books/tutorial/uiswing/misc/modality.html

For document modality a modal dialog blocks windows which are part of the same
document (a document is a top-level window without an owner), so all rooted at
the same ownerless top-level window. It doesn't block access to other windows
which have different top-level windows. The example above uses two distinct
chains which are independently modal, but also an application modal dialog
which blocks both the other chains. Also, windows which are themselves
descendants of a modal dialog box are still accessible when the modal dialog is
displayed.
 
D

Daniele Futtorovic

Daniele Futtorovic said:
In reality, my application has a number of these Actions. I
want the user to be able to continue to select other Actions,
even if he or she is not yet finished with the current one.

If I make the dialog modal, the main window becomes
inaccessible and no other buttons can be clicked. If I set
the ModalityType of my QuestionnaireDialog to DOCUMENT_TYPE,
I *almost* get what I want. Now I can indeed start multiple
actions (and I take care to disable the originating Action as
long as its Questionnaire is showing, so you can not start
the same action twice), but unfortunately the last-started
Questionnaire will always be the only available to user
input. You cannot select any of the earlier Questionnaire
dialogs -- they are visible, but disabled. You can start
multiple actions, but you have to unwind the dialog stack one
by one. [...]
I see. I might note that your design may be a bit awkward (in
terms of "good UI design"), and may confuse some users. You
might want to look for better ways to visually represent the
flow you envision.

Hmmm. I don't want to abuse the newsgroup and anyone's time, so
if nobody is up for this discussion I will completely understand,
but let me explain the context in a bit more detail. I am always
interested in suggestions to improve the UI.

My application has to present the user with a main console from
which various "experiments" can be chosen by clicking on a
button/link/whatever. Users have to be free to run these
experiments at random and simultaneously (and even multiple
instances of the same one), hence my desire to not block the
entire application with one experiment's modal dialogs.

These non-modal experiments all take the form of third-party
"black box" applications that I am starting with ProcessBuilder.
So they all have their own GUIs that are *not* under my control
at all. This is why I feel I can't really use the suggestion made
earlier of giving every experiment its own tab pane to run in.
the tab pane would be empty -- there will be windows and frames
all over the place anyway (and I know *that* may end up confusing
the user, but the 'multiple simultaneous apps' is a basic
requirement I cannot escape from),

Oh dear.

Now some, not all, of these experiments, need to be run bookended
by pre- and post-run questionnaires the user has to fill in. The
user *is* allowed to cancel the questionnaire, after which the
experiment app will be launched regardless (but in 'play' mode --
the experiment will not be logged in the experiment database).

And so that is why I want to be able to execute multiple
simultaneous Actions, with every Action in effect being a modal
wrapper around an application I have no further control over
during its run.

Does my UI approach make a little more sense now?

How much sense does coercion make? :)

If you have to do it like that, then you have to do it like that. My
remarks were made under the assumption you had full control over the app.

Design issues aside, have you tried the code I suggested? You won't be
blocked that way.
 

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,580
Members
45,053
Latest member
BrodieSola

Latest Threads

Top