changeset 1830:237257431e93

Clicking outside of OverlayPanel closes the overlay Reviewed-by: jerboaa, neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-October/016611.html PR2659
author Andrew Azores <aazores@redhat.com>
date Fri, 09 Oct 2015 10:13:28 -0400
parents 3f37d769e279
children 747a32443e86
files client/swing/src/main/java/com/redhat/thermostat/client/swing/OverlayContainer.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/HeaderPanel.java client/swing/src/main/java/com/redhat/thermostat/client/swing/components/OverlayPanel.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/views/VmInformationPanel.java vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingView.java
diffstat 6 files changed, 163 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/OverlayContainer.java	Fri Oct 09 10:13:28 2015 -0400
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012-2015 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing;
+
+import com.redhat.thermostat.client.swing.components.OverlayPanel;
+
+public interface OverlayContainer {
+    OverlayPanel getOverlay();
+}
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/HeaderPanel.java	Fri Oct 09 10:13:26 2015 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/HeaderPanel.java	Fri Oct 09 10:13:28 2015 -0400
@@ -179,6 +179,10 @@
         controlPanel.add(theButton);
         hasButtons = true;
     }
+
+    public void addOverlayCloseListeners(OverlayPanel overlayPanel) {
+        headerPanel.addMouseListener(overlayPanel.getClickOutCloseListener(headerPanel));
+    }
     
     class PreferencesPopup extends ThermostatPopupMenu {
         JMenuItem preferencesMenu;
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/OverlayPanel.java	Fri Oct 09 10:13:26 2015 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/OverlayPanel.java	Fri Oct 09 10:13:28 2015 -0400
@@ -50,10 +50,13 @@
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionAdapter;
 import java.awt.geom.Area;
 import java.awt.geom.GeneralPath;
 import java.awt.image.BufferedImage;
+import java.util.EventListener;
+import java.util.EventObject;
 
 import javax.swing.AbstractAction;
 import javax.swing.Box;
@@ -62,6 +65,7 @@
 import javax.swing.JComponent;
 import javax.swing.JPanel;
 import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
 
 import com.redhat.thermostat.client.swing.GraphicsUtils;
 import com.redhat.thermostat.client.ui.Palette;
@@ -181,14 +185,25 @@
         closeButton.addMouseListener(closeButtonVisibilityListener);
         closeButton.addMouseListener(new CloseButtonBackgroundColorListener());
 
+        addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                boolean withinContentBounds = content.getBounds().contains(e.getPoint());
+                boolean withinTitleBounds = titlePane.getBounds().contains(e.getPoint());
+                boolean clickedBorder = !withinContentBounds && !withinTitleBounds;
+                if (clickedBorder && isVisible()) {
+                    fireCloseEvent();
+                }
+            }
+        });
+
         // filter events, we don't want them to reach components through us
-        addMouseMotionListener(new MouseMotionAdapter() {
-        });
+        addMouseMotionListener(new MouseMotionAdapter() {});
         addKeyListener(new KeyAdapter() {});
         closeButton.addActionListener(new ActionListener() {
             @Override
             public void actionPerformed(ActionEvent actionEvent) {
-                setOverlayVisible(false);
+                fireCloseEvent();
             }
         });
         setFocusTraversalKeysEnabled(false);
@@ -198,7 +213,7 @@
         javax.swing.Action closeOverlay = new AbstractAction() {
             @Override
             public void actionPerformed(ActionEvent e) {
-                setOverlayVisible(false);
+                fireCloseEvent();
             }
         };
         getActionMap().put("close", closeOverlay);
@@ -219,7 +234,54 @@
     public void removeAll() {
         content.removeAll();
     }
-    
+
+    /**
+     * Provides a {@link MouseListener} intended for use in GlassPanes. This listener generates
+     * close events if the overlay is visible, and notifies all registered
+     * {@link CloseEventListener} instances on
+     * this overlay of the close event. Then the click event is passed through to whichever component
+     * would have received the event had the GlassPane not intercepted it. This allows for clicking outside
+     * of an overlay to notify the hosting view that it should close the overlay.
+     */
+    public MouseListener getClickOutCloseListener(final Component parent) {
+        return new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (isVisible()) {
+                    fireCloseEvent();
+                }
+                Component component = SwingUtilities.getDeepestComponentAt(parent, e.getX(), e.getY());
+                if (component != parent) {
+                    MouseEvent converted = SwingUtilities.convertMouseEvent(parent, e, component);
+                    component.dispatchEvent(converted);
+                }
+            }
+        };
+    }
+
+    private void fireCloseEvent() {
+        CloseEvent event = new CloseEvent(this);
+        Object[] listeners = listenerList.getListeners(CloseEventListener.class);
+        for (int i = listeners.length - 1; i >= 0; i--) {
+            ((CloseEventListener) listeners[i]).closeRequested(event);
+        }
+    }
+
+    /**
+     * Add a listener which will decide what to do when this OverlayPanel is requested to be closed.
+     * If the host view summons and hides the overlay using a toggle button, for example, then the view
+     * should provide a listener which performs a click event upon the toggle button. If there are no other
+     * UI elements which reflect the state of the overlay's visibility then it is safe for the listener
+     * to simply call {@link #setOverlayVisible(boolean)} directly.
+     */
+    public void addCloseEventListener(CloseEventListener listener) {
+        listenerList.add(CloseEventListener.class, listener);
+    }
+
+    public void removeCloseEventListener(CloseEventListener listener) {
+        listenerList.remove(CloseEventListener.class, listener);
+    }
+
     @Override
     protected void paintComponent(Graphics g) {
 
@@ -256,6 +318,9 @@
     
     /**
      * Sets the visibility of this panel.
+     * Users of the OverlayPanel generally should not call this method directly. Instead,
+     * use {@link #getClickOutCloseListener(Component)} and
+     * {@link #addCloseEventListener(CloseEventListener)}.
      */
     public void setOverlayVisible(boolean visible) {
         super.setVisible(visible);
@@ -397,5 +462,22 @@
             closeButton.setIcon(getCloseButtonVisibleIcon());
         }
     }
+
+    public interface CloseEventListener extends EventListener {
+        void closeRequested(CloseEvent event);
+    }
+
+    public static class CloseEvent extends EventObject {
+
+        public CloseEvent(OverlayPanel source) {
+            super(source);
+        }
+
+        @Override
+        public OverlayPanel getSource() {
+            return (OverlayPanel) source;
+        }
+    }
+
 }
 
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java	Fri Oct 09 10:13:26 2015 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java	Fri Oct 09 10:13:28 2015 -0400
@@ -186,22 +186,20 @@
         LocalizedString title = translator.localize(LocaleResources.PROGRESS_NOTIFICATION_AREA_TITLE);
         final OverlayPanel overlay = new OverlayPanel(title, false);
         glassPane.add(overlay);
-
-        overlay.addMouseListener(new MouseAdapter() {
+        glassPane.addMouseListener(overlay.getClickOutCloseListener(glassPane));
+        overlay.addCloseEventListener(new OverlayPanel.CloseEventListener() {
             @Override
-            public void mouseClicked(MouseEvent e) {
-                overlay.setOverlayVisible(false);
+            public void closeRequested(OverlayPanel.CloseEvent event) {
+                event.getSource().setOverlayVisible(false);
             }
         });
 
         overlay.add(aggregateNotificationArea, BorderLayout.CENTER);
         notifier.addPropertyChangeListener(new com.redhat.thermostat.common.
-                                           ActionListener<SwingProgressNotifier.PropertyChange>()
-        {
+                ActionListener<SwingProgressNotifier.PropertyChange>() {
             @Override
             public void actionPerformed(com.redhat.thermostat.common.
-                                        ActionEvent<PropertyChange> actionEvent)
-            {
+                                                ActionEvent<PropertyChange> actionEvent) {
                 overlay.setOverlayVisible(false);
             }
         });
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/views/VmInformationPanel.java	Fri Oct 09 10:13:26 2015 -0400
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/views/VmInformationPanel.java	Fri Oct 09 10:13:28 2015 -0400
@@ -45,6 +45,7 @@
 
 import com.redhat.thermostat.client.core.views.UIComponent;
 import com.redhat.thermostat.client.core.views.VmInformationView;
+import com.redhat.thermostat.client.swing.OverlayContainer;
 import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.shared.locale.LocalizedString;
@@ -71,6 +72,10 @@
         if (view instanceof SwingComponent) {
             SwingComponent panel = (SwingComponent)view;
             tabPane.insertTab(title.getContents(), null, panel.getUiComponent(), null, tabCount);
+            if (view instanceof OverlayContainer) {
+                OverlayContainer overlayContainer = (OverlayContainer) view;
+                tabPane.addMouseListener(overlayContainer.getOverlay().getClickOutCloseListener(tabPane));
+            }
             tabCount++;
         } else {
             String message = ""
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingView.java	Fri Oct 09 10:13:26 2015 -0400
+++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingView.java	Fri Oct 09 10:13:28 2015 -0400
@@ -41,22 +41,19 @@
 import java.awt.EventQueue;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.awt.event.KeyEvent;
 import java.io.File;
 import java.util.List;
 
-import javax.swing.AbstractAction;
 import javax.swing.BoxLayout;
-import javax.swing.JComponent;
 import javax.swing.JFileChooser;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
-import javax.swing.KeyStroke;
 import javax.swing.OverlayLayout;
 import javax.swing.SwingUtilities;
 
 import com.redhat.thermostat.client.core.views.BasicView;
 import com.redhat.thermostat.client.swing.IconResource;
+import com.redhat.thermostat.client.swing.OverlayContainer;
 import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.client.swing.components.ActionButton;
 import com.redhat.thermostat.client.swing.components.ActionToggleButton;
@@ -80,7 +77,7 @@
 import com.redhat.thermostat.vm.heap.analysis.common.DumpFile;
 import com.redhat.thermostat.vm.heap.analysis.common.HeapDump;
 
-public class HeapSwingView extends HeapView implements SwingComponent {
+public class HeapSwingView extends HeapView implements SwingComponent, OverlayContainer {
 
     private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
 
@@ -142,11 +139,12 @@
         stack.add(overlay);
         stack.add(stats);
         stats.setOpaque(false);
-        
+
         overlay.setAlignmentX(-1.f);
         overlay.setAlignmentY(1.f);
 
         overview.setContent(stack);
+        overview.addOverlayCloseListeners(overlay);
         new ComponentVisibilityNotifier().initialize(overview, notifier);
 
         Icon takeDumpIcon = new Icon(HeapIconResources.getIcon(HeapIconResources.TRIGGER_HEAP_DUMP));
@@ -160,7 +158,7 @@
             }
         });
         overview.addToolBarButton(takeDumpIconButton);
-                
+
         Icon listDumpIcon = IconResource.HISTORY.getIcon();
         showHeapListButton = new ActionToggleButton(listDumpIcon, translator.localize(LocaleResources.LIST_DUMPS_ACTION));
         showHeapListButton.setToolTipText(translator.localize(LocaleResources.LIST_DUMPS_ACTION).getContents());
@@ -176,9 +174,15 @@
                 }
             }
         });
-        
+
         overview.addToolBarButton(showHeapListButton);
-        
+        overlay.addCloseEventListener(new OverlayPanel.CloseEventListener() {
+            @Override
+            public void closeRequested(OverlayPanel.CloseEvent event) {
+                showHeapListButton.doClick();
+            }
+        });
+
         // at the beginning, only the overview is visible
         visiblePane.add(overview);
         
@@ -323,6 +327,11 @@
     }
 
     @Override
+    public OverlayPanel getOverlay() {
+        return overlay;
+    }
+
+    @Override
     public void notifyHeapDumpComplete() {
         enableHeapDumping();
     }