/* * 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.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; /** * A panel that lays its children out in arbitrary * {@link com.google.gwt.layout.client.Layout.Layer layers} using the * {@link Layout} class. * * <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> * <h3>Example</h3> * {@example com.google.gwt.examples.LayoutPanelExample} * </p> * * <h3>Use in UiBinder Templates</h3> * <p> * LayoutPanel elements in {@link com.google.gwt.uibinder.client.UiBinder * UiBinder} templates lay out their children with arbitrary constraints, using * <g:layer> elements. Each layer may have any of the following constraint * attributes specified as CSS length attributes: <code>left</code>, * <code>top</code>, <code>right</code>, <code>bottom</code>, <code>width</code> * , and <code>height</code>. * * <p> * Precisely zero or two constraints are required for each axis (horizontal and * vertical). Specifying no constraints implies that the child should fill that * axis completely. * * <p> * The valid sets of horizontal constraints are: * <dl> * <dt>(none) * <dd>Fill the parent's horizontal axis * <dt>left, width * <dd>Fixed width, positioned from parent's left edge * <dt>right, width * <dd>Fixed width, positioned from parent's right edge * <dt>left, right * <dd>Width implied by fixed distance from parent's left and right edges * </dl> * * <p> * The valid sets of vertical constraints are: * <dl> * <dt>(none) * <dd>Fill the parent's vertical axis * <dt>top, height * <dd>Fixed height, positioned from parent's top edge * <dt>bottom, height * <dd>Fixed height, positioned from parent's bottom edge * <dt>top, bottom * <dd>Height implied by fixed distance from parent's top and bottom edges * </dl> * * <p> * The values of constraint attributes can be any valid <a * href='http://www.w3.org/TR/CSS21/syndata.html#length-units'>CSS length</a>, * such as <code>1px</code>, <code>3em</code>, or <code>0</code> (zero lengths require no * units). * * <p> * For example: * * <pre> * <g:LayoutPanel> * <!-- No constraints causes the layer to fill the parent --> * <g:layer> * <g:Label>Lorem ipsum...</g:Label> * </g:layer> * <!-- Position horizontally 25% from each edge; * Vertically 4px from the top and 10em tall. --> * <g:layer left='25%' right='25%' top='4px' height='10em'> * <g:Label>Header</g:Label> * </g:layer> * </g:LayoutPanel> * </pre> */ public class LayoutPanel extends ComplexPanel implements AnimatedLayout, RequiresResize, ProvidesResize { private final Layout layout; private final LayoutCommand layoutCmd; /** * Creates an empty layout panel. */ public LayoutPanel() { setElement(Document.get().createDivElement()); layout = new Layout(getElement()); layoutCmd = new LayoutCommand(layout); } /** * Adds a widget to this panel. * * <p> * By default, each child will fill the panel. To build more interesting * layouts, set child widgets' layout constraints using * {@link #setWidgetLeftRight(Widget, double, Style.Unit, double, Style.Unit)} * and related methods. * </p> * * @param widget the widget to be added */ @Override 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) { layoutCmd.schedule(duration, callback); } public void forceLayout() { layoutCmd.cancel(); layout.layout(); onResize(); } /** * Gets the container element wrapping the given child widget. * * @param child * @return the widget's container element */ public Element getWidgetContainerElement(Widget child) { assertIsChild(child); return getLayer(child).getContainerElement(); } /** * Inserts a widget before the specified index. * * <p> * By default, each child will fill the panel. To build more interesting * layouts, set child widgets' layout constraints using * {@link #setWidgetLeftRight(Widget, double, Style.Unit, double, Style.Unit)} * and related methods. * </p> * * <p> * Inserting a widget in this way has no effect on the DOM structure, but can * be useful for other panels that wrap LayoutPanel to maintain insertion * order. * </p> * * @param widget the widget to be inserted * @param beforeIndex the index before which it will be inserted * @throws IndexOutOfBoundsException if <code>beforeIndex</code> is out of * range */ public void insert(Widget widget, int beforeIndex) { // Detach new child. widget.removeFromParent(); // Logical attach. getChildren().insert(widget, beforeIndex); // Physical attach. Layer layer = layout.attachChild(widget.getElement(), widget); widget.setLayoutData(layer); // Adopt. adopt(widget); animate(0); } 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) { layout.removeChild((Layer) w.getLayoutData()); } return removed; } /** * Sets the child widget's bottom and height values. * * @param child * @param bottom * @param bottomUnit * @param height * @param heightUnit */ public void setWidgetBottomHeight(Widget child, double bottom, Unit bottomUnit, double height, Unit heightUnit) { assertIsChild(child); getLayer(child).setBottomHeight(bottom, bottomUnit, height, heightUnit); animate(0); } /** * Overloaded version for IsWidget. * * @see #setWidgetBottomHeight(Widget,double, Style.Unit, double, Style.Unit) */ public void setWidgetBottomHeight(IsWidget child, double bottom, Unit bottomUnit, double height, Unit heightUnit) { this.setWidgetBottomHeight(child.asWidget(), bottom, bottomUnit, height, heightUnit); } /** * 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); } /** * Overloaded version for IsWidget. * * @see #setWidgetLeftRight(Widget,double, Style.Unit, double, Style.Unit) */ public void setWidgetLeftRight(IsWidget child, double left, Unit leftUnit, double right, Unit rightUnit) { this.setWidgetLeftRight(child.asWidget(), left, leftUnit, right, rightUnit); } /** * 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); } /** * Overloaded version for IsWidget. * * @see #setWidgetLeftWidth(Widget,double, Style.Unit, double, Style.Unit) */ public void setWidgetLeftWidth(IsWidget child, double left, Unit leftUnit, double width, Unit widthUnit) { this.setWidgetLeftWidth(child.asWidget(), left, leftUnit, width, widthUnit); } /** * 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); } /** * Overloaded version for IsWidget. * * @see #setWidgetRightWidth(Widget,double, Style.Unit, double, Style.Unit) */ public void setWidgetRightWidth(IsWidget child, double right, Unit rightUnit, double width, Unit widthUnit) { this.setWidgetRightWidth(child.asWidget(), right, rightUnit, width, widthUnit); } /** * 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); } /** * Overloaded version for IsWidget. * * @see #setWidgetTopBottom(Widget,double, Style.Unit, double, Style.Unit) */ public void setWidgetTopBottom(IsWidget child, double top, Unit topUnit, double bottom, Unit bottomUnit) { this.setWidgetTopBottom(child.asWidget(), top, topUnit, bottom, bottomUnit); } /** * 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); } /** * Overloaded version for IsWidget. * * @see #setWidgetTopHeight(Widget,double, Style.Unit, double, Style.Unit) */ public void setWidgetTopHeight(IsWidget child, double top, Unit topUnit, double height, Unit heightUnit) { this.setWidgetTopHeight(child.asWidget(), top, topUnit, height, heightUnit); } /** * 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); } /** * Shows or hides the given widget and its layer. This method explicitly * calls {@link UIObject#setVisible(boolean)} on the child widget and ensures * that its associated layer is shown/hidden. * * @param child * @param visible */ public void setWidgetVisible(Widget child, boolean visible) { assertIsChild(child); getLayer(child).setVisible(visible); child.setVisible(visible); animate(0); } @Override protected void onLoad() { layout.onAttach(); } @Override 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(); } }