Eliminates the need to call layout() explicitly on *LayoutPanel, which
should greatly simplify their use. RequiresLayout becomes
AnimatedLayout, which is now just a convenient moniker for the ability
of a panel to layout its children.
This also necessitated a change to LayoutPanel, such that it no longer
exposes the underlying Layout.Layer objects associated with each widget.
Review: http://gwt-code-reviews.appspot.com/89818
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6624 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java b/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java
index a639a12..f150ff9 100644
--- a/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java
+++ b/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java
@@ -53,7 +53,7 @@
Window.setMargin("0px");
// Special-case stuff to make topPanel overhang a bit.
- Element topElem = outer.getContainerElementFor(topPanel);
+ Element topElem = outer.getWidgetContainerElement(topPanel);
topElem.getStyle().setZIndex(2);
topElem.getStyle().setOverflow(Overflow.VISIBLE);
@@ -69,6 +69,5 @@
// displayed.
RootLayoutPanel root = RootLayoutPanel.get();
root.add(outer);
- root.layout();
}
}
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java b/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java
index b017fad..0ea1f15 100644
--- a/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java
+++ b/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java
@@ -78,7 +78,7 @@
dock.add(new ScrollPanel(table));
header.setWidth("100%");
table.setWidth("100%");
- dock.layout();
+
initWidget(dock);
setStyleName("mail-List");
diff --git a/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java b/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java
index 28687a1..7634684 100644
--- a/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java
+++ b/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java
@@ -33,8 +33,6 @@
interface Binder extends UiBinder<StackLayoutPanel, Shortcuts> { }
private static final Binder binder = GWT.create(Binder.class);
- private StackLayoutPanel stackPanel;
-
@UiField Mailboxes mailboxes;
@UiField Tasks tasks;
@UiField Contacts contacts;
@@ -45,12 +43,6 @@
* @param images a bundle that provides the images for this widget
*/
public Shortcuts() {
- initWidget(stackPanel = binder.createAndBindUi(this));
- }
-
- @Override
- protected void onLoad() {
- // Show the mailboxes group by default.
- stackPanel.showWidget(mailboxes);
+ initWidget(binder.createAndBindUi(this));
}
}
diff --git a/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java b/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java
index 6474fb1..6daf3c0 100644
--- a/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java
+++ b/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java
@@ -33,18 +33,10 @@
p.addWest(new HTML("west"), 2);
p.add(new HTML("center"));
- // Note the explicit call to layout(). This is required for the layout to
- // take effect.
- p.layout();
-
// Attach the LayoutPanel to the RootLayoutPanel. The latter will listen for
// resize events on the window to ensure that its children are informed of
// possible size changes.
RootLayoutPanel rp = RootLayoutPanel.get();
rp.add(p);
-
- // The RootLayoutPanel also requires that its layout() method be explicitly
- // called for the initial layout to take effect.
- rp.layout();
}
}
diff --git a/user/javadoc/com/google/gwt/examples/LayoutPanelExample.java b/user/javadoc/com/google/gwt/examples/LayoutPanelExample.java
index 3fc43d1..9d0e49c 100644
--- a/user/javadoc/com/google/gwt/examples/LayoutPanelExample.java
+++ b/user/javadoc/com/google/gwt/examples/LayoutPanelExample.java
@@ -18,7 +18,6 @@
import static com.google.gwt.dom.client.Style.Unit.PCT;
import com.google.gwt.core.client.EntryPoint;
-import com.google.gwt.layout.client.Layout.Layer;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
@@ -34,22 +33,13 @@
p.add(childOne);
p.add(childTwo);
- Layer layerOne = p.getLayer(childOne), layerTwo = p.getLayer(childTwo);
- layerOne.setLeftWidth(0, PCT, 50, PCT);
- layerTwo.setRightWidth(0, PCT, 50, PCT);
-
- // Note the explicit call to layout(). This is required for the layout to
- // take effect.
- p.layout();
+ p.setWidgetLeftWidth(childOne, 0, PCT, 50, PCT);
+ p.setWidgetRightWidth(childTwo, 0, PCT, 50, PCT);
// Attach the LayoutPanel to the RootLayoutPanel. The latter will listen for
// resize events on the window to ensure that its children are informed of
// possible size changes.
RootLayoutPanel rp = RootLayoutPanel.get();
rp.add(p);
-
- // The RootLayoutPanel also requires that its layout() method be explicitly
- // called for the initial layout to take effect.
- rp.layout();
}
}
diff --git a/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java b/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java
index ba71d4e..1943e5e 100644
--- a/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java
+++ b/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java
@@ -29,18 +29,10 @@
p.addNorth(new HTML("list"), 384);
p.add(new HTML("details"));
- // Note the explicit call to layout(). This is required for the layout to
- // take effect.
- p.layout();
-
// Attach the LayoutPanel to the RootLayoutPanel. The latter will listen for
// resize events on the window to ensure that its children are informed of
// possible size changes.
RootLayoutPanel rp = RootLayoutPanel.get();
rp.add(p);
-
- // The RootLayoutPanel also requires that its layout() method be explicitly
- // called for the initial layout to take effect.
- rp.layout();
}
}
diff --git a/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java b/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java
index b493b01..deee46b 100644
--- a/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java
+++ b/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java
@@ -30,18 +30,10 @@
p.add(new HTML("that"), new HTML("[that]"), 384);
p.add(new HTML("the other"), new HTML("[the other]"), 0);
- // Note the explicit call to layout(). This is required for the layout to
- // take effect.
- p.layout();
-
// Attach the LayoutPanel to the RootLayoutPanel. The latter will listen for
// resize events on the window to ensure that its children are informed of
// possible size changes.
RootLayoutPanel rp = RootLayoutPanel.get();
rp.add(p);
-
- // The RootLayoutPanel also requires that its layout() method be explicitly
- // called for the initial layout to take effect.
- rp.layout();
}
}
diff --git a/user/javadoc/com/google/gwt/examples/TabLayoutPanelExample.java b/user/javadoc/com/google/gwt/examples/TabLayoutPanelExample.java
index 78d1c2e..05ddbb7 100644
--- a/user/javadoc/com/google/gwt/examples/TabLayoutPanelExample.java
+++ b/user/javadoc/com/google/gwt/examples/TabLayoutPanelExample.java
@@ -35,9 +35,5 @@
// possible size changes.
RootLayoutPanel rp = RootLayoutPanel.get();
rp.add(p);
-
- // The RootLayoutPanel requires that its layout() method be explicitly
- // called for the initial layout to take effect.
- rp.layout();
}
}
diff --git a/user/src/com/google/gwt/layout/client/Layout.java b/user/src/com/google/gwt/layout/client/Layout.java
index 483a805..c5d9d10 100644
--- a/user/src/com/google/gwt/layout/client/Layout.java
+++ b/user/src/com/google/gwt/layout/client/Layout.java
@@ -449,32 +449,7 @@
* called after updating any of its children's {@link Layer layers}.
*/
public void layout() {
- for (Layer l : layers) {
- l.left = l.sourceLeft = l.targetLeft;
- l.top = l.sourceTop = l.targetTop;
- l.right = l.sourceRight = l.targetRight;
- l.bottom = l.sourceBottom = l.targetBottom;
- l.width = l.sourceWidth = l.targetWidth;
- l.height = l.sourceHeight = l.targetHeight;
-
- l.setLeft = l.setTargetLeft;
- l.setTop = l.setTargetTop;
- l.setRight = l.setTargetRight;
- l.setBottom = l.setTargetBottom;
- l.setWidth = l.setTargetWidth;
- l.setHeight = l.setTargetHeight;
-
- l.leftUnit = l.targetLeftUnit;
- l.topUnit = l.targetTopUnit;
- l.rightUnit = l.targetRightUnit;
- l.bottomUnit = l.targetBottomUnit;
- l.widthUnit = l.targetWidthUnit;
- l.heightUnit = l.targetHeightUnit;
-
- impl.layout(l);
- }
-
- impl.finalizeLayout(parentElem);
+ layout(0);
}
/**
@@ -495,6 +470,41 @@
* @param callback the animation callback
*/
public void layout(int duration, final AnimationCallback callback) {
+ // If there's no actual animation going on, don't do any of the expensive
+ // constraint calculations or anything like that.
+ if (duration == 0) {
+ for (Layer l : layers) {
+ l.left = l.sourceLeft = l.targetLeft;
+ l.top = l.sourceTop = l.targetTop;
+ l.right = l.sourceRight = l.targetRight;
+ l.bottom = l.sourceBottom = l.targetBottom;
+ l.width = l.sourceWidth = l.targetWidth;
+ l.height = l.sourceHeight = l.targetHeight;
+
+ l.setLeft = l.setTargetLeft;
+ l.setTop = l.setTargetTop;
+ l.setRight = l.setTargetRight;
+ l.setBottom = l.setTargetBottom;
+ l.setWidth = l.setTargetWidth;
+ l.setHeight = l.setTargetHeight;
+
+ l.leftUnit = l.targetLeftUnit;
+ l.topUnit = l.targetTopUnit;
+ l.rightUnit = l.targetRightUnit;
+ l.bottomUnit = l.targetBottomUnit;
+ l.widthUnit = l.targetWidthUnit;
+ l.heightUnit = l.targetHeightUnit;
+
+ impl.layout(l);
+ }
+
+ impl.finalizeLayout(parentElem);
+ if (callback != null) {
+ callback.onAnimationComplete();
+ }
+ return;
+ }
+
// Deal with constraint changes (e.g. left-width => right-width, etc)
int parentWidth = parentElem.getClientWidth();
int parentHeight = parentElem.getClientHeight();
diff --git a/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
index 71dd0ac..836a762 100644
--- a/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java
@@ -83,9 +83,6 @@
childFieldName);
}
}
-
- // Emit the layout() call.
- writer.addStatement("%s.layout();", fieldName);
}
private String addMethodName(XMLElement elem) {
diff --git a/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java b/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java
index 00395c9..d7081a9 100644
--- a/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java
+++ b/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java
@@ -73,9 +73,6 @@
writer.addStatement("%s.add(%s, %s, %s);", fieldName, childFieldName,
headerFieldName, size);
}
-
- // Emit the layout() call.
- writer.addStatement("%s.layout();", fieldName);
}
private boolean isElementType(XMLElement parent, XMLElement child, String type) {
diff --git a/user/src/com/google/gwt/user/client/ui/AnimatedLayout.java b/user/src/com/google/gwt/user/client/ui/AnimatedLayout.java
new file mode 100644
index 0000000..6ad86ba
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/AnimatedLayout.java
@@ -0,0 +1,64 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.ui;
+
+import com.google.gwt.layout.client.Layout.AnimationCallback;
+
+/**
+ * Specifies that a panel can animate between layouts.
+ *
+ * <p>
+ * The normal use pattern is to set all childrens' positions, then to call
+ * {@link #animate(int)} to move them to their new positions over some period
+ * of time.
+ * </p>
+ */
+public interface AnimatedLayout {
+
+ /**
+ * Layout children, animating over the specified period of time.
+ *
+ * @param duration the animation duration, in milliseconds
+ */
+ void animate(int duration);
+
+ /**
+ * Layout children, animating over the specified period of time.
+ *
+ * <p>
+ * This method provides a callback that will be informed of animation updates.
+ * This can be used to create more complex animation effects.
+ * </p>
+ *
+ * @param duration the animation duration, in milliseconds
+ * @param callback the animation callback
+ */
+ void animate(final int duration, final AnimationCallback callback);
+
+ /**
+ * Layout children immediately.
+ *
+ * <p>
+ * This is not normally necessary, unless you want to update child widgets'
+ * positions explicitly to create a starting point for a subsequent call to
+ * {@link #animate(int)}.
+ * </p>
+ *
+ * @see #animate(int)
+ * @see #animate(int, AnimationCallback)
+ */
+ void forceLayout();
+}
diff --git a/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
index f34524b..b339f15 100644
--- a/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java
@@ -26,15 +26,8 @@
* allows its last widget to take up the remaining space in its center.
*
* <p>
- * Whenever children are added to, or removed from, this panel, you must call
- * one of {@link #layout()}, {@link #layout(int)}, or
- * {@link #layout(int, com.google.gwt.layout.client.Layout.AnimationCallback)}
- * to update the panel's layout.
- * </p>
- *
- * <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>
+ * 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>
*
@@ -50,12 +43,12 @@
*
* TODO(jgw): RTL support.
*/
-public class DockLayoutPanel extends ComplexPanel implements RequiresLayout,
+public class DockLayoutPanel extends ComplexPanel implements AnimatedLayout,
RequiresResize, ProvidesResize {
/**
- * Used in {@link DockLayoutPanel#addEast(Widget, double)} et al to
- * specify the direction in which a child widget will be added.
+ * Used in {@link DockLayoutPanel#addEast(Widget, double)} et al to specify
+ * the direction in which a child widget will be added.
*/
public enum Direction {
NORTH, EAST, SOUTH, WEST, CENTER, LINE_START, LINE_END
@@ -78,9 +71,21 @@
}
}
+ private class DockAnimateCommand extends LayoutCommand {
+ public DockAnimateCommand(Layout layout) {
+ super(layout);
+ }
+
+ @Override
+ protected void doBeforeLayout() {
+ doLayout();
+ }
+ }
+
private final Unit unit;
private Widget center;
private final Layout layout;
+ private final LayoutCommand animCmd;
/**
* Creates an empty dock panel.
@@ -92,6 +97,7 @@
setElement(Document.get().createDivElement());
layout = new Layout(getElement());
+ animCmd = new DockAnimateCommand(layout);
}
/**
@@ -145,43 +151,50 @@
insert(widget, Direction.WEST, size, null);
}
+ public void animate(int duration) {
+ animate(0, null);
+ }
+
+ public void animate(int duration, final Layout.AnimationCallback callback) {
+ animCmd.schedule(duration, callback);
+ }
+
+ public void forceLayout() {
+ animCmd.cancel();
+ doLayout();
+ layout.layout();
+ onResize();
+ }
+
/**
- * Gets the container element associated with the given child widget.
+ * Gets the container element wrapping the given child widget.
*
- * <p>
- * The container element is created by the {@link Layout} class. This should
- * be used with certain styles, such as
- * {@link com.google.gwt.dom.client.Style#setZIndex(int)}, that must be
- * applied to the container, rather than directly to the child widget.
- * </p>
- *
- * TODO(jgw): Is this really the best way to do this?
- *
- * @param widget the widget whose container element is to be retrieved
+ * @param child
* @return the widget's container element
*/
- public Element getContainerElementFor(Widget widget) {
- assertIsChild(widget);
- return ((LayoutData) widget.getLayoutData()).layer.getContainerElement();
+ public Element getWidgetContainerElement(Widget child) {
+ assertIsChild(child);
+ return ((LayoutData) child.getLayoutData()).layer.getContainerElement();
}
/**
* Gets the layout direction of the given child widget.
*
- * @param w the widget to be queried
+ * @param child the widget to be queried
* @return the widget's layout direction, or <code>null</code> if it is not a
* child of this panel
*/
- public Direction getWidgetDirection(Widget w) {
- if (w.getParent() != this) {
+ public Direction getWidgetDirection(Widget child) {
+ assertIsChild(child);
+ if (child.getParent() != this) {
return null;
}
- return ((LayoutData) w.getLayoutData()).direction;
+ return ((LayoutData) child.getLayoutData()).direction;
}
/**
- * Adds a widget to the east edge of the dock, inserting it before an
- * existing widget.
+ * Adds a widget to the east edge of the dock, inserting it before an existing
+ * widget.
*
* @param widget the widget to be added
* @param size the child widget's size
@@ -219,8 +232,8 @@
}
/**
- * Adds a widget to the west edge of the dock, inserting it before an
- * existing widget.
+ * Adds a widget to the west edge of the dock, inserting it before an existing
+ * widget.
*
* @param widget the widget to be added
* @param size the child widget's size
@@ -231,83 +244,6 @@
insert(widget, Direction.WEST, size, before);
}
- public void layout() {
- layout(0);
- }
-
- public void layout(int duration) {
- layout(0, null);
- }
-
- public void layout(int duration, final Layout.AnimationCallback callback) {
- int left = 0, top = 0, right = 0, bottom = 0;
-
- for (Widget child : getChildren()) {
- LayoutData data = (LayoutData) child.getLayoutData();
- Layer layer = data.layer;
-
- switch (data.direction) {
- case NORTH:
- layer.setLeftRight(left, unit, right, unit);
- layer.setTopHeight(top, unit, data.size, unit);
- top += data.size;
- break;
-
- case SOUTH:
- layer.setLeftRight(left, unit, right, unit);
- layer.setBottomHeight(bottom, unit, data.size, unit);
- bottom += data.size;
- break;
-
- case WEST:
- layer.setTopBottom(top, unit, bottom, unit);
- layer.setLeftWidth(left, unit, data.size, unit);
- left += data.size;
- break;
-
- case EAST:
- layer.setTopBottom(top, unit, bottom, unit);
- layer.setRightWidth(right, unit, data.size, unit);
- right += data.size;
- break;
-
- case CENTER:
- layer.setLeftRight(left, unit, right, unit);
- layer.setTopBottom(top, unit, bottom, unit);
- break;
- }
- }
-
- layout.layout(duration, new Layout.AnimationCallback() {
- public void onAnimationComplete() {
- for (Widget child : getChildren()) {
- LayoutData data = (LayoutData) child.getLayoutData();
- if (data.size != data.oldSize) {
- data.oldSize = data.size;
- if (child instanceof RequiresResize) {
- ((RequiresResize) child).onResize();
- }
- }
-
- if (callback != null) {
- callback.onAnimationComplete();
- }
- }
- }
-
- public void onLayout(Layer layer, double progress) {
- Widget child = (Widget) layer.getUserObject();
- if (child instanceof RequiresResize) {
- ((RequiresResize) child).onResize();
- }
-
- if (callback != null) {
- callback.onLayout(layer, progress);
- }
- }
- });
- }
-
public void onResize() {
for (Widget child : getChildren()) {
if (child instanceof RequiresResize) {
@@ -378,6 +314,9 @@
// Adopt.
adopt(widget);
+
+ // Update the layout.
+ animate(0);
}
@Override
@@ -393,4 +332,44 @@
private void assertIsChild(Widget widget) {
assert (widget == null) || (widget.getParent() == this) : "The specified widget is not a child of this panel";
}
+
+ private void doLayout() {
+ int left = 0, top = 0, right = 0, bottom = 0;
+
+ for (Widget child : getChildren()) {
+ LayoutData data = (LayoutData) child.getLayoutData();
+ Layer layer = data.layer;
+
+ switch (data.direction) {
+ case NORTH:
+ layer.setLeftRight(left, unit, right, unit);
+ layer.setTopHeight(top, unit, data.size, unit);
+ top += data.size;
+ break;
+
+ case SOUTH:
+ layer.setLeftRight(left, unit, right, unit);
+ layer.setBottomHeight(bottom, unit, data.size, unit);
+ bottom += data.size;
+ break;
+
+ case WEST:
+ layer.setTopBottom(top, unit, bottom, unit);
+ layer.setLeftWidth(left, unit, data.size, unit);
+ left += data.size;
+ break;
+
+ case EAST:
+ layer.setTopBottom(top, unit, bottom, unit);
+ layer.setRightWidth(right, unit, data.size, unit);
+ right += data.size;
+ break;
+
+ case CENTER:
+ layer.setLeftRight(left, unit, right, unit);
+ layer.setTopBottom(top, unit, bottom, unit);
+ break;
+ }
+ }
+ }
}
diff --git a/user/src/com/google/gwt/user/client/ui/LayoutCommand.java b/user/src/com/google/gwt/user/client/ui/LayoutCommand.java
new file mode 100644
index 0000000..9de09d2
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/ui/LayoutCommand.java
@@ -0,0 +1,115 @@
+/*
+ * 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
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.ui;
+
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.layout.client.Layout;
+import com.google.gwt.layout.client.Layout.AnimationCallback;
+import com.google.gwt.layout.client.Layout.Layer;
+
+/**
+ * A scheduled command used by animated layouts to ensure that only layout is
+ * ever performed for a panel within a given user event.
+ *
+ * <p>
+ * Note: This class assumes that {@link Layout.Layer#getUserObject()} will
+ * return the widget associated with a given layer.
+ * </p>
+ */
+public class LayoutCommand implements ScheduledCommand {
+
+ private boolean scheduled, canceled;
+ private int duration;
+ private Layout.AnimationCallback callback;
+ private final Layout layout;
+
+ /**
+ * Creates a new command for the given layout object
+ *
+ * @param layout
+ */
+ public LayoutCommand(Layout layout) {
+ this.layout = layout;
+ }
+
+ public final void execute() {
+ if (canceled) {
+ return;
+ }
+
+ scheduled = false;
+
+ doBeforeLayout();
+
+ layout.layout(duration, new Layout.AnimationCallback() {
+ public void onAnimationComplete() {
+ // Chain to the passed callback.
+ if (callback != null) {
+ callback.onAnimationComplete();
+ }
+ }
+
+ public void onLayout(Layer layer, double progress) {
+ // Inform the child associated with this layer that its size may
+ // have changed.
+ Widget child = (Widget) layer.getUserObject();
+ if (child instanceof RequiresResize) {
+ ((RequiresResize) child).onResize();
+ }
+
+ // Chain to the passed callback.
+ if (callback != null) {
+ callback.onLayout(layer, progress);
+ }
+ }
+ });
+ }
+
+ /**
+ * Called before the layout is executed. Override this method to perform any
+ * work that needs to happen just before it.
+ */
+ protected void doBeforeLayout() {
+ }
+
+ /**
+ * Schedules a layout. The duration and callback passed to this method will
+ * supercede any previous call that has not yet been executed.
+ *
+ * @param duration
+ * @param callback
+ */
+ public void schedule(int duration, AnimationCallback callback) {
+ this.duration = duration;
+ this.callback = callback;
+
+ canceled = false;
+ if (!scheduled) {
+ scheduled = true;
+ Scheduler.get().scheduleFinally(this);
+ }
+ }
+
+ /**
+ * Cancels this command. A subsequent call to
+ * {@link #schedule(int, AnimationCallback)} will re-enable it.
+ */
+ public void cancel() {
+ // There's no way to "unschedule" a command, so we use a canceled flag.
+ canceled = true;
+ }
+}
diff --git a/user/src/com/google/gwt/user/client/ui/LayoutPanel.java b/user/src/com/google/gwt/user/client/ui/LayoutPanel.java
index 637c904..452c0db 100644
--- a/user/src/com/google/gwt/user/client/ui/LayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/LayoutPanel.java
@@ -16,7 +16,11 @@
package com.google.gwt.user.client.ui;
import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.layout.client.Layout;
+import com.google.gwt.layout.client.Layout.Alignment;
+import com.google.gwt.layout.client.Layout.AnimationCallback;
import com.google.gwt.layout.client.Layout.Layer;
/**
@@ -25,13 +29,6 @@
* {@link Layout} class.
*
* <p>
- * Whenever children are added to, or removed from, this panel, you must call
- * one of {@link #layout()}, {@link #layout(int)}, or
- * {@link #layout(int, com.google.gwt.layout.client.Layout.AnimationCallback)}
- * to update the panel's layout.
- * </p>
- *
- * <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.
@@ -46,13 +43,12 @@
* <h3>Example</h3>
* {@example com.google.gwt.examples.LayoutPanelExample}
* </p>
- *
- * TODO: implements IndexedPanel (I think)
*/
-public class LayoutPanel extends ComplexPanel implements RequiresLayout,
+public class LayoutPanel extends ComplexPanel implements AnimatedLayout,
RequiresResize, ProvidesResize {
private final Layout layout;
+ private final LayoutCommand animCmd;
/**
* Creates an empty layout panel.
@@ -60,6 +56,7 @@
public LayoutPanel() {
setElement(Document.get().createDivElement());
layout = new Layout(getElement());
+ animCmd = new LayoutCommand(layout);
}
/**
@@ -67,9 +64,9 @@
*
* <p>
* By default, each child will fill the panel. To build more interesting
- * layouts, use {@link #getLayer(Widget)} to get the
- * {@link com.google.gwt.layout.client.Layout.Layer} associated with each
- * child, and set its layout constraints as desired.
+ * layouts, set child widgets' layout constraints using
+ * {@link #setWidgetLeftRight(Widget, double, Unit, double, Unit)} and related
+ * methods.
* </p>
*
* @param widget the widget to be added
@@ -78,23 +75,29 @@
insert(widget, getWidgetCount());
}
+ public void animate(int duration) {
+ animate(duration, null);
+ }
+
+ public void animate(final int duration, final AnimationCallback callback) {
+ animCmd.schedule(duration, callback);
+ }
+
+ public void forceLayout() {
+ animCmd.cancel();
+ layout.layout();
+ onResize();
+ }
+
/**
- * Gets the {@link Layer} associated with the given widget. This layer may be
- * used to manipulate the child widget's layout constraints.
+ * Gets the container element wrapping the given child widget.
*
- * <p>
- * After you have made changes to any of the child widgets' constraints, you
- * must call one of the {@link RequiresLayout} methods for those changes to
- * be reflected visually.
- * </p>
- *
- * @param child the child widget whose layer is to be retrieved
- * @return the associated layer
+ * @param child
+ * @return the widget's container element
*/
- public Layout.Layer getLayer(Widget child) {
- assert child.getParent() == this :
- "The requested widget is not a child of this panel";
- return (Layout.Layer) child.getLayoutData();
+ public Element getWidgetContainerElement(Widget child) {
+ assertIsChild(child);
+ return getLayer(child).getContainerElement();
}
/**
@@ -102,9 +105,9 @@
*
* <p>
* By default, each child will fill the panel. To build more interesting
- * layouts, use {@link #getLayer(Widget)} to get the
- * {@link com.google.gwt.layout.client.Layout.Layer} associated with each
- * child, and set its layout constraints as desired.
+ * layouts, set child widgets' layout constraints using
+ * {@link #setWidgetLeftRight(Widget, double, Unit, double, Unit)} and related
+ * methods.
* </p>
*
* <p>
@@ -131,39 +134,8 @@
// Adopt.
adopt(widget);
- }
- public void layout() {
- layout.layout();
- }
-
- public void layout(int duration) {
- layout.layout(duration);
- }
-
- public void layout(int duration, final Layout.AnimationCallback callback) {
- layout.layout(duration, new Layout.AnimationCallback() {
- public void onAnimationComplete() {
- // Chain to the passed callback.
- if (callback != null) {
- callback.onAnimationComplete();
- }
- }
-
- public void onLayout(Layer layer, double progress) {
- // Inform the child associated with this layer that its size may have
- // changed.
- Widget child = (Widget) layer.getUserObject();
- if (child instanceof RequiresResize) {
- ((RequiresResize) child).onResize();
- }
-
- // Chain to the passed callback.
- if (callback != null) {
- callback.onLayout(layer, progress);
- }
- }
- });
+ animate(0);
}
public void onResize() {
@@ -184,12 +156,123 @@
}
/**
- * Gets the {@link Layout} instance associated with this widget.
+ * Sets the child widget's bottom and height values.
*
- * @return this widget's layout instance
+ * @param child
+ * @param bottom
+ * @param bottomUnit
+ * @param height
+ * @param heightUnit
*/
- protected Layout getLayout() {
- return layout;
+ public void setWidgetBottomHeight(Widget child, double bottom,
+ Unit bottomUnit, double height, Unit heightUnit) {
+ assertIsChild(child);
+ getLayer(child).setBottomHeight(bottom, bottomUnit, height, heightUnit);
+ animate(0);
+ }
+
+ /**
+ * Sets the child widget's horizontal position within its layer.
+ *
+ * @param child
+ * @param position
+ */
+ public void setWidgetHorizontalPosition(Widget child, Alignment position) {
+ assertIsChild(child);
+ getLayer(child).setChildHorizontalPosition(position);
+ animate(0);
+ }
+
+ /**
+ * Sets the child widget's left and right values.
+ *
+ * @param child
+ * @param left
+ * @param leftUnit
+ * @param right
+ * @param rightUnit
+ */
+ public void setWidgetLeftRight(Widget child, double left, Unit leftUnit,
+ double right, Unit rightUnit) {
+ assertIsChild(child);
+ getLayer(child).setLeftRight(left, leftUnit, right, rightUnit);
+ animate(0);
+ }
+
+ /**
+ * Sets the child widget's left and width values.
+ *
+ * @param child
+ * @param left
+ * @param leftUnit
+ * @param width
+ * @param widthUnit
+ */
+ public void setWidgetLeftWidth(Widget child, double left, Unit leftUnit,
+ double width, Unit widthUnit) {
+ assertIsChild(child);
+ getLayer(child).setLeftWidth(left, leftUnit, width, widthUnit);
+ animate(0);
+ }
+
+ /**
+ * Sets the child widget's right and width values.
+ *
+ * @param child
+ * @param right
+ * @param rightUnit
+ * @param width
+ * @param widthUnit
+ */
+ public void setWidgetRightWidth(Widget child, double right, Unit rightUnit,
+ double width, Unit widthUnit) {
+ assertIsChild(child);
+ getLayer(child).setRightWidth(right, rightUnit, width, widthUnit);
+ animate(0);
+ }
+
+ /**
+ * Sets the child widget's top and bottom values.
+ *
+ * @param child
+ * @param top
+ * @param topUnit
+ * @param bottom
+ * @param bottomUnit
+ */
+ public void setWidgetTopBottom(Widget child, double top, Unit topUnit,
+ double bottom, Unit bottomUnit) {
+ assertIsChild(child);
+ getLayer(child).setTopBottom(top, topUnit, bottom, bottomUnit);
+ animate(0);
+ }
+
+ /**
+ * Sets the child widget's top and height values.
+ *
+ * @param child
+ * @param top
+ * @param topUnit
+ * @param height
+ * @param heightUnit
+ */
+ public void setWidgetTopHeight(Widget child, double top, Unit topUnit,
+ double height, Unit heightUnit) {
+ assertIsChild(child);
+ getLayer(child).setTopHeight(top, topUnit, height, heightUnit);
+ animate(0);
+ }
+
+ /**
+ * Sets the child widget's vertical position within its layer.
+ *
+ * @param child
+ * @param position
+ */
+ public void setWidgetVerticalPosition(Widget child, Alignment position) {
+ assertIsChild(child);
+ getLayer(child).setChildVerticalPosition(position);
+ animate(0);
}
@Override
@@ -201,4 +284,23 @@
protected void onUnload() {
layout.onDetach();
}
+
+ /**
+ * Gets the {@link Layout} instance associated with this widget. This is made
+ * package-protected for use by {@link RootLayoutPanel}.
+ *
+ * @return this widget's layout instance
+ */
+ Layout getLayout() {
+ return layout;
+ }
+
+ private void assertIsChild(Widget widget) {
+ assert (widget == null) || (widget.getParent() == this) : "The specified widget is not a child of this panel";
+ }
+
+ private Layout.Layer getLayer(Widget child) {
+ assert child.getParent() == this : "The requested widget is not a child of this panel";
+ return (Layout.Layer) child.getLayoutData();
+ }
}
diff --git a/user/src/com/google/gwt/user/client/ui/RequiresLayout.java b/user/src/com/google/gwt/user/client/ui/RequiresLayout.java
deleted file mode 100644
index 680c9a6..0000000
--- a/user/src/com/google/gwt/user/client/ui/RequiresLayout.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.user.client.ui;
-
-import com.google.gwt.layout.client.Layout;
-
-/**
- * Designates that a widget requires a method to be explicitly called after its
- * children are modified.
- *
- * <p>
- * Widgets that implement this interface perform some layout work that will not
- * be fully realized until {@link #layout()} or one of its overloads is called.
- * This is required after adding or removing child widgets, and after any other
- * operations that the implementor designates as requiring layout. Note that
- * only <em>one</em> call to {@link #layout()} is required after any number of
- * modifications.
- * </p>
- */
-public interface RequiresLayout {
-
- /**
- * Layout children immediately.
- *
- * @see #layout(int)
- * @see #layout(int, com.google.gwt.layout.client.Layout.AnimationCallback)
- */
- void layout();
-
- /**
- * Layout children, animating over the specified period of time.
- *
- * @param duration the animation duration, in milliseconds
- *
- * @see #layout()
- * @see #layout(int, com.google.gwt.layout.client.Layout.AnimationCallback)
- */
- void layout(int duration);
-
- /**
- * Layout children, animating over the specified period of time.
- *
- * <p>
- * This method provides a callback that will be informed of animation updates.
- * This can be used to create more complex animation effects.
- * </p>
- *
- * @param duration the animation duration, in milliseconds
- * @param callback the animation callback
- *
- * @see #layout()
- * @see #layout(int, com.google.gwt.layout.client.Layout.AnimationCallback)
- */
- void layout(int duration, final Layout.AnimationCallback callback);
-}
diff --git a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
index 0af2857..c1c13a2 100644
--- a/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java
@@ -173,7 +173,7 @@
layoutCommand = new Command() {
public void execute() {
layoutCommand = null;
- layout();
+ forceLayout();
}
};
DeferredCommand.addCommand(layoutCommand);
diff --git a/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java b/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java
index a280d32..6298394 100644
--- a/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java
@@ -15,9 +15,9 @@
*/
package com.google.gwt.user.client.ui;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.layout.client.Layout.AnimationCallback;
-import com.google.gwt.layout.client.Layout.Layer;
import com.google.gwt.user.client.Event;
import java.util.ArrayList;
@@ -52,7 +52,7 @@
* - default style.
*/
public class StackLayoutPanel extends Composite implements HasWidgets,
- RequiresLayout, RequiresResize, ProvidesResize {
+ RequiresResize, ProvidesResize {
private class ClickWrapper extends Composite {
private Widget target;
@@ -75,16 +75,11 @@
public double headerSize;
public Widget header;
public Widget widget;
- public Layer widgetLayer;
- public Layer headerLayer;
- public LayoutData(Widget widget, Widget header, double headerSize,
- Layer widgetLayer, Layer headerLayer) {
+ public LayoutData(Widget widget, Widget header, double headerSize) {
this.widget = widget;
this.header = header;
this.headerSize = headerSize;
- this.widgetLayer = widgetLayer;
- this.headerLayer = headerLayer;
}
}
@@ -122,18 +117,15 @@
layoutPanel.add(wrapper);
layoutPanel.add(widget);
- Layer headerLayer = layoutPanel.getLayer(wrapper);
- headerLayer.setLeftRight(0, Unit.PX, 0, Unit.PX);
+ layoutPanel.setWidgetLeftRight(wrapper, 0, Unit.PX, 0, Unit.PX);
+ layoutPanel.setWidgetLeftRight(widget, 0, Unit.PX, 0, Unit.PX);
- Layer widgetLayer = layoutPanel.getLayer(widget);
- widgetLayer.setLeftRight(0, Unit.PX, 0, Unit.PX);
-
- LayoutData data = new LayoutData(widget, wrapper, headerSize, widgetLayer,
- headerLayer);
+ LayoutData data = new LayoutData(widget, wrapper, headerSize);
layoutData.add(data);
if (visibleWidget == null) {
- visibleWidget = widget;
+ // Don't animate the initial widget display.
+ showWidget(widget, 0);
}
}
@@ -169,46 +161,6 @@
};
}
- public void layout() {
- layout(0);
- }
-
- public void layout(int duration) {
- layout(duration, null);
- }
-
- public void layout(int duration, AnimationCallback callback) {
- int top = 0, bottom = 0;
- int i = 0, visibleIndex = -1;
- for (; i < layoutData.size(); ++i) {
- LayoutData data = layoutData.get(i);
- data.headerLayer.setTopHeight(top, unit, data.headerSize, unit);
-
- top += data.headerSize;
-
- data.widgetLayer.setTopHeight(top, unit, 0, unit);
-
- if (data.widget == visibleWidget) {
- visibleIndex = i;
- break;
- }
- }
-
- assert visibleIndex != -1;
-
- for (int j = layoutData.size() - 1; j > i; --j) {
- LayoutData data = layoutData.get(j);
- data.headerLayer.setBottomHeight(bottom, unit, data.headerSize, unit);
- data.widgetLayer.setBottomHeight(bottom, unit, 0, unit);
- bottom += data.headerSize;
- }
-
- LayoutData data = layoutData.get(visibleIndex);
- data.widgetLayer.setTopBottom(top, unit, bottom, unit);
-
- layoutPanel.layout(duration, callback);
- }
-
public void onResize() {
layoutPanel.onResize();
}
@@ -230,7 +182,49 @@
* @param widget the child widget to be shown.
*/
public void showWidget(Widget widget) {
+ showWidget(widget, ANIMATION_TIME);
+ }
+
+ private void animate(int duration) {
+ int top = 0, bottom = 0;
+ int i = 0, visibleIndex = -1;
+ for (; i < layoutData.size(); ++i) {
+ LayoutData data = layoutData.get(i);
+ layoutPanel.setWidgetTopHeight(data.header, top, unit, data.headerSize,
+ unit);
+
+ top += data.headerSize;
+
+ layoutPanel.setWidgetTopHeight(data.widget, top, unit, 0, unit);
+
+ if (data.widget == visibleWidget) {
+ visibleIndex = i;
+ break;
+ }
+ }
+
+ assert visibleIndex != -1;
+
+ for (int j = layoutData.size() - 1; j > i; --j) {
+ LayoutData data = layoutData.get(j);
+ layoutPanel.setWidgetBottomHeight(data.header, bottom, unit,
+ data.headerSize, unit);
+ layoutPanel.setWidgetBottomHeight(data.widget, bottom, unit, 0, unit);
+ bottom += data.headerSize;
+ }
+
+ LayoutData data = layoutData.get(visibleIndex);
+ layoutPanel.setWidgetTopBottom(data.widget, top, unit, bottom, unit);
+
+ layoutPanel.animate(duration);
+ }
+
+ private void showWidget(Widget widget, final int duration) {
visibleWidget = widget;
- layout(ANIMATION_TIME);
+ Scheduler.get().scheduleFinally(new ScheduledCommand() {
+ public void execute() {
+ animate(duration);
+ }
+ });
}
}
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 cf72920..4335fc3 100644
--- a/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java
@@ -29,7 +29,6 @@
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.Layer;
import java.util.ArrayList;
import java.util.Iterator;
@@ -60,8 +59,8 @@
* - Update style mechanism (gwt-Tab, etc. not really sufficient).
*/
public class TabLayoutPanel extends LayoutComposite implements HasWidgets,
- ProvidesResize, IndexedPanel,
- HasBeforeSelectionHandlers<Integer>, HasSelectionHandlers<Integer> {
+ ProvidesResize, IndexedPanel, HasBeforeSelectionHandlers<Integer>,
+ HasSelectionHandlers<Integer> {
private static final int BIG_ENOUGH_TO_NOT_WRAP = 16384;
@@ -127,12 +126,9 @@
initWidget(panel);
panel.add(tabBar);
- Layer layer = panel.getLayer(tabBar);
- layer.setLeftRight(0, Unit.PX, 0, Unit.PX);
- layer.setTopHeight(0, Unit.PX, barHeight, barUnit);
- panel.layout();
-
- panel.getLayer(tabBar).setChildVerticalPosition(Alignment.END);
+ panel.setWidgetLeftRight(tabBar, 0, Unit.PX, 0, Unit.PX);
+ panel.setWidgetTopHeight(tabBar, 0, Unit.PX, barHeight, barUnit);
+ panel.setWidgetVerticalPosition(tabBar, Alignment.END);
// Make the tab bar extremely wide so that tabs themselves never wrap.
// (Its layout container is overflow:hidden)
@@ -356,13 +352,13 @@
// Update the tabs being selected and unselected.
if (selectedIndex != -1) {
- Layer layer = panel.getLayer(children.get(selectedIndex));
- layer.getContainerElement().getStyle().setVisibility(Visibility.HIDDEN);
+ Element container = panel.getWidgetContainerElement(children.get(selectedIndex));
+ container.getStyle().setVisibility(Visibility.HIDDEN);
tabs.get(selectedIndex).setSelected(false);
}
- Layer layer = panel.getLayer(children.get(index));
- layer.getContainerElement().getStyle().setVisibility(Visibility.VISIBLE);
+ Element container = panel.getWidgetContainerElement(children.get(index));
+ container.getStyle().setVisibility(Visibility.VISIBLE);
tabs.get(index).setSelected(true);
selectedIndex = index;
@@ -445,10 +441,9 @@
}
private void layoutChild(Widget child) {
- Layer layer = panel.getLayer(child);
- layer.setLeftRight(0, Unit.PX, 0, Unit.PX);
- layer.setTopBottom(barHeight, barUnit, 0, Unit.PX);
- layer.getContainerElement().getStyle().setVisibility(Visibility.HIDDEN);
- panel.layout();
+ panel.setWidgetLeftRight(child, 0, Unit.PX, 0, Unit.PX);
+ panel.setWidgetTopBottom(child, barHeight, barUnit, 0, Unit.PX);
+ panel.getWidgetContainerElement(child).getStyle().setVisibility(
+ Visibility.HIDDEN);
}
}