Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[albireo-dev] Swing popup management

I've committed code to manually hide Swing popups in cases where they are not properly hidden automatically. This avoids most of the cases where two menus are displayed simultaneously.

On Windows (all JDKs, all SWT versions): Swing popups are manually hidden when opening a SWT menu (e.g. main menu or toolbar). They are also manually hidden when the containing shell is moved or resized. Unfortunately, they are not hidden when the system menu (top left corner) is open. I don't know of a solution for that problem.

On Gtk (JDK 1.5, all SWT versions): Swing popups are manually hidden when opening a SWT menu (e.g. main menu or toolbar).

A new view has been added (AWT Popup View) which allows you to open both a Swing and an AWT popup. (AWT popups actually dismiss correctly with no extra help from us so far, but it's good to test them anyway.)
### Eclipse Workspace Patch 1.0
#P org.eclipse.albireo.examples.plugin
Index: plugin.xml
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.examples.plugin/plugin.xml,v
retrieving revision 1.14
diff -u -r1.14 plugin.xml
--- plugin.xml	18 Feb 2008 01:04:20 -0000	1.14
+++ plugin.xml	27 Feb 2008 06:19:13 -0000
@@ -104,6 +104,12 @@
             id="org.eclipse.albireo.examples.plugin.focusTraversalView"
             name="Focus Traversal View">
       </view>
+      <view
+            category="org.eclipse.albireo"
+            class="org.eclipse.albireo.examples.plugin.views.AwtPopupView"
+            id="org.eclipse.albireo.examples.plugin.awtPopupView"
+            name="AWT Popup View">
+      </view>
    </extension>
    <extension
          point="org.eclipse.ui.actionSets">
Index: src/org/eclipse/albireo/examples/plugin/views/AwtPopupView.java
===================================================================
RCS file: src/org/eclipse/albireo/examples/plugin/views/AwtPopupView.java
diff -N src/org/eclipse/albireo/examples/plugin/views/AwtPopupView.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/albireo/examples/plugin/views/AwtPopupView.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,117 @@
+package org.eclipse.albireo.examples.plugin.views;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.GridLayout;
+import java.awt.Label;
+import java.awt.MenuItem;
+import java.awt.PopupMenu;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.JTable;
+
+import org.eclipse.albireo.core.SwingControl;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.part.ViewPart;
+
+public class AwtPopupView extends ViewPart {
+
+    private SwingControl swingControl;
+
+    public AwtPopupView() {
+        // TODO Auto-generated constructor stub
+    }
+
+    public void createPartControl(Composite parent) {
+        swingControl = new SwingControl(parent, SWT.NONE) {
+
+            protected JComponent createSwingComponent() {
+                JPanel panel = new JPanel(new GridLayout(2, 1, 20, 20));
+                JLabel swingLabel = new JLabel("Open Swing Popup Here");
+                swingLabel.setFocusable(true);
+                swingLabel.setBorder(BorderFactory.createLineBorder(Color.GREEN));
+                addSwingPopup(swingLabel);
+                panel.add(swingLabel);
+                JLabel awtLabel = new JLabel("Open AWT Popup Here");
+                awtLabel.setFocusable(true);
+                awtLabel.setBorder(BorderFactory.createLineBorder(Color.BLUE));
+                addAwtPopup(awtLabel);
+                panel.add(awtLabel);
+                return panel;
+
+            }
+
+            public Composite getLayoutableAncestor() {
+                return getParent();
+            }
+
+        };
+
+    }
+    
+    private void addAwtPopup(Component c) {
+        final PopupMenu popup = new PopupMenu();
+        MenuItem item = new MenuItem("AWT Item 1");
+        popup.add(item);
+
+        item = new MenuItem("AWT Item 2");
+        popup.add(item);
+
+        c.add(popup);
+        c.addMouseListener(new MouseAdapter() {
+
+            public void mousePressed(MouseEvent e) {
+                show(popup, e);
+            }
+
+            public void mouseReleased(MouseEvent e) {
+                show(popup, e);
+            }
+
+            private void show(final PopupMenu popup, MouseEvent e) {
+                if (e.isPopupTrigger()) {
+                    popup.show(e.getComponent(), e.getX(), e.getY());
+                }
+            }
+            
+        });
+    }
+
+    private void addSwingPopup(Component c) {
+        final JPopupMenu popup = new JPopupMenu();
+        JMenuItem item = new JMenuItem("Swing Item 1");
+        popup.add(item);
+        item = new JMenuItem("Swing Item 2");
+        popup.add(item);
+        
+        c.addMouseListener(new MouseAdapter() {
+
+            public void mousePressed(MouseEvent e) {
+                show(popup, e);
+            }
+
+            public void mouseReleased(MouseEvent e) {
+                show(popup, e);
+            }
+
+            private void show(final JPopupMenu popup, MouseEvent e) {
+                if (e.isPopupTrigger()) {
+                    popup.show(e.getComponent(), e.getX(), e.getY());
+                }
+            }
+            
+        });
+    }
+    public void setFocus() {
+        swingControl.setFocus();
+    }
+}
#P org.eclipse.albireo.core
Index: src/org/eclipse/albireo/core/AwtEnvironment.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/core/AwtEnvironment.java,v
retrieving revision 1.11
diff -u -r1.11 AwtEnvironment.java
--- src/org/eclipse/albireo/core/AwtEnvironment.java	20 Feb 2008 07:24:26 -0000	1.11
+++ src/org/eclipse/albireo/core/AwtEnvironment.java	27 Feb 2008 06:19:15 -0000
@@ -12,20 +12,29 @@
  *******************************************************************************/
 package org.eclipse.albireo.core;
 
+import java.awt.Component;
+import java.awt.Container;
 import java.awt.EventQueue;
 import java.awt.Frame;
+import java.awt.KeyboardFocusManager;
+import java.awt.Window;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
+import javax.swing.JPopupMenu;
 import javax.swing.UnsupportedLookAndFeelException;
 
+import org.eclipse.albireo.internal.AwtDialogListener;
 import org.eclipse.albireo.internal.FocusDebugging;
 import org.eclipse.albireo.internal.FocusHandler;
+import org.eclipse.albireo.internal.Platform;
 import org.eclipse.albireo.internal.SwtInputBlocker;
-import org.eclipse.albireo.internal.AwtDialogListener;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTException;
 import org.eclipse.swt.awt.SWT_AWT;
@@ -152,6 +161,11 @@
         
         // Listen for AWT modal dialogs to make them modal application-wide
         dialogListener = new AwtDialogListener(display);
+        
+        // Dismiss AWT popups when SWT menus are shown (not needed in JDK1.6)
+        if (HIDE_SWING_POPUPS_ON_SWT_MENU_OPEN) {
+            display.addFilter(SWT.Show, menuListener);
+        }
     }
 
     /**
@@ -353,5 +367,83 @@
             });
         }
     }
+    //==================== Swing Popup Management ================================
+    // (Note there are no known problems with AWT popups (java.awt.PopupMenu), so this code
+    // ignores them)
+    
+    // This listener helps ensure that Swing popup menus are properly dismissed when
+    // a menu item off the SWT main menu bar (or tool bar) is shown.
+    private final Listener menuListener = new Listener() {
+        public void handleEvent(Event event) {
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    hidePopups();
+                }
+            });
+        }
+    };
+    
+    private static final boolean HIDE_SWING_POPUPS_ON_SWT_MENU_OPEN = 
+        (Platform.isGtk() && Platform.JAVA_VERSION < Platform.javaVersion(1, 6, 0)) ||  // GTK: pre-Java1.6
+        (Platform.isWin32());                                                           // Win32: all JDKs
+
+    // Returns true if any popup has been hidden
+    protected boolean hidePopups() {
+        boolean result = false;
+        List popups = new ArrayList();
+        assert EventQueue.isDispatchThread();    // On AWT event thread
+        
+        Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
+        if (window == null) {
+            return false;
+        }
+        
+        // Look for popups inside the frame's component hierarchy. 
+        // Lightweight popups will be found here. 
+        findContainedPopups(window, popups);
+        
+        // Also look for popups in the frame's window hierachy. 
+        // Heavyweight popups will be found here.
+        findOwnedPopups(window, popups);
+        
+        // System.err.println("Hiding popups, count=" + popups.size());
+        for (Iterator iter = popups.iterator(); iter.hasNext();) {
+            Component popup = (Component)iter.next();
+            if (popup.isVisible()) {
+                result = true;
+                popup.setVisible(false);
+            }
+        }
+        return result;
+    }
+
+    protected void findOwnedPopups(Window window, List popups) {
+        assert window != null;
+        assert EventQueue.isDispatchThread();    // On AWT event thread
+        
+        Window[] ownedWindows = window.getOwnedWindows();
+        for (int i = 0; i < ownedWindows.length; i++) {
+            findContainedPopups(ownedWindows[i], popups);
+            findOwnedPopups(ownedWindows[i], popups);
+        }
+    }
+
+    protected void findContainedPopups(Container container, List popups) {
+        assert container != null;
+        assert popups != null;
+        assert EventQueue.isDispatchThread();    // On AWT event thread
+        
+        Component[] components = container.getComponents();
+        for (int i = 0; i < components.length; i++) {
+            Component c = components[i];
+            // JPopupMenu is a container, so check for it first
+            if (c instanceof JPopupMenu) {
+                popups.add(c);
+            } else if (c instanceof Container) {
+                findContainedPopups((Container)c, popups);
+            }
+        }
+    }
+
 
 }
Index: src/org/eclipse/albireo/core/SwingControl.java
===================================================================
RCS file: /cvsroot/technology/org.eclipse.albireo/org.eclipse.albireo.core/src/org/eclipse/albireo/core/SwingControl.java,v
retrieving revision 1.44
diff -u -r1.44 SwingControl.java
--- src/org/eclipse/albireo/core/SwingControl.java	26 Feb 2008 21:17:21 -0000	1.44
+++ src/org/eclipse/albireo/core/SwingControl.java	27 Feb 2008 06:19:17 -0000
@@ -38,6 +38,8 @@
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTException;
 import org.eclipse.swt.awt.SWT_AWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
 import org.eclipse.swt.graphics.Color;
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.FontData;
@@ -186,6 +188,11 @@
             ComponentDebugging.addComponentSizeDebugListeners(frame);
 
         initializeFocusManagement();
+
+        if (HIDE_SWING_POPUPS_ON_SWT_SHELL_BOUNDS_CHANGE) {
+            getShell().addControlListener(shellControlListener);
+        }
+        
     }
     
 
@@ -1029,7 +1036,37 @@
             listener.preferredSizeChanged(event);
         }
     }
+
+    //==================== Swing Popup Management ================================
     
+    private static final boolean HIDE_SWING_POPUPS_ON_SWT_SHELL_BOUNDS_CHANGE = 
+        (Platform.isWin32());                                                           // Win32: all JDKs
+
+    // Dismiss Swing popups when the main window is moved or resized. (It would be 
+    // better to dismiss popups whenever the titlebar or trim is clicked, but 
+    // there does not seem to be a way. This is the best we can do)
+    // 
+    // This is particularly important when the Swing popup overlaps an edge of the 
+    // containing SWT shell. If (on win32) the shell is moved, the overlapping 
+    // popup will not move which looks very strange.
+    private final ControlListener shellControlListener = new ControlListener() {
+        public void controlMoved(ControlEvent e) {
+            scheduleHide();
+        }
+
+        public void controlResized(ControlEvent e) {
+            scheduleHide();
+        }
+
+        private void scheduleHide() {
+            EventQueue.invokeLater(new Runnable() {
+                public void run() {
+                    AwtEnvironment.getInstance(display).hidePopups();
+                }
+            });
+        }
+    };
+
     public String toString() {
         return super.toString() + " [frame=" + ((frame != null) ? frame.getName() : "null") + "]";
     }

Back to the top