]> source.dussan.org Git - vaadin-framework.git/commitdiff
Refactor client-side ordered layouts (#9893) 88/488/1
authorLeif Åstrand <leif@vaadin.com>
Tue, 11 Dec 2012 12:14:48 +0000 (14:14 +0200)
committerLeif Åstrand <leif@vaadin.com>
Tue, 11 Dec 2012 12:14:48 +0000 (14:14 +0200)
* Rename VOrderedLayout to VAbstractOrderedLayout
* Move VAbstractOrderedLayout to the orderedlayout package
* Extract Slot to a standalone class with Icon as a private inner class
* Extract CaptionPosition to a standalone class

Change-Id: Id9450b30838a4511bd3c3b7cf371e145274ff198

client/src/com/vaadin/client/ComponentLocator.java
client/src/com/vaadin/client/ui/VHorizontalLayout.java
client/src/com/vaadin/client/ui/VOrderedLayout.java [deleted file]
client/src/com/vaadin/client/ui/VVerticalLayout.java
client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
client/src/com/vaadin/client/ui/orderedlayout/CaptionPosition.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/orderedlayout/Slot.java [new file with mode: 0644]
client/src/com/vaadin/client/ui/orderedlayout/VAbstractOrderedLayout.java [new file with mode: 0644]

index 854c8535c4a57edad915285e8277e8068771b654..249c3f78f4777c13fc51848fe4c5b8437d14095a 100644 (file)
@@ -29,10 +29,10 @@ import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.ui.SubPartAware;
 import com.vaadin.client.ui.VCssLayout;
 import com.vaadin.client.ui.VGridLayout;
-import com.vaadin.client.ui.VOrderedLayout;
 import com.vaadin.client.ui.VTabsheetPanel;
 import com.vaadin.client.ui.VUI;
 import com.vaadin.client.ui.VWindow;
+import com.vaadin.client.ui.orderedlayout.VAbstractOrderedLayout;
 import com.vaadin.client.ui.window.WindowConnector;
 import com.vaadin.shared.AbstractComponentState;
 import com.vaadin.shared.Connector;
@@ -315,7 +315,7 @@ public class ComponentLocator {
                 String childIndexString = part.substring("domChild[".length(),
                         part.length() - 1);
 
-                if (Util.findWidget(baseElement, null) instanceof VOrderedLayout) {
+                if (Util.findWidget(baseElement, null) instanceof VAbstractOrderedLayout) {
                     if (element.hasChildNodes()) {
                         Element e = element.getFirstChildElement().cast();
                         String cn = e.getClassName();
@@ -563,10 +563,10 @@ public class ComponentLocator {
                 }
 
                 // ChildComponentContainer has been removed and replaced with
-                // VOrderLayout.Slot's
-                if (w instanceof VOrderedLayout
+                // Slot
+                if (w instanceof VAbstractOrderedLayout
                         && "ChildComponentContainer".equals(widgetClassName)) {
-                    widgetClassName = "VOrderedLayout$Slot";
+                    widgetClassName = "Slot";
                 }
 
                 if (w instanceof VTabsheetPanel && widgetPosition != 0) {
@@ -662,8 +662,8 @@ public class ComponentLocator {
                         }
                         widgetPosition--;
 
-                    } else if (w instanceof VOrderedLayout
-                            && "VOrderedLayout$Slot".equals(simpleName2)) {
+                    } else if (w instanceof VAbstractOrderedLayout
+                            && "Slot".equals(simpleName2)) {
                         child = ((SimplePanel) child).getWidget();
                         simpleName2 = Util.getSimpleName(child);
                         if (widgetClassName.equals(simpleName2)) {
index 53a933e37440c21ec8a2ccc206db647019736b2e..a75e681bcf1dbd25e4051463e2c9a870d9c621cf 100644 (file)
 package com.vaadin.client.ui;
 
 import com.vaadin.client.StyleConstants;
+import com.vaadin.client.ui.orderedlayout.VAbstractOrderedLayout;
 
 /**
  * Represents a layout where the children is ordered vertically
  */
-public class VHorizontalLayout extends VOrderedLayout {
+public class VHorizontalLayout extends VAbstractOrderedLayout {
 
     public static final String CLASSNAME = "v-horizontallayout";
 
diff --git a/client/src/com/vaadin/client/ui/VOrderedLayout.java b/client/src/com/vaadin/client/ui/VOrderedLayout.java
deleted file mode 100644 (file)
index bbcb2f8..0000000
+++ /dev/null
@@ -1,1196 +0,0 @@
-/*
- * Copyright 2011 Vaadin Ltd.
- * 
- * 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.vaadin.client.ui;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.google.gwt.dom.client.Node;
-import com.google.gwt.dom.client.Style;
-import com.google.gwt.dom.client.Style.Unit;
-import com.google.gwt.regexp.shared.MatchResult;
-import com.google.gwt.regexp.shared.RegExp;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SimplePanel;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import com.vaadin.client.LayoutManager;
-import com.vaadin.client.StyleConstants;
-import com.vaadin.client.Util;
-import com.vaadin.client.ui.layout.ElementResizeListener;
-import com.vaadin.shared.ui.AlignmentInfo;
-import com.vaadin.shared.ui.MarginInfo;
-
-/**
- * Base class for ordered layouts
- */
-public class VOrderedLayout extends FlowPanel {
-
-    private static final String ALIGN_CLASS_PREFIX = "v-align-";
-
-    protected boolean spacing = false;
-
-    /** For internal use only. May be removed or replaced in the future. */
-    public boolean vertical = true;
-
-    protected boolean definedHeight = false;
-
-    private Map<Widget, Slot> widgetToSlot = new HashMap<Widget, Slot>();
-
-    private Element expandWrapper;
-
-    private LayoutManager layoutManager;
-
-    public VOrderedLayout(boolean vertical) {
-        this.vertical = vertical;
-    }
-
-    /**
-     * Add or move a slot to another index.
-     * <p>
-     * For internal use only. May be removed or replaced in the future.
-     * <p>
-     * You should note that the index does not refer to the DOM index if
-     * spacings are used. If spacings are used then the index will be adjusted
-     * to include the spacings when inserted.
-     * <p>
-     * For instance when using spacing the index converts to DOM index in the
-     * following way:
-     * 
-     * <pre>
-     * index : 0 -> DOM index: 0
-     * index : 1 -> DOM index: 1
-     * index : 2 -> DOM index: 3
-     * index : 3 -> DOM index: 5
-     * index : 4 -> DOM index: 7
-     * </pre>
-     * 
-     * When using this method never account for spacings.
-     * </p>
-     * 
-     * @param slot
-     *            The slot to move or add
-     * @param index
-     *            The index where the slot should be placed.
-     */
-    public void addOrMoveSlot(Slot slot, int index) {
-        if (slot.getParent() == this) {
-            int currentIndex = getWidgetIndex(slot);
-            if (index == currentIndex) {
-                return;
-            }
-        }
-
-        insert(slot, index);
-
-        /*
-         * We need to confirm spacings are correctly applied after each insert.
-         */
-        setSpacing(spacing);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void insert(Widget child, Element container, int beforeIndex,
-            boolean domInsert) {
-        // Validate index; adjust if the widget is already a child of this
-        // panel.
-        beforeIndex = adjustIndex(child, beforeIndex);
-
-        // Detach new child.
-        child.removeFromParent();
-
-        // Logical attach.
-        getChildren().insert(child, beforeIndex);
-
-        // Physical attach.
-        container = expandWrapper != null ? expandWrapper : getElement();
-        if (domInsert) {
-            if (spacing) {
-                if (beforeIndex != 0) {
-                    /*
-                     * Since the spacing elements are located at the same DOM
-                     * level as the slots we need to take them into account when
-                     * calculating the slot position.
-                     * 
-                     * The spacing elements are always located before the actual
-                     * slot except for the first slot which do not have a
-                     * spacing element like this
-                     * 
-                     * |<slot1><spacing2><slot2><spacing3><slot3>...|
-                     */
-                    beforeIndex = beforeIndex * 2 - 1;
-                }
-            }
-            DOM.insertChild(container, child.getElement(), beforeIndex);
-        } else {
-            DOM.appendChild(container, child.getElement());
-        }
-
-        // Adopt.
-        adopt(child);
-    }
-
-    /**
-     * Remove a slot from the layout
-     * 
-     * @param widget
-     * @return
-     */
-    public void removeWidget(Widget widget) {
-        Slot slot = widgetToSlot.get(widget);
-        remove(slot);
-        widgetToSlot.remove(widget);
-    }
-
-    /**
-     * Get the containing slot for a widget. If no slot is found a new slot is
-     * created and returned.
-     * 
-     * @param widget
-     *            The widget whose slot you want to get
-     * 
-     * @return
-     */
-    public Slot getSlot(Widget widget) {
-        Slot slot = widgetToSlot.get(widget);
-        if (slot == null) {
-            slot = new Slot(widget);
-            widgetToSlot.put(widget, slot);
-        }
-        return slot;
-    }
-
-    /**
-     * Gets a slot based on the widget element. If no slot is found then null is
-     * returned.
-     * 
-     * @param widgetElement
-     *            The element of the widget ( Same as getWidget().getElement() )
-     * @return
-     */
-    public Slot getSlot(Element widgetElement) {
-        for (Map.Entry<Widget, Slot> entry : widgetToSlot.entrySet()) {
-            if (entry.getKey().getElement() == widgetElement) {
-                return entry.getValue();
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Defines where the caption should be placed
-     */
-    public enum CaptionPosition {
-        TOP, RIGHT, BOTTOM, LEFT
-    }
-
-    /**
-     * Represents a slot which contains the actual widget in the layout.
-     */
-    public final class Slot extends SimplePanel {
-
-        public static final String SLOT_CLASSNAME = "v-slot";
-
-        private Element spacer;
-        private Element captionWrap;
-        private Element caption;
-        private Element captionText;
-        private Icon icon;
-        private Element errorIcon;
-        private Element requiredIcon;
-
-        private ElementResizeListener captionResizeListener;
-
-        private ElementResizeListener widgetResizeListener;
-
-        private ElementResizeListener spacingResizeListener;
-
-        // Caption is placed after component unless there is some part which
-        // moves it above.
-        private CaptionPosition captionPosition = CaptionPosition.RIGHT;
-
-        private AlignmentInfo alignment;
-
-        private double expandRatio = -1;
-
-        /**
-         * Constructor
-         * 
-         * @param widget
-         *            The widget to put in the slot
-         * 
-         * @param layoutManager
-         *            The layout manager used by the layout
-         */
-        private Slot(Widget widget) {
-            setStyleName(SLOT_CLASSNAME);
-            setWidget(widget);
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see
-         * com.google.gwt.user.client.ui.SimplePanel#remove(com.google.gwt.user
-         * .client.ui.Widget)
-         */
-        @Override
-        public boolean remove(Widget w) {
-            detachListeners();
-            return super.remove(w);
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see
-         * com.google.gwt.user.client.ui.SimplePanel#setWidget(com.google.gwt
-         * .user.client.ui.Widget)
-         */
-        @Override
-        public void setWidget(Widget w) {
-            detachListeners();
-            super.setWidget(w);
-            attachListeners();
-        }
-
-        /**
-         * Attached resize listeners to the widget, caption and spacing elements
-         */
-        private void attachListeners() {
-            if (getWidget() != null && getLayoutManager() != null) {
-                LayoutManager lm = getLayoutManager();
-                if (getCaptionElement() != null
-                        && captionResizeListener != null) {
-                    lm.addElementResizeListener(getCaptionElement(),
-                            captionResizeListener);
-                }
-                if (widgetResizeListener != null) {
-                    lm.addElementResizeListener(getWidget().getElement(),
-                            widgetResizeListener);
-                }
-                if (getSpacingElement() != null
-                        && spacingResizeListener != null) {
-                    lm.addElementResizeListener(getSpacingElement(),
-                            spacingResizeListener);
-                }
-            }
-        }
-
-        /**
-         * Detaches resize listeners from the widget, caption and spacing
-         * elements
-         */
-        private void detachListeners() {
-            if (getWidget() != null && getLayoutManager() != null) {
-                LayoutManager lm = getLayoutManager();
-                if (getCaptionElement() != null
-                        && captionResizeListener != null) {
-                    lm.removeElementResizeListener(getCaptionElement(),
-                            captionResizeListener);
-                }
-                if (widgetResizeListener != null) {
-                    lm.removeElementResizeListener(getWidget().getElement(),
-                            widgetResizeListener);
-                }
-                if (getSpacingElement() != null
-                        && spacingResizeListener != null) {
-                    lm.removeElementResizeListener(getSpacingElement(),
-                            spacingResizeListener);
-                }
-            }
-        }
-
-        public ElementResizeListener getCaptionResizeListener() {
-            return captionResizeListener;
-        }
-
-        public void setCaptionResizeListener(
-                ElementResizeListener captionResizeListener) {
-            detachListeners();
-            this.captionResizeListener = captionResizeListener;
-            attachListeners();
-        }
-
-        public ElementResizeListener getWidgetResizeListener() {
-            return widgetResizeListener;
-        }
-
-        public void setWidgetResizeListener(
-                ElementResizeListener widgetResizeListener) {
-            detachListeners();
-            this.widgetResizeListener = widgetResizeListener;
-            attachListeners();
-        }
-
-        public ElementResizeListener getSpacingResizeListener() {
-            return spacingResizeListener;
-        }
-
-        public void setSpacingResizeListener(
-                ElementResizeListener spacingResizeListener) {
-            detachListeners();
-            this.spacingResizeListener = spacingResizeListener;
-            attachListeners();
-        }
-
-        /**
-         * Returns the alignment for the slot
-         * 
-         */
-        public AlignmentInfo getAlignment() {
-            return alignment;
-        }
-
-        /**
-         * Sets the style names for the slot containing the widget
-         * 
-         * @param stylenames
-         *            The style names for the slot
-         */
-        protected void setStyleNames(String... stylenames) {
-            setStyleName(SLOT_CLASSNAME);
-            if (stylenames != null) {
-                for (String stylename : stylenames) {
-                    addStyleDependentName(stylename);
-                }
-            }
-
-            // Ensure alignment style names are correct
-            setAlignment(alignment);
-        }
-
-        /**
-         * Sets how the widget is aligned inside the slot
-         * 
-         * @param alignment
-         *            The alignment inside the slot
-         */
-        public void setAlignment(AlignmentInfo alignment) {
-            this.alignment = alignment;
-
-            if (alignment != null && alignment.isHorizontalCenter()) {
-                addStyleName(ALIGN_CLASS_PREFIX + "center");
-                removeStyleName(ALIGN_CLASS_PREFIX + "right");
-            } else if (alignment != null && alignment.isRight()) {
-                addStyleName(ALIGN_CLASS_PREFIX + "right");
-                removeStyleName(ALIGN_CLASS_PREFIX + "center");
-            } else {
-                removeStyleName(ALIGN_CLASS_PREFIX + "right");
-                removeStyleName(ALIGN_CLASS_PREFIX + "center");
-            }
-
-            if (alignment != null && alignment.isVerticalCenter()) {
-                addStyleName(ALIGN_CLASS_PREFIX + "middle");
-                removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
-            } else if (alignment != null && alignment.isBottom()) {
-                addStyleName(ALIGN_CLASS_PREFIX + "bottom");
-                removeStyleName(ALIGN_CLASS_PREFIX + "middle");
-            } else {
-                removeStyleName(ALIGN_CLASS_PREFIX + "middle");
-                removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
-            }
-        }
-
-        /**
-         * Set how the slot should be expanded relative to the other slots
-         * 
-         * @param expandRatio
-         *            The ratio of the space the slot should occupy
-         * 
-         */
-        public void setExpandRatio(double expandRatio) {
-            this.expandRatio = expandRatio;
-        }
-
-        /**
-         * Get the expand ratio for the slot. The expand ratio describes how the
-         * slot should be resized compared to other slots in the layout
-         * 
-         * @return
-         */
-        public double getExpandRatio() {
-            return expandRatio;
-        }
-
-        /**
-         * Set the spacing for the slot. The spacing determines if there should
-         * be empty space around the slot when the slot.
-         * 
-         * @param spacing
-         *            Should spacing be enabled
-         */
-        public void setSpacing(boolean spacing) {
-            if (spacing && spacer == null) {
-                spacer = DOM.createDiv();
-                spacer.addClassName("v-spacing");
-
-                /*
-                 * This has to be done here for the initial render. In other
-                 * cases where the spacer already exists onAttach will handle
-                 * it.
-                 */
-                getElement().getParentElement().insertBefore(spacer,
-                        getElement());
-            } else if (!spacing && spacer != null) {
-                spacer.removeFromParent();
-                spacer = null;
-            }
-        }
-
-        /**
-         * Get the element which is added to make the spacing
-         * 
-         * @return
-         */
-        public Element getSpacingElement() {
-            return spacer;
-        }
-
-        /**
-         * Does the slot have spacing
-         */
-        public boolean hasSpacing() {
-            return getSpacingElement() != null;
-        }
-
-        /**
-         * Get the vertical amount in pixels of the spacing
-         */
-        protected int getVerticalSpacing() {
-            if (spacer == null) {
-                return 0;
-            } else if (getLayoutManager() != null) {
-                return getLayoutManager().getOuterHeight(spacer);
-            }
-            return spacer.getOffsetHeight();
-        }
-
-        /**
-         * Get the horizontal amount of pixels of the spacing
-         * 
-         * @return
-         */
-        protected int getHorizontalSpacing() {
-            if (spacer == null) {
-                return 0;
-            } else if (getLayoutManager() != null) {
-                return getLayoutManager().getOuterWidth(spacer);
-            }
-            return spacer.getOffsetWidth();
-        }
-
-        /**
-         * Set the position of the caption relative to the slot
-         * 
-         * @param captionPosition
-         *            The position of the caption
-         */
-        public void setCaptionPosition(CaptionPosition captionPosition) {
-            if (caption == null) {
-                return;
-            }
-            captionWrap.removeClassName("v-caption-on-"
-                    + this.captionPosition.name().toLowerCase());
-
-            this.captionPosition = captionPosition;
-            if (captionPosition == CaptionPosition.BOTTOM
-                    || captionPosition == CaptionPosition.RIGHT) {
-                captionWrap.appendChild(caption);
-            } else {
-                captionWrap.insertFirst(caption);
-            }
-
-            captionWrap.addClassName("v-caption-on-"
-                    + captionPosition.name().toLowerCase());
-        }
-
-        /**
-         * Get the position of the caption relative to the slot
-         */
-        public CaptionPosition getCaptionPosition() {
-            return captionPosition;
-        }
-
-        /**
-         * Set the caption of the slot
-         * 
-         * @param captionText
-         *            The text of the caption
-         * @param iconUrl
-         *            The icon URL
-         * @param styles
-         *            The style names
-         * @param error
-         *            The error message
-         * @param showError
-         *            Should the error message be shown
-         * @param required
-         *            Is the (field) required
-         * @param enabled
-         *            Is the component enabled
-         */
-        public void setCaption(String captionText, String iconUrl,
-                List<String> styles, String error, boolean showError,
-                boolean required, boolean enabled) {
-
-            // TODO place for optimization: check if any of these have changed
-            // since last time, and only run those changes
-
-            // Caption wrappers
-            if (captionText != null || iconUrl != null || error != null
-                    || required) {
-                if (caption == null) {
-                    caption = DOM.createDiv();
-                    captionWrap = DOM.createDiv();
-                    captionWrap.addClassName(StyleConstants.UI_WIDGET);
-                    captionWrap.addClassName("v-has-caption");
-                    getElement().appendChild(captionWrap);
-                    captionWrap.appendChild(getWidget().getElement());
-                }
-            } else if (caption != null) {
-                getElement().appendChild(getWidget().getElement());
-                captionWrap.removeFromParent();
-                caption = null;
-                captionWrap = null;
-            }
-
-            // Caption text
-            if (captionText != null) {
-                if (this.captionText == null) {
-                    this.captionText = DOM.createSpan();
-                    this.captionText.addClassName("v-captiontext");
-                    caption.appendChild(this.captionText);
-                }
-                if (captionText.trim().equals("")) {
-                    this.captionText.setInnerHTML("&nbsp;");
-                } else {
-                    this.captionText.setInnerText(captionText);
-                }
-            } else if (this.captionText != null) {
-                this.captionText.removeFromParent();
-                this.captionText = null;
-            }
-
-            // Icon
-            if (iconUrl != null) {
-                if (icon == null) {
-                    icon = new Icon();
-                    caption.insertFirst(icon.getElement());
-                }
-                icon.setUri(iconUrl);
-            } else if (icon != null) {
-                icon.getElement().removeFromParent();
-                icon = null;
-            }
-
-            // Required
-            if (required) {
-                if (requiredIcon == null) {
-                    requiredIcon = DOM.createSpan();
-                    // TODO decide something better (e.g. use CSS to insert the
-                    // character)
-                    requiredIcon.setInnerHTML("*");
-                    requiredIcon.setClassName("v-required-field-indicator");
-                }
-                caption.appendChild(requiredIcon);
-            } else if (requiredIcon != null) {
-                requiredIcon.removeFromParent();
-                requiredIcon = null;
-            }
-
-            // Error
-            if (error != null && showError) {
-                if (errorIcon == null) {
-                    errorIcon = DOM.createSpan();
-                    errorIcon.setClassName("v-errorindicator");
-                }
-                caption.appendChild(errorIcon);
-            } else if (errorIcon != null) {
-                errorIcon.removeFromParent();
-                errorIcon = null;
-            }
-
-            if (caption != null) {
-                // Styles
-                caption.setClassName("v-caption");
-
-                if (styles != null) {
-                    for (String style : styles) {
-                        caption.addClassName("v-caption-" + style);
-                    }
-                }
-
-                if (enabled) {
-                    caption.removeClassName("v-disabled");
-                } else {
-                    caption.addClassName("v-disabled");
-                }
-
-                // Caption position
-                if (captionText != null || iconUrl != null) {
-                    setCaptionPosition(CaptionPosition.TOP);
-                } else {
-                    setCaptionPosition(CaptionPosition.RIGHT);
-                }
-            }
-        }
-
-        /**
-         * Does the slot have a caption
-         */
-        public boolean hasCaption() {
-            return caption != null;
-        }
-
-        /**
-         * Get the slots caption element
-         */
-        public Element getCaptionElement() {
-            return caption;
-        }
-
-        /**
-         * Set if the slot has a relative width
-         * 
-         * @param relativeWidth
-         *            True if slot uses relative width, false if the slot has a
-         *            static width
-         */
-        private boolean relativeWidth = false;
-
-        public void setRelativeWidth(boolean relativeWidth) {
-            this.relativeWidth = relativeWidth;
-            updateRelativeSize(relativeWidth, "width");
-        }
-
-        /**
-         * Set if the slot has a relative height
-         * 
-         * @param relativeHeight
-         *            Trie if the slot uses a relative height, false if the slot
-         *            has a static height
-         */
-        private boolean relativeHeight = false;
-
-        public void setRelativeHeight(boolean relativeHeight) {
-            this.relativeHeight = relativeHeight;
-            updateRelativeSize(relativeHeight, "height");
-        }
-
-        /**
-         * Updates the captions size if the slot is relative
-         * 
-         * @param isRelativeSize
-         *            Is the slot relatived sized
-         * @param direction
-         *            The directorion of the caption
-         */
-        private void updateRelativeSize(boolean isRelativeSize, String direction) {
-            if (isRelativeSize && hasCaption()) {
-                captionWrap.getStyle().setProperty(
-                        direction,
-                        getWidget().getElement().getStyle()
-                                .getProperty(direction));
-                captionWrap.addClassName("v-has-" + direction);
-            } else if (hasCaption()) {
-                if (direction.equals("height")) {
-                    captionWrap.getStyle().clearHeight();
-                } else {
-                    captionWrap.getStyle().clearWidth();
-                }
-                captionWrap.removeClassName("v-has-" + direction);
-                captionWrap.getStyle().clearPaddingTop();
-                captionWrap.getStyle().clearPaddingRight();
-                captionWrap.getStyle().clearPaddingBottom();
-                captionWrap.getStyle().clearPaddingLeft();
-                caption.getStyle().clearMarginTop();
-                caption.getStyle().clearMarginRight();
-                caption.getStyle().clearMarginBottom();
-                caption.getStyle().clearMarginLeft();
-            }
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see
-         * com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt
-         * .user.client.Event)
-         */
-        @Override
-        public void onBrowserEvent(Event event) {
-            super.onBrowserEvent(event);
-            if (DOM.eventGetType(event) == Event.ONLOAD
-                    && icon.getElement() == DOM.eventGetTarget(event)) {
-                if (getLayoutManager() != null) {
-                    getLayoutManager().layoutLater();
-                } else {
-                    updateCaptionOffset(caption);
-                }
-            }
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see com.google.gwt.user.client.ui.SimplePanel#getContainerElement()
-         */
-        @Override
-        protected Element getContainerElement() {
-            if (captionWrap == null) {
-                return getElement();
-            } else {
-                return captionWrap;
-            }
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see com.google.gwt.user.client.ui.Widget#onDetach()
-         */
-        @Override
-        protected void onDetach() {
-            if (spacer != null) {
-                spacer.removeFromParent();
-            }
-            super.onDetach();
-        }
-
-        /*
-         * (non-Javadoc)
-         * 
-         * @see com.google.gwt.user.client.ui.Widget#onAttach()
-         */
-        @Override
-        protected void onAttach() {
-            super.onAttach();
-            if (spacer != null) {
-                getElement().getParentElement().insertBefore(spacer,
-                        getElement());
-            }
-        }
-    }
-
-    /**
-     * The icon for each widget. Located in the caption of the slot.
-     */
-    public static class Icon extends UIObject {
-
-        public static final String CLASSNAME = "v-icon";
-
-        private String myUrl;
-
-        /**
-         * Constructor
-         */
-        public Icon() {
-            setElement(DOM.createImg());
-            DOM.setElementProperty(getElement(), "alt", "");
-            setStyleName(CLASSNAME);
-        }
-
-        /**
-         * Set the URL where the icon is located
-         * 
-         * @param url
-         *            A fully qualified URL
-         */
-        public void setUri(String url) {
-            if (!url.equals(myUrl)) {
-                /*
-                 * Start sinking onload events, widgets responsibility to react.
-                 * We must do this BEFORE we set src as IE fires the event
-                 * immediately if the image is found in cache (#2592).
-                 */
-                sinkEvents(Event.ONLOAD);
-
-                DOM.setElementProperty(getElement(), "src", url);
-                myUrl = url;
-            }
-        }
-    }
-
-    /**
-     * Set the layout manager for the layout
-     * 
-     * @param manager
-     *            The layout manager to use
-     */
-    public void setLayoutManager(LayoutManager manager) {
-        layoutManager = manager;
-    }
-
-    /**
-     * Get the layout manager used by this layout
-     * 
-     */
-    public LayoutManager getLayoutManager() {
-        return layoutManager;
-    }
-
-    /**
-     * Deducts the caption position by examining the wrapping element.
-     * <p>
-     * For internal use only. May be removed or replaced in the future.
-     * 
-     * @param captionWrap
-     *            The wrapping element
-     * 
-     * @return The caption position
-     */
-    public CaptionPosition getCaptionPositionFromElement(Element captionWrap) {
-        RegExp captionPositionRegexp = RegExp.compile("v-caption-on-(\\S+)");
-
-        // Get caption position from the classname
-        MatchResult matcher = captionPositionRegexp.exec(captionWrap
-                .getClassName());
-        if (matcher == null || matcher.getGroupCount() < 2) {
-            return CaptionPosition.TOP;
-        }
-        String captionClass = matcher.getGroup(1);
-        CaptionPosition captionPosition = CaptionPosition.valueOf(
-                CaptionPosition.class, captionClass.toUpperCase());
-        return captionPosition;
-    }
-
-    /**
-     * Update the offset off the caption relative to the slot
-     * <p>
-     * For internal use only. May be removed or replaced in the future.
-     * 
-     * @param caption
-     *            The caption element
-     */
-    public void updateCaptionOffset(Element caption) {
-
-        Element captionWrap = caption.getParentElement().cast();
-
-        Style captionWrapStyle = captionWrap.getStyle();
-        captionWrapStyle.clearPaddingTop();
-        captionWrapStyle.clearPaddingRight();
-        captionWrapStyle.clearPaddingBottom();
-        captionWrapStyle.clearPaddingLeft();
-
-        Style captionStyle = caption.getStyle();
-        captionStyle.clearMarginTop();
-        captionStyle.clearMarginRight();
-        captionStyle.clearMarginBottom();
-        captionStyle.clearMarginLeft();
-
-        // Get caption position from the classname
-        CaptionPosition captionPosition = getCaptionPositionFromElement(captionWrap);
-
-        if (captionPosition == CaptionPosition.LEFT
-                || captionPosition == CaptionPosition.RIGHT) {
-            int captionWidth;
-            if (layoutManager != null) {
-                captionWidth = layoutManager.getOuterWidth(caption)
-                        - layoutManager.getMarginWidth(caption);
-            } else {
-                captionWidth = caption.getOffsetWidth();
-            }
-            if (captionWidth > 0) {
-                if (captionPosition == CaptionPosition.LEFT) {
-                    captionWrapStyle.setPaddingLeft(captionWidth, Unit.PX);
-                    captionStyle.setMarginLeft(-captionWidth, Unit.PX);
-                } else {
-                    captionWrapStyle.setPaddingRight(captionWidth, Unit.PX);
-                    captionStyle.setMarginRight(-captionWidth, Unit.PX);
-                }
-            }
-        }
-        if (captionPosition == CaptionPosition.TOP
-                || captionPosition == CaptionPosition.BOTTOM) {
-            int captionHeight;
-            if (layoutManager != null) {
-                captionHeight = layoutManager.getOuterHeight(caption)
-                        - layoutManager.getMarginHeight(caption);
-            } else {
-                captionHeight = caption.getOffsetHeight();
-            }
-            if (captionHeight > 0) {
-                if (captionPosition == CaptionPosition.TOP) {
-                    captionWrapStyle.setPaddingTop(captionHeight, Unit.PX);
-                    captionStyle.setMarginTop(-captionHeight, Unit.PX);
-                } else {
-                    captionWrapStyle.setPaddingBottom(captionHeight, Unit.PX);
-                    captionStyle.setMarginBottom(-captionHeight, Unit.PX);
-                }
-            }
-        }
-    }
-
-    /**
-     * Set the margin of the layout
-     * 
-     * @param marginInfo
-     *            The margin information
-     */
-    public void setMargin(MarginInfo marginInfo) {
-        if (marginInfo != null) {
-            setStyleName("v-margin-top", marginInfo.hasTop());
-            setStyleName("v-margin-right", marginInfo.hasRight());
-            setStyleName("v-margin-bottom", marginInfo.hasBottom());
-            setStyleName("v-margin-left", marginInfo.hasLeft());
-        }
-    }
-
-    /**
-     * Turn on or off spacing in the layout
-     * 
-     * @param spacing
-     *            True if spacing should be used, false if not
-     */
-    public void setSpacing(boolean spacing) {
-        this.spacing = spacing;
-        for (Slot slot : widgetToSlot.values()) {
-            if (getWidgetIndex(slot) > 0) {
-                slot.setSpacing(spacing);
-            } else {
-                slot.setSpacing(false);
-            }
-        }
-    }
-
-    /**
-     * Triggers a recalculation of the expand width and heights
-     */
-    private void recalculateExpands() {
-        double total = 0;
-        for (Slot slot : widgetToSlot.values()) {
-            if (slot.getExpandRatio() > -1) {
-                total += slot.getExpandRatio();
-            } else {
-                if (vertical) {
-                    slot.getElement().getStyle().clearHeight();
-                } else {
-                    slot.getElement().getStyle().clearWidth();
-                }
-            }
-        }
-        for (Slot slot : widgetToSlot.values()) {
-            if (slot.getExpandRatio() > -1) {
-                if (vertical) {
-                    slot.setHeight((100 * (slot.getExpandRatio() / total))
-                            + "%");
-                    if (slot.relativeHeight) {
-                        Util.notifyParentOfSizeChange(this, true);
-                    }
-                } else {
-                    slot.setWidth((100 * (slot.getExpandRatio() / total)) + "%");
-                    if (slot.relativeWidth) {
-                        Util.notifyParentOfSizeChange(this, true);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Removes elements used to expand a slot.
-     * <p>
-     * For internal use only. May be removed or replaced in the future.
-     */
-    public void clearExpand() {
-        if (expandWrapper != null) {
-            for (; expandWrapper.getChildCount() > 0;) {
-                Element el = expandWrapper.getChild(0).cast();
-                getElement().appendChild(el);
-                if (vertical) {
-                    el.getStyle().clearHeight();
-                    el.getStyle().clearMarginTop();
-                } else {
-                    el.getStyle().clearWidth();
-                    el.getStyle().clearMarginLeft();
-                }
-            }
-            expandWrapper.removeFromParent();
-            expandWrapper = null;
-        }
-    }
-
-    /**
-     * Adds elements used to expand a slot
-     */
-    public void updateExpand() {
-        boolean isExpanding = false;
-        for (Widget slot : getChildren()) {
-            if (((Slot) slot).getExpandRatio() > -1) {
-                isExpanding = true;
-            } else {
-                if (vertical) {
-                    slot.getElement().getStyle().clearHeight();
-                } else {
-                    slot.getElement().getStyle().clearWidth();
-                }
-            }
-            slot.getElement().getStyle().clearMarginLeft();
-            slot.getElement().getStyle().clearMarginTop();
-        }
-
-        if (isExpanding) {
-            if (expandWrapper == null) {
-                expandWrapper = DOM.createDiv();
-                expandWrapper.setClassName("v-expand");
-                for (; getElement().getChildCount() > 0;) {
-                    Node el = getElement().getChild(0);
-                    expandWrapper.appendChild(el);
-                }
-                getElement().appendChild(expandWrapper);
-            }
-
-            int totalSize = 0;
-            for (Widget w : getChildren()) {
-                Slot slot = (Slot) w;
-                if (slot.getExpandRatio() == -1) {
-
-                    if (layoutManager != null) {
-                        // TODO check caption position
-                        if (vertical) {
-                            int size = layoutManager.getOuterHeight(slot
-                                    .getWidget().getElement())
-                                    - layoutManager.getMarginHeight(slot
-                                            .getWidget().getElement());
-                            if (slot.hasCaption()) {
-                                size += layoutManager.getOuterHeight(slot
-                                        .getCaptionElement())
-                                        - layoutManager.getMarginHeight(slot
-                                                .getCaptionElement());
-                            }
-                            if (size > 0) {
-                                totalSize += size;
-                            }
-                        } else {
-                            int max = -1;
-                            max = layoutManager.getOuterWidth(slot.getWidget()
-                                    .getElement())
-                                    - layoutManager.getMarginWidth(slot
-                                            .getWidget().getElement());
-                            if (slot.hasCaption()) {
-                                int max2 = layoutManager.getOuterWidth(slot
-                                        .getCaptionElement())
-                                        - layoutManager.getMarginWidth(slot
-                                                .getCaptionElement());
-                                max = Math.max(max, max2);
-                            }
-                            if (max > 0) {
-                                totalSize += max;
-                            }
-                        }
-                    } else {
-                        totalSize += vertical ? slot.getOffsetHeight() : slot
-                                .getOffsetWidth();
-                    }
-                }
-                // TODO fails in Opera, always returns 0
-                int spacingSize = vertical ? slot.getVerticalSpacing() : slot
-                        .getHorizontalSpacing();
-                if (spacingSize > 0) {
-                    totalSize += spacingSize;
-                }
-            }
-
-            // When we set the margin to the first child, we don't need
-            // overflow:hidden in the layout root element, since the wrapper
-            // would otherwise be placed outside of the layout root element
-            // and block events on elements below it.
-            if (vertical) {
-                expandWrapper.getStyle().setPaddingTop(totalSize, Unit.PX);
-                expandWrapper.getFirstChildElement().getStyle()
-                        .setMarginTop(-totalSize, Unit.PX);
-            } else {
-                expandWrapper.getStyle().setPaddingLeft(totalSize, Unit.PX);
-                expandWrapper.getFirstChildElement().getStyle()
-                        .setMarginLeft(-totalSize, Unit.PX);
-            }
-
-            recalculateExpands();
-        }
-    }
-
-    /**
-     * Perform a recalculation of the layout height
-     */
-    public void recalculateLayoutHeight() {
-        // Only needed if a horizontal layout is undefined high, and contains
-        // relative height children or vertical alignments
-        if (vertical || definedHeight) {
-            return;
-        }
-
-        boolean hasRelativeHeightChildren = false;
-        boolean hasVAlign = false;
-
-        for (Widget slot : getChildren()) {
-            Widget widget = ((Slot) slot).getWidget();
-            String h = widget.getElement().getStyle().getHeight();
-            if (h != null && h.indexOf("%") > -1) {
-                hasRelativeHeightChildren = true;
-            }
-            AlignmentInfo a = ((Slot) slot).getAlignment();
-            if (a != null && (a.isVerticalCenter() || a.isBottom())) {
-                hasVAlign = true;
-            }
-        }
-
-        if (hasRelativeHeightChildren || hasVAlign) {
-            int newHeight;
-            if (layoutManager != null) {
-                newHeight = layoutManager.getOuterHeight(getElement())
-                        - layoutManager.getMarginHeight(getElement());
-            } else {
-                newHeight = getElement().getOffsetHeight();
-            }
-            VOrderedLayout.this.getElement().getStyle()
-                    .setHeight(newHeight, Unit.PX);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setHeight(String height) {
-        super.setHeight(height);
-        definedHeight = (height != null && !"".equals(height));
-    }
-
-    /**
-     * Sets the slots style names. The style names will be prefixed with the
-     * v-slot prefix.
-     * 
-     * @param stylenames
-     *            The style names of the slot.
-     */
-    public void setSlotStyleNames(Widget widget, String... stylenames) {
-        Slot slot = getSlot(widget);
-        if (slot == null) {
-            throw new IllegalArgumentException(
-                    "A slot for the widget could not be found. Has the widget been added to the layout?");
-        }
-        slot.setStyleNames(stylenames);
-    }
-
-}
index 1a3ce332f5d9f1534543dcf3139fd5a492861532..2a81e61d9a394d048b82b2d5e74abb3f9dfb5e62 100644 (file)
 package com.vaadin.client.ui;
 
 import com.vaadin.client.StyleConstants;
+import com.vaadin.client.ui.orderedlayout.VAbstractOrderedLayout;
 
 /**
  * Represents a layout where the children is ordered vertically
  */
-public class VVerticalLayout extends VOrderedLayout {
+public class VVerticalLayout extends VAbstractOrderedLayout {
 
     public static final String CLASSNAME = "v-verticallayout";
 
index 65736397cb6d951c8c781c69c8bd082aa9032b0e..e062d37dd6d229b3d4fbfb0962282aa8ca7bedbe 100644 (file)
@@ -29,9 +29,6 @@ import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
 import com.vaadin.client.ui.AbstractFieldConnector;
 import com.vaadin.client.ui.AbstractLayoutConnector;
 import com.vaadin.client.ui.LayoutClickEventHandler;
-import com.vaadin.client.ui.VOrderedLayout;
-import com.vaadin.client.ui.VOrderedLayout.CaptionPosition;
-import com.vaadin.client.ui.VOrderedLayout.Slot;
 import com.vaadin.client.ui.layout.ElementResizeEvent;
 import com.vaadin.client.ui.layout.ElementResizeListener;
 import com.vaadin.shared.AbstractFieldState;
@@ -199,8 +196,8 @@ public abstract class AbstractOrderedLayoutConnector extends
      * @see com.vaadin.client.ui.AbstractComponentConnector#getWidget()
      */
     @Override
-    public VOrderedLayout getWidget() {
-        return (VOrderedLayout) super.getWidget();
+    public VAbstractOrderedLayout getWidget() {
+        return (VAbstractOrderedLayout) super.getWidget();
     }
 
     /**
@@ -300,7 +297,7 @@ public abstract class AbstractOrderedLayoutConnector extends
 
         List<ComponentConnector> previousChildren = event.getOldChildren();
         int currentIndex = 0;
-        VOrderedLayout layout = getWidget();
+        VAbstractOrderedLayout layout = getWidget();
 
         layout.setSpacing(getState().spacing);
 
diff --git a/client/src/com/vaadin/client/ui/orderedlayout/CaptionPosition.java b/client/src/com/vaadin/client/ui/orderedlayout/CaptionPosition.java
new file mode 100644 (file)
index 0000000..612d00b
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2012 Vaadin Ltd.
+ * 
+ * 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.vaadin.client.ui.orderedlayout;
+
+/**
+ * Defines where the caption should be placed
+ */
+public enum CaptionPosition {
+    TOP, RIGHT, BOTTOM, LEFT
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java
new file mode 100644 (file)
index 0000000..094a7f1
--- /dev/null
@@ -0,0 +1,658 @@
+/*
+ * Copyright 2012 Vaadin Ltd.
+ * 
+ * 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.vaadin.client.ui.orderedlayout;
+
+import java.util.List;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.LayoutManager;
+import com.vaadin.client.StyleConstants;
+import com.vaadin.client.ui.layout.ElementResizeListener;
+import com.vaadin.shared.ui.AlignmentInfo;
+
+/**
+ * Represents a slot which contains the actual widget in the layout.
+ */
+public final class Slot extends SimplePanel {
+    /**
+     * The icon for each widget. Located in the caption of the slot.
+     */
+    private static class Icon extends UIObject {
+
+        public static final String CLASSNAME = "v-icon";
+
+        private String myUrl;
+
+        /**
+         * Constructor
+         */
+        public Icon() {
+            setElement(DOM.createImg());
+            DOM.setElementProperty(getElement(), "alt", "");
+            setStyleName(CLASSNAME);
+        }
+
+        /**
+         * Set the URL where the icon is located
+         * 
+         * @param url
+         *            A fully qualified URL
+         */
+        public void setUri(String url) {
+            if (!url.equals(myUrl)) {
+                /*
+                 * Start sinking onload events, widgets responsibility to react.
+                 * We must do this BEFORE we set src as IE fires the event
+                 * immediately if the image is found in cache (#2592).
+                 */
+                sinkEvents(Event.ONLOAD);
+
+                DOM.setElementProperty(getElement(), "src", url);
+                myUrl = url;
+            }
+        }
+    }
+
+    private static final String ALIGN_CLASS_PREFIX = "v-align-";
+
+    private final VAbstractOrderedLayout layout;
+
+    public static final String SLOT_CLASSNAME = "v-slot";
+
+    private Element spacer;
+    private Element captionWrap;
+    private Element caption;
+    private Element captionText;
+    private Icon icon;
+    private Element errorIcon;
+    private Element requiredIcon;
+
+    private ElementResizeListener captionResizeListener;
+
+    private ElementResizeListener widgetResizeListener;
+
+    private ElementResizeListener spacingResizeListener;
+
+    // Caption is placed after component unless there is some part which
+    // moves it above.
+    private CaptionPosition captionPosition = CaptionPosition.RIGHT;
+
+    private AlignmentInfo alignment;
+
+    private double expandRatio = -1;
+
+    /**
+     * Constructor
+     * 
+     * @param widget
+     *            The widget to put in the slot
+     * @param layout
+     *            TODO
+     * 
+     * @param layoutManager
+     *            The layout manager used by the layout
+     */
+    public Slot(VAbstractOrderedLayout layout, Widget widget) {
+        this.layout = layout;
+        setStyleName(SLOT_CLASSNAME);
+        setWidget(widget);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.google.gwt.user.client.ui.SimplePanel#remove(com.google.gwt.user
+     * .client.ui.Widget)
+     */
+    @Override
+    public boolean remove(Widget w) {
+        detachListeners();
+        return super.remove(w);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.google.gwt.user.client.ui.SimplePanel#setWidget(com.google.gwt
+     * .user.client.ui.Widget)
+     */
+    @Override
+    public void setWidget(Widget w) {
+        detachListeners();
+        super.setWidget(w);
+        attachListeners();
+    }
+
+    /**
+     * Attached resize listeners to the widget, caption and spacing elements
+     */
+    private void attachListeners() {
+        if (getWidget() != null && layout.getLayoutManager() != null) {
+            LayoutManager lm = layout.getLayoutManager();
+            if (getCaptionElement() != null && captionResizeListener != null) {
+                lm.addElementResizeListener(getCaptionElement(),
+                        captionResizeListener);
+            }
+            if (widgetResizeListener != null) {
+                lm.addElementResizeListener(getWidget().getElement(),
+                        widgetResizeListener);
+            }
+            if (getSpacingElement() != null && spacingResizeListener != null) {
+                lm.addElementResizeListener(getSpacingElement(),
+                        spacingResizeListener);
+            }
+        }
+    }
+
+    /**
+     * Detaches resize listeners from the widget, caption and spacing elements
+     */
+    private void detachListeners() {
+        if (getWidget() != null && layout.getLayoutManager() != null) {
+            LayoutManager lm = layout.getLayoutManager();
+            if (getCaptionElement() != null && captionResizeListener != null) {
+                lm.removeElementResizeListener(getCaptionElement(),
+                        captionResizeListener);
+            }
+            if (widgetResizeListener != null) {
+                lm.removeElementResizeListener(getWidget().getElement(),
+                        widgetResizeListener);
+            }
+            if (getSpacingElement() != null && spacingResizeListener != null) {
+                lm.removeElementResizeListener(getSpacingElement(),
+                        spacingResizeListener);
+            }
+        }
+    }
+
+    public ElementResizeListener getCaptionResizeListener() {
+        return captionResizeListener;
+    }
+
+    public void setCaptionResizeListener(
+            ElementResizeListener captionResizeListener) {
+        detachListeners();
+        this.captionResizeListener = captionResizeListener;
+        attachListeners();
+    }
+
+    public ElementResizeListener getWidgetResizeListener() {
+        return widgetResizeListener;
+    }
+
+    public void setWidgetResizeListener(
+            ElementResizeListener widgetResizeListener) {
+        detachListeners();
+        this.widgetResizeListener = widgetResizeListener;
+        attachListeners();
+    }
+
+    public ElementResizeListener getSpacingResizeListener() {
+        return spacingResizeListener;
+    }
+
+    public void setSpacingResizeListener(
+            ElementResizeListener spacingResizeListener) {
+        detachListeners();
+        this.spacingResizeListener = spacingResizeListener;
+        attachListeners();
+    }
+
+    /**
+     * Returns the alignment for the slot
+     * 
+     */
+    public AlignmentInfo getAlignment() {
+        return alignment;
+    }
+
+    /**
+     * Sets the style names for the slot containing the widget
+     * 
+     * @param stylenames
+     *            The style names for the slot
+     */
+    protected void setStyleNames(String... stylenames) {
+        setStyleName(SLOT_CLASSNAME);
+        if (stylenames != null) {
+            for (String stylename : stylenames) {
+                addStyleDependentName(stylename);
+            }
+        }
+
+        // Ensure alignment style names are correct
+        setAlignment(alignment);
+    }
+
+    /**
+     * Sets how the widget is aligned inside the slot
+     * 
+     * @param alignment
+     *            The alignment inside the slot
+     */
+    public void setAlignment(AlignmentInfo alignment) {
+        this.alignment = alignment;
+
+        if (alignment != null && alignment.isHorizontalCenter()) {
+            addStyleName(ALIGN_CLASS_PREFIX + "center");
+            removeStyleName(ALIGN_CLASS_PREFIX + "right");
+        } else if (alignment != null && alignment.isRight()) {
+            addStyleName(ALIGN_CLASS_PREFIX + "right");
+            removeStyleName(ALIGN_CLASS_PREFIX + "center");
+        } else {
+            removeStyleName(ALIGN_CLASS_PREFIX + "right");
+            removeStyleName(ALIGN_CLASS_PREFIX + "center");
+        }
+
+        if (alignment != null && alignment.isVerticalCenter()) {
+            addStyleName(ALIGN_CLASS_PREFIX + "middle");
+            removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
+        } else if (alignment != null && alignment.isBottom()) {
+            addStyleName(ALIGN_CLASS_PREFIX + "bottom");
+            removeStyleName(ALIGN_CLASS_PREFIX + "middle");
+        } else {
+            removeStyleName(ALIGN_CLASS_PREFIX + "middle");
+            removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
+        }
+    }
+
+    /**
+     * Set how the slot should be expanded relative to the other slots
+     * 
+     * @param expandRatio
+     *            The ratio of the space the slot should occupy
+     * 
+     */
+    public void setExpandRatio(double expandRatio) {
+        this.expandRatio = expandRatio;
+    }
+
+    /**
+     * Get the expand ratio for the slot. The expand ratio describes how the
+     * slot should be resized compared to other slots in the layout
+     * 
+     * @return
+     */
+    public double getExpandRatio() {
+        return expandRatio;
+    }
+
+    /**
+     * Set the spacing for the slot. The spacing determines if there should be
+     * empty space around the slot when the slot.
+     * 
+     * @param spacing
+     *            Should spacing be enabled
+     */
+    public void setSpacing(boolean spacing) {
+        if (spacing && spacer == null) {
+            spacer = DOM.createDiv();
+            spacer.addClassName("v-spacing");
+
+            /*
+             * This has to be done here for the initial render. In other cases
+             * where the spacer already exists onAttach will handle it.
+             */
+            getElement().getParentElement().insertBefore(spacer, getElement());
+        } else if (!spacing && spacer != null) {
+            spacer.removeFromParent();
+            spacer = null;
+        }
+    }
+
+    /**
+     * Get the element which is added to make the spacing
+     * 
+     * @return
+     */
+    public Element getSpacingElement() {
+        return spacer;
+    }
+
+    /**
+     * Does the slot have spacing
+     */
+    public boolean hasSpacing() {
+        return getSpacingElement() != null;
+    }
+
+    /**
+     * Get the vertical amount in pixels of the spacing
+     */
+    protected int getVerticalSpacing() {
+        if (spacer == null) {
+            return 0;
+        } else if (layout.getLayoutManager() != null) {
+            return layout.getLayoutManager().getOuterHeight(spacer);
+        }
+        return spacer.getOffsetHeight();
+    }
+
+    /**
+     * Get the horizontal amount of pixels of the spacing
+     * 
+     * @return
+     */
+    protected int getHorizontalSpacing() {
+        if (spacer == null) {
+            return 0;
+        } else if (layout.getLayoutManager() != null) {
+            return layout.getLayoutManager().getOuterWidth(spacer);
+        }
+        return spacer.getOffsetWidth();
+    }
+
+    /**
+     * Set the position of the caption relative to the slot
+     * 
+     * @param captionPosition
+     *            The position of the caption
+     */
+    public void setCaptionPosition(CaptionPosition captionPosition) {
+        if (caption == null) {
+            return;
+        }
+        captionWrap.removeClassName("v-caption-on-"
+                + this.captionPosition.name().toLowerCase());
+
+        this.captionPosition = captionPosition;
+        if (captionPosition == CaptionPosition.BOTTOM
+                || captionPosition == CaptionPosition.RIGHT) {
+            captionWrap.appendChild(caption);
+        } else {
+            captionWrap.insertFirst(caption);
+        }
+
+        captionWrap.addClassName("v-caption-on-"
+                + captionPosition.name().toLowerCase());
+    }
+
+    /**
+     * Get the position of the caption relative to the slot
+     */
+    public CaptionPosition getCaptionPosition() {
+        return captionPosition;
+    }
+
+    /**
+     * Set the caption of the slot
+     * 
+     * @param captionText
+     *            The text of the caption
+     * @param iconUrl
+     *            The icon URL
+     * @param styles
+     *            The style names
+     * @param error
+     *            The error message
+     * @param showError
+     *            Should the error message be shown
+     * @param required
+     *            Is the (field) required
+     * @param enabled
+     *            Is the component enabled
+     */
+    public void setCaption(String captionText, String iconUrl,
+            List<String> styles, String error, boolean showError,
+            boolean required, boolean enabled) {
+
+        // TODO place for optimization: check if any of these have changed
+        // since last time, and only run those changes
+
+        // Caption wrappers
+        if (captionText != null || iconUrl != null || error != null || required) {
+            if (caption == null) {
+                caption = DOM.createDiv();
+                captionWrap = DOM.createDiv();
+                captionWrap.addClassName(StyleConstants.UI_WIDGET);
+                captionWrap.addClassName("v-has-caption");
+                getElement().appendChild(captionWrap);
+                captionWrap.appendChild(getWidget().getElement());
+            }
+        } else if (caption != null) {
+            getElement().appendChild(getWidget().getElement());
+            captionWrap.removeFromParent();
+            caption = null;
+            captionWrap = null;
+        }
+
+        // Caption text
+        if (captionText != null) {
+            if (this.captionText == null) {
+                this.captionText = DOM.createSpan();
+                this.captionText.addClassName("v-captiontext");
+                caption.appendChild(this.captionText);
+            }
+            if (captionText.trim().equals("")) {
+                this.captionText.setInnerHTML("&nbsp;");
+            } else {
+                this.captionText.setInnerText(captionText);
+            }
+        } else if (this.captionText != null) {
+            this.captionText.removeFromParent();
+            this.captionText = null;
+        }
+
+        // Icon
+        if (iconUrl != null) {
+            if (icon == null) {
+                icon = new Icon();
+                caption.insertFirst(icon.getElement());
+            }
+            icon.setUri(iconUrl);
+        } else if (icon != null) {
+            icon.getElement().removeFromParent();
+            icon = null;
+        }
+
+        // Required
+        if (required) {
+            if (requiredIcon == null) {
+                requiredIcon = DOM.createSpan();
+                // TODO decide something better (e.g. use CSS to insert the
+                // character)
+                requiredIcon.setInnerHTML("*");
+                requiredIcon.setClassName("v-required-field-indicator");
+            }
+            caption.appendChild(requiredIcon);
+        } else if (requiredIcon != null) {
+            requiredIcon.removeFromParent();
+            requiredIcon = null;
+        }
+
+        // Error
+        if (error != null && showError) {
+            if (errorIcon == null) {
+                errorIcon = DOM.createSpan();
+                errorIcon.setClassName("v-errorindicator");
+            }
+            caption.appendChild(errorIcon);
+        } else if (errorIcon != null) {
+            errorIcon.removeFromParent();
+            errorIcon = null;
+        }
+
+        if (caption != null) {
+            // Styles
+            caption.setClassName("v-caption");
+
+            if (styles != null) {
+                for (String style : styles) {
+                    caption.addClassName("v-caption-" + style);
+                }
+            }
+
+            if (enabled) {
+                caption.removeClassName("v-disabled");
+            } else {
+                caption.addClassName("v-disabled");
+            }
+
+            // Caption position
+            if (captionText != null || iconUrl != null) {
+                setCaptionPosition(CaptionPosition.TOP);
+            } else {
+                setCaptionPosition(CaptionPosition.RIGHT);
+            }
+        }
+    }
+
+    /**
+     * Does the slot have a caption
+     */
+    public boolean hasCaption() {
+        return caption != null;
+    }
+
+    /**
+     * Get the slots caption element
+     */
+    public Element getCaptionElement() {
+        return caption;
+    }
+
+    private boolean relativeWidth = false;
+
+    /**
+     * Set if the slot has a relative width
+     * 
+     * @param relativeWidth
+     *            True if slot uses relative width, false if the slot has a
+     *            static width
+     */
+    public void setRelativeWidth(boolean relativeWidth) {
+        this.relativeWidth = relativeWidth;
+        updateRelativeSize(relativeWidth, "width");
+    }
+
+    public boolean hasRelativeWidth() {
+        return relativeWidth;
+    }
+
+    private boolean relativeHeight = false;
+
+    /**
+     * Set if the slot has a relative height
+     * 
+     * @param relativeHeight
+     *            True if the slot uses a relative height, false if the slot has
+     *            a static height
+     */
+    public void setRelativeHeight(boolean relativeHeight) {
+        this.relativeHeight = relativeHeight;
+        updateRelativeSize(relativeHeight, "height");
+    }
+
+    public boolean hasRelativeHeight() {
+        return relativeHeight;
+    }
+
+    /**
+     * Updates the captions size if the slot is relative
+     * 
+     * @param isRelativeSize
+     *            Is the slot relatively sized
+     * @param direction
+     *            The direction of the caption
+     */
+    private void updateRelativeSize(boolean isRelativeSize, String direction) {
+        if (isRelativeSize && hasCaption()) {
+            captionWrap.getStyle().setProperty(direction,
+                    getWidget().getElement().getStyle().getProperty(direction));
+            captionWrap.addClassName("v-has-" + direction);
+        } else if (hasCaption()) {
+            if (direction.equals("height")) {
+                captionWrap.getStyle().clearHeight();
+            } else {
+                captionWrap.getStyle().clearWidth();
+            }
+            captionWrap.removeClassName("v-has-" + direction);
+            captionWrap.getStyle().clearPaddingTop();
+            captionWrap.getStyle().clearPaddingRight();
+            captionWrap.getStyle().clearPaddingBottom();
+            captionWrap.getStyle().clearPaddingLeft();
+            caption.getStyle().clearMarginTop();
+            caption.getStyle().clearMarginRight();
+            caption.getStyle().clearMarginBottom();
+            caption.getStyle().clearMarginLeft();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt
+     * .user.client.Event)
+     */
+    @Override
+    public void onBrowserEvent(Event event) {
+        super.onBrowserEvent(event);
+        if (DOM.eventGetType(event) == Event.ONLOAD
+                && icon.getElement() == DOM.eventGetTarget(event)) {
+            if (layout.getLayoutManager() != null) {
+                layout.getLayoutManager().layoutLater();
+            } else {
+                layout.updateCaptionOffset(caption);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.google.gwt.user.client.ui.SimplePanel#getContainerElement()
+     */
+    @Override
+    protected Element getContainerElement() {
+        if (captionWrap == null) {
+            return getElement();
+        } else {
+            return captionWrap;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.google.gwt.user.client.ui.Widget#onDetach()
+     */
+    @Override
+    protected void onDetach() {
+        if (spacer != null) {
+            spacer.removeFromParent();
+        }
+        super.onDetach();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.google.gwt.user.client.ui.Widget#onAttach()
+     */
+    @Override
+    protected void onAttach() {
+        super.onAttach();
+        if (spacer != null) {
+            getElement().getParentElement().insertBefore(spacer, getElement());
+        }
+    }
+}
\ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VAbstractOrderedLayout.java b/client/src/com/vaadin/client/ui/orderedlayout/VAbstractOrderedLayout.java
new file mode 100644 (file)
index 0000000..00b7092
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2011 Vaadin Ltd.
+ * 
+ * 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.vaadin.client.ui.orderedlayout;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.regexp.shared.MatchResult;
+import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.LayoutManager;
+import com.vaadin.client.Util;
+import com.vaadin.shared.ui.AlignmentInfo;
+import com.vaadin.shared.ui.MarginInfo;
+
+/**
+ * Base class for ordered layouts
+ */
+public class VAbstractOrderedLayout extends FlowPanel {
+
+    protected boolean spacing = false;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean vertical = true;
+
+    protected boolean definedHeight = false;
+
+    private Map<Widget, Slot> widgetToSlot = new HashMap<Widget, Slot>();
+
+    private Element expandWrapper;
+
+    private LayoutManager layoutManager;
+
+    public VAbstractOrderedLayout(boolean vertical) {
+        this.vertical = vertical;
+    }
+
+    /**
+     * Add or move a slot to another index.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     * <p>
+     * You should note that the index does not refer to the DOM index if
+     * spacings are used. If spacings are used then the index will be adjusted
+     * to include the spacings when inserted.
+     * <p>
+     * For instance when using spacing the index converts to DOM index in the
+     * following way:
+     * 
+     * <pre>
+     * index : 0 -> DOM index: 0
+     * index : 1 -> DOM index: 1
+     * index : 2 -> DOM index: 3
+     * index : 3 -> DOM index: 5
+     * index : 4 -> DOM index: 7
+     * </pre>
+     * 
+     * When using this method never account for spacings.
+     * </p>
+     * 
+     * @param slot
+     *            The slot to move or add
+     * @param index
+     *            The index where the slot should be placed.
+     */
+    public void addOrMoveSlot(Slot slot, int index) {
+        if (slot.getParent() == this) {
+            int currentIndex = getWidgetIndex(slot);
+            if (index == currentIndex) {
+                return;
+            }
+        }
+
+        insert(slot, index);
+
+        /*
+         * We need to confirm spacings are correctly applied after each insert.
+         */
+        setSpacing(spacing);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void insert(Widget child, Element container, int beforeIndex,
+            boolean domInsert) {
+        // Validate index; adjust if the widget is already a child of this
+        // panel.
+        beforeIndex = adjustIndex(child, beforeIndex);
+
+        // Detach new child.
+        child.removeFromParent();
+
+        // Logical attach.
+        getChildren().insert(child, beforeIndex);
+
+        // Physical attach.
+        container = expandWrapper != null ? expandWrapper : getElement();
+        if (domInsert) {
+            if (spacing) {
+                if (beforeIndex != 0) {
+                    /*
+                     * Since the spacing elements are located at the same DOM
+                     * level as the slots we need to take them into account when
+                     * calculating the slot position.
+                     * 
+                     * The spacing elements are always located before the actual
+                     * slot except for the first slot which do not have a
+                     * spacing element like this
+                     * 
+                     * |<slot1><spacing2><slot2><spacing3><slot3>...|
+                     */
+                    beforeIndex = beforeIndex * 2 - 1;
+                }
+            }
+            DOM.insertChild(container, child.getElement(), beforeIndex);
+        } else {
+            DOM.appendChild(container, child.getElement());
+        }
+
+        // Adopt.
+        adopt(child);
+    }
+
+    /**
+     * Remove a slot from the layout
+     * 
+     * @param widget
+     * @return
+     */
+    public void removeWidget(Widget widget) {
+        Slot slot = widgetToSlot.get(widget);
+        remove(slot);
+        widgetToSlot.remove(widget);
+    }
+
+    /**
+     * Get the containing slot for a widget. If no slot is found a new slot is
+     * created and returned.
+     * 
+     * @param widget
+     *            The widget whose slot you want to get
+     * 
+     * @return
+     */
+    public Slot getSlot(Widget widget) {
+        Slot slot = widgetToSlot.get(widget);
+        if (slot == null) {
+            slot = new Slot(this, widget);
+            widgetToSlot.put(widget, slot);
+        }
+        return slot;
+    }
+
+    /**
+     * Gets a slot based on the widget element. If no slot is found then null is
+     * returned.
+     * 
+     * @param widgetElement
+     *            The element of the widget ( Same as getWidget().getElement() )
+     * @return
+     */
+    public Slot getSlot(Element widgetElement) {
+        for (Map.Entry<Widget, Slot> entry : widgetToSlot.entrySet()) {
+            if (entry.getKey().getElement() == widgetElement) {
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Set the layout manager for the layout
+     * 
+     * @param manager
+     *            The layout manager to use
+     */
+    public void setLayoutManager(LayoutManager manager) {
+        layoutManager = manager;
+    }
+
+    /**
+     * Get the layout manager used by this layout
+     * 
+     */
+    public LayoutManager getLayoutManager() {
+        return layoutManager;
+    }
+
+    /**
+     * Deducts the caption position by examining the wrapping element.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     * 
+     * @param captionWrap
+     *            The wrapping element
+     * 
+     * @return The caption position
+     */
+    public CaptionPosition getCaptionPositionFromElement(Element captionWrap) {
+        RegExp captionPositionRegexp = RegExp.compile("v-caption-on-(\\S+)");
+
+        // Get caption position from the classname
+        MatchResult matcher = captionPositionRegexp.exec(captionWrap
+                .getClassName());
+        if (matcher == null || matcher.getGroupCount() < 2) {
+            return CaptionPosition.TOP;
+        }
+        String captionClass = matcher.getGroup(1);
+        CaptionPosition captionPosition = CaptionPosition.valueOf(
+                CaptionPosition.class, captionClass.toUpperCase());
+        return captionPosition;
+    }
+
+    /**
+     * Update the offset off the caption relative to the slot
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     * 
+     * @param caption
+     *            The caption element
+     */
+    public void updateCaptionOffset(Element caption) {
+
+        Element captionWrap = caption.getParentElement().cast();
+
+        Style captionWrapStyle = captionWrap.getStyle();
+        captionWrapStyle.clearPaddingTop();
+        captionWrapStyle.clearPaddingRight();
+        captionWrapStyle.clearPaddingBottom();
+        captionWrapStyle.clearPaddingLeft();
+
+        Style captionStyle = caption.getStyle();
+        captionStyle.clearMarginTop();
+        captionStyle.clearMarginRight();
+        captionStyle.clearMarginBottom();
+        captionStyle.clearMarginLeft();
+
+        // Get caption position from the classname
+        CaptionPosition captionPosition = getCaptionPositionFromElement(captionWrap);
+
+        if (captionPosition == CaptionPosition.LEFT
+                || captionPosition == CaptionPosition.RIGHT) {
+            int captionWidth;
+            if (layoutManager != null) {
+                captionWidth = layoutManager.getOuterWidth(caption)
+                        - layoutManager.getMarginWidth(caption);
+            } else {
+                captionWidth = caption.getOffsetWidth();
+            }
+            if (captionWidth > 0) {
+                if (captionPosition == CaptionPosition.LEFT) {
+                    captionWrapStyle.setPaddingLeft(captionWidth, Unit.PX);
+                    captionStyle.setMarginLeft(-captionWidth, Unit.PX);
+                } else {
+                    captionWrapStyle.setPaddingRight(captionWidth, Unit.PX);
+                    captionStyle.setMarginRight(-captionWidth, Unit.PX);
+                }
+            }
+        }
+        if (captionPosition == CaptionPosition.TOP
+                || captionPosition == CaptionPosition.BOTTOM) {
+            int captionHeight;
+            if (layoutManager != null) {
+                captionHeight = layoutManager.getOuterHeight(caption)
+                        - layoutManager.getMarginHeight(caption);
+            } else {
+                captionHeight = caption.getOffsetHeight();
+            }
+            if (captionHeight > 0) {
+                if (captionPosition == CaptionPosition.TOP) {
+                    captionWrapStyle.setPaddingTop(captionHeight, Unit.PX);
+                    captionStyle.setMarginTop(-captionHeight, Unit.PX);
+                } else {
+                    captionWrapStyle.setPaddingBottom(captionHeight, Unit.PX);
+                    captionStyle.setMarginBottom(-captionHeight, Unit.PX);
+                }
+            }
+        }
+    }
+
+    /**
+     * Set the margin of the layout
+     * 
+     * @param marginInfo
+     *            The margin information
+     */
+    public void setMargin(MarginInfo marginInfo) {
+        if (marginInfo != null) {
+            setStyleName("v-margin-top", marginInfo.hasTop());
+            setStyleName("v-margin-right", marginInfo.hasRight());
+            setStyleName("v-margin-bottom", marginInfo.hasBottom());
+            setStyleName("v-margin-left", marginInfo.hasLeft());
+        }
+    }
+
+    /**
+     * Turn on or off spacing in the layout
+     * 
+     * @param spacing
+     *            True if spacing should be used, false if not
+     */
+    public void setSpacing(boolean spacing) {
+        this.spacing = spacing;
+        for (Slot slot : widgetToSlot.values()) {
+            if (getWidgetIndex(slot) > 0) {
+                slot.setSpacing(spacing);
+            } else {
+                slot.setSpacing(false);
+            }
+        }
+    }
+
+    /**
+     * Triggers a recalculation of the expand width and heights
+     */
+    private void recalculateExpands() {
+        double total = 0;
+        for (Slot slot : widgetToSlot.values()) {
+            if (slot.getExpandRatio() > -1) {
+                total += slot.getExpandRatio();
+            } else {
+                if (vertical) {
+                    slot.getElement().getStyle().clearHeight();
+                } else {
+                    slot.getElement().getStyle().clearWidth();
+                }
+            }
+        }
+        for (Slot slot : widgetToSlot.values()) {
+            if (slot.getExpandRatio() > -1) {
+                if (vertical) {
+                    slot.setHeight((100 * (slot.getExpandRatio() / total))
+                            + "%");
+                    if (slot.hasRelativeHeight()) {
+                        Util.notifyParentOfSizeChange(this, true);
+                    }
+                } else {
+                    slot.setWidth((100 * (slot.getExpandRatio() / total)) + "%");
+                    if (slot.hasRelativeWidth()) {
+                        Util.notifyParentOfSizeChange(this, true);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes elements used to expand a slot.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public void clearExpand() {
+        if (expandWrapper != null) {
+            for (; expandWrapper.getChildCount() > 0;) {
+                Element el = expandWrapper.getChild(0).cast();
+                getElement().appendChild(el);
+                if (vertical) {
+                    el.getStyle().clearHeight();
+                    el.getStyle().clearMarginTop();
+                } else {
+                    el.getStyle().clearWidth();
+                    el.getStyle().clearMarginLeft();
+                }
+            }
+            expandWrapper.removeFromParent();
+            expandWrapper = null;
+        }
+    }
+
+    /**
+     * Adds elements used to expand a slot
+     */
+    public void updateExpand() {
+        boolean isExpanding = false;
+        for (Widget slot : getChildren()) {
+            if (((Slot) slot).getExpandRatio() > -1) {
+                isExpanding = true;
+            } else {
+                if (vertical) {
+                    slot.getElement().getStyle().clearHeight();
+                } else {
+                    slot.getElement().getStyle().clearWidth();
+                }
+            }
+            slot.getElement().getStyle().clearMarginLeft();
+            slot.getElement().getStyle().clearMarginTop();
+        }
+
+        if (isExpanding) {
+            if (expandWrapper == null) {
+                expandWrapper = DOM.createDiv();
+                expandWrapper.setClassName("v-expand");
+                for (; getElement().getChildCount() > 0;) {
+                    Node el = getElement().getChild(0);
+                    expandWrapper.appendChild(el);
+                }
+                getElement().appendChild(expandWrapper);
+            }
+
+            int totalSize = 0;
+            for (Widget w : getChildren()) {
+                Slot slot = (Slot) w;
+                if (slot.getExpandRatio() == -1) {
+
+                    if (layoutManager != null) {
+                        // TODO check caption position
+                        if (vertical) {
+                            int size = layoutManager.getOuterHeight(slot
+                                    .getWidget().getElement())
+                                    - layoutManager.getMarginHeight(slot
+                                            .getWidget().getElement());
+                            if (slot.hasCaption()) {
+                                size += layoutManager.getOuterHeight(slot
+                                        .getCaptionElement())
+                                        - layoutManager.getMarginHeight(slot
+                                                .getCaptionElement());
+                            }
+                            if (size > 0) {
+                                totalSize += size;
+                            }
+                        } else {
+                            int max = -1;
+                            max = layoutManager.getOuterWidth(slot.getWidget()
+                                    .getElement())
+                                    - layoutManager.getMarginWidth(slot
+                                            .getWidget().getElement());
+                            if (slot.hasCaption()) {
+                                int max2 = layoutManager.getOuterWidth(slot
+                                        .getCaptionElement())
+                                        - layoutManager.getMarginWidth(slot
+                                                .getCaptionElement());
+                                max = Math.max(max, max2);
+                            }
+                            if (max > 0) {
+                                totalSize += max;
+                            }
+                        }
+                    } else {
+                        totalSize += vertical ? slot.getOffsetHeight() : slot
+                                .getOffsetWidth();
+                    }
+                }
+                // TODO fails in Opera, always returns 0
+                int spacingSize = vertical ? slot.getVerticalSpacing() : slot
+                        .getHorizontalSpacing();
+                if (spacingSize > 0) {
+                    totalSize += spacingSize;
+                }
+            }
+
+            // When we set the margin to the first child, we don't need
+            // overflow:hidden in the layout root element, since the wrapper
+            // would otherwise be placed outside of the layout root element
+            // and block events on elements below it.
+            if (vertical) {
+                expandWrapper.getStyle().setPaddingTop(totalSize, Unit.PX);
+                expandWrapper.getFirstChildElement().getStyle()
+                        .setMarginTop(-totalSize, Unit.PX);
+            } else {
+                expandWrapper.getStyle().setPaddingLeft(totalSize, Unit.PX);
+                expandWrapper.getFirstChildElement().getStyle()
+                        .setMarginLeft(-totalSize, Unit.PX);
+            }
+
+            recalculateExpands();
+        }
+    }
+
+    /**
+     * Perform a recalculation of the layout height
+     */
+    public void recalculateLayoutHeight() {
+        // Only needed if a horizontal layout is undefined high, and contains
+        // relative height children or vertical alignments
+        if (vertical || definedHeight) {
+            return;
+        }
+
+        boolean hasRelativeHeightChildren = false;
+        boolean hasVAlign = false;
+
+        for (Widget slot : getChildren()) {
+            Widget widget = ((Slot) slot).getWidget();
+            String h = widget.getElement().getStyle().getHeight();
+            if (h != null && h.indexOf("%") > -1) {
+                hasRelativeHeightChildren = true;
+            }
+            AlignmentInfo a = ((Slot) slot).getAlignment();
+            if (a != null && (a.isVerticalCenter() || a.isBottom())) {
+                hasVAlign = true;
+            }
+        }
+
+        if (hasRelativeHeightChildren || hasVAlign) {
+            int newHeight;
+            if (layoutManager != null) {
+                newHeight = layoutManager.getOuterHeight(getElement())
+                        - layoutManager.getMarginHeight(getElement());
+            } else {
+                newHeight = getElement().getOffsetHeight();
+            }
+            getElement().getStyle().setHeight(newHeight, Unit.PX);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setHeight(String height) {
+        super.setHeight(height);
+        definedHeight = (height != null && !"".equals(height));
+    }
+
+    /**
+     * Sets the slots style names. The style names will be prefixed with the
+     * v-slot prefix.
+     * 
+     * @param stylenames
+     *            The style names of the slot.
+     */
+    public void setSlotStyleNames(Widget widget, String... stylenames) {
+        Slot slot = getSlot(widget);
+        if (slot == null) {
+            throw new IllegalArgumentException(
+                    "A slot for the widget could not be found. Has the widget been added to the layout?");
+        }
+        slot.setStyleNames(stylenames);
+    }
+
+}