James wrote:
I have found several more issues relating to focus and the SWT/Swing
integration. I have breifly outlined each issue below and posted my code
modifications at the bottom. I suspect I may not be clear enough in
describing both the issues, the solutions, and how they work, so please
ask
any questions you might have. I would imagine others have periodically
noticed at least some of these issues as well.
Any thoughts on these Gordon?
James, first of all, thank you for sharing your fixes and improvements. I
have some comments and questions below.
My workarounds affect EmbeddedSwingComposite, SwtFocusHandler, and
AwtFocusHandler classes.
The issues:
#1
When using keyboard navigation, the default code misses sending the focus
to
the AWT control on its first pass through (the returned focus component
is
null). This can be solved by setting awtHasFocus to true at the top of
the
gainFocus method instead of at the bottom. This will ensure that an
attempt
is made to find a focusable swing control on the first focus attempt.
Yes, this is definitely a bug in the article. Another solution is to
change the EmbeddedChildFocusTraversalPolicy.getCurrentComponent() method
to call super.getDefaultComponent, rather than this.getDefaultComponent.
#2
I use an EmbeddedSwingComponent as the full contents of an editor
(consequently, the IWorkbenchPart's setFocus method sets the focus to the
EmbeddedSwingComponent). When issue #1 is fixed (ie, the AWT control
automatically gets the focus when the SWT control gets the focus), there
is
occasionally a deadlock in Java 1.6 when several editors are opened at
once
(even though only asycExec and invokeLater calls are used). The deadlock
is
happening way down in AWT Code in the WPanelPeer class and is triggered
by
the component.requestFocus inside of the awtHandler's gainFocus method.
This seems to be solved when issue #3 is solved.
It's now pretty clear that Component.requestFocus has to be used carefully
to avoid deadlock (and in our case other problems). The main thing is to
make sure that it is always called from the AWT event thread.
Unfortunately, the article code calls it from the SWT thread in two
places. For example, I've now re-implemented
EmbeddedSwingComposite.setFocus() as follows:
public boolean setFocus() {
checkWidget();
//if (!isFocusable()) {
// return false;
//}
if (swtHandler != null) {
// Setting focus on the embedded component must posted to the
// AWT thread because:
// 1) The AWT Component.requestFocus method can deadlock if
// called simultaneously from different threads.
// 2) The embedded component is created asynhronously (on the
// AWT thread), so it may not exist yet.
// The call below does the appropriate posting. Since it is
done
// asynchronously, it is impossible to know the result, so
// always return true.
swtHandler.transferFocusToAwt();
return true;
} else {
return super.setFocus();
}
}
transferFocusToAwt is what you would expect:
void transferFocusToAwt() {
EventQueue.invokeLater(new Runnable() {
public void run() {
awtHandler.gainFocus();
}
});
}
A similar change is needed to the EmbeddedSwingComposite.forceFocus
method.
I suspect these fixes will eliminate the deadlock problem you saw and that
your solution to #3, though useful in its own right, simply made the
deadlock less likely.
#3
When the EmbeddedSwingComponent is the only focusable component inside of
a
Tab Folder, the awtHandler.gainFocus call causes a malfunction in
keyboard
navigation. When the tab labels get the focus (a tab's label is
underlined), typically one can switch between tabs by using the arrow
keys
and then dive into the current tab using the 'tab' key. To do this, the
SWT
Tab Folder seems to momentarily set the focus to the tab's contents and
then
return focus to the tab label.
The problem is that we are telling AWT to take the focus when the tab's
contents gets the focus, so the focus doesn't go back to tab's label. I
have added set/get AbortFocus methods to the SwtFocusHandler. The abort
focus value is set whenever the SWT Control's focus is gained or lost.
This
way if the SWT control loses focus before AWT has a chance to set its
focus
we can decide not to set the AWT focus.
I suspect the deadlock also has something to do with this quick transfer
of
focus.
I didn't know you could navigate this way, so it clearly wasn't tested
:-). Your solution looks like a good one.
#4
There is a memory leak when you dispose of the SWT Control even if you
set
the frame to null. This leak is present in Java 1.5 update 11 and Java
1.6.
To remove the memory leak, the frame needs to have its
FocusableWindowState
set to false and it should be disposed of when the EmbeddedSwingComponent
is
disposed of. The frame should be disposed of in the Swing EDT.
Interesting. SWT_AWT.new_Frame() installs a listener that is supposed to
dispose the frame (on the AWT event thread) when the composite is
disposed. Any ideas on why this is not happening?
Is the need to set FocusableWindowState to false maybe due to this bug?
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6469530.
Or something else? I'm wondering if there's something that needs to be
reported to Sun...
I ran into a similar leak that was triggered by adding components to the
frame from outside the AWT EDT. See
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6411042.