# HG changeset patch # User Andrew Azores # Date 1444400008 14400 # Node ID 237257431e93a0797e307571b2ac069895b9ed3d # Parent 3f37d769e27976b767098b58c5f974205fc92356 Clicking outside of OverlayPanel closes the overlay Reviewed-by: jerboaa, neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-October/016611.html PR2659 diff -r 3f37d769e279 -r 237257431e93 client/swing/src/main/java/com/redhat/thermostat/client/swing/OverlayContainer.java --- /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 + * . + * + * 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(); +} diff -r 3f37d769e279 -r 237257431e93 client/swing/src/main/java/com/redhat/thermostat/client/swing/components/HeaderPanel.java --- 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; diff -r 3f37d769e279 -r 237257431e93 client/swing/src/main/java/com/redhat/thermostat/client/swing/components/OverlayPanel.java --- 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; + } + } + } diff -r 3f37d769e279 -r 237257431e93 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/MainWindow.java --- 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() - { + ActionListener() { @Override public void actionPerformed(com.redhat.thermostat.common. - ActionEvent actionEvent) - { + ActionEvent actionEvent) { overlay.setOverlayVisible(false); } }); diff -r 3f37d769e279 -r 237257431e93 client/swing/src/main/java/com/redhat/thermostat/client/swing/internal/views/VmInformationPanel.java --- 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 = "" diff -r 3f37d769e279 -r 237257431e93 vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/HeapSwingView.java --- 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 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(); }