Adds PopupPanel.setGlassEnabled(), along with related tests and sample
changes.
Review: http://gwt-code-reviews.appspot.com/93809
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6709 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
index 1d04d8f..b13b6f2 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/popups/CwDialogBox.java
@@ -102,6 +102,7 @@
public Widget onInitialize() {
// Create the dialog box
final DialogBox dialogBox = createDialogBox();
+ dialogBox.setGlassEnabled(true);
dialogBox.setAnimationEnabled(true);
// Create a button to show the dialog Box
diff --git a/user/javadoc/com/google/gwt/examples/DialogBoxExample.java b/user/javadoc/com/google/gwt/examples/DialogBoxExample.java
index e1adac3..d39c350 100644
--- a/user/javadoc/com/google/gwt/examples/DialogBoxExample.java
+++ b/user/javadoc/com/google/gwt/examples/DialogBoxExample.java
@@ -30,6 +30,12 @@
// Set the dialog box's caption.
setText("My First Dialog");
+ // Enable animation.
+ setAnimationEnabled(true);
+
+ // Enable glass background.
+ setGlassEnabled(true);
+
// DialogBox is a SimplePanel, so you have to set its widget property to
// whatever you want its contents to be.
Button ok = new Button("OK");
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 64eff3a..231ceee 100644
--- a/user/src/com/google/gwt/user/client/ui/PopupPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
@@ -21,9 +21,15 @@
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.dom.client.Style.Position;
+import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
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.shared.HandlerRegistration;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.Command;
@@ -67,6 +73,8 @@
* <dd>the outside of the popup</dd>
* <dt>.gwt-PopupPanel .popupContent</dt>
* <dd>the wrapper around the content</dd>
+ * <dt>.gwt-PopupGlass</dt>
+ * <dd>the glass background behind the popup</dd>
* </dl>
*/
@SuppressWarnings("deprecation")
@@ -97,8 +105,8 @@
*
* <ul>
* <li>CENTER - Expand from the center of the popup</li>
- * <li>ONE_WAY_CORNER - Expand from the top left corner, do not animate
- * hiding </li>
+ * <li>ONE_WAY_CORNER - Expand from the top left corner, do not animate hiding
+ * </li>
* </ul>
*/
static enum AnimationType {
@@ -122,7 +130,12 @@
/**
* A boolean indicating whether we are showing or hiding the popup.
*/
- private boolean showing = false;
+ private boolean showing;
+
+ /**
+ * A boolean indicating whether the glass element is currently attached.
+ */
+ private boolean glassShowing;
/**
* Create a new {@link ResizeAnimation}.
@@ -157,6 +170,8 @@
// the animation. If we move this to onStart, the animation will look
// choppy or not run at all.
if (showing) {
+ maybeShowGlass();
+
// Set the position attribute, and then attach to the DOM. Otherwise,
// the PopupPanel will appear to 'jump' from its static/relative
// position to its absolute position (issue #1231).
@@ -184,6 +199,7 @@
@Override
protected void onComplete() {
if (!showing) {
+ maybeShowGlass();
RootPanel.get().remove(curPanel);
impl.onHide(curPanel.getElement());
}
@@ -244,7 +260,26 @@
+ "px)";
}
+ /**
+ * Show or hide the glass.
+ */
+ private void maybeShowGlass() {
+ if (showing) {
+ if (curPanel.isGlassEnabled) {
+ Document.get().getBody().appendChild(curPanel.glass);
+ glassShowing = true;
+
+ Window.addResizeHandler(curPanel.glassResizer);
+ curPanel.glassResizer.onResize(null);
+ }
+ } else if (glassShowing) {
+ Document.get().getBody().removeChild(curPanel.glass);
+ glassShowing = false;
+ }
+ }
+
private void onInstantaneousRun() {
+ maybeShowGlass();
if (showing) {
// Set the position attribute, and then attach to the DOM. Otherwise,
// the PopupPanel will appear to 'jump' from its static/relative
@@ -276,6 +311,35 @@
private static final PopupImpl impl = GWT.create(PopupImpl.class);
/**
+ * Window resize handler used to keep the glass the proper size.
+ */
+ private ResizeHandler glassResizer = new ResizeHandler() {
+ public void onResize(ResizeEvent event) {
+ Style style = glass.getStyle();
+
+ int winWidth = Window.getClientWidth();
+ int winHeight = Window.getClientHeight();
+
+ // Hide the glass while checking the document size. Otherwise it would
+ // interfere with the measurement.
+ style.setDisplay(Display.NONE);
+ style.setWidth(0, Unit.PX);
+ style.setHeight(0, Unit.PX);
+
+ int width = Document.get().getScrollWidth();
+ int height = Document.get().getScrollHeight();
+
+ // Set the glass size to the larger of the window's client size or the
+ // document's scroll size.
+ style.setWidth(Math.max(width, winWidth), Unit.PX);
+ style.setHeight(Math.max(height, winHeight), Unit.PX);
+
+ // The size is set. Show the glass again.
+ style.setDisplay(Display.BLOCK);
+ }
+ };
+
+ /**
* If true, animate the opening of this popup from the center. If false,
* animate it open from top to bottom, and do not animate closing. Use false
* to animate menus.
@@ -291,6 +355,16 @@
private String desiredWidth;
+ /**
+ * The glass element.
+ */
+ private Element glass;
+
+ /**
+ * A boolean indicating that a glass element should be used.
+ */
+ private boolean isGlassEnabled;
+
private boolean isAnimationEnabled = false;
// the left style attribute in pixels
@@ -479,8 +553,8 @@
}
/**
- * Returns <code>true</code> if the popup should be automatically hidden
- * when the user clicks outside of it.
+ * Returns <code>true</code> if the popup should be automatically hidden when
+ * the user clicks outside of it.
*
* @return true if autoHide is enabled, false if disabled
*/
@@ -489,6 +563,16 @@
}
/**
+ * Returns <code>true</code> if a glass element will be displayed under
+ * the {@link PopupPanel}.
+ *
+ * @return true if enabled
+ */
+ public boolean isGlassEnabled() {
+ return isGlassEnabled;
+ }
+
+ /**
* Returns <code>true</code> if keyboard or mouse events that do not target
* the PopupPanel or its children should be ignored.
*
@@ -599,8 +683,8 @@
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method on the object returned by {@link #addCloseHandler} instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by {@link #addCloseHandler} instead
*/
@Deprecated
public void removePopupListener(PopupListener listener) {
@@ -622,6 +706,25 @@
}
/**
+ * 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.
+ *
+ * @param enabled true to enable, false to disable
+ */
+ public void setGlassEnabled(boolean enabled) {
+ this.isGlassEnabled = enabled;
+ if (enabled && glass == null) {
+ glass = Document.get().createDivElement();
+ glass.setClassName("gwt-PopupGlass");
+
+ glass.getStyle().setPosition(Position.ABSOLUTE);
+ glass.getStyle().setLeft(0, Unit.PX);
+ glass.getStyle().setTop(0, Unit.PX);
+ }
+ }
+
+ /**
* Sets the height of the panel's child widget. If the panel's child widget
* has not been set, the height passed in will be cached and used to set the
* height immediately after the child widget is set.
@@ -730,8 +833,8 @@
* <code>visibility</code> style attribute. You need to call {@link #show()}
* to actually attached/detach the {@link PopupPanel} to the page.
*
- * @param visible <code>true</code> to show the object, <code>false</code>
- * to hide it
+ * @param visible <code>true</code> to show the object, <code>false</code> to
+ * hide it
* @see #show()
* @see #hide()
*/
@@ -814,6 +917,16 @@
return impl.getContainerElement(getPopupImplElement());
}
+ /**
+ * Get the glass element used by this {@link PopupPanel}. The element is not
+ * created until it is enabled via {@link #setGlassEnabled(boolean)}.
+ *
+ * @return the glass element, or null if not created
+ */
+ protected Element getGlassElement() {
+ return glass;
+ }
+
@Override
protected com.google.gwt.user.client.Element getStyleElement() {
return impl.getStyleElement(getPopupImplElement());
@@ -889,11 +1002,11 @@
* @param elt The Element on which <code>blur()</code> will be invoked
*/
private native void blur(Element elt) /*-{
- // Issue 2390: blurring the body causes IE to disappear to the background
- if (elt.blur && elt != $doc.body) {
- elt.blur();
- }
- }-*/;
+ // Issue 2390: blurring the body causes IE to disappear to the background
+ if (elt.blur && elt != $doc.body) {
+ elt.blur();
+ }
+ }-*/;
/**
* Does the event target one of the partner elements?
diff --git a/user/src/com/google/gwt/user/theme/chrome/public/gwt/chrome/chrome.css b/user/src/com/google/gwt/user/theme/chrome/public/gwt/chrome/chrome.css
index 91decf2..3fd7335 100644
--- a/user/src/com/google/gwt/user/theme/chrome/public/gwt/chrome/chrome.css
+++ b/user/src/com/google/gwt/user/theme/chrome/public/gwt/chrome/chrome.css
@@ -479,6 +479,12 @@
overflow: hidden;
}
+.gwt-PopupGlass {
+ background-color: #000;
+ opacity: 0.3;
+ filter: alpha(opacity=30);
+}
+
.gwt-PushButton-up,
.gwt-PushButton-up-hovering,
.gwt-PushButton-up-disabled,
diff --git a/user/src/com/google/gwt/user/theme/chrome/public/gwt/chrome/chrome_rtl.css b/user/src/com/google/gwt/user/theme/chrome/public/gwt/chrome/chrome_rtl.css
index 8fae597..21d0021 100644
--- a/user/src/com/google/gwt/user/theme/chrome/public/gwt/chrome/chrome_rtl.css
+++ b/user/src/com/google/gwt/user/theme/chrome/public/gwt/chrome/chrome_rtl.css
@@ -479,6 +479,12 @@
overflow: hidden;
}
+.gwt-PopupGlass {
+ background-color: #000;
+ opacity: 0.3;
+ filter: alpha(opacity=30);
+}
+
.gwt-PushButton-up,
.gwt-PushButton-up-hovering,
.gwt-PushButton-up-disabled,
diff --git a/user/src/com/google/gwt/user/theme/dark/public/gwt/dark/dark.css b/user/src/com/google/gwt/user/theme/dark/public/gwt/dark/dark.css
index 1bde5b3..89225c0 100644
--- a/user/src/com/google/gwt/user/theme/dark/public/gwt/dark/dark.css
+++ b/user/src/com/google/gwt/user/theme/dark/public/gwt/dark/dark.css
@@ -463,6 +463,12 @@
overflow: hidden;
}
+.gwt-PopupGlass {
+ background-color: #0cf;
+ opacity: 0.3;
+ filter: alpha(opacity=30);
+}
+
.gwt-PushButton-up,
.gwt-PushButton-up-hovering,
.gwt-PushButton-up-disabled,
diff --git a/user/src/com/google/gwt/user/theme/dark/public/gwt/dark/dark_rtl.css b/user/src/com/google/gwt/user/theme/dark/public/gwt/dark/dark_rtl.css
index 881bd4d..5629801 100644
--- a/user/src/com/google/gwt/user/theme/dark/public/gwt/dark/dark_rtl.css
+++ b/user/src/com/google/gwt/user/theme/dark/public/gwt/dark/dark_rtl.css
@@ -463,6 +463,12 @@
overflow: hidden;
}
+.gwt-PopupGlass {
+ background-color: #0cf;
+ opacity: 0.3;
+ filter: alpha(opacity=30);
+}
+
.gwt-PushButton-up,
.gwt-PushButton-up-hovering,
.gwt-PushButton-up-disabled,
diff --git a/user/src/com/google/gwt/user/theme/standard/public/gwt/standard/standard.css b/user/src/com/google/gwt/user/theme/standard/public/gwt/standard/standard.css
index fc62adb..4875b67 100644
--- a/user/src/com/google/gwt/user/theme/standard/public/gwt/standard/standard.css
+++ b/user/src/com/google/gwt/user/theme/standard/public/gwt/standard/standard.css
@@ -479,6 +479,12 @@
overflow: hidden;
}
+.gwt-PopupGlass {
+ background-color: #000;
+ opacity: 0.3;
+ filter: alpha(opacity=30);
+}
+
.gwt-PushButton-up,
.gwt-PushButton-up-hovering,
.gwt-PushButton-up-disabled,
diff --git a/user/src/com/google/gwt/user/theme/standard/public/gwt/standard/standard_rtl.css b/user/src/com/google/gwt/user/theme/standard/public/gwt/standard/standard_rtl.css
index c6b7ee4..e2d75f3 100644
--- a/user/src/com/google/gwt/user/theme/standard/public/gwt/standard/standard_rtl.css
+++ b/user/src/com/google/gwt/user/theme/standard/public/gwt/standard/standard_rtl.css
@@ -479,6 +479,12 @@
overflow: hidden;
}
+.gwt-PopupGlass {
+ background-color: #000;
+ opacity: 0.3;
+ filter: alpha(opacity=30);
+}
+
.gwt-PushButton-up,
.gwt-PushButton-up-hovering,
.gwt-PushButton-up-disabled,
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 697c7f1..bab4190 100644
--- a/user/test/com/google/gwt/user/client/ui/PopupTest.java
+++ b/user/test/com/google/gwt/user/client/ui/PopupTest.java
@@ -17,10 +17,12 @@
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
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.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
@@ -50,7 +52,7 @@
}
@Override
- public Element getContainerElement() {
+ public com.google.gwt.user.client.Element getContainerElement() {
return super.getContainerElement();
}
@@ -172,6 +174,81 @@
testDependantPopupPanel(primaryPopup);
}
+ public void testGlassPanelDisabled() {
+ // Verify that the glass is disabled by default
+ PopupPanel popup = createPopupPanel();
+ assertFalse(popup.isGlassEnabled());
+ assertNull(popup.getGlassElement());
+
+ // Verify the glass panel is never created
+ popup.show();
+ assertNull(popup.getGlassElement());
+ popup.hide();
+ }
+
+ public void testGlassDisabledWhileShowing() {
+ // Show the popup and glass panel
+ PopupPanel popup = createPopupPanel();
+ popup.setGlassEnabled(true);
+ Element glass = popup.getGlassElement();
+ popup.show();
+
+ // Disable the glass panel and hide the popup
+ popup.setGlassEnabled(false);
+ assertTrue(isAttached(glass));
+ popup.hide();
+ assertFalse(isAttached(glass));
+
+ // Show the popup and verify that glass is no longer used
+ popup.show();
+ assertFalse(isAttached(glass));
+ popup.hide();
+ }
+
+ public void testGlassEnabled() {
+ // Verify that the glass is disabled by default
+ PopupPanel popup = createPopupPanel();
+ assertFalse(popup.isGlassEnabled());
+ assertNull(popup.getGlassElement());
+
+ // Enable the glass panel and verify it is created
+ popup.setGlassEnabled(true);
+ Element glass = popup.getGlassElement();
+ assertNotNull(glass);
+ assertFalse(isAttached(glass));
+
+ // Show the popup and verify the glass panel is added
+ popup.show();
+ assertTrue(isAttached(glass));
+
+ // Hide the popup and verify the glass panel is removed
+ popup.hide();
+ assertFalse(isAttached(glass));
+ }
+
+ public void testGlassEnabledWhileShowing() {
+ // Verify that the glass is disabled by default
+ PopupPanel popup = createPopupPanel();
+ assertFalse(popup.isGlassEnabled());
+ assertNull(popup.getGlassElement());
+
+ // Show the popup and enable the glass panel
+ popup.show();
+ popup.setGlassEnabled(true);
+ Element glass = popup.getGlassElement();
+ assertNotNull(glass);
+ assertFalse(isAttached(glass));
+
+ // Hide the popup and verify the glass panel is removed
+ popup.hide();
+ assertFalse(isAttached(glass));
+
+ // Show the popup and verify the glas is now used
+ popup.show();
+ assertTrue(isAttached(glass));
+ popup.hide();
+ }
+
/**
* Test that the onLoad method is only called once when showing the popup.
*/
@@ -256,8 +333,8 @@
// Ensure that hiding the popup fires the appropriate events.
delayTestFinish(1000);
- popup.addPopupListener(new PopupListener() {
- public void onPopupClosed(PopupPanel sender, boolean autoClosed) {
+ popup.addCloseHandler(new CloseHandler<PopupPanel>() {
+ public void onClose(CloseEvent<PopupPanel> event) {
finishTest();
}
});
@@ -342,4 +419,8 @@
}
}.schedule(2000);
}
+
+ private boolean isAttached(Element elem) {
+ return Document.get().getBody().isOrHasChild(elem);
+ }
}