Adding a new DeckLayoutPanel that displays one of many widgets and transitions between them using a horizontal or vertical sliding animation. This is useful for mobile web apps that want to create a slide effect between pages. Integrated DeckLayoutPanel into TabLayoutPanel so both widgets can share the animated transitions. I also replaced the TabPanel Showcase sample with a TabLayoutPanel sample to show off the new feature.
This patch also fixes some subtle bugs in the general Layout code. Calling forceLayout/animate(0) to layout immediately now cancels any pending animation. Previously, the animation was only cancelled if a new animation was started. In IE8, we only round pixels values. PCT values are not rounded, which would lower the resolution of the layout. In TabLayoutPanel, we now handle the case where a tab header is removed via header.removeFromParent().
Review at http://gwt-code-reviews.appspot.com/1340803
Review by: sbrubaker@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9689 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource.properties b/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource.properties
index 77234ed..9972325 100644
--- a/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource.properties
+++ b/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource.properties
@@ -222,7 +222,7 @@
cwSuggestBoxDescription = Generate suggestions via RPC calls to the server or static data on the page
cwSuggestBoxLabel = <b>Choose a word:</b>
cwSuggestBoxWords = 1337, apple, about, ant, bruce, banana, bobv, canada, coconut, compiler, donut, deferred binding, dessert topping, eclair, ecc, frog attack, floor wax, fitz, google, gosh, gwt, hollis, haskell, hammer, in the flinks, internets, ipso facto, jat, jgw, java, jens, knorton, kaitlyn, kangaroo, la grange, lars, love, morrildl, max, maddie, mloofle, mmendez, nail, narnia, null, optimizations, obfuscation, original, ping pong, polymorphic, pleather, quotidian, quality, qu'est-ce que c'est, ready state, ruby, rdayal, subversion, superclass, scottb, tobyr, the dans, ~ tilde, undefined, unit tests, under 100ms, vtbl, vidalia, vector graphics, w3c, web experience, work around, w00t!, xml, xargs, xeno, yacc, yank (the vi command), zealot, zoe, zebra
-cwTabPanelName = Tab Panel
+cwTabPanelName = Tab Layout Panel
cwTabPanelDescription = Divide content over multiple tabs.
cwTabPanelTab0 = Click one of the tabs to see more content.
cwTabPanelTab2 = Tabs are highly customizable using CSS.
diff --git a/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_ar.properties b/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_ar.properties
index 348fa31..57e0a9f 100644
--- a/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_ar.properties
+++ b/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_ar.properties
@@ -126,7 +126,7 @@
cwFileUploadSelectFile = <b>قم باختيار ملف:</b>
cwFileUploadSuccessful = تم تصعيد الملف!
cwFileUploadButton = تصعيد الملف
-cwFlexTableName = جدول فلكس
+cwFlexTableName = علامة التبويب تخطيط لوحة
cwFlexTableDescription = ان جدول فلكس يدعم اتساع الصفوف والاعمدة مما يتيح لك ترتيب المعلومات بطرق مختلفة
cwFlexTableAddRow = اضافة صف
cwFlexTableDetails = هذا هو جدول فلكس والذي يدعم <b>colspan</b> و <b>rowspans</b>. يمكنك تنسيق الصفحة الخاصة بك كجدول من نوع خاص
diff --git a/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_fr.properties b/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_fr.properties
index f06ccbd..65ed56d 100644
--- a/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_fr.properties
+++ b/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_fr.properties
@@ -209,7 +209,7 @@
cwSuggestBoxDescription = Permet de générer des suggestions par l'intermédiaire d'appels de procédure distante (RPC) au serveur ou de données statiques sur la page.
cwSuggestBoxLabel = <b>Choisir un mot:</b>
cwSuggestBoxWords = 1337, apple, about, ant, bruce, banana, bobv, canada, coconut, compiler, donut, deferred binding, dessert topping, eclair, ecc, frog attack, floor wax, fitz, google, gosh, gwt, hollis, haskell, hammer, in the flinks, internets, ipso facto, jat, jgw, java, jens, knorton, kaitlyn, kangaroo, la grange, lars, love, morrildl, max, maddie, mloofle, mmendez, nail, narnia, null, optimizations, obfuscation, original, ping pong, polymorphic, pleather, quotidian, quality, qu'est-ce que c'est, ready state, ruby, rdayal, subversion, superclass, scottb, tobyr, the dans, ~ tilde, undefined, unit tests, under 100ms, vtbl, vidalia, vector graphics, w3c, web experience, work around, w00t!, xml, xargs, xeno, yacc, yank (the vi command), zealot, zoe, zebra
-cwTabPanelName = Panneau d'onglets
+cwTabPanelName = Disposition du panneau Tab
cwTabPanelDescription = Permet de répartir le contenu en plusieurs onglets.
cwTabPanelTab0 = Cliquez sur l'un des onglets pour afficher du contenu supplémentaire.
cwTabPanelTab2 = Grâce au langage CSS, les onglets sont presque entièrement personnalisables.
diff --git a/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_zh.properties b/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_zh.properties
index 20ac7ef..2696a26 100644
--- a/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_zh.properties
+++ b/samples/showcase/src/com/google/gwt/i18n/client/LocalizableResource_zh.properties
@@ -209,7 +209,7 @@
cwSuggestBoxDescription = 通过服务器RPC调用或页面的静态数据来生成建议
cwSuggestBoxLabel = <b>选择字词:</b>
cwSuggestBoxWords = 1337, apple, about, ant, bruce, banana, bobv, canada, coconut, compiler, donut, deferred binding, dessert topping, eclair, ecc, frog attack, floor wax, fitz, google, gosh, gwt, hollis, haskell, hammer, in the flinks, internets, ipso facto, jat, jgw, java, jens, knorton, kaitlyn, kangaroo, la grange, lars, love, morrildl, max, maddie, mloofle, mmendez, nail, narnia, null, optimizations, obfuscation, original, ping pong, polymorphic, pleather, quotidian, quality, qu'est-ce que c'est, ready state, ruby, rdayal, subversion, superclass, scottb, tobyr, the dans, ~ tilde, undefined, unit tests, under 100ms, vtbl, vidalia, vector graphics, w3c, web experience, work around, w00t!, xml, xargs, xeno, yacc, yank (the vi command), zealot, zoe, zebra
-cwTabPanelName = 标签面板
+cwTabPanelName = 布局面板标签
cwTabPanelDescription = 通过多个标签划分内容。
cwTabPanelTab0 = 点击标签可查看更多内容。
cwTabPanelTab2 = 标签可通过 CSS 实现高度自定义化。
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/MainMenuTreeViewModel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/MainMenuTreeViewModel.java
index f5f16fc..fababb8 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/MainMenuTreeViewModel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/MainMenuTreeViewModel.java
@@ -48,7 +48,7 @@
import com.google.gwt.sample.showcase.client.content.panels.CwFlowPanel;
import com.google.gwt.sample.showcase.client.content.panels.CwHorizontalPanel;
import com.google.gwt.sample.showcase.client.content.panels.CwHorizontalSplitPanel;
-import com.google.gwt.sample.showcase.client.content.panels.CwTabPanel;
+import com.google.gwt.sample.showcase.client.content.panels.CwTabLayoutPanel;
import com.google.gwt.sample.showcase.client.content.panels.CwVerticalPanel;
import com.google.gwt.sample.showcase.client.content.panels.CwVerticalSplitPanel;
import com.google.gwt.sample.showcase.client.content.popups.CwBasicPopup;
@@ -346,8 +346,8 @@
RunAsyncCode.runAsyncCode(CwDockPanel.class));
category.addExample(new CwDisclosurePanel(constants),
RunAsyncCode.runAsyncCode(CwDisclosurePanel.class));
- category.addExample(new CwTabPanel(constants),
- RunAsyncCode.runAsyncCode(CwTabPanel.class));
+ category.addExample(new CwTabLayoutPanel(constants),
+ RunAsyncCode.runAsyncCode(CwTabLayoutPanel.class));
category.addExample(new CwHorizontalSplitPanel(constants),
RunAsyncCode.runAsyncCode(CwHorizontalSplitPanel.class));
category.addExample(new CwVerticalSplitPanel(constants),
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/ShowcaseConstants.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/ShowcaseConstants.java
index c701534..bcc2e6a 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/ShowcaseConstants.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/ShowcaseConstants.java
@@ -46,7 +46,7 @@
import com.google.gwt.sample.showcase.client.content.panels.CwFlowPanel;
import com.google.gwt.sample.showcase.client.content.panels.CwHorizontalPanel;
import com.google.gwt.sample.showcase.client.content.panels.CwHorizontalSplitPanel;
-import com.google.gwt.sample.showcase.client.content.panels.CwTabPanel;
+import com.google.gwt.sample.showcase.client.content.panels.CwTabLayoutPanel;
import com.google.gwt.sample.showcase.client.content.panels.CwVerticalPanel;
import com.google.gwt.sample.showcase.client.content.panels.CwVerticalSplitPanel;
import com.google.gwt.sample.showcase.client.content.popups.CwBasicPopup;
@@ -71,7 +71,7 @@
CwBasicButton.CwConstants, CwCustomButton.CwConstants,
CwListBox.CwConstants, CwSuggestBox.CwConstants, CwTree.CwConstants,
CwMenuBar.CwConstants, CwFlowPanel.CwConstants,
- CwDisclosurePanel.CwConstants, CwTabPanel.CwConstants,
+ CwDisclosurePanel.CwConstants, CwTabLayoutPanel.CwConstants,
CwDockPanel.CwConstants, CwHorizontalPanel.CwConstants,
CwHorizontalSplitPanel.CwConstants, CwVerticalPanel.CwConstants,
CwVerticalSplitPanel.CwConstants, CwBasicPopup.CwConstants,
diff --git a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabLayoutPanel.java
similarity index 82%
rename from samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
rename to samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabLayoutPanel.java
index 47c36be..c6d74bd 100644
--- a/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabPanel.java
+++ b/samples/showcase/src/com/google/gwt/sample/showcase/client/content/panels/CwTabLayoutPanel.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
+import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.i18n.client.Constants;
import com.google.gwt.sample.showcase.client.ContentWidget;
import com.google.gwt.sample.showcase.client.Showcase;
@@ -24,10 +25,10 @@
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseSource;
import com.google.gwt.sample.showcase.client.ShowcaseAnnotations.ShowcaseStyle;
import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.DecoratedTabPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
/**
@@ -36,7 +37,7 @@
@ShowcaseStyle({
".gwt-DecoratedTabBar", "html>body .gwt-DecoratedTabBar",
"* html .gwt-DecoratedTabBar", ".gwt-TabPanel"})
-public class CwTabPanel extends ContentWidget {
+public class CwTabLayoutPanel extends ContentWidget {
/**
* The constants used in this Content Widget.
*/
@@ -64,7 +65,7 @@
*
* @param constants the constants
*/
- public CwTabPanel(CwConstants constants) {
+ public CwTabLayoutPanel(CwConstants constants) {
super(constants.cwTabPanelName(), constants.cwTabPanelDescription(), true);
this.constants = constants;
}
@@ -72,14 +73,13 @@
/**
* Initialize this example.
*/
- @SuppressWarnings("deprecation")
@ShowcaseSource
@Override
public Widget onInitialize() {
// Create a tab panel
- DecoratedTabPanel tabPanel = new DecoratedTabPanel();
- tabPanel.setWidth("400px");
- tabPanel.setAnimationEnabled(true);
+ TabLayoutPanel tabPanel = new TabLayoutPanel(2.0, Unit.EM);
+ tabPanel.setPixelSize(600, 400);
+ tabPanel.setAnimationDuration(1000);
// Add a home tab
String[] tabTitles = constants.cwTabPanelTabs();
@@ -87,9 +87,9 @@
tabPanel.add(homeText, tabTitles[0]);
// Add a tab with an image
- VerticalPanel vPanel = new VerticalPanel();
- vPanel.add(new Image(Showcase.images.gwtLogo()));
- tabPanel.add(vPanel, tabTitles[1]);
+ SimplePanel imageContainer = new SimplePanel();
+ imageContainer.setWidget(new Image(Showcase.images.gwtLogo()));
+ tabPanel.add(imageContainer, tabTitles[1]);
// Add a tab
HTML moreInfo = new HTML(constants.cwTabPanelTab2());
@@ -103,7 +103,7 @@
@Override
protected void asyncOnInitialize(final AsyncCallback<Widget> callback) {
- GWT.runAsync(CwTabPanel.class, new RunAsyncCallback() {
+ GWT.runAsync(CwTabLayoutPanel.class, new RunAsyncCallback() {
public void onFailure(Throwable caught) {
callback.onFailure(caught);
diff --git a/user/src/com/google/gwt/layout/client/Layout.java b/user/src/com/google/gwt/layout/client/Layout.java
index f1f214a..df84b04 100644
--- a/user/src/com/google/gwt/layout/client/Layout.java
+++ b/user/src/com/google/gwt/layout/client/Layout.java
@@ -480,6 +480,11 @@
* @param callback the animation callback
*/
public void layout(int duration, final AnimationCallback callback) {
+ // Cancel the old animation, if there is one.
+ if (animation != null) {
+ animation.cancel();
+ }
+
// If there's no actual animation going on, don't do any of the expensive
// constraint calculations or anything like that.
if (duration == 0) {
@@ -523,11 +528,6 @@
adjustVerticalConstraints(parentHeight, l);
}
- // Cancel the old animation, if there is one.
- if (animation != null) {
- animation.cancel();
- }
-
animation = new Animation() {
@Override
protected void onCancel() {
@@ -536,11 +536,11 @@
@Override
protected void onComplete() {
+ animation = null;
layout();
if (callback != null) {
callback.onAnimationComplete();
}
- animation = null;
}
@Override
diff --git a/user/src/com/google/gwt/layout/client/LayoutImplIE8.java b/user/src/com/google/gwt/layout/client/LayoutImplIE8.java
index ecfd916..9b2a879 100644
--- a/user/src/com/google/gwt/layout/client/LayoutImplIE8.java
+++ b/user/src/com/google/gwt/layout/client/LayoutImplIE8.java
@@ -151,6 +151,7 @@
default:
value = value * getUnitSizeInPixels(layer.container, unit, vertical);
+ value = (int) (value + 0.5); // Round to the nearest pixel.
unit = Unit.PX;
break;
}
@@ -161,8 +162,7 @@
}
}
- layer.getContainerElement().getStyle().setProperty(prop,
- (int) (value + 0.5), unit);
+ layer.getContainerElement().getStyle().setProperty(prop, value, unit);
}
private void updateVisibility(Element container) {
diff --git a/user/src/com/google/gwt/user/client/ui/DeckLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/DeckLayoutPanel.java
new file mode 100644
index 0000000..7cdd76d
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/DeckLayoutPanel.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.ui;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.layout.client.Layout;
+import com.google.gwt.layout.client.Layout.AnimationCallback;
+import com.google.gwt.layout.client.Layout.Layer;
+
+/**
+ * A panel that displays all of its child widgets in a 'deck', where only one
+ * can be visible at a time. It is used by
+ * {@link com.google.gwt.user.client.ui.TabLayoutPanel}.
+ *
+ * <p>
+ * This widget will <em>only</em> work in standards mode, which requires that
+ * the HTML page in which it is run have an explicit <!DOCTYPE>
+ * declaration.
+ * </p>
+ *
+ * <p>
+ * Once a widget has been added to a DeckPanel, its visibility, width, and
+ * height attributes will be manipulated. When the widget is removed from the
+ * DeckPanel, it will be visible, and its width and height attributes will be
+ * cleared.
+ * </p>
+ */
+public class DeckLayoutPanel extends ComplexPanel implements AnimatedLayout,
+ RequiresResize, ProvidesResize, InsertPanel.ForIsWidget, AcceptsOneWidget {
+
+ /**
+ * {@link LayoutCommand} used by this widget.
+ */
+ private class DeckAnimateCommand extends LayoutCommand {
+ public DeckAnimateCommand(Layout layout) {
+ super(layout);
+ }
+
+ @Override
+ public void schedule(int duration, final AnimationCallback callback) {
+ super.schedule(duration, new AnimationCallback() {
+ public void onAnimationComplete() {
+ DeckLayoutPanel.this.doAfterLayout();
+ if (callback != null) {
+ callback.onAnimationComplete();
+ }
+ }
+
+ public void onLayout(Layer layer, double progress) {
+ if (callback != null) {
+ callback.onLayout(layer, progress);
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void doBeforeLayout() {
+ DeckLayoutPanel.this.doBeforeLayout();
+ }
+ }
+
+ private int animationDuration = 0;
+ private boolean isAnimationVertical;
+ private Widget hidingWidget;
+ private Widget lastVisibleWidget;
+ private final Layout layout;
+ private final LayoutCommand layoutCmd;
+ private Widget visibleWidget;
+
+ /**
+ * Creates an empty deck panel.
+ */
+ public DeckLayoutPanel() {
+ setElement(Document.get().createDivElement());
+ layout = new Layout(getElement());
+ layoutCmd = new DeckAnimateCommand(layout);
+ }
+
+ @Override
+ public void add(Widget w) {
+ insert(w, getWidgetCount());
+ }
+
+ public void animate(int duration) {
+ animate(duration, null);
+ }
+
+ public void animate(int duration, AnimationCallback callback) {
+ layoutCmd.schedule(duration, callback);
+ }
+
+ public void forceLayout() {
+ layoutCmd.cancel();
+ doBeforeLayout();
+ layout.layout();
+ doAfterLayout();
+ onResize();
+ }
+
+ /**
+ * Get the duration of the animated transition between tabs.
+ *
+ * @return the duration in milliseconds
+ */
+ public int getAnimationDuration() {
+ return animationDuration;
+ }
+
+ /**
+ * Gets the currently-visible widget.
+ *
+ * @return the visible widget, or null if not visible
+ */
+ public Widget getVisibleWidget() {
+ return visibleWidget;
+ }
+
+ /**
+ * Gets the index of the currently-visible widget.
+ *
+ * @return the visible widget's index
+ */
+ public int getVisibleWidgetIndex() {
+ return getWidgetIndex(visibleWidget);
+ }
+
+ public void insert(IsWidget w, int beforeIndex) {
+ insert(asWidgetOrNull(w), beforeIndex);
+ }
+
+ public void insert(Widget widget, int beforeIndex) {
+ Widget before = (beforeIndex < getWidgetCount()) ? getWidget(beforeIndex)
+ : null;
+ insert(widget, before);
+ }
+
+ /**
+ * Insert a widget before the specified widget. If the widget is already a
+ * child of this panel, this method behaves as though {@link #remove(Widget)}
+ * had already been called.
+ *
+ * @param widget the widget to be added
+ * @param before the widget before which to insert the new child, or
+ * <code>null</code> to append
+ */
+ public void insert(Widget widget, Widget before) {
+ assertIsChild(before);
+
+ // Detach new child.
+ widget.removeFromParent();
+
+ // Logical attach.
+ WidgetCollection children = getChildren();
+ if (before == null) {
+ children.add(widget);
+ } else {
+ int index = children.indexOf(before);
+ children.insert(widget, index);
+ }
+
+ // Physical attach.
+ Layer layer = layout.attachChild(widget.getElement(), (before != null)
+ ? before.getElement() : null, widget);
+ setWidgetVisible(widget, layer, false);
+ widget.setLayoutData(layer);
+
+ // Adopt.
+ adopt(widget);
+
+ // Update the layout.
+ animate(0);
+ }
+
+ /**
+ * Check whether or not transitions slide in vertically or horizontally.
+ * Defaults to horizontally.
+ *
+ * @return true for vertical transitions, false for horizontal
+ */
+ public boolean isAnimationVertical() {
+ return isAnimationVertical;
+ }
+
+ public void onResize() {
+ for (Widget child : getChildren()) {
+ if (child instanceof RequiresResize) {
+ ((RequiresResize) child).onResize();
+ }
+ }
+ }
+
+ @Override
+ public boolean remove(Widget w) {
+ boolean removed = super.remove(w);
+ if (removed) {
+ Layer layer = (Layer) w.getLayoutData();
+ layout.removeChild(layer);
+ w.setLayoutData(null);
+
+ if (visibleWidget == w) {
+ visibleWidget = null;
+ }
+ if (hidingWidget == w) {
+ hidingWidget = null;
+ }
+ if (lastVisibleWidget == w) {
+ lastVisibleWidget = null;
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Set the duration of the animated transition between tabs.
+ *
+ * @param duration the duration in milliseconds.
+ */
+ public void setAnimationDuration(int duration) {
+ this.animationDuration = duration;
+ }
+
+ /**
+ * Set whether or not transitions slide in vertically or horizontally.
+ *
+ * @param isVertical true for vertical transitions, false for horizontal
+ */
+ public void setAnimationVertical(boolean isVertical) {
+ this.isAnimationVertical = isVertical;
+ }
+
+ /**
+ * Show the specified widget. If the widget is not a child of this panel, it
+ * is added to the end of the panel. If the specified widget is null, the
+ * currently-visible widget will be hidden.
+ *
+ * @param w the widget to show, and add if not a child
+ */
+ public void setWidget(IsWidget w) {
+ // Hide the currently visible widget.
+ if (w == null) {
+ showWidget(null);
+ return;
+ }
+
+ // Add the widget if it is not already a child.
+ if (w.asWidget().getParent() != this) {
+ add(w);
+ }
+
+ // Show the widget.
+ showWidget(w.asWidget());
+ }
+
+ /**
+ * Shows the widget at the specified index. This causes the currently- visible
+ * widget to be hidden.
+ *
+ * @param index the index of the widget to be shown
+ */
+ public void showWidget(int index) {
+ checkIndexBoundsForAccess(index);
+ showWidget(getWidget(index));
+ }
+
+ /**
+ * Shows the widget at the specified index. This causes the currently- visible
+ * widget to be hidden.
+ *
+ * @param widget the widget to be shown
+ */
+ public void showWidget(Widget widget) {
+ if (widget == visibleWidget) {
+ return;
+ }
+
+ assertIsChild(widget);
+ visibleWidget = widget;
+ animate((widget == null) ? 0 : animationDuration);
+ }
+
+ /**
+ * Assert that the specified widget is null or a child of this widget.
+ *
+ * @param widget the widget to check
+ */
+ void assertIsChild(Widget widget) {
+ assert (widget == null) || (widget.getParent() == this) : "The specified widget is not a child of this panel";
+ }
+
+ /**
+ * Hide the widget that just slid out of view.
+ */
+ private void doAfterLayout() {
+ if (hidingWidget != null) {
+ Layer layer = (Layer) hidingWidget.getLayoutData();
+ setWidgetVisible(hidingWidget, layer, false);
+ layout.layout();
+ hidingWidget = null;
+ }
+ }
+
+ /**
+ * Initialize the location of the widget that will slide into view.
+ */
+ private void doBeforeLayout() {
+ Layer oldLayer = (lastVisibleWidget == null) ? null
+ : (Layer) lastVisibleWidget.getLayoutData();
+ Layer newLayer = (visibleWidget == null) ? null
+ : (Layer) visibleWidget.getLayoutData();
+
+ // Calculate the direction that the new widget will enter.
+ int oldIndex = getWidgetIndex(lastVisibleWidget);
+ int newIndex = getWidgetIndex(visibleWidget);
+ double direction = (oldIndex < newIndex) ? 100.0 : -100.0;
+ double vDirection = isAnimationVertical ? direction : 0.0;
+ double hDirection = isAnimationVertical ? 0.0
+ : LocaleInfo.getCurrentLocale().isRTL() ? -direction : direction;
+
+ // Position the layers in their start positions.
+ if (oldLayer != null) {
+ // The old layer starts centered in the panel.
+ oldLayer.setTopHeight(0.0, Unit.PCT, 100.0, Unit.PCT);
+ oldLayer.setLeftWidth(0.0, Unit.PCT, 100.0, Unit.PCT);
+ setWidgetVisible(lastVisibleWidget, oldLayer, true);
+ }
+ if (newLayer != null) {
+ // The new layer starts off to one side.
+ newLayer.setTopHeight(vDirection, Unit.PCT, 100.0, Unit.PCT);
+ newLayer.setLeftWidth(hDirection, Unit.PCT, 100.0, Unit.PCT);
+ setWidgetVisible(visibleWidget, newLayer, true);
+ }
+ layout.layout();
+
+ // Set the end positions of the layers.
+ if (oldLayer != null) {
+ // The old layer ends off to one side.
+ oldLayer.setTopHeight(-vDirection, Unit.PCT, 100.0, Unit.PCT);
+ oldLayer.setLeftWidth(-hDirection, Unit.PCT, 100.0, Unit.PCT);
+ setWidgetVisible(lastVisibleWidget, oldLayer, true);
+ }
+ if (newLayer != null) {
+ // The new layer ends centered in the panel.
+ newLayer.setTopHeight(0.0, Unit.PCT, 100.0, Unit.PCT);
+ newLayer.setLeftWidth(0.0, Unit.PCT, 100.0, Unit.PCT);
+ /*
+ * The call to layout() above could have canceled an existing layout
+ * animation, which could cause this widget to be hidden if the user
+ * toggles between two visible widgets. We set it visible again to ensure
+ * that it ends up visible.
+ */
+ setWidgetVisible(visibleWidget, newLayer, true);
+ }
+
+ hidingWidget = lastVisibleWidget;
+ lastVisibleWidget = visibleWidget;
+ }
+
+ private void setWidgetVisible(Widget w, Layer layer, boolean visible) {
+ layer.setVisible(visible);
+
+ /*
+ * Set the visibility of the widget. This is used by lazy panel to
+ * initialize the widget.
+ */
+ w.setVisible(visible);
+ }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java
index 4e154d5..8cd476e 100644
--- a/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java
@@ -17,7 +17,6 @@
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
@@ -29,6 +28,8 @@
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.layout.client.Layout.Alignment;
+import com.google.gwt.layout.client.Layout.AnimationCallback;
+import com.google.gwt.resources.client.CommonResources;
import com.google.gwt.safehtml.shared.SafeHtml;
import java.util.ArrayList;
@@ -94,10 +95,10 @@
* </pre>
*/
public class TabLayoutPanel extends ResizeComposite implements HasWidgets,
- ProvidesResize, IndexedPanel.ForIsWidget,
+ ProvidesResize, IndexedPanel.ForIsWidget, AnimatedLayout,
HasBeforeSelectionHandlers<Integer>, HasSelectionHandlers<Integer> {
- private static class Tab extends SimplePanel {
+ private class Tab extends SimplePanel {
private Element inner;
public Tab(Widget child) {
@@ -108,17 +109,29 @@
setStyleName(TAB_STYLE);
inner.setClassName(TAB_INNER_STYLE);
- // TODO: float:left may not be enough. If there are tabs of differing
- // heights, the shorter ones will top-align, rather than bottom-align,
- // which is what we would want. display:inline-block fixes this, but
- // needs lots of cross-browser hacks to work properly.
- getElement().getStyle().setFloat(Style.Float.LEFT);
+ getElement().addClassName(CommonResources.getInlineBlockStyle());
}
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
+ @Override
+ public boolean remove(Widget w) {
+ /*
+ * Removal of items from the TabBar is delegated to the TabLayoutPanel to
+ * ensure consistency.
+ */
+ int index = tabs.indexOf(this);
+ if (index < 0) {
+ // This tab is no longer in the panel, so just remove the widget.
+ return super.remove(w);
+ } else {
+ // Delegate to the TabLayoutPanel.
+ return TabLayoutPanel.this.remove(index);
+ }
+ }
+
public void setSelected(boolean selected) {
if (selected) {
addStyleDependentName("selected");
@@ -132,6 +145,59 @@
return inner.cast();
}
}
+
+ /**
+ * This extension of DeckLayoutPanel overrides the public mutator methods to
+ * prevent external callers from adding to the state of the DeckPanel.
+ * <p>
+ * Removal of Widgets is supported so that WidgetCollection.WidgetIterator
+ * operates as expected.
+ * </p>
+ * <p>
+ * We ensure that the DeckLayoutPanel cannot become of of sync with its
+ * associated TabBar by delegating all mutations to the TabBar to this
+ * implementation of DeckLayoutPanel.
+ * </p>
+ */
+ private class TabbedDeckLayoutPanel extends DeckLayoutPanel {
+
+ @Override
+ public void add(Widget w) {
+ throw new UnsupportedOperationException(
+ "Use TabLayoutPanel.add() to alter the DeckLayoutPanel");
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException(
+ "Use TabLayoutPanel.clear() to alter the DeckLayoutPanel");
+ }
+
+ @Override
+ public void insert(Widget w, int beforeIndex) {
+ throw new UnsupportedOperationException(
+ "Use TabLayoutPanel.insert() to alter the DeckLayoutPanel");
+ }
+
+ @Override
+ public boolean remove(Widget w) {
+ /*
+ * Removal of items from the DeckLayoutPanel is delegated to the
+ * TabLayoutPanel to ensure consistency.
+ */
+ return TabLayoutPanel.this.remove(w);
+ }
+
+ protected void insertProtected(Widget w, int beforeIndex) {
+ super.insert(w, beforeIndex);
+ }
+
+ protected void removeProtected(Widget w) {
+ super.remove(w);
+ }
+ }
+
+ private static final String CONTENT_CONTAINER_STYLE = "gwt-TabLayoutPanelContentContainer";
private static final String CONTENT_STYLE = "gwt-TabLayoutPanelContent";
private static final String TAB_STYLE = "gwt-TabLayoutPanelTab";
@@ -139,12 +205,9 @@
private static final int BIG_ENOUGH_TO_NOT_WRAP = 16384;
- private final WidgetCollection children = new WidgetCollection(this);
+ private final TabbedDeckLayoutPanel deckPanel = new TabbedDeckLayoutPanel();
private final FlowPanel tabBar = new FlowPanel();
private final ArrayList<Tab> tabs = new ArrayList<Tab>();
- private final double barHeight;
- private final Unit barUnit;
- private final LayoutPanel panel;
private int selectedIndex = -1;
/**
@@ -154,17 +217,21 @@
* @param barUnit the unit in which the tab bar size is specified
*/
public TabLayoutPanel(double barHeight, Unit barUnit) {
- this.barHeight = barHeight;
- this.barUnit = barUnit;
-
- panel = new LayoutPanel();
+ LayoutPanel panel = new LayoutPanel();
initWidget(panel);
+ // Add the tab bar to the panel.
panel.add(tabBar);
panel.setWidgetLeftRight(tabBar, 0, Unit.PX, 0, Unit.PX);
panel.setWidgetTopHeight(tabBar, 0, Unit.PX, barHeight, barUnit);
panel.setWidgetVerticalPosition(tabBar, Alignment.END);
+ // Add the deck panel to the panel.
+ deckPanel.addStyleName(CONTENT_CONTAINER_STYLE);
+ panel.add(deckPanel);
+ panel.setWidgetLeftRight(deckPanel, 0, Unit.PX, 0, Unit.PX);
+ panel.setWidgetTopBottom(deckPanel, barHeight, barUnit, 0, Unit.PX);
+
// Make the tab bar extremely wide so that tabs themselves never wrap.
// (Its layout container is overflow:hidden)
tabBar.getElement().getStyle().setWidth(BIG_ENOUGH_TO_NOT_WRAP, Unit.PX);
@@ -260,6 +327,14 @@
return addHandler(handler, SelectionEvent.getType());
}
+ public void animate(int duration) {
+ animate(duration, null);
+ }
+
+ public void animate(int duration, AnimationCallback callback) {
+ deckPanel.animate(duration, callback);
+ }
+
public void clear() {
Iterator<Widget> it = iterator();
while (it.hasNext()) {
@@ -268,6 +343,19 @@
}
}
+ public void forceLayout() {
+ deckPanel.forceLayout();
+ }
+
+ /**
+ * Get the duration of the animated transition between tabs.
+ *
+ * @return the duration in milliseconds
+ */
+ public int getAnimationDuration() {
+ return deckPanel.getAnimationDuration();
+ }
+
/**
* Gets the index of the currently-selected tab.
*
@@ -310,15 +398,14 @@
* Returns the widget at the given index.
*/
public Widget getWidget(int index) {
- checkIndex(index);
- return children.get(index);
+ return deckPanel.getWidget(index);
}
/**
* Returns the number of tabs and widgets.
*/
public int getWidgetCount() {
- return children.size();
+ return deckPanel.getWidgetCount();
}
/**
@@ -332,7 +419,7 @@
* Returns the index of the given child, or -1 if it is not a child.
*/
public int getWidgetIndex(Widget child) {
- return children.indexOf(child);
+ return deckPanel.getWidgetIndex(child);
}
/**
@@ -429,8 +516,18 @@
insert(child, new Tab(tab), beforeIndex);
}
+ /**
+ * Check whether or not transitions slide in vertically or horizontally.
+ * Defaults to horizontally.
+ *
+ * @return true for vertical transitions, false for horizontal
+ */
+ public boolean isAnimationVertical() {
+ return deckPanel.isAnimationVertical();
+ }
+
public Iterator<Widget> iterator() {
- return children.iterator();
+ return deckPanel.iterator();
}
public boolean remove(int index) {
@@ -438,13 +535,13 @@
return false;
}
- Widget child = children.get(index);
+ Widget child = getWidget(index);
tabBar.remove(index);
- panel.remove(child);
+ deckPanel.removeProtected(child);
child.removeStyleName(CONTENT_STYLE);
- children.remove(index);
- tabs.remove(index);
+ Tab tab = tabs.remove(index);
+ tab.getWidget().removeFromParent();
if (index == selectedIndex) {
// If the selected tab is being removed, select the first tab (if there
@@ -462,7 +559,7 @@
}
public boolean remove(Widget w) {
- int index = children.indexOf(w);
+ int index = getWidgetIndex(w);
if (index == -1) {
return false;
}
@@ -503,13 +600,10 @@
// Update the tabs being selected and unselected.
if (selectedIndex != -1) {
- Widget child = children.get(selectedIndex);
- panel.setWidgetVisible(child, false);
tabs.get(selectedIndex).setSelected(false);
}
- Widget child = children.get(index);
- panel.setWidgetVisible(child, true);
+ deckPanel.showWidget(index);
tabs.get(index).setSelected(true);
selectedIndex = index;
@@ -553,6 +647,24 @@
}
/**
+ * Set the duration of the animated transition between tabs.
+ *
+ * @param duration the duration in milliseconds.
+ */
+ public void setAnimationDuration(int duration) {
+ deckPanel.setAnimationDuration(duration);
+ }
+
+ /**
+ * Set whether or not transitions slide in vertically or horizontally.
+ *
+ * @param isVertical true for vertical transitions, false for horizontal
+ */
+ public void setAnimationVertical(boolean isVertical) {
+ deckPanel.setAnimationVertical(isVertical);
+ }
+
+ /**
* Sets a tab's HTML contents.
*
* Use care when setting an object's HTML; it is an easy way to expose
@@ -590,11 +702,11 @@
}
private void checkChild(Widget child) {
- assert children.contains(child);
+ assert getWidgetIndex(child) >= 0 : "Child is not a part of this panel";
}
private void checkIndex(int index) {
- assert (index >= 0) && (index < children.size()) : "Index out of bounds";
+ assert (index >= 0) && (index < getWidgetCount()) : "Index out of bounds";
}
private void insert(final Widget child, Tab tab, int beforeIndex) {
@@ -610,7 +722,7 @@
}
}
- children.insert(child, beforeIndex);
+ deckPanel.insertProtected(child, beforeIndex);
tabs.add(beforeIndex, tab);
tabBar.insert(tab, beforeIndex);
@@ -620,8 +732,7 @@
}
});
- panel.insert(child, beforeIndex);
- layoutChild(child);
+ child.addStyleName(CONTENT_STYLE);
if (selectedIndex == -1) {
selectTab(0);
@@ -631,12 +742,4 @@
selectedIndex++;
}
}
-
- private void layoutChild(Widget child) {
- panel.setWidgetLeftRight(child, 0, Unit.PX, 0, Unit.PX);
- panel.setWidgetTopBottom(child, barHeight, barUnit, 0, Unit.PX);
- panel.setWidgetVisible(child, false);
- child.addStyleName(CONTENT_STYLE);
- child.setVisible(false);
- }
}
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 1aeea05..3975c6d 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
@@ -1114,10 +1114,15 @@
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabs {
}
+.gwt-TabLayoutPanelContentContainer {
+ border-color: #bcbcbc;
+ border-style: solid;
+ border-width: 2px 1px 1px;
+}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelContent {
border-color: #bcbcbc;
border-style: solid;
- border-width: 3px 2px 2px;
+ border-width: 1px;
overflow: hidden;
padding: 6px;
}
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 903419c..579acfb 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
@@ -1115,10 +1115,15 @@
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabs {
}
+.gwt-TabLayoutPanelContentContainer {
+ border-color: #bcbcbc;
+ border-style: solid;
+ border-width: 2px 1px 1px;
+}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelContent {
border-color: #bcbcbc;
border-style: solid;
- border-width: 3px 2px 2px;
+ border-width: 1px;
overflow: hidden;
padding: 6px;
}
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 e28a3ac..3db6c91 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
@@ -1064,10 +1064,15 @@
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabs {
}
+.gwt-TabLayoutPanelContentContainer {
+ border-color: #00CCFF;
+ border-style: solid;
+ border-width: 2px 1px 1px;
+}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelContent {
border-color: #00CCFF;
border-style: solid;
- border-width: 3px 2px 2px;
+ border-width: 1px;
overflow: hidden;
padding: 6px;
}
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 dd1e5fb..bd5b404 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
@@ -1065,10 +1065,15 @@
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabs {
}
+.gwt-TabLayoutPanelContentContainer {
+ border-color: #00CCFF;
+ border-style: solid;
+ border-width: 2px 1px 1px;
+}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelContent {
border-color: #00CCFF;
border-style: solid;
- border-width: 3px 2px 2px;
+ border-width: 1px;
overflow: hidden;
padding: 6px;
}
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 c991fc1..095549a 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
@@ -1113,10 +1113,15 @@
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabs {
}
+.gwt-TabLayoutPanelContentContainer {
+ border-color: #92c1f0;
+ border-style: solid;
+ border-width: 2px 1px 1px;
+}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelContent {
border-color: #92c1f0;
border-style: solid;
- border-width: 3px 2px 2px;
+ border-width: 1px;
overflow: hidden;
padding: 6px;
}
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 de61132..62c5fac 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
@@ -1114,10 +1114,15 @@
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabs {
}
+.gwt-TabLayoutPanelContentContainer {
+ border-color: #92c1f0;
+ border-style: solid;
+ border-width: 2px 1px 1px;
+}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelContent {
border-color: #92c1f0;
border-style: solid;
- border-width: 3px 2px 2px;
+ border-width: 1px;
overflow: hidden;
padding: 6px;
}
diff --git a/user/test/com/google/gwt/user/UISuite.java b/user/test/com/google/gwt/user/UISuite.java
index d88f438..bb342be 100644
--- a/user/test/com/google/gwt/user/UISuite.java
+++ b/user/test/com/google/gwt/user/UISuite.java
@@ -38,6 +38,7 @@
import com.google.gwt.user.client.ui.DOMTest;
import com.google.gwt.user.client.ui.DateBoxTest;
import com.google.gwt.user.client.ui.DatePickerTest;
+import com.google.gwt.user.client.ui.DeckLayoutPanelTest;
import com.google.gwt.user.client.ui.DeckPanelTest;
import com.google.gwt.user.client.ui.DecoratedPopupTest;
import com.google.gwt.user.client.ui.DecoratedStackPanelTest;
@@ -144,6 +145,7 @@
suite.addTestSuite(DateBoxTest.class);
suite.addTestSuite(DateChangeEventTest.class);
suite.addTestSuite(DatePickerTest.class);
+ suite.addTestSuite(DeckLayoutPanelTest.class);
suite.addTestSuite(DeckPanelTest.class);
suite.addTestSuite(DecoratedPopupTest.class);
suite.addTestSuite(DecoratedStackPanelTest.class);
diff --git a/user/test/com/google/gwt/user/client/ui/DeckLayoutPanelTest.java b/user/test/com/google/gwt/user/client/ui/DeckLayoutPanelTest.java
new file mode 100644
index 0000000..87ee8e8
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/ui/DeckLayoutPanelTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.user.client.ui;
+
+/**
+ * Test for {@link DeckLayoutPanel}.
+ */
+public class DeckLayoutPanelTest extends PanelTestBase<DeckLayoutPanel> {
+
+ public void testSetWidget() {
+ DeckLayoutPanel deck = createPanel();
+ Label[] labels = new Label[2];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = new Label("content" + i);
+ }
+
+ // Set a widget that isn't a child.
+ deck.setWidget(labels[0]);
+ assertEquals(deck, labels[0].getParent());
+ assertEquals(0, deck.getVisibleWidgetIndex());
+ assertEquals(labels[0], deck.getVisibleWidget());
+
+ // Set another widget that isn't a child.
+ deck.setWidget(labels[1]);
+ assertEquals(deck, labels[0].getParent());
+ assertEquals(deck, labels[1].getParent());
+ assertEquals(0, deck.getWidgetIndex(labels[0]));
+ assertEquals(1, deck.getWidgetIndex(labels[1]));
+ assertEquals(1, deck.getVisibleWidgetIndex());
+ assertEquals(labels[1], deck.getVisibleWidget());
+
+ // Set a widget that is a child.
+ deck.setWidget(labels[0]);
+ assertEquals(deck, labels[0].getParent());
+ assertEquals(deck, labels[1].getParent());
+ assertEquals(0, deck.getWidgetIndex(labels[0]));
+ assertEquals(1, deck.getWidgetIndex(labels[1]));
+ assertEquals(0, deck.getVisibleWidgetIndex());
+ assertEquals(labels[0], deck.getVisibleWidget());
+
+ // Set the widget to null.
+ deck.setWidget(null);
+ assertEquals(deck, labels[0].getParent());
+ assertEquals(deck, labels[1].getParent());
+ assertEquals(0, deck.getWidgetIndex(labels[0]));
+ assertEquals(1, deck.getWidgetIndex(labels[1]));
+ assertEquals(-1, deck.getVisibleWidgetIndex());
+ assertEquals(null, deck.getVisibleWidget());
+ }
+
+ /**
+ * Tests both {@link DeckLayoutPanel#showWidget(int)} and
+ * {@link DeckLayoutPanel#showWidget(Widget)}.
+ */
+ public void testShowWidget() {
+ DeckLayoutPanel deck = createPanel();
+ Label[] labels = new Label[3];
+ for (int i = 0; i < labels.length; i++) {
+ labels[i] = new Label("content" + i);
+ deck.add(labels[i]);
+ }
+
+ // Show widget at index 1, make sure it becomes visible.
+ deck.showWidget(1);
+ assertEquals(1, deck.getVisibleWidgetIndex());
+ assertEquals(labels[1], deck.getVisibleWidget());
+ deck.forceLayout();
+ assertFalse(labels[0].isVisible());
+ assertTrue(labels[1].isVisible());
+ assertFalse(labels[2].isVisible());
+
+ // Show widget at index 0, make sure widget 1 becomes invisible.
+ deck.showWidget(labels[0]);
+ assertEquals(0, deck.getVisibleWidgetIndex());
+ assertEquals(labels[0], deck.getVisibleWidget());
+ deck.forceLayout();
+ assertTrue(labels[0].isVisible());
+ assertFalse(labels[1].isVisible());
+ assertFalse(labels[2].isVisible());
+ }
+
+ @Override
+ protected DeckLayoutPanel createPanel() {
+ return new DeckLayoutPanel();
+ }
+}
diff --git a/user/test/com/google/gwt/user/client/ui/TabLayoutPanelTest.java b/user/test/com/google/gwt/user/client/ui/TabLayoutPanelTest.java
index 8cb1ce5..a9ffe65 100644
--- a/user/test/com/google/gwt/user/client/ui/TabLayoutPanelTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TabLayoutPanelTest.java
@@ -1,12 +1,12 @@
/*
* Copyright 2009 Google Inc.
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -266,15 +266,20 @@
assertEquals("", p.getTabWidget(insert).getElement().getInnerHTML());
assertEquals("", p.getTabWidget(add).getElement().getInnerHTML());
- assertEquals("inserted text", p.getTabWidget(insText).getElement().getInnerHTML());
- assertEquals("added text", p.getTabWidget(addText).getElement().getInnerHTML());
- assertEquals("<b>inserted html</b>", p.getTabWidget(insHtml).getElement().getInnerHTML().toLowerCase());
- assertEquals("<b>added html</b>", p.getTabWidget(addHtml).getElement().getInnerHTML().toLowerCase());
+ assertEquals("inserted text",
+ p.getTabWidget(insText).getElement().getInnerHTML());
+ assertEquals("added text",
+ p.getTabWidget(addText).getElement().getInnerHTML());
+ assertEquals("<b>inserted html</b>",
+ p.getTabWidget(insHtml).getElement().getInnerHTML().toLowerCase());
+ assertEquals("<b>added html</b>",
+ p.getTabWidget(addHtml).getElement().getInnerHTML().toLowerCase());
assertEquals(inserted.w, p.getTabWidget(insWidget));
assertEquals(added.w, p.getTabWidget(addWidget));
class Handler implements SelectionHandler<Integer> {
boolean fired = false;
+
public void onSelection(SelectionEvent<Integer> event) {
fired = true;
}
@@ -322,6 +327,48 @@
assertTrue(p.getWidget(1) == baz);
}
+ /**
+ * Test that removing a widget removes the associated tab.
+ */
+ public void testRemoveWidgetFromParent() {
+ TabLayoutPanel p = new TabLayoutPanel(2, Unit.EM);
+
+ Label content0 = new Label("Content 0");
+ Label header0 = new Label("Header 0");
+ p.add(content0, header0);
+ Label content1 = new Label("Content 1");
+ Label header1 = new Label("Header 1");
+ p.add(content1, header1);
+ Label content2 = new Label("Content 2");
+ Label header2 = new Label("Header 2");
+ p.add(content2, header2);
+ assertEquals(3, p.getWidgetCount());
+ assertEquals(content0, p.getWidget(0));
+ assertEquals(content1, p.getWidget(1));
+ assertEquals(content2, p.getWidget(2));
+ assertEquals(header0, p.getTabWidget(0));
+ assertEquals(header1, p.getTabWidget(1));
+ assertEquals(header2, p.getTabWidget(2));
+
+ // Remove content.
+ content1.removeFromParent();
+ assertNull(content1.getParent());
+ assertNull(header1.getParent());
+ assertEquals(2, p.getWidgetCount());
+ assertEquals(content0, p.getWidget(0));
+ assertEquals(content2, p.getWidget(1));
+ assertEquals(header0, p.getTabWidget(0));
+ assertEquals(header2, p.getTabWidget(1));
+
+ // Remove a header.
+ header2.removeFromParent();
+ assertNull(content2.getParent());
+ assertNull(header2.getParent());
+ assertEquals(1, p.getWidgetCount());
+ assertEquals(content0, p.getWidget(0));
+ assertEquals(header0, p.getTabWidget(0));
+ }
+
public void testSelectionEvents() {
TabLayoutPanel p = new TabLayoutPanel(2, Unit.EM);
RootPanel.get().add(p);
@@ -366,6 +413,7 @@
}
// Initially, the first widget should be visible.
+ p.forceLayout();
assertTrue(labels[0].isVisible());
assertFalse(labels[1].isVisible());
assertFalse(labels[2].isVisible());
@@ -373,12 +421,14 @@
// Show widget at index 1, make sure it becomes visible, and the one at
// index 0 is hidden.
p.selectTab(1);
+ p.forceLayout();
assertFalse(labels[0].isVisible());
assertTrue(labels[1].isVisible());
assertFalse(labels[2].isVisible());
// Show widget at index 0, make sure it changed back to the initial state.
p.selectTab(0);
+ p.forceLayout();
assertTrue(labels[0].isVisible());
assertFalse(labels[1].isVisible());
assertFalse(labels[2].isVisible());
@@ -419,7 +469,7 @@
p.add(new Button("foo"), new Label("foo"));
p.add(new Button("bar"), new Label("bar"));
- assertEquals(p.getTabWidget(0).getElement().getOffsetTop(), p.getTabWidget(
- 1).getElement().getOffsetTop());
+ assertEquals(p.getTabWidget(0).getElement().getOffsetTop(),
+ p.getTabWidget(1).getElement().getOffsetTop());
}
}