Reputation: 2006
I have a JFilechooser
placed on top of a JFrame
glass pane. When I select the "look in" popup it seems to be showing up behind the JFilechooser
. I fixed this a while ago with a solution I found online to force the popup to be a heavyweight component:
try {
field = PopupFactory.class.getDeclaredField(
"forceHeavyWeightPopupKey");
field.setAccessible(true);
fileChooser.putClientProperty(field.get(null), true);
} catch (NoSuchFieldException ex) {
java.util.logging.Logger.getLogger(LibraryUI.class.getName()).
log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
java.util.logging.Logger.getLogger(LibraryUI.class.getName()).
log(Level.SEVERE, null, ex);
}
It seems that this solution worked for JDK 1.6 and not 1.7. When I run it now I get the exception: java.lang.NoSuchFieldException: PopupFactory_FORCE_HEAVYWEIGHT_POPUP
I'm not sure what the solution for 1.7 would be to make that popup stay on top of the JFilechooser.
Upvotes: 1
Views: 472
Reputation: 51536
Relying on hidden implementation details carries the risk of change without notice :-) If you do it - the obvious recomendation is don't never-ever in production code - be prepared to dig deep into the bowels of the source if the old hack stops working.
In this particular case, the change from jdk6 to jdk7 was to move the key from a private field in PopupFactory to a value of the package-private enum ClientPropertyKey. So you have to adjust the reflection code to:
public void forceHeavyWeight(JCompoennt fileChooser) {
try {
String name = "javax.swing.ClientPropertyKey";
Class<?> keyClazz = Class.forName(name);
Field field = keyClazz.getDeclaredField("PopupFactory_FORCE_HEAVYWEIGHT_POPUP");
field.setAccessible(true);
Object fieldValue = field.get(null);
fileChooser.putClientProperty(fieldValue, true);
} catch (Exception ex) {
// doesn't really matter what we do here, lost anyway ;-)
logSomehow(ex);
}
}
Update
After digging a bit, I think that the behaviour (showing the popup in z-order under the component added to the glassPane) is a bug. Same misbehaviour for toolTips set to components in the glassPane as well.
The technical reason is that, once the PopupFactory decided that a lightWeight popup is just fine, it inserts it into the layeredPane of the top-level ancestor of the invoker, no matter whether or not the invoker actually is part of the layeredPane's hierarchy. The excact location in PopupFactoryLightWeightPopup.show():
// suitable parent of the invoker, to add the popup
Container parent = null;
if (owner != null) {
parent = (owner instanceof Container? (Container)owner : owner.getParent());
}
// Try to find a JLayeredPane and Window to add
for (Container p = parent; p != null; p = p.getParent()) {
if (p instanceof JRootPane) {
if (p.getParent() instanceof JInternalFrame) {
// Continue, so that if there is a higher JRootPane, we'll
// pick it up.
continue;
}
parent = ((JRootPane)p).getLayeredPane();
// the implied assumption for correct visuals is that the assert below passes
// would fail if the invoker is
// located in the glassPane hierarchy
assertTrue(SwingUtilities.isDescendingFrom(owner, parent);
} else ....
}
// with the failing assumption above, the popup is inserted below its invoker
if (parent instanceof JLayeredPane) {
parent.add(component, JLayeredPane.POPUP_LAYER, 0);
} else ...
}
The place to fix would be higher up in the decision chain whether or not a heavyWeight popup is needed: best in PopupFactory.getPopupType(Component ..)
(that's were the hack above is effective) which unfortunately is private, so there is no way to subclass and make it behave. In fact there is no way at all to safely fix it, all approaches I have seen so far require to go dirty and access package/private fields or methods via reflection - which will fail in security-restricted contexts.
Personally, I tend to prefer a dirty factory (accessing super's setPopupType to force a heavyweight if !isDecending(invoker, layeredPane)
over a dirty clientProperty for two reasons:
Upvotes: 2