Community
Participate
Working Groups
Steps: - Import "org.eclipse.ui.workbench". - Add a print statement to "WorkbenchPage#ActivationList#setActive(IWorkbenchPart part)". - Run as run-time workbench. - Switch to Java perspective. - Import a project. - Open a Java file. - Select the "Hierarchy" view, then the "Package Explorer" (give focus). - Notice that switching to the "Package Explorer" view calls setActive() on the open file before calling setActive() on that view. This behavior is affecting the "Ctrl+F7" behavior by altering the ordering of the items in that list. I am using: Eclipse Platform Version: 3.0.0 Build id: 200405040800 I also tried this in 2.1 and M8, and everything worked fine. No unnecessary “setActive()” calls were being made.
This problem is caused by hiding the control with focus and then calling Control.fixFocus(). The algorithm to select the next control to gain focus is causing the editor to gain focus since its the first focusable child from the same parent.
Michael's analysis is correct. The new focus handling is causing grief for workbench part activation. You can see this if you add the following to WorkbenchPage.setActivePart, after the initial == check: System.err.println("activating " + (newPart == null ? null : newPart.getTitle())); Thread.dumpStack(); Concrete steps are: - in new Java perspective, - open some editor - activate Package Explorer - click on Hierarchy view tab - workbench hides the Package Explorer's control - fixFocus gives focus to the editor - workbench picks this up as an editor part activation *** previous 2 steps did not happen before - workbench activates Hierarchy The stack trace for the editor activation is: activating .classpath java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1064) at org.eclipse.ui.internal.WorkbenchPage.setActivePart(WorkbenchPage.java:2569) at org.eclipse.ui.internal.WorkbenchPage.requestActivation(WorkbenchPage.java:2307) at org.eclipse.ui.internal.PartPane.requestActivation(PartPane.java:246) at org.eclipse.ui.internal.EditorPane.requestActivation(EditorPane.java:150) at org.eclipse.ui.internal.PartPane.handleEvent(PartPane.java:226) at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:82) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:796) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:820) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:801) at org.eclipse.swt.widgets.Shell.setActiveControl(Shell.java:913) at org.eclipse.swt.widgets.Control.sendFocusEvent(Control.java:1756) at org.eclipse.swt.widgets.Control.WM_SETFOCUS(Control.java:4120) at org.eclipse.swt.widgets.Canvas.WM_SETFOCUS(Canvas.java:239) at org.eclipse.swt.widgets.Control.windowProc(Control.java:3012) at org.eclipse.swt.widgets.Display.windowProc(Display.java:3160) at org.eclipse.swt.internal.win32.OS.SetFocus(Native Method) at org.eclipse.swt.widgets.Control.forceFocus(Control.java:599) at org.eclipse.swt.widgets.Control.setFocus(Control.java:2037) at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:447) at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445) at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445) at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445) at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445) at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445) at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445) at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:445) at org.eclipse.swt.widgets.Control.fixFocus(Control.java:560) at org.eclipse.swt.widgets.Control.setVisible(Control.java:2402) at org.eclipse.ui.internal.LayoutPart.setVisible(LayoutPart.java:240) at org.eclipse.ui.internal.PartPane.setVisible(PartPane.java:259) at org.eclipse.ui.internal.ViewPane.setVisible(ViewPane.java:630) at org.eclipse.ui.internal.PresentableViewPart.setVisible(PresentableViewPart.java:112) at org.eclipse.ui.internal.presentations.BasicStackPresentation.selectPart(BasicStackPresentation.java:790) at org.eclipse.ui.internal.PartStack.refreshPresentationSelection(PartStack.java:666) at org.eclipse.ui.internal.PartStack.setSelection(PartStack.java:649) at org.eclipse.ui.internal.PartTabFolder.setSelection(PartTabFolder.java:108) at org.eclipse.ui.internal.PartStack.presentationSelectionChanged(PartStack.java:457) at org.eclipse.ui.internal.PartStack.access$0(PartStack.java:447) at org.eclipse.ui.internal.PartStack$1.selectPart(PartStack.java:77) at org.eclipse.ui.internal.presentations.BasicStackPresentation$4.handleEvent(BasicStackPresentation.java:148) at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:82) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:796) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:820) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:805) at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:613) at org.eclipse.swt.custom.CTabFolder.setSelection(CTabFolder.java:2966) at org.eclipse.swt.custom.CTabFolder.onMouse(CTabFolder.java:1855) at org.eclipse.swt.custom.CTabFolder$1.handleEvent(CTabFolder.java:288) at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:82) at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:796) at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:2594) at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2272) at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:1351) at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1322) at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:241) at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:140) at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:90) at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:283) at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:242) at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:119) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:324) at org.eclipse.core.launcher.Main.basicRun(Main.java:269) at org.eclipse.core.launcher.Main.run(Main.java:700) at org.eclipse.core.launcher.Main.main(Main.java:684)
Where should focus be assigned when a control is hidden? Originally (way back when before 2.0?), we did not attempt to fix the focus. Some platforms fixed focus and others did not, causing portablility problems. Not sure how to proceed but I am open to suggestions.
I think keeping focus unchanged is preferable. If the app is hiding/showing controls, it will need to adjust focus itself, but should not get free focus changes. Not sure what algorithm the other platforms follow.
This is where AWT struggled for years and ended up with some complicated/"corrective" solution in JDK 1.4 and further in 1.5. They have written a lot of info around this whole focus management issue. What is interesting is that SWT is relying on the OS to have the correct order whereas AWT relies on its own hierarchy and policy. This isn't going to be an easy problem to solve in the short term. Just my $0.02.
The fact that we "fix focus" is relied upon in Eclipse so we can't take the code out. Originally, I was against doing this but there were just too many problems and the code went in (keys going nowhere etc.). If the application knows where focus is supposed to go, can it set focus before hiding the control?
Does a programmatic change to focus fire focus changed or activation listeners? If so, then we'd have to remove such listeners, otherwise the workbench would issue part activation callbacks before part deactivation.
*** Bug 61289 has been marked as a duplicate of this bug. ***
Steve, we need to sit down and sort this out.
A programatic change of focus fires the listeners. Something to think about: Is the problem specific to Eclipse (and SWT) or is it a general UI problem. For example, Motif will "fix focus" for you when you hide a widget. How are Motif C programs supposed to work around this? Talk to you tomorrow.
I'm also wondering why this is only showing up now. If the fixFocus change was made prior to M8, then this must be due to a workbench change. Or were there changes in this area in SWT in the last couple of weeks?
The fixFocus() behaviour in SWT existed even in 2.1. We did not encounter the problem in 2.1 because when clicking on a view's tab, CTabFolder's mouse down handler would force focus to the CTabFolder before firing the selection event. So when the workbench hid the previously selected part, it was not the focus control. Testing on 2.1, there were cases where the problem was hit even there, e.g. when opening or closing a view, we just didn't notice because views are opened/closed much less frequently than switching between them. I did notice a flicker of the editor tab when closing a view though (when the previously activated part was also a view). I've looked into whether we can avoid fixFocus() by changing the order of setVisible calls, but it's trickier than that since the order would have to be: - setVisible(true) on new control - setFocus() on new control - setVisible(false) on old control This is not only difficult to achieve given the way the responsibilities are split between presentation and workbench, it would also affect when workbench part lifecycle notifications are fired (see IPartListener2). Instead, I've put in a workaround in LayoutPart.setVisible. If the argument is false, it first forces focus on the shell. This seems safer than forcing focus on the container's control since the container may be going away too. This fixes the editor activation problem for all 3 cases: switching between views in the same folder, showing a view in the same folder as the active one, and closing a view when the previous active part was in the same folder. This also reduces flicker.
*** Bug 56361 has been marked as a duplicate of this bug. ***
This looks like the right code: - setVisible(true) on new control - setFocus() on new control - setVisible(false) on old control Veronika, was this changed for accessibility? I thought that CTabFolder was supposed to forceFocus() to its tabs when it got a mouse down. Please don't change the code back without consulting Nick. Thanks.
I was able to workaround my problems with the following change in selectPart in my stackPresentation class. It's not the best solution but it works for now. public void selectPart(IPresentablePart toSelect) { if (toSelect == current) { return; } tabFolder.setFocus(); final IPresentablePart old = current; Display.getDefault().asyncExec(new Runnable() { public void run() { if (old != null) { old.setVisible(false); } } }); current = toSelect; if (current != null) { tabFolder.setSelection(indexOf(current)); setControlSize(); current.setVisible(true); tabFolder.setFocus(); } }
For accessibility, the following changed in CTabFolder: On mouse down, a Selection event is sent. This used to be followed by a call to setFocus(). For accessibility, the setFocus call was removed. Note that mouse down automatically gives focus to the CTabFolder. If the content of the CTabItems are set, the CTabFolder makes the content of the selected tab visible and then hides the content of the previously selected tab. When setVisible(false) is called on the previously selected content, focus automatically goes to the newly selected content (due to behaviour of setVisible(false)). In addition, for accessibility, on page traversal and arrow traversal, a forceFocus is called to keep the focus in the tabs of the CTabFolder.
How does the mouse down automatically give focus to the CTabFolder? In the 2.1 code, there was an explicit forceFocus before the selection event notification. I don't see this in the 3.0 code.
Matt, in your patch, doing the setFocus or forceFocus before setVisible(false) should work. You shouldn't need the asyncExec.
The asyncExec may have bad side effects on the order of workbench part visibility notifications as well. It's also possible for the part to have been disposed by the time the asyncExec gets to run, which may cause widget is disposed exceptions.
LRESULT WM_LBUTTONDOWN (int wParam, int lParam) { LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam); /* Set focus for a canvas with no children */ if ((state & CANVAS) != 0) { if ((style & SWT.NO_FOCUS) == 0 && hooksKeys ()) { if (OS.GetWindow (handle, OS.GW_CHILD) == 0) setFocus (); } } return result; } In 2.1, the UI did not create children of the CTabFolder. In recent 3.0 builds, it appears that a child is being created (ViewForm).
Just realized the asyncExec was left overs from some trial and error. Thanks for the heads up.
I've changed the workaround in LayoutPart.setVisible from: if (!makeVisible) { ctrl.getShell().forceFocus(); to: if (!makeVisible && isFocusAncestor(ctrl)) { ctrl.getShell().forceFocus(); since there were cases where non-active stack was changing the visible part, and warping focus to the shell due to the workaround. Matt, you should also add this check.
I have verified that "WorkbenchPage#ActivationList#setActive(IWorkbenchPart part)" does not get called on editors when navigating through views. I am using Build id: 200405181200 on Mac OS X and Windows XP.
Verified using Build id: 200405190010 on Windows XP and Mac OS X.
Reopening since I had to back out of the change in comment #22 since it was causing failures in the DnD test suite's calls to testInvariants. But we now believe the change is OK, so I've re-released it and commented out the check in PartStack.testInvariants.
Closing. The isFocusAncestor check will be in I20040520-0010.
Without the isFocusAncestor check, it was causing unwanted activations when openEditor with activate=false was called, such as when selecting different stack frames in the debugger when the editor could not be reused, e.g. when crossing between .java and .class files.
*** Bug 63060 has been marked as a duplicate of this bug. ***
I have verified that "setActive" gets called once per view/editor, with Build id: 200405210800. Verified on Windows XP and Mac OS 10.3.3.