 |
Dialog Diatribe
Posted by hansmuller on October 27, 2006 at 05:48 PM | Comments (23)
I've been writing the occasional small application recently and
now and then I blunder into a problem with Java SE that's,
uh..., well, annoying. I realize that I'm not the only one
who's had this experience and I'm probably not the only one who
seeks relief by writing a lengthy diatribe and then sending it
to whomever might be guilty of creating the situation. Of
course, in my case that's often me, and since relief usually
doesn't come from berating oneself, I'm guilty of sending the
occasional long crabby missive to the people who are currently
responsible for maintaining things that I'm probably responsible
for bollocksing up in the first place. It's not a particularly
endearing habit.
I sent the following to Swing's technical lead,
Shannon
Hickey, and he confirmed that the details, though twisted
with bile, are essentially correct. So in the interest of
furthering my own therapy, and also to ensure that some record of
this will be stored away in Google's indices till the end
of time, I thought I'd share.
I would think that a fairly common idiom in a Swing application
would be to popup a dialog in response to selecting a menu item.
Given Matisse, we'll
assume that the JDialog has been created with the IDE, rather
than some JOptionPane convenience method, and given rudimentary
aesthetics, assume the dialog should be centered over the menu
item's frame. Accomplishing this seems to be much too
difficult:
public void showMyDialog(ActionEvent e) {
// How to find the Dialog's Frame owner?
Window dialogOwner = null;
JDialog dialog = new MyDialog(dialogOwner, true); // true => modal Dialog
dialog.pack();
// How to center the Dialog?
dailog.setVisible(true);
}
The first problem to deal with is mapping from the menu item's
ActionEvent to the frame that contains the menu item. The
frame will be the dialog's owner as well as the component we're
going to center the dialog relative to.
There seems to be an overabundance of SwingUtilities methods that
address this trivial problem:
Window getWindowAncestor(Component c)
Window windowForComponent(Component c)
Component getRoot(Component c)
Sadly, none of them "work" for a JMenuItem. They all simply
traipse up the parent chain and in our case they find a
JPopupMenu and then null.
To find the Frame that owns a JMenuItem, we have to follow the JPopupMenu's
"invoker" property, which gets us back into the component
hierarchy. So to find the frame that corresponds to an ActionEvent
one must write (!):
Frame frameForActionEvent(ActionEvent e) {
if (e.getSource() instanceof Component) {
Component c = (Component)e.getSource();
while(c != null) {
if (c instanceof Frame) {
return (Frame)c;
}
c = (c instanceof JPopupMenu) ? ((JPopupMenu)c).getInvoker() : c.getParent();
}
}
return null;
}
It would more useful to have written
windowForActionEvent but sadly support for
creating Dialogs whose owner is a Window (the parent class for
Frames and Dialogs) only appeared in Java SE 6, and I needed
code that worked for Java SE 5. It's also worth noting that
this works for Applets too, although you'd be forgiven for not
guessing that this is true. Applets do have a Frame parent
that's created by the Java plugin and whose bounds are the same
as the Applet (Panel) itself.
But we're still not done, because we must also center the dialog
over the frame. Naturally there are other useful positions for
the dialog. Centering a dialog over its frame happens to be what
started me on this quest.
I have it on good authority that
Windows.setLocationRelativeTo() is the handy method for
this job. The javadoc for this method isn't promising:
Sets the location of the window relative to the specified
component.
OK so far. Except it sounds like I'm going to have to compute the
relative origin of my dialog and deal with edge (of the screen)
conditions. Yech.
If the component is not currently showing, or c is null, the
window is placed at the center of the screen. The center point
can be determined with GraphicsEnvironment.getCenterPoint
[sic]
Huh? What does "the component" refer to in this sentence? I
assume they're not referring to this Window and I have to
wonder what "showing" means in this context. Is is the same
thing as getVisible() being true? If not, do I
have to hope that my menu item is still "showing" when this
method is called? And what's this advice about
getCenterPoint (and where's the period)? In my
case the Window and its menu item are visible, so I'm hoping
none of this stuff applies. Because I don't really understand
it.
If the bottom of the component is offscreen, the window is
placed to the side of the Component that is closest to the
center of the screen. So if the Component is on the right part
of the screen, the Window is placed to its left, and visa
versa.
This, no doubt, means that the method will endeavor to find a
location for my dialog that respects the relative location I've
specified, without making part of the dialog appear off-screen.
Good, I think.
So I still appear to be stuck with computing an origin for my
dialog that centers it relative to its owner. Before I code
that, I try leaving the origin of the new dialog at 0,0, which
is the default:
public void showMyDialog(ActionEvent e) {
Window dialogOwner = frameForActionEvent(e);
JDialog dialog = new MyDialog(dialogOwner, true);
dialog.pack();
aboutBox.setLocationRelativeTo(dialogOwner);
dialog.setVisible(true);
}
Miraculously, this works. The dialog appears centered over the
dialogOwner unless that would cause the dialog to appear
off-screen. I have no idea why it works, since according to the
"spec" (and the name of the method) I should have had to compute
an appropriate relative origin for the dialog. But I guess I
don't.
Frankly, I think this whole mess is a mini-travesty. If I'm
going to show a dialog, I should be able to do so without
writing code that digs around the component hierarchy and
without experimentally determining what something as simple (and
not terribly useful) as Window.setLocationRelativeTo() does.
There, that feels a little better.
A
seven year old bug that covers the menu item to frame lookup
problem is still open. Given the fact that it's accumulated
exactly 0 votes in that time, perhaps no one has ever cared about
the problem quite as much as I do at this moment. I would think
that a cleaner way to handle this case would be some static
methods that handled the entire idiom, for example:
public void showMyDialog(AWTEvent event) {
Window dialogOwner = Window.eventToWindow(event);
JDialog dialog = new MyDialog(dialogOwner, true);
Window.showModalDialog(dialog); // Center dialog relative to its owner
}
And Shannon suggested that the method name might be rationalized
as implying that the Window is be moved to a location that makes
its relationship to the component parameter obvious.
Typically that means centering the Window relative to the
component. In return for that tortured explanation, I had to
agree to file an RFE about the
Window.setLocationRelativeTo() javadoc. I haven't
done so yet. But I will.
Thanks for listening.
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
How about Window.showModalDialog(window, location, effect) where location is north, south, east, west, or center. Center is obvious. North gives you mac-like dialog on the top. 'Effect' could be something like a window slide-in (again like on a mac), or alpha fade-in.
(just dreamin')
jeff
Posted by: jdinkins on October 27, 2006 at 06:11 PM
-
"Given the fact that it's accumulated exactly 0 votes in that time, perhaps no one has ever cared about the problem quite as much as I do at this moment."
Or, more likely, they just have three other things they care about more.
Posted by: coxcu on October 27, 2006 at 08:09 PM
-
> "Given the fact that it's accumulated exactly 0 votes in that time"
I wonder when, if ever, Sun is going to understand that the voting systems doesn't work. It has been said again and again and for instance on every JavaOne alumni meeting I've attended. The Bug Charade and everything around it doesn't work. Period. It's a usability disaster and basically gives nothing back to the developer, so why bother?
OK, I haven't gotten my coffee yet. Sorry.. ;-)
Cheers,
Mikael.
Posted by: mikaelgrev on October 28, 2006 at 01:23 AM
-
I ran into this quite painfully ;-) I just replaced the SwingUtilities.windowForComponent() calls with my own utility class. In this class I made the assumption that the only time this method won't find a window is when the main window is involved (I don't have popups in my dialogs) so I just used Frame.getFrames() found the first visible frame (only one visible top level frame at any time) and displayed on that... Terrible hack but it worked for me.
I would have submitted a bug or looked for one but finding a bug like that is very hard within the bug database especially when you are cramped for time on a release... Its a bit of a problem to find a bug in such a huge database that has such "odd" categories (not that bugzilla is better).
Posted by: vprise on October 28, 2006 at 03:15 AM
-
Dear Hans,
In case you do any changes to the JOptionPane API, please consider implementing support for document modal dialogs.
In J2SE6, we finally got support for this in AWT class Dialog. But without some higher level support in Swing, I fear this API will be underused.
I implemented an API for document modal dialogs and file choosers specifically for Mac OS X some time ago: JSheet.
Best, Werner
Posted by: wrandelshofer on October 29, 2006 at 07:29 AM
-
> Huh? What does "the component" refer to in this sentence?
This "component" is the component passed as a parameter to the method setLocationRelativeTo()
> I have to wonder what "showing" means in this context
isShowing() is a method in java.awt.Component class. It is used to verify that a screen location for the component can be queries, see getLocationOnScreen() method in java.awt.Component class
Posted by: ixmal on October 30, 2006 at 05:48 AM
-
good article......solved a whole lot of probs.....
Jaji
Posted by: lukman_jaji on October 30, 2006 at 09:46 AM
-
I always delegated the action item to a mediator class.
This allowed me the option of creating a static dialog (preferred) or creating a new one on each event call.
My mediator always had a static method for getting the Frame which you had to have laying around.
The version of TFrame on the java.net site is pretty old. It actually had a slew of Action Items and they were created with a reference back to the main Frame object.
So I would create the Action Items when I created the Toolbar and MenuBar.
Then after creating all of that I would reference these with the mediator.
The hardest thing in a large Swing application is order of contruction and an easy way to get to something.
I've never been a big fan of putting business logic in a listener tied to a component unless it was very simple. Anything complex I always registered the component and delegated listeners to a mediator class.
I would keep a static hash of ActionItems and JComponents.
Then I could from anywhere update, change or notify what ever I needed.
There is NOTHING worse than being in a very large Swing application and someone wanting you to disable or enable components based on an event that is 3 dialogs/panels away.
Let me know if you need more info but the problem you mention is why everyone doing serious Java Swing applications wrote their own custom framework. ;-)
This problem is old news. ;-) My biggest gripe these days is how to easily deal with multiple monitor displays. The video driver for my Matrox G450 tends to give interesting values back to the JVM.
Regards,
cupofjoe
Posted by: cupofjoe on October 31, 2006 at 08:53 AM
-
I've been out for a few days and haven't been able to respond to any
of the comments. Will do so now; hopefully someone will take a look.
Posted by: hansmuller on November 01, 2006 at 10:35 AM
-
[response to Jeff Dinkins], per:
Window.showModalDialog(window, location, effect)
What you're suggesting would definitely be an improvement. The
overall goal has to be making the common cases trivially, as well as
make it easy to "do the right thing". I've often shuddered to see a
slapped together app popup a dialog in the upper left hand corner of
the screen. I wish that was a little more difficult :-).
Posted by: hansmuller on November 01, 2006 at 10:36 AM
-
[response to Mikael Grev] per:
... The Bug Charade and everything around it doesn't
work. Period. It's a usability disaster and basically gives nothing
back to the developer, so why bother?
OK, I haven't gotten my coffee yet. Sorry.. ;-)
It's been a few days, no doubt you've had a cup of coffee now. I
doubt that anyone thinks that the Bug Parade is the apotheosis
of bug/rfe management. How would you improve it?
Posted by: hansmuller on November 01, 2006 at 10:37 AM
-
[response to vprise] Sorry to hear that you had to hack your
way through this little thicket too. I agree that finding an existing
bug that matches a problem that you've run into is a difficult, I have
to admit that Google is often a more direct path to developers who've
had similar problems and who may have left a solution behind.
Posted by: hansmuller on November 01, 2006 at 10:38 AM
-
[response to Werner Randelshofer]
I'd be interested to hear what sort of higher level support for
(document) modal dialogs you think would be worth adding to Swing. As
far (just a quick look) JSheet provides roughly the same sort of
conveneince methods as JOptionPane.
Posted by: hansmuller on November 01, 2006 at 10:38 AM
-
[response to cupofjoe]
The point of my blog was just that the common idiom I'd coded
shouldn't have required so much effort, e.g. one shouldn't need a
custom application framework for something so simple. Of the many
valid points you made, I particularly liked this one:
The hardest thing in a large Swing application is order of contruction and an easy way to get to something.
I've found this to be true too, and the difficulty is almost always
self-inflicted. I hope we'll find a way to for most developers to
get this kind of thing right with JSR-296.
Posted by: hansmuller on November 01, 2006 at 10:39 AM
-
Hans,
I'm of course talking about the version that we as users see, you might have another system on the inside? Basically everything with it is bad. And yes, I have gotten plenty of coffee.
The thing is that there is no way to communicate around a bug in a decent manner. The bug forum software is buggy and has no threads (you can't see who is responding to what). You don't know who the evaluator is (xxxxxx@xxxxx.xxx). The voting system is very inflexible and it takes no consideration regarding how much time and effort you actually is spending. A newbie and a really really helpful contributor all have three votes, in all. If it is like Sun say, that the voting has a deep effect on your priority for a fix, I am scared.
There is no formal way to reopen a bug. Everything feels 1997 in there. There are only "open" and "closed" categories, several more should be added. Search is a joke (and a bad one at that).
Anyone with even a little bit of usability training can find things to improve when it comes to UI.
I've filed a few bugs in the past but the process was so time consuming and gave nothing back so I work around bugs in silent nowadays.
Here is some more I've written before (under mgrev):
http://today.java.net/pub/pq/24
Cheers,
Mikael Grev
Posted by: mikaelgrev on November 01, 2006 at 01:42 PM
-
Here is some more info regarding the bug parade: http://www.javalobby.org/java/forums/m91817273.html#91817273
Posted by: mikaelgrev on November 01, 2006 at 01:45 PM
-
More discussion about what is wrong with bug parade:
http://weblogs.java.net/blog/robogeek/archive/2006/09/bug_tracking_sy.html
Posted by: coxcu on November 02, 2006 at 08:31 AM
-
[response to Mikael Grev] per improving the bug parade.
I've looked at the bugtraq comments that you
referred to and also at
Dave Herron's blog on this topic. I hope you can appreciate that
I didn't intend to inspire a debate about bug tracking by writing this
blog. It was really all about a small missing link in Swing that had
to do with popping up a dialog from a menu item's action.
Nevertheless, I did ask for some suggestions about improving the bug parade,
and you are on record with some sensible ideas (I'm paraphrasing):
Provide a rich web started client for interacting with the bug
database. You mentioned this three times.
We use such a client within Sun and I wish there was a public
version for everyone else to use instead of the clunky browser
bug parade client.
Give people a way to weigh their votes, e.g. a scale from -5
(least important) to +5 (most important), and don't limit
the number of bugs a developer can vote on.
This sounds reasonable however given the large community that
uses the bug database, I suspect that it would be difficult to
ensure that everyone used a more flexible system consistently.
Automatically detect voters who are trying to game the
system.
No doubt by peering into their souls with a web cam :-).
Seriously: I agree that heuristic could be applied that might
appear to balance competing interests as reflected by voting.
I think it's more likely that what you'd really be doing is
just complicating the rules of a game.
Thread comments properly: it should be possible to reply to
a specific comment/author.
I completely agree.
Identify users by role, e.g. Sun developer/manager/politician
or "senior poster", "poster", "newbee" etc.
I'm the last person you'd want to ask about how to set up
a system for assigning such roles however I agree that it
might help one interpret bug descriptions and comments and
so on.
Fine grained access controls by role. For example only
some kinds of voting would be limited to users with specific roles.
There's a little of this implicit in the existing system.
I suspect that trying to institute a more elaborate system of
access controls would be a wee bit controversial.
Polling: give users an opporunity to take the pulse of everyone
who's interested in a particular bug.
If used responsibly I think "custom polling" would make it much
easier to summarize developers' feedback. It could also become
a tool for rabble rousing.
Posted by: hansmuller on November 02, 2006 at 10:18 AM
-
[response to hansmuller] [...]I'd be interested to hear what sort of higher level support for (document) modal dialogs you think would be worth adding to Swing [...] JSheet provides roughly the same sort of conveneince methods as JOptionPane.
JSheet adds a SheetListener to the convenience methods of JOptionPane and of JFileChooser. The JOptionPane pane methods return after the user has approved or dismissed the dialog. Whereas the JSheet methods return immediately, and later the SheetListener is notified when the user has approved or dismissed the dialog.
Before using this approach, I tried Foxtrot, but for document modal dialogs, the approach that Foxtrot uses does not work at all.
Thanks, Werner.
Posted by: wrandelshofer on November 02, 2006 at 10:31 PM
-
Re. centering dialogs, don't forget that a naive implementation can look bad on a multiheaded display (Xinerama etc.) - you don't want the dialog split across two displays even if the owner frame is. Currently the NB IDE uses: http://www.netbeans.org/source/browse/~checkout~/openide/util/src/org/openide/util/Utilities.java#findCenterBounds
Posted by: jglick on November 06, 2006 at 08:02 AM
-
exactly! Hopefully the new JSR will address that. Things like this have been driving me crazy for years. In response, we've ended up developing our own abstractions around the UI : SystemShell, Workspace, etc., b/c Swing shouldn't be that hard, and developing swing apps in a corporate environment is like trying to get a Lotus Notes developer these days, the ones that can be productive day one are hard to find (b/c the cost of being that good is too high in Swing). Personally, I can't wait for the app framework to gain traction. The desktop, is still IMO, a neglected area for Java. It's just been waiting for something better.
Posted by: bcchun on November 08, 2006 at 05:10 AM
-
Sorry to intrude with an out of topic, but I'm starting to develop a new swing app and I would like to use the classes described in the JSR 296... if only these were available...
Any hope to see them out soon?
Posted by: aaime on November 18, 2006 at 09:01 AM
-
Hans listed these three methods that don't work on a JMenuItem:
* Window getWindowAncestor(Component c)
* Window windowForComponent(Component c)
* Component getRoot(Component c)
To these, I would add this one:
* SwingUtilities.getRootPane(Component c)
All of these use a loop based on a variation of parent = parent.getParent(). They all trip up when "parent' is a JPopupMenu. I believe all of these could be fixed if Sun override JPopupMenu.getParent() to call getInvoker(). Does anybody know if there's a downside to a fix like this?
Posted by: miguelm on December 06, 2006 at 02:17 PM
|