Revision: 6624 Author: [email protected] Date: Tue Nov 3 14:14:13 2009 Log: 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 http://code.google.com/p/google-web-toolkit/source/detail?r=6624 Added: /trunk/user/src/com/google/gwt/user/client/ui/AnimatedLayout.java /trunk/user/src/com/google/gwt/user/client/ui/LayoutCommand.java Deleted: /trunk/user/src/com/google/gwt/user/client/ui/RequiresLayout.java Modified: /trunk/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java /trunk/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java /trunk/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java /trunk/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java /trunk/user/javadoc/com/google/gwt/examples/LayoutPanelExample.java /trunk/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java /trunk/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java /trunk/user/javadoc/com/google/gwt/examples/TabLayoutPanelExample.java /trunk/user/src/com/google/gwt/layout/client/Layout.java /trunk/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java /trunk/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java /trunk/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java /trunk/user/src/com/google/gwt/user/client/ui/LayoutPanel.java /trunk/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java /trunk/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java /trunk/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java ======================================= --- /dev/null +++ /trunk/user/src/com/google/gwt/user/client/ui/AnimatedLayout.java Tue Nov 3 14:14:13 2009 @@ -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(); +} ======================================= --- /dev/null +++ /trunk/user/src/com/google/gwt/user/client/ui/LayoutCommand.java Tue Nov 3 14:14:13 2009 @@ -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; + } +} ======================================= --- /trunk/user/src/com/google/gwt/user/client/ui/RequiresLayout.java Wed Oct 28 09:10:53 2009 +++ /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); -} ======================================= --- /trunk/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java Tue Sep 22 14:34:43 2009 +++ /trunk/samples/mail/src/com/google/gwt/sample/mail/client/Mail.java Tue Nov 3 14:14:13 2009 @@ -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(); } } ======================================= --- /trunk/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java Tue Oct 6 14:10:56 2009 +++ /trunk/samples/mail/src/com/google/gwt/sample/mail/client/MailList.java Tue Nov 3 14:14:13 2009 @@ -78,7 +78,7 @@ dock.add(new ScrollPanel(table)); header.setWidth("100%"); table.setWidth("100%"); - dock.layout(); + initWidget(dock); setStyleName("mail-List"); ======================================= --- /trunk/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java Tue Sep 22 14:34:43 2009 +++ /trunk/samples/mail/src/com/google/gwt/sample/mail/client/Shortcuts.java Tue Nov 3 14:14:13 2009 @@ -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)); } } ======================================= --- /trunk/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java Tue Sep 22 14:34:43 2009 +++ /trunk/user/javadoc/com/google/gwt/examples/DockLayoutPanelExample.java Tue Nov 3 14:14:13 2009 @@ -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(); } } ======================================= --- /trunk/user/javadoc/com/google/gwt/examples/LayoutPanelExample.java Thu Sep 3 12:28:14 2009 +++ /trunk/user/javadoc/com/google/gwt/examples/LayoutPanelExample.java Tue Nov 3 14:14:13 2009 @@ -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(); } } ======================================= --- /trunk/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java Wed Sep 23 10:09:22 2009 +++ /trunk/user/javadoc/com/google/gwt/examples/SplitLayoutPanelExample.java Tue Nov 3 14:14:13 2009 @@ -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(); } } ======================================= --- /trunk/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java Thu Sep 10 05:47:55 2009 +++ /trunk/user/javadoc/com/google/gwt/examples/StackLayoutPanelExample.java Tue Nov 3 14:14:13 2009 @@ -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(); } } ======================================= --- /trunk/user/javadoc/com/google/gwt/examples/TabLayoutPanelExample.java Fri Oct 16 12:16:25 2009 +++ /trunk/user/javadoc/com/google/gwt/examples/TabLayoutPanelExample.java Tue Nov 3 14:14:13 2009 @@ -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(); } } ======================================= --- /trunk/user/src/com/google/gwt/layout/client/Layout.java Mon Nov 2 12:05:30 2009 +++ /trunk/user/src/com/google/gwt/layout/client/Layout.java Tue Nov 3 14:14:13 2009 @@ -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(); ======================================= --- /trunk/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java Mon Nov 2 20:08:34 2009 +++ /trunk/user/src/com/google/gwt/uibinder/parsers/DockLayoutPanelParser.java Tue Nov 3 14:14:13 2009 @@ -83,9 +83,6 @@ childFieldName); } } - - // Emit the layout() call. - writer.addStatement("%s.layout();", fieldName); } private String addMethodName(XMLElement elem) { ======================================= --- /trunk/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java Mon Nov 2 20:08:34 2009 +++ /trunk/user/src/com/google/gwt/uibinder/parsers/StackLayoutPanelParser.java Tue Nov 3 14:14:13 2009 @@ -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) { ======================================= --- /trunk/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java Wed Oct 28 09:10:53 2009 +++ /trunk/user/src/com/google/gwt/user/client/ui/DockLayoutPanel.java Tue Nov 3 14:14:13 2009 @@ -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 @@ -77,10 +70,22 @@ this.layer = layer; } } + + 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); } /** @@ -144,44 +150,51 @@ public void addWest(Widget widget, double size) { 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 @@ -230,83 +243,6 @@ public void insertWest(Widget widget, double size, Widget before) { 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()) { @@ -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; + } + } + } +} ======================================= --- /trunk/user/src/com/google/gwt/user/client/ui/LayoutPanel.java Wed Oct 28 12:55:56 2009 +++ /trunk/user/src/com/google/gwt/user/client/ui/LayoutPanel.java Tue Nov 3 14:14:13 2009 @@ -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; /** @@ -24,13 +28,6 @@ * {...@link com.google.gwt.layout.client.Layout.Layer layers} using the * {...@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> @@ -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 @@ -77,24 +74,30 @@ public void add(Widget widget) { 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(); + } +} ======================================= --- /trunk/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java Wed Oct 28 12:55:56 2009 +++ /trunk/user/src/com/google/gwt/user/client/ui/SplitLayoutPanel.java Tue Nov 3 14:14:13 2009 @@ -173,7 +173,7 @@ layoutCommand = new Command() { public void execute() { layoutCommand = null; - layout(); + forceLayout(); } }; DeferredCommand.addCommand(layoutCommand); ======================================= --- /trunk/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java Wed Oct 28 09:10:53 2009 +++ /trunk/user/src/com/google/gwt/user/client/ui/StackLayoutPanel.java Tue Nov 3 14:14:13 2009 @@ -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); - - Layer widgetLayer = layoutPanel.getLayer(widget); - widgetLayer.setLeftRight(0, Unit.PX, 0, Unit.PX); - - LayoutData data = new LayoutData(widget, wrapper, headerSize, widgetLayer, - headerLayer); + layoutPanel.setWidgetLeftRight(wrapper, 0, Unit.PX, 0, Unit.PX); + layoutPanel.setWidgetLeftRight(widget, 0, Unit.PX, 0, Unit.PX); + + 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,24 +161,41 @@ }; } - public void layout() { - layout(0); + public void onResize() { + layoutPanel.onResize(); } - public void layout(int duration) { - layout(duration, null); + public boolean remove(Widget child) { + if (child.getParent() != this) { + return false; + } + + LayoutData data = (LayoutData) child.getLayoutData(); + layoutPanel.remove(data.header); + layoutPanel.remove(child); + return true; } - public void layout(int duration, AnimationCallback callback) { + /** + * Shows the specified widget. + * + * @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); - data.headerLayer.setTopHeight(top, unit, data.headerSize, unit); + layoutPanel.setWidgetTopHeight(data.header, top, unit, data.headerSize, + unit); top += data.headerSize; - data.widgetLayer.setTopHeight(top, unit, 0, unit); + layoutPanel.setWidgetTopHeight(data.widget, top, unit, 0, unit); if (data.widget == visibleWidget) { visibleIndex = i; @@ -198,39 +207,24 @@ 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); + 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); - data.widgetLayer.setTopBottom(top, unit, bottom, unit); - - layoutPanel.layout(duration, callback); + layoutPanel.setWidgetTopBottom(data.widget, top, unit, bottom, unit); + + layoutPanel.animate(duration); } - public void onResize() { - layoutPanel.onResize(); - } - - public boolean remove(Widget child) { - if (child.getParent() != this) { - return false; - } - - LayoutData data = (LayoutData) child.getLayoutData(); - layoutPanel.remove(data.header); - layoutPanel.remove(child); - return true; - } - - /** - * Shows the specified widget. - * - * @param widget the child widget to be shown. - */ - public void showWidget(Widget widget) { + private void showWidget(Widget widget, final int duration) { visibleWidget = widget; - layout(ANIMATION_TIME); + Scheduler.get().scheduleFinally(new ScheduledCommand() { + public void execute() { + animate(duration); + } + }); } } ======================================= --- /trunk/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java Wed Oct 28 09:10:53 2009 +++ /trunk/user/src/com/google/gwt/user/client/ui/TabLayoutPanel.java Tue Nov 3 14:14:13 2009 @@ -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); } } --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
