Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[albireo-dev] Navigating RCP view stacks with the keyboard

There is a common RCP situation that Albireo was not handling well. I've committed a fix for this problem.

The case was first described in eclipse.technology.albireo:

http://dev.eclipse.org/newslists/news.eclipse.technology.albireo/msg00010.html

(Issue #3)

There are rapid changes in focus when navigating among RCP views with the keyboard. When moving to a new tab, focus is first set on the main view part composite, but then it is immediately restored to the tab itself. This allows continued navigation.

This behavior was disrupted when a SwingControl was the main view part composite. Focus was essentially stolen by the Swing component, and restoring the focus to the tab would then fail. This may of course be timing-dependent, but it happened very consistently for me on both Windows and Linux.

The fix involves using asyncExec to defer all of our code that runs on the SWT activation event for SwingControl. This allows for focus to be shifted back to the tab before our code runs. When our code finally runs, it checks to see if focus has been taken away from the SwingControl. If so, it aborts.

This is not a general solution, but it helps for an important use case. I doubt a general solution is possible.

### Eclipse Workspace Patch 1.0
#P org.eclipse.albireo.core
Index: src/org/eclipse/albireo/internal/FocusHandler.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/internal/FocusHandler.java,v
retrieving revision 1.21
diff -u -r1.21 FocusHandler.java
--- src/org/eclipse/albireo/internal/FocusHandler.java	8 May 2008 21:18:02 -0000	1.21
+++ src/org/eclipse/albireo/internal/FocusHandler.java	31 Jul 2008 21:17:38 -0000
@@ -469,6 +469,35 @@
         }
     }
 
+    protected void doActivation(int swtTraversal) {
+        if (!swingControl.isFocusControl()) {
+            // We've lost focus, don't activate the underlying AWT window
+            if (verboseFocusEvents) {
+                trace("Focus lost before activating AWT window");
+            }
+            return;
+        }
+        
+        // Process deferred de-activation first (Windows only). 
+        // If a deactivation has been deferred (see SWT.Deactivate case below), handle it here. 
+        // Ideally we would have already deactivated as soon as some other control gets focus, but
+        // the deactivation code does nothing in that case. On the other hand, if we just ignore 
+        // the deactivation altogether, the subsequent Activate triggered by this event, does
+        // nothing and the embedded frame never gets focus. So we do the deactivate right here, 
+        // just before the activation. 
+        if (Platform.isWin32() && pendingDeactivate) {
+            synthesizeWindowActivation(false);
+            pendingDeactivate = false;
+        }
+        
+        if (Platform.isWin32() && (synthesizeMethod != null)) {
+            // Activate the window now
+            synthesizeWindowActivation(true);
+        }
+
+        adjustFocusForSwtTraversal(swtTraversal);
+    }
+
     // ========== Listener implementations
     
 
@@ -501,29 +530,32 @@
                     isActiveSwt = true;
                     activeBorderless = borderless;
                     
-                    // Process deferred de-activation first (Windows only). 
-                    // If a deactivation has been deferred (see SWT.Deactivate case below), handle it here. 
-                    // Ideally we would have already deactivated as soon as some other control gets focus, but
-                    // the deactivation code does nothing in that case. On the other hand, if we just ignore 
-                    // the deactivation altogether, the subsequent Activate triggered by this event, does
-                    // nothing and the embedded frame never gets focus. So we do the deactivate right here, 
-                    // just before the activation. 
-                    if (Platform.isWin32() && pendingDeactivate) {
-                        synthesizeWindowActivation(false);
-                        pendingDeactivate = false;
-                    }
+                    // The currentSwtTraversal will change below. Save its value for the asyncExecs
+                    final int swtTraversal = currentSwtTraversal;
+                    
+                    // We use asyncExec to defer the activation and focus setting in the underlying AWT frame. 
+                    // This allows proper handling of the case where focus is briefly 
+                    // set to the Swing control and immediately moved to a SWT component. (The deferred 
+                    // handling will abort if focus has been lost on the Swing control)
+                    //
+                    // This case is common when navigating among tabs in an RCP view stack with the 
+                    // left and right arrow keys. Focus is briefly given to the main view component and
+                    // then it is returned to the view tab for further navigation. If we did not defer 
+                    // the activation, then focus cannot be restored to the view tab.
+                    display.asyncExec(new Runnable() {
+                        public void run() {
+                            doActivation(swtTraversal);
+                        }
+                    });
                     
-                    if (Platform.isWin32() && (Platform.SWT_VERSION < Platform.SWT_FIX216431) && (synthesizeMethod != null)) {
-                        // Override SWT_AWT-installed listener with the correct way to activate
-                        synthesizeWindowActivation(true);
+                    // On windows, the actual activation needs to be deferred until doActivation() to
+                    // prevent the problem described above, so veto the activation normally done by SWT_AWT. 
+                    if (Platform.isWin32() && synthesizeMethod != null) {
                         if (verboseFocusEvents) {
                             trace("Consuming SWT.Activate event: " + event);
                         }
-                        // Prevent the SWT_AWT-installed listener from running
                         event.type = SWT.None;
                     }
-
-                    adjustFocusForSwtTraversal(currentSwtTraversal);
                     
                     break;
 
@@ -539,9 +571,12 @@
                     // To work around this problem, we defer the deactivation
                     // of the embedded frame here. See the SWT.Activate case above for processing of the 
                     // deferred event. 
-                    if (Platform.isWin32()) {
+                    if (Platform.isWin32() && (synthesizeMethod != null)) {
                         pendingDeactivate = true;
                         // Prevent the SWT_AWT-installed listener from running (and deactivating the frame).
+                        if (verboseFocusEvents) {
+                            trace("Consuming SWT.Activate event: " + event);
+                        }
                         event.type = SWT.None;
                         break;
                     }

Back to the top