Adding PopupPanel#setAutoHideOnHistoryEventsEnabled() method to autoHide popups when the history token changes.

Patch by: jlabanca
Review by: jgw
Issue: 3052



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7391 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/ui/PopupPanel.java b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
index f247d68..e9ed609 100644
--- a/user/src/com/google/gwt/user/client/ui/PopupPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
@@ -30,6 +30,8 @@
 import com.google.gwt.event.logical.shared.HasCloseHandlers;
 import com.google.gwt.event.logical.shared.ResizeEvent;
 import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
 import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.i18n.client.LocaleInfo;
 import com.google.gwt.user.client.Command;
@@ -37,6 +39,7 @@
 import com.google.gwt.user.client.DeferredCommand;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.EventPreview;
+import com.google.gwt.user.client.History;
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.Event.NativePreviewEvent;
 import com.google.gwt.user.client.Event.NativePreviewHandler;
@@ -68,7 +71,7 @@
  * which is commonly used to gray out the widgets behind it. It can be enabled
  * using {@link #setGlassEnabled(boolean)}. It has a default style name of
  * "gwt-PopupPanelGlass", which can be changed using
- * {@link #setGlassStyleName()}.
+ * {@link #setGlassStyleName(String)}.
  * </p>
  * 
  * <p>
@@ -364,6 +367,7 @@
   private AnimationType animType = AnimationType.CENTER;
 
   private boolean autoHide, previewAllNativeEvents, modal, showing;
+  private boolean autoHideOnHistoryEvents;
 
   private List<Element> autoHidePartners;
 
@@ -390,6 +394,7 @@
   private int leftPosition = -1;
 
   private HandlerRegistration nativePreviewHandlerRegistration;
+  private HandlerRegistration historyHandlerRegistration;
 
   /**
    * The {@link ResizeAnimation} used to open and close the {@link PopupPanel}s.
@@ -419,11 +424,13 @@
    * Creates an empty popup panel, specifying its "auto-hide" property.
    * 
    * @param autoHide <code>true</code> if the popup should be automatically
-   *          hidden when the user clicks outside of it
+   *          hidden when the user clicks outside of it or the history token
+   *          changes.
    */
   public PopupPanel(boolean autoHide) {
     this();
     this.autoHide = autoHide;
+    this.autoHideOnHistoryEvents = autoHide;
   }
 
   /**
@@ -431,7 +438,8 @@
    * properties.
    * 
    * @param autoHide <code>true</code> if the popup should be automatically
-   *          hidden when the user clicks outside of it
+   *          hidden when the user clicks outside of it or the history token
+   *          changes.
    * @param modal <code>true</code> if keyboard or mouse events that do not
    *          target the PopupPanel or its children should be ignored
    */
@@ -482,7 +490,8 @@
 
     int left = (Window.getClientWidth() - getOffsetWidth()) >> 1;
     int top = (Window.getClientHeight() - getOffsetHeight()) >> 1;
-    setPopupPosition(Math.max(Window.getScrollLeft() + left, 0), Math.max(Window.getScrollTop() + top, 0));
+    setPopupPosition(Math.max(Window.getScrollLeft() + left, 0), Math.max(
+        Window.getScrollTop() + top, 0));
 
     if (!initiallyShowing) {
       setAnimationEnabled(initiallyAnimated);
@@ -592,8 +601,19 @@
   }
 
   /**
-   * Returns <code>true</code> if a glass element will be displayed under
-   * the {@link PopupPanel}.
+   * Returns <code>true</code> if the popup should be automatically hidden when
+   * the history token changes, such as when the user presses the browser's back
+   * button.
+   * 
+   * @return true if enabled, false if disabled
+   */
+  public boolean isAutoHideOnHistoryEventsEnabled() {
+    return autoHideOnHistoryEvents;
+  }
+
+  /**
+   * Returns <code>true</code> if a glass element will be displayed under the
+   * {@link PopupPanel}.
    * 
    * @return true if enabled
    */
@@ -710,7 +730,7 @@
       autoHidePartners.remove(partner);
     }
   }
-  
+
   /**
    * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
    *             object returned by {@link #addCloseHandler} instead
@@ -735,9 +755,20 @@
   }
 
   /**
+   * Enable or disable autoHide on history change events. When enabled, the
+   * popup will be automatically hidden when the history token changes, such as
+   * when the user presses the browser's back button. Disabled by default.
+   * 
+   * @param enabled true to enable, false to disable
+   */
+  public void setAutoHideOnHistoryEventsEnabled(boolean enabled) {
+    this.autoHideOnHistoryEvents = enabled;
+  }
+
+  /**
    * When enabled, the background will be blocked with a semi-transparent pane
-   * the next time it is shown. If the PopupPanel is already visible, the
-   * glass will not be displayed until it is hidden and shown again.
+   * the next time it is shown. If the PopupPanel is already visible, the glass
+   * will not be displayed until it is hidden and shown again.
    * 
    * @param enabled true to enable, false to disable
    */
@@ -1353,9 +1384,18 @@
           previewNativeEvent(event);
         }
       });
+      historyHandlerRegistration = History.addValueChangeHandler(new ValueChangeHandler<String>() {
+        public void onValueChange(ValueChangeEvent<String> event) {
+          if (autoHideOnHistoryEvents) {
+            hide();
+          }
+        }
+      });
     } else if (nativePreviewHandlerRegistration != null) {
       nativePreviewHandlerRegistration.removeHandler();
       nativePreviewHandlerRegistration = null;
+      historyHandlerRegistration.removeHandler();
+      historyHandlerRegistration = null;
     }
   }
 }
diff --git a/user/src/com/google/gwt/user/datepicker/client/DateBox.java b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
index 610851f..7105c80 100644
--- a/user/src/com/google/gwt/user/datepicker/client/DateBox.java
+++ b/user/src/com/google/gwt/user/datepicker/client/DateBox.java
@@ -264,11 +264,10 @@
    */
   public DateBox(DatePicker picker, Date date, Format format) {
     this.picker = picker;
-    this.popup = new PopupPanel();
+    this.popup = new PopupPanel(true);
     assert format != null : "You may not construct a date box with a null format";
     this.format = format;
 
-    popup.setAutoHideEnabled(true);
     popup.addAutoHidePartner(box.getElement());
     popup.setWidget(picker);
     popup.setStyleName("dateBoxPopup");
diff --git a/user/test/com/google/gwt/user/client/ui/PopupTest.java b/user/test/com/google/gwt/user/client/ui/PopupTest.java
index bab4190..8db63fb 100644
--- a/user/test/com/google/gwt/user/client/ui/PopupTest.java
+++ b/user/test/com/google/gwt/user/client/ui/PopupTest.java
@@ -23,6 +23,7 @@
 import com.google.gwt.junit.DoNotRunWith;
 import com.google.gwt.junit.Platform;
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.History;
 import com.google.gwt.user.client.Timer;
 import com.google.gwt.user.client.Window;
 
@@ -128,6 +129,21 @@
     popup.removeAutoHidePartner(partner0);
   }
 
+  public void testAutoHideOnHistoryEvent() {
+    PopupPanel popup = createPopupPanel();
+    popup.show();
+    assertTrue(popup.isShowing());
+
+    // When autoHideOnHistoryEvent is disabled, the popup remains visible.
+    History.newItem("popupToken0");
+    assertTrue(popup.isShowing());
+
+    // When autoHideOnHistoryEvent is enabled, the popup is hidden.
+    popup.setAutoHideOnHistoryEventsEnabled(true);
+    History.newItem("popupToken1");
+    assertFalse(popup.isShowing());
+  }
+
   /**
    * Tests that a large PopupPanel is not positioned off the top or left edges
    * of the browser window, making part of the panel unreachable.