Hi Clemens,
I can't say exactly how the fix for JDK-7160604 could change the behavior like you described (this
requires some more time to investigate).
However, an obvious question comes to my mind - why don't you make your popup non-focusable? I
suppose you manage navigation through the content of your "auto-completion list" somehow by
intercepting the up/down keys on the textfiled, right? In that case, you don't need your popup be a
focusable component. Does it work for you?
Also, just a side note. As a straightforward workaround to the focus issue, you could invoke your
requestFocus later, either via calling invokeLater() or by invoking it at the time the popup is
shown (I'm not sure you can listen for the componentShown(), but at least, you can listen for
focusGained() on its content). This would make the things happen in a fixed order: popup_show then
request_focus. (Currently, it seems the order is broken somehow.) But still, making the popup simply
non-focusable looks better.
Regards,
Anton.
On 02.11.2014 17:52, Clemens Eisserer wrote:
HI,
Starting with the rollout of Java8 via the auto-updater we received
several reports that one of our applications has servere focus issues.
The application registers a KeyListener on a JTextField showing an
auto-completion JPopupMenu every time a character is typed.
Every time a key is typed, the following sequence is run:
- Hide existing JPopupMenu in case there is one
- Create new JPopupMenu and populate with contents (in this case a JTable)
- JPopupMenu.show() below the JTextField which caused the keyTyped-event
- JTextField.requestFocus(), so the user can continue typing.
I've created a self-contained testcase available here:
http://pastebin.com/GEQ1ZW28
This worked fine with Java6 & Java7, however since JDK8-b119 it failes
when the popup is heavyweight - for lightweight popups it still
succeeds (unfourtunately, we need heavyweight ones).
I tried to analyze what happend and registered a global focus listener:
This is the focus-history without the fix for JDK-7160604 - once the
JTextField got it's focus no changes can be observed, despite new
JPopups are being opened:
javax.swing.JTextField[,241,115,197x19,...
javax.swing.JRootPane[,5,27,955x384,.....
javax.swing.JTextField[,241,115,197x19,... (focus stays here, despite
new JPopups are beeing opened and old ones closed)
With the fix, the focus history looks like this:
javax.swing.JTextField[,241,115,197x19,....
javax.swing.JTextField[,241,115,197x19,....
javax.swing.JRootPane[,5,27,938x588,...(focus stays here, no further
keyEvents are received by the JTextField)
Beside some refactorings, the only functional change I can spot is
that the order of the assignment of the "popup"-member and
popup.show() have changed in JPopupMenu.showPopup():
Popup newPopup = getUI().getPopup(this, desiredLocationX,
desiredLocationY);
popupFactory.setPopupType(PopupFactory.LIGHT_WEIGHT_POPUP);
popup = newPopup;
newPopup.show();
If I change the order of newPopup.show() and popup=newPopup, the old
behaviour is restored.
At least it looks somehow suspicious the original author introduced a
variable "newPopup" (and the patch still uses it without a good
reason, "popup" could be assigned directly), so at first sight it
looks intentional to assign popup only after show() was called.
During the call to newPopup.show() the method JPopupMenu.isVisible()
is called several times.
Before the fix for JDK-7160604 it would return false, now it returns
true - the focus issue mentioned is triggered by returning "true" at
the following call-stack (full one available at
http://pastebin.com/4qG8h7Td).
at javax.swing.JPopupMenu.isVisible(JPopupMenu.java:854)
at
javax.swing.SortingFocusTraversalPolicy.enumerateCycle(SortingFocusTraversalPolicy.java:141)
at
javax.swing.SortingFocusTraversalPolicy.enumerateCycle(SortingFocusTraversalPolicy.java:156)
at
javax.swing.SortingFocusTraversalPolicy.enumerateCycle(SortingFocusTraversalPolicy.java:156)
at
javax.swing.SortingFocusTraversalPolicy.enumerateCycle(SortingFocusTraversalPolicy.java:156)
at
javax.swing.SortingFocusTraversalPolicy.enumerateCycle(SortingFocusTraversalPolicy.java:156)
at
javax.swing.SortingFocusTraversalPolicy.enumerateAndSortCycle(SortingFocusTraversalPolicy.java:135)
at
javax.swing.SortingFocusTraversalPolicy.getFocusTraversalCycle(SortingFocusTraversalPolicy.java:110)
at
javax.swing.SortingFocusTraversalPolicy.getFirstComponent(SortingFocusTraversalPolicy.java:445)
at
javax.swing.LayoutFocusTraversalPolicy.getFirstComponent(LayoutFocusTraversalPolicy.java:166)
at
javax.swing.SortingFocusTraversalPolicy.getDefaultComponent(SortingFocusTraversalPolicy.java:535)
at java.awt.Window.isFocusableWindow(Window.java:2496)
If I introspect the call-stack and return "false" only when
"enumerateCycle" is contained in the stack, everything works again.
So from what I understand the JPopupMenu would never receive focus
before the fix, because during show() it wasn't believed to be
visible.
What I wonder, is the code in the testcase even supposed to work - or
did it just because of an accident?
Also a mystery remains why the JPopupMenu has to contain a JTable to
trigger the issue - with a JLabel everything works like it ever did.
Thank you in advance, Clemens