From: John Ahlroos Date: Wed, 5 Sep 2012 14:58:12 +0000 (+0300) Subject: Refactored and cleaned up the newly add ordered layouts X-Git-Tag: 7.0.0.beta1~187 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2fd07a0bd26754d72e77810bc280c6abac740f29;p=vaadin-framework.git Refactored and cleaned up the newly add ordered layouts --- diff --git a/client/src/com/vaadin/client/ComponentLocator.java b/client/src/com/vaadin/client/ComponentLocator.java index 42b54ca49c..ce3ac43812 100644 --- a/client/src/com/vaadin/client/ComponentLocator.java +++ b/client/src/com/vaadin/client/ComponentLocator.java @@ -27,8 +27,7 @@ import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ui.SubPartAware; import com.vaadin.client.ui.gridlayout.VGridLayout; -import com.vaadin.client.ui.orderedlayout.VBoxLayout; -import com.vaadin.client.ui.orderedlayout.VMeasuringOrderedLayout; +import com.vaadin.client.ui.orderedlayout.VOrderedLayout; import com.vaadin.client.ui.tabsheet.VTabsheetPanel; import com.vaadin.client.ui.ui.VUI; import com.vaadin.client.ui.window.VWindow; @@ -270,7 +269,7 @@ public class ComponentLocator { String childIndexString = part.substring("domChild[".length(), part.length() - 1); - if (Util.findWidget(baseElement, null) instanceof VBoxLayout) { + if (Util.findWidget(baseElement, null) instanceof VOrderedLayout) { if (element.hasChildNodes()) { Element e = element.getFirstChildElement().cast(); String cn = e.getClassName(); @@ -508,14 +507,9 @@ public class ComponentLocator { continue; } - if ("VVerticalLayout".equals(widgetClassName) - || "VHorizontalLayout".equals(widgetClassName)) { - widgetClassName = "VBoxLayout"; - } - - if (w instanceof VBoxLayout + if (w instanceof VOrderedLayout && "ChildComponentContainer".equals(widgetClassName)) { - widgetClassName = "VBoxLayout$Slot"; + widgetClassName = "VOrderedLayout$Slot"; } if (w instanceof VTabsheetPanel && widgetPosition != 0) { @@ -532,7 +526,7 @@ public class ComponentLocator { * (which would originally have found the widget inside the * ChildComponentContainer) */ - if ((w instanceof VMeasuringOrderedLayout || w instanceof VGridLayout) + if ((w instanceof VGridLayout) && "ChildComponentContainer".equals(widgetClassName) && i + 1 < parts.length) { @@ -542,11 +536,6 @@ public class ComponentLocator { String[] nextSplit = nextPart.split("\\[", 2); String nextWidgetClassName = nextSplit[0]; - if ("VVerticalLayout".equals(nextWidgetClassName) - || "VHorizontalLayout".equals(nextWidgetClassName)) { - nextWidgetClassName = "VBoxLayout"; - } - // Find the n:th child and count the number of children with // the same type before it int nextIndex = 0; @@ -616,8 +605,8 @@ public class ComponentLocator { } widgetPosition--; - } else if (w instanceof VBoxLayout - && "VBoxLayout$Slot".equals(simpleName2)) { + } else if (w instanceof VOrderedLayout + && "VOrderedLayout$Slot".equals(simpleName2)) { child = ((SimplePanel) child).getWidget(); simpleName2 = Util.getSimpleName(child); if (widgetClassName.equals(simpleName2)) { diff --git a/client/src/com/vaadin/client/ui/orderedlayout/AbstractBoxLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/AbstractBoxLayoutConnector.java deleted file mode 100644 index 6402d0696f..0000000000 --- a/client/src/com/vaadin/client/ui/orderedlayout/AbstractBoxLayoutConnector.java +++ /dev/null @@ -1,607 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.client.ui.orderedlayout; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.user.client.Element; -import com.vaadin.client.ComponentConnector; -import com.vaadin.client.ConnectorHierarchyChangeEvent; -import com.vaadin.client.Util; -import com.vaadin.client.communication.RpcProxy; -import com.vaadin.client.communication.StateChangeEvent; -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.layout.ElementResizeEvent; -import com.vaadin.client.ui.layout.ElementResizeListener; -import com.vaadin.client.ui.orderedlayout.VBoxLayout.CaptionPosition; -import com.vaadin.client.ui.orderedlayout.VBoxLayout.Slot; -import com.vaadin.shared.AbstractFieldState; -import com.vaadin.shared.ComponentConstants; -import com.vaadin.shared.communication.URLReference; -import com.vaadin.shared.ui.AlignmentInfo; -import com.vaadin.shared.ui.LayoutClickRpc; -import com.vaadin.shared.ui.MarginInfo; -import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc; -import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState; - -public abstract class AbstractBoxLayoutConnector extends - AbstractLayoutConnector /* implements PostLayoutListener */{ - - AbstractOrderedLayoutServerRpc rpc; - - private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler( - this) { - - @Override - protected ComponentConnector getChildComponent(Element element) { - return Util.getConnectorForElement(getConnection(), getWidget(), - element); - } - - @Override - protected LayoutClickRpc getLayoutClickRPC() { - return rpc; - }; - - }; - - @Override - public void init() { - super.init(); - rpc = RpcProxy.create(AbstractOrderedLayoutServerRpc.class, this); - getWidget().setLayoutManager(getLayoutManager()); - } - - @Override - public AbstractOrderedLayoutState getState() { - return (AbstractOrderedLayoutState) super.getState(); - } - - @Override - public VBoxLayout getWidget() { - return (VBoxLayout) super.getWidget(); - } - - /** - * For bookkeeping. Used to determine if extra calculations are needed for - * horizontal layout. - */ - private HashSet hasVerticalAlignment = new HashSet(); - - /** - * For bookkeeping. Used to determine if extra calculations are needed for - * horizontal layout. - */ - private HashSet hasRelativeHeight = new HashSet(); - - /** - * For bookkeeping. Used to determine if extra calculations are needed for - * horizontal layout. - */ - private HashSet hasExpandRatio = new HashSet(); - - /** - * For bookkeeping. Used in extra calculations for horizontal layout. - */ - private HashSet needsMeasure = new HashSet(); - - /** - * For bookkeeping. Used in extra calculations for horizontal layout. - */ - // private HashMap childElementHeight = new - // HashMap(); - - /** - * For bookkeeping. Used in extra calculations for horizontal layout. - */ - private HashMap childCaptionElementHeight = new HashMap(); - - @Override - public void updateCaption(ComponentConnector child) { - Slot slot = getWidget().getSlot(child); - - - String caption = child.getState().caption; - URLReference iconUrl = child.getState().resources - .get(ComponentConstants.ICON_RESOURCE); - String iconUrlString = iconUrl != null ? iconUrl.toString() : null; - List styles = child.getState().styles; - String error = child.getState().errorMessage; - boolean showError = error != null; - if (child.getState() instanceof AbstractFieldState) { - AbstractFieldState abstractFieldState = (AbstractFieldState) child - .getState(); - showError = showError && !abstractFieldState.hideErrors; - } - boolean required = false; - if (child instanceof AbstractFieldConnector) { - required = ((AbstractFieldConnector) child).isRequired(); - } - boolean enabled = child.getState().enabled; - - slot.setCaption(caption, iconUrlString, styles, error, showError, - required, - enabled); - - slot.setRelativeWidth(child.isRelativeWidth()); - slot.setRelativeHeight(child.isRelativeHeight()); - - if (slot.hasCaption()) { - CaptionPosition pos = slot.getCaptionPosition(); - getLayoutManager().addElementResizeListener( - slot.getCaptionElement(), slotCaptionResizeListener); - if (child.isRelativeHeight() - && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) { - getWidget().updateCaptionOffset(slot.getCaptionElement()); - } else if (child.isRelativeWidth() - && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) { - getWidget().updateCaptionOffset(slot.getCaptionElement()); - } - } else { - childCaptionElementHeight.remove(child.getWidget().getElement()); - } - - updateLayoutHeight(); - - if (needsExpand()) { - updateExpand(); - } - } - - @Override - public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) { - super.onConnectorHierarchyChange(event); - - List previousChildren = event.getOldChildren(); - int currentIndex = 0; - VBoxLayout layout = getWidget(); - - for (ComponentConnector child : getChildComponents()) { - Slot slot = layout.getSlot(child); - if (slot.getParent() != layout) { - child.addStateChangeHandler(childStateChangeHandler); - } - layout.addOrMoveSlot(slot, currentIndex++); - } - - for (ComponentConnector child : previousChildren) { - if (child.getParent() != this) { - Slot slot = layout.getSlot(child); - hasVerticalAlignment.remove(child); - hasRelativeHeight.remove(child); - hasExpandRatio.remove(child); - needsMeasure.remove(child.getWidget().getElement()); - // childElementHeight.remove(child.getWidget().getElement()); - childCaptionElementHeight - .remove(child.getWidget().getElement()); - getLayoutManager().removeElementResizeListener( - child.getWidget().getElement(), - childComponentResizeListener); - if (slot.hasCaption()) { - getLayoutManager() - .removeElementResizeListener( - slot.getCaptionElement(), - slotCaptionResizeListener); - } - if (slot.getSpacingElement() != null) { - getLayoutManager().removeElementResizeListener( - slot.getSpacingElement(), spacingResizeListener); - } - child.removeStateChangeHandler(childStateChangeHandler); - layout.removeSlot(child.getWidget()); - } - } - - // If some component is added/removed, we need to recalculate the expand - if (needsExpand()) { - updateExpand(); - } else { - getWidget().clearExpand(); - } - - } - - @Override - public void onStateChanged(StateChangeEvent stateChangeEvent) { - super.onStateChanged(stateChangeEvent); - - clickEventHandler.handleEventHandlerRegistration(); - getWidget().setMargin(new MarginInfo(getState().marginsBitmask)); - getWidget().setSpacing(getState().spacing); - - hasExpandRatio.clear(); - hasVerticalAlignment.clear(); - hasRelativeHeight.clear(); - needsMeasure.clear(); - - boolean equalExpandRatio = getWidget().vertical ? !isUndefinedHeight() - : !isUndefinedWidth(); - for (ComponentConnector child : getChildComponents()) { - double expandRatio = getState().childData.get(child).expandRatio; - if (expandRatio > 0) { - equalExpandRatio = false; - break; - } - } - - for (ComponentConnector child : getChildComponents()) { - Slot slot = getWidget().getSlot(child); - - AlignmentInfo alignment = new AlignmentInfo( - getState().childData.get(child).alignmentBitmask); - slot.setAlignment(alignment); - - double expandRatio = getState().childData.get(child).expandRatio; - - if (equalExpandRatio) { - expandRatio = 1; - } else if (expandRatio == 0) { - expandRatio = -1; - } - slot.setExpandRatio(expandRatio); - - // Bookkeeping to identify special cases that need extra - // calculations - if (alignment.isVerticalCenter() || alignment.isBottom()) { - hasVerticalAlignment.add(child); - } - - if (expandRatio > 0) { - hasExpandRatio.add(child); - } - - // if (child.getState().isRelativeHeight()) { - // hasRelativeHeight.add(child); - // } else { - // needsMeasure.add(child.getWidget().getElement()); - // } - } - - updateAllSlotListeners(); - - updateLayoutHeight(); - } - - StateChangeHandler childStateChangeHandler = new StateChangeHandler() { - @Override - public void onStateChanged(StateChangeEvent stateChangeEvent) { - - ComponentConnector child = (ComponentConnector) stateChangeEvent - .getConnector(); - - // We need to update the slot size if the component size is changed - // to relative - Slot slot = getWidget().getSlot(child); - slot.setRelativeWidth(child.isRelativeWidth()); - slot.setRelativeHeight(child.isRelativeHeight()); - - // For relative sized widgets, we need to set the caption offset - // if (slot.hasCaption()) { - // CaptionPosition pos = slot.getCaptionPosition(); - // if (child.isRelativeHeight() - // && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) - // { - // getWidget().updateCaptionOffset(slot.getCaptionElement()); - // } else if (child.isRelativeWidth() - // && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) - // { - // getWidget().updateCaptionOffset(slot.getCaptionElement()); - // } - // } - - updateSlotListeners(child); - // updateAllSlotListeners(); - - updateLayoutHeight(); - } - }; - - private boolean needsFixedHeight() { - if (!getWidget().vertical - && isUndefinedHeight() - && (hasRelativeHeight.size() > 0 || (hasVerticalAlignment - .size() > 0 && hasVerticalAlignment.size() < getChildren() - .size()))) { - return true; - } - return false; - } - - private boolean needsExpand() { - boolean canApplyExpand = (getWidget().vertical && !isUndefinedHeight()) - || (!getWidget().vertical && !isUndefinedWidth()); - return hasExpandRatio.size() > 0 && canApplyExpand; - } - - private void updateAllSlotListeners() { - for (ComponentConnector child : getChildComponents()) { - updateSlotListeners(child); - } - // if (needsFixedHeight()) { - // getWidget().clearHeight(); - // setLayoutHeightListener(true); - // getLayoutManager().setNeedsMeasure(AbstractBoxLayoutConnector.this); - // } else { - // setLayoutHeightListener(false); - // } - } - - /** - * Add/remove necessary ElementResizeListeners for one slot. This should be - * called after each update to the slot's or it's widget. - */ - private void updateSlotListeners(ComponentConnector child) { - Slot slot = getWidget().getSlot(child); - - // Clear all possible listeners first - dontListen(slot.getWidget().getElement(), childComponentResizeListener); - if (slot.hasCaption()) { - dontListen(slot.getCaptionElement(), slotCaptionResizeListener); - } - if (slot.hasSpacing()) { - dontListen(slot.getSpacingElement(), spacingResizeListener); - } - - // Add all necessary listeners - if (needsFixedHeight()) { - listen(slot.getWidget().getElement(), childComponentResizeListener); - if (slot.hasCaption()) { - listen(slot.getCaptionElement(), slotCaptionResizeListener); - } - } else if ((child.isRelativeHeight() || child.isRelativeWidth()) - && slot.hasCaption()) { - // If the slot has caption, we need to listen for it's size changes - // in order to update the padding/margin offset for relative sized - // components - listen(slot.getCaptionElement(), slotCaptionResizeListener); - } - - if (needsExpand()) { - listen(slot.getWidget().getElement(), childComponentResizeListener); - if (slot.hasSpacing()) { - listen(slot.getSpacingElement(), spacingResizeListener); - } - } - - if (child.isRelativeHeight()) { - hasRelativeHeight.add(child); - needsMeasure.remove(child.getWidget().getElement()); - } else { - hasRelativeHeight.remove(child); - needsMeasure.add(child.getWidget().getElement()); - } - - } - - // public void postLayout() { - // if (needsFixedHeight()) { - // // Re-measure all elements that are available - // for (Element el : needsMeasure) { - // childElementHeight.put(el, getLayoutManager() - // .getOuterHeight(el)); - // - // Element captionElement = el.getParentElement() - // .getFirstChildElement().cast(); - // if (captionElement.getClassName().contains("v-caption")) { - // childCaptionElementHeight.put(el, getLayoutManager() - // .getOuterHeight(captionElement)); - // } - // } - // // System.out.println(" ### Child sizes: " - // // + childElementHeight.values().toString()); - // // System.out.println(" ### Caption sizes: " - // // + childCaptionElementHeight.values().toString()); - // - // int height = getMaxHeight() - // + getLayoutManager().getBorderHeight( - // getWidget().getElement()) - // + getLayoutManager().getPaddingHeight( - // getWidget().getElement()); - // getWidget().getElement().getStyle().setHeight(height, Unit.PX); - // } - // } - - // private ElementResizeListener layoutResizeListener = new - // ElementResizeListener() { - // public void onElementResize(ElementResizeEvent e) { - // updateLayoutHeight(); - // if (needsExpand() && (isUndefinedHeight() || isUndefinedWidth())) { - // updateExpand(); - // } - // } - // }; - - private ElementResizeListener slotCaptionResizeListener = new ElementResizeListener() { - @Override - public void onElementResize(ElementResizeEvent e) { - - // Get all needed element references - Element captionElement = (Element) e.getElement().cast(); - - // Caption position determines if the widget element is the first or - // last child inside the caption wrap - CaptionPosition pos = getWidget().getCaptionPositionFromElement( - (Element) captionElement.getParentElement().cast()); - - // The default is the last child - Element widgetElement = captionElement.getParentElement() - .getLastChild().cast(); - - // ...but if caption position is bottom or right, the widget is the - // first child - if (pos == CaptionPosition.BOTTOM || pos == CaptionPosition.RIGHT) { - widgetElement = captionElement.getParentElement() - .getFirstChildElement().cast(); - } - - if (captionElement == widgetElement) { - // Caption element already detached - dontListen(captionElement, slotCaptionResizeListener); - childCaptionElementHeight.remove(widgetElement); - return; - } - - String widgetWidth = widgetElement.getStyle().getWidth(); - String widgetHeight = widgetElement.getStyle().getHeight(); - - if (widgetHeight.endsWith("%") - && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) { - getWidget().updateCaptionOffset(captionElement); - } else if (widgetWidth.endsWith("%") - && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) { - getWidget().updateCaptionOffset(captionElement); - } - - int h = getLayoutManager().getOuterHeight(captionElement) - - getLayoutManager().getMarginHeight(captionElement); - childCaptionElementHeight.put(widgetElement, h); - - // if (needsFixedHeight()) { - // getWidget().clearHeight(); - // getLayoutManager().setNeedsMeasure( - // AbstractBoxLayoutConnector.this); - // } - - updateLayoutHeight(); - - if (needsExpand()) { - updateExpand(); - } - } - }; - - private ElementResizeListener childComponentResizeListener = new ElementResizeListener() { - @Override - public void onElementResize(ElementResizeEvent e) { - // int h = getLayoutManager().getOuterHeight(e.getElement()); - // childElementHeight.put((Element) e.getElement().cast(), h); - updateLayoutHeight(); - - if (needsExpand()) { - updateExpand(); - } - } - }; - - private ElementResizeListener spacingResizeListener = new ElementResizeListener() { - @Override - public void onElementResize(ElementResizeEvent e) { - if (needsExpand()) { - updateExpand(); - } - } - }; - - private void updateLayoutHeight() { - if (needsFixedHeight()) { - int h = getMaxHeight(); - h += getLayoutManager().getBorderHeight(getWidget().getElement()) - + getLayoutManager().getPaddingHeight( - getWidget().getElement()); - getWidget().getElement().getStyle().setHeight(h, Unit.PX); - getLayoutManager().setNeedsMeasure(this); - } - } - - private void updateExpand() { - // System.out.println("All sizes: " - // + childElementHeight.values().toString() + " - Caption sizes: " - // + childCaptionElementHeight.values().toString()); - getWidget().updateExpand(); - } - - private int getMaxHeight() { - int highestNonRelative = -1; - int highestRelative = -1; - - for (ComponentConnector child : getChildComponents()) { - // TODO would be more efficient to measure the slot element if both - // caption and child widget elements need to be measured. Keeping - // track of what to measure is the most difficult part of this - // layout. - Element el = child.getWidget().getElement(); - CaptionPosition pos = getWidget().getCaptionPositionFromElement( - (Element) el.getParentElement().cast()); - if (needsMeasure.contains(el)) { - int h = getLayoutManager().getOuterHeight(el); - String sHeight = el.getStyle().getHeight(); - // Only add the caption size to the height of the slot if - // coption position is top or bottom - if (childCaptionElementHeight.containsKey(el) - && (sHeight == null || !sHeight.endsWith("%")) - && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) { - h += childCaptionElementHeight.get(el); - } - if (h > highestNonRelative) { - highestNonRelative = h; - } - } else { - int h = getLayoutManager().getOuterHeight(el); - if (childCaptionElementHeight.containsKey(el) - && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) { - h += childCaptionElementHeight.get(el); - } - if (h > highestRelative) { - highestRelative = h; - } - } - } - return highestNonRelative > -1 ? highestNonRelative : highestRelative; - } - - @Override - public void onUnregister() { - // Cleanup all ElementResizeListeners - - // dontListen(getWidget().getElement(), layoutResizeListener); - - for (ComponentConnector child : getChildComponents()) { - Slot slot = getWidget().getSlot(child); - if (slot.hasCaption()) { - dontListen(slot.getCaptionElement(), slotCaptionResizeListener); - } - - if (slot.getSpacingElement() != null) { - dontListen(slot.getSpacingElement(), spacingResizeListener); - } - - dontListen(slot.getWidget().getElement(), - childComponentResizeListener); - } - - super.onUnregister(); - } - - // private void setLayoutHeightListener(boolean add) { - // if (add) { - // listen(getWidget().getElement(), layoutResizeListener); - // } else { - // dontListen(getWidget().getElement(), layoutResizeListener); - // if (!needsExpand()) { - // System.out.println("Clearing element sizes"); - // childElementHeight.clear(); - // childCaptionElementHeight.clear(); - // } - // } - // } - - /* - * Convenience methods - */ - - private void listen(Element el, ElementResizeListener listener) { - getLayoutManager().addElementResizeListener(el, listener); - } - - private void dontListen(Element el, ElementResizeListener listener) { - getLayoutManager().removeElementResizeListener(el, listener); - } - -} diff --git a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java index 5b1462b33c..8807fcfbc0 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java @@ -15,35 +15,46 @@ */ package com.vaadin.client.ui.orderedlayout; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; -import com.vaadin.client.DirectionalManagedLayout; -import com.vaadin.client.LayoutManager; import com.vaadin.client.Util; -import com.vaadin.client.VCaption; import com.vaadin.client.communication.RpcProxy; import com.vaadin.client.communication.StateChangeEvent; +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.layout.ComponentConnectorLayoutSlot; -import com.vaadin.client.ui.layout.VLayoutSlot; +import com.vaadin.client.ui.layout.ElementResizeEvent; +import com.vaadin.client.ui.layout.ElementResizeListener; +import com.vaadin.client.ui.orderedlayout.VOrderedLayout.CaptionPosition; +import com.vaadin.client.ui.orderedlayout.VOrderedLayout.Slot; +import com.vaadin.shared.AbstractFieldState; +import com.vaadin.shared.ComponentConstants; +import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.AlignmentInfo; import com.vaadin.shared.ui.LayoutClickRpc; import com.vaadin.shared.ui.MarginInfo; import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc; import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState; +/** + * Base class for vertical and horizontal ordered layouts + */ public abstract class AbstractOrderedLayoutConnector extends - AbstractLayoutConnector implements DirectionalManagedLayout { + AbstractLayoutConnector { AbstractOrderedLayoutServerRpc rpc; + /* + * Handlers & Listeners + */ + private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler( this) { @@ -57,275 +68,522 @@ public abstract class AbstractOrderedLayoutConnector extends protected LayoutClickRpc getLayoutClickRPC() { return rpc; }; + }; + + private StateChangeHandler childStateChangeHandler = new StateChangeHandler() { + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + + ComponentConnector child = (ComponentConnector) stateChangeEvent + .getConnector(); + + // We need to update the slot size if the component size is changed + // to relative + Slot slot = getWidget().getSlot(child.getWidget()); + slot.setRelativeWidth(child.isRelativeWidth()); + slot.setRelativeHeight(child.isRelativeHeight()); + + updateSlotListeners(child); + // updateAllSlotListeners(); + + updateLayoutHeight(); + } + }; + + private ElementResizeListener slotCaptionResizeListener = new ElementResizeListener() { + @Override + public void onElementResize(ElementResizeEvent e) { + + // Get all needed element references + Element captionElement = (Element) e.getElement().cast(); + + // Caption position determines if the widget element is the first or + // last child inside the caption wrap + CaptionPosition pos = getWidget().getCaptionPositionFromElement( + (Element) captionElement.getParentElement().cast()); + + // The default is the last child + Element widgetElement = captionElement.getParentElement() + .getLastChild().cast(); + + // ...but if caption position is bottom or right, the widget is the + // first child + if (pos == CaptionPosition.BOTTOM || pos == CaptionPosition.RIGHT) { + widgetElement = captionElement.getParentElement() + .getFirstChildElement().cast(); + } + + if (captionElement == widgetElement) { + // Caption element already detached + rmeoveResizeListener(captionElement, slotCaptionResizeListener); + childCaptionElementHeight.remove(widgetElement); + return; + } + String widgetWidth = widgetElement.getStyle().getWidth(); + String widgetHeight = widgetElement.getStyle().getHeight(); + + if (widgetHeight.endsWith("%") + && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) { + getWidget().updateCaptionOffset(captionElement); + } else if (widgetWidth.endsWith("%") + && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) { + getWidget().updateCaptionOffset(captionElement); + } + + int h = getLayoutManager().getOuterHeight(captionElement) + - getLayoutManager().getMarginHeight(captionElement); + childCaptionElementHeight.put(widgetElement, h); + + updateLayoutHeight(); + + if (needsExpand()) { + getWidget().updateExpand(); + } + } + }; + + private ElementResizeListener childComponentResizeListener = new ElementResizeListener() { + @Override + public void onElementResize(ElementResizeEvent e) { + updateLayoutHeight(); + if (needsExpand()) { + getWidget().updateExpand(); + } + } }; + private ElementResizeListener spacingResizeListener = new ElementResizeListener() { + @Override + public void onElementResize(ElementResizeEvent e) { + if (needsExpand()) { + getWidget().updateExpand(); + } + } + }; + + /* + * (non-Javadoc) + * + * @see com.vaadin.client.ui.AbstractComponentConnector#init() + */ @Override public void init() { super.init(); rpc = RpcProxy.create(AbstractOrderedLayoutServerRpc.class, this); - getLayoutManager().registerDependency(this, - getWidget().spacingMeasureElement); + getWidget().setLayoutManager(getLayoutManager()); } + /* + * (non-Javadoc) + * + * @see com.vaadin.client.ui.AbstractLayoutConnector#getState() + */ @Override - public void onUnregister() { - LayoutManager lm = getLayoutManager(); - - VMeasuringOrderedLayout layout = getWidget(); - lm.unregisterDependency(this, layout.spacingMeasureElement); + public AbstractOrderedLayoutState getState() { + return (AbstractOrderedLayoutState) super.getState(); + } - // Unregister child caption listeners - for (ComponentConnector child : getChildComponents()) { - VLayoutSlot slot = layout.getSlotForChild(child.getWidget()); - slot.setCaption(null); - } + /* + * (non-Javadoc) + * + * @see com.vaadin.client.ui.AbstractComponentConnector#getWidget() + */ + @Override + public VOrderedLayout getWidget() { + return (VOrderedLayout) super.getWidget(); } + /** + * For bookkeeping. Used to determine if extra calculations are needed for + * horizontal layout. + */ + private HashSet hasVerticalAlignment = new HashSet(); + + /** + * For bookkeeping. Used to determine if extra calculations are needed for + * horizontal layout. + */ + private HashSet hasRelativeHeight = new HashSet(); + + /** + * For bookkeeping. Used to determine if extra calculations are needed for + * horizontal layout. + */ + private HashSet hasExpandRatio = new HashSet(); + + /** + * For bookkeeping. Used in extra calculations for horizontal layout. + */ + private HashSet needsMeasure = new HashSet(); + + /** + * For bookkeeping. Used in extra calculations for horizontal layout. + */ + private HashMap childCaptionElementHeight = new HashMap(); + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.ComponentContainerConnector#updateCaption(com.vaadin + * .client.ComponentConnector) + */ @Override - public AbstractOrderedLayoutState getState() { - return (AbstractOrderedLayoutState) super.getState(); + public void updateCaption(ComponentConnector child) { + Slot slot = getWidget().getSlot(child.getWidget()); + + String caption = child.getState().caption; + URLReference iconUrl = child.getState().resources + .get(ComponentConstants.ICON_RESOURCE); + String iconUrlString = iconUrl != null ? iconUrl.toString() : null; + List styles = child.getState().styles; + String error = child.getState().errorMessage; + boolean showError = error != null; + if (child.getState() instanceof AbstractFieldState) { + AbstractFieldState abstractFieldState = (AbstractFieldState) child + .getState(); + showError = showError && !abstractFieldState.hideErrors; + } + boolean required = false; + if (child instanceof AbstractFieldConnector) { + required = ((AbstractFieldConnector) child).isRequired(); + } + boolean enabled = child.getState().enabled; + + slot.setCaption(caption, iconUrlString, styles, error, showError, + required, + enabled); + + slot.setRelativeWidth(child.isRelativeWidth()); + slot.setRelativeHeight(child.isRelativeHeight()); + + if (slot.hasCaption()) { + CaptionPosition pos = slot.getCaptionPosition(); + getLayoutManager().addElementResizeListener( + slot.getCaptionElement(), slotCaptionResizeListener); + if (child.isRelativeHeight() + && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) { + getWidget().updateCaptionOffset(slot.getCaptionElement()); + } else if (child.isRelativeWidth() + && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) { + getWidget().updateCaptionOffset(slot.getCaptionElement()); + } + } else { + childCaptionElementHeight.remove(child.getWidget().getElement()); + } + + updateLayoutHeight(); + + if (needsExpand()) { + getWidget().updateExpand(); + } } + /* + * (non-Javadoc) + * + * @see com.vaadin.client.ui.AbstractComponentContainerConnector# + * onConnectorHierarchyChange + * (com.vaadin.client.ConnectorHierarchyChangeEvent) + */ @Override - public void updateCaption(ComponentConnector component) { - VMeasuringOrderedLayout layout = getWidget(); - if (VCaption.isNeeded(component.getState())) { - VLayoutSlot layoutSlot = layout.getSlotForChild(component - .getWidget()); - VCaption caption = layoutSlot.getCaption(); - if (caption == null) { - caption = new VCaption(component, getConnection()); - - Widget widget = component.getWidget(); - - layout.setCaption(widget, caption); + public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) { + super.onConnectorHierarchyChange(event); + + List previousChildren = event.getOldChildren(); + int currentIndex = 0; + VOrderedLayout layout = getWidget(); + + for (ComponentConnector child : getChildComponents()) { + Slot slot = layout.getSlot(child.getWidget()); + if (slot.getParent() != layout) { + child.addStateChangeHandler(childStateChangeHandler); + } + layout.addOrMoveSlot(slot, currentIndex++); + } + + for (ComponentConnector child : previousChildren) { + if (child.getParent() != this) { + Slot slot = layout.getSlot(child.getWidget()); + hasVerticalAlignment.remove(child); + hasRelativeHeight.remove(child); + hasExpandRatio.remove(child); + needsMeasure.remove(child.getWidget().getElement()); + childCaptionElementHeight + .remove(child.getWidget().getElement()); + getLayoutManager().removeElementResizeListener( + child.getWidget().getElement(), + childComponentResizeListener); + if (slot.hasCaption()) { + getLayoutManager() + .removeElementResizeListener( + slot.getCaptionElement(), + slotCaptionResizeListener); + } + if (slot.getSpacingElement() != null) { + getLayoutManager().removeElementResizeListener( + slot.getSpacingElement(), spacingResizeListener); + } + child.removeStateChangeHandler(childStateChangeHandler); + layout.removeWidget(child.getWidget()); } - caption.updateCaption(); + } + + // If some component is added/removed, we need to recalculate the expand + if (needsExpand()) { + getWidget().updateExpand(); } else { - layout.setCaption(component.getWidget(), null); - getLayoutManager().setNeedsLayout(this); + getWidget().clearExpand(); } - } - @Override - public VMeasuringOrderedLayout getWidget() { - return (VMeasuringOrderedLayout) super.getWidget(); } + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.ui.AbstractComponentConnector#onStateChanged(com.vaadin + * .client.communication.StateChangeEvent) + */ @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); clickEventHandler.handleEventHandlerRegistration(); + getWidget().setMargin(new MarginInfo(getState().marginsBitmask)); + getWidget().setSpacing(getState().spacing); - VMeasuringOrderedLayout layout = getWidget(); + hasExpandRatio.clear(); + hasVerticalAlignment.clear(); + hasRelativeHeight.clear(); + needsMeasure.clear(); + boolean equalExpandRatio = getWidget().vertical ? !isUndefinedHeight() + : !isUndefinedWidth(); for (ComponentConnector child : getChildComponents()) { - VLayoutSlot slot = layout.getSlotForChild(child.getWidget()); + double expandRatio = getState().childData.get(child).expandRatio; + if (expandRatio > 0) { + equalExpandRatio = false; + break; + } + } + + for (ComponentConnector child : getChildComponents()) { + Slot slot = getWidget().getSlot(child.getWidget()); AlignmentInfo alignment = new AlignmentInfo( getState().childData.get(child).alignmentBitmask); slot.setAlignment(alignment); double expandRatio = getState().childData.get(child).expandRatio; - slot.setExpandRatio(expandRatio); - } - layout.updateMarginStyleNames(new MarginInfo(getState().marginsBitmask)); - layout.updateSpacingStyleName(getState().spacing); + if (equalExpandRatio) { + expandRatio = 1; + } else if (expandRatio == 0) { + expandRatio = -1; + } + slot.setExpandRatio(expandRatio); - getLayoutManager().setNeedsLayout(this); - } + // Bookkeeping to identify special cases that need extra + // calculations + if (alignment.isVerticalCenter() || alignment.isBottom()) { + hasVerticalAlignment.add(child); + } - private int getSizeForInnerSize(int size, boolean isVertical) { - LayoutManager layoutManager = getLayoutManager(); - Element element = getWidget().getElement(); - if (isVertical) { - return size + layoutManager.getBorderHeight(element) - + layoutManager.getPaddingHeight(element); - } else { - return size + layoutManager.getBorderWidth(element) - + layoutManager.getPaddingWidth(element); + if (expandRatio > 0) { + hasExpandRatio.add(child); + } } - } - - private static String getSizeProperty(boolean isVertical) { - return isVertical ? "height" : "width"; - } - private boolean isUndefinedInDirection(boolean isVertical) { - if (isVertical) { - return isUndefinedHeight(); - } else { - return isUndefinedWidth(); - } - } + updateAllSlotListeners(); - private int getInnerSizeInDirection(boolean isVertical) { - if (isVertical) { - return getLayoutManager().getInnerHeight(getWidget().getElement()); - } else { - return getLayoutManager().getInnerWidth(getWidget().getElement()); - } + updateLayoutHeight(); } - private void layoutPrimaryDirection() { - VMeasuringOrderedLayout layout = getWidget(); - boolean isVertical = layout.isVertical; - boolean isUndefined = isUndefinedInDirection(isVertical); - int startPadding = getStartPadding(isVertical); - int endPadding = getEndPadding(isVertical); - int spacingSize = getSpacingInDirection(isVertical); - int allocatedSize; - - if (isUndefined) { - allocatedSize = -1; - } else { - allocatedSize = getInnerSizeInDirection(isVertical); - } - - allocatedSize = layout.layoutPrimaryDirection(spacingSize, - allocatedSize, startPadding, endPadding); - - Style ownStyle = getWidget().getElement().getStyle(); - if (isUndefined) { - int outerSize = getSizeForInnerSize(allocatedSize, isVertical); - ownStyle.setPropertyPx(getSizeProperty(isVertical), outerSize); - reportUndefinedSize(outerSize, isVertical); - } else { - ownStyle.setProperty(getSizeProperty(isVertical), - getDefinedSize(isVertical)); + /** + * Does the layout need a fixed height? + */ + private boolean needsFixedHeight() { + if (!getWidget().vertical + && isUndefinedHeight() + && (hasRelativeHeight.size() > 0 || (hasVerticalAlignment + .size() > 0 && hasVerticalAlignment.size() < getChildren() + .size()))) { + return true; } + return false; } - private void reportUndefinedSize(int outerSize, boolean isVertical) { - if (isVertical) { - getLayoutManager().reportOuterHeight(this, outerSize); - } else { - getLayoutManager().reportOuterWidth(this, outerSize); - } + /** + * Does the layout need to expand? + */ + private boolean needsExpand() { + boolean canApplyExpand = (getWidget().vertical && !isUndefinedHeight()) + || (!getWidget().vertical && !isUndefinedWidth()); + return hasExpandRatio.size() > 0 && canApplyExpand; } - private int getSpacingInDirection(boolean isVertical) { - if (isVertical) { - return getLayoutManager().getOuterHeight( - getWidget().spacingMeasureElement); - } else { - return getLayoutManager().getOuterWidth( - getWidget().spacingMeasureElement); + /** + * Add slot listeners + */ + private void updateAllSlotListeners() { + for (ComponentConnector child : getChildComponents()) { + updateSlotListeners(child); } } - private void layoutSecondaryDirection() { - VMeasuringOrderedLayout layout = getWidget(); - boolean isVertical = layout.isVertical; - boolean isUndefined = isUndefinedInDirection(!isVertical); - - int startPadding = getStartPadding(!isVertical); - int endPadding = getEndPadding(!isVertical); - - int allocatedSize; - if (isUndefined) { - allocatedSize = -1; - } else { - allocatedSize = getInnerSizeInDirection(!isVertical); + /** + * Add/remove necessary ElementResizeListeners for one slot. This should be + * called after each update to the slot's or it's widget. + */ + private void updateSlotListeners(ComponentConnector child) { + Slot slot = getWidget().getSlot(child.getWidget()); + + // Clear all possible listeners first + rmeoveResizeListener(slot.getWidget().getElement(), childComponentResizeListener); + if (slot.hasCaption()) { + rmeoveResizeListener(slot.getCaptionElement(), slotCaptionResizeListener); } - - allocatedSize = layout.layoutSecondaryDirection(allocatedSize, - startPadding, endPadding); - - Style ownStyle = getWidget().getElement().getStyle(); - - if (isUndefined) { - int outerSize = getSizeForInnerSize(allocatedSize, - !getWidget().isVertical); - ownStyle.setPropertyPx(getSizeProperty(!getWidget().isVertical), - outerSize); - reportUndefinedSize(outerSize, !isVertical); - } else { - ownStyle.setProperty(getSizeProperty(!getWidget().isVertical), - getDefinedSize(!getWidget().isVertical)); + if (slot.hasSpacing()) { + rmeoveResizeListener(slot.getSpacingElement(), spacingResizeListener); } - } - private String getDefinedSize(boolean isVertical) { - if (isVertical) { - return getState().height == null ? "" : getState().height; - } else { - return getState().width == null ? "" : getState().width; + // Add all necessary listeners + if (needsFixedHeight()) { + addResizeListener(slot.getWidget().getElement(), childComponentResizeListener); + if (slot.hasCaption()) { + addResizeListener(slot.getCaptionElement(), slotCaptionResizeListener); + } + } else if ((child.isRelativeHeight() || child.isRelativeWidth()) + && slot.hasCaption()) { + // If the slot has caption, we need to listen for it's size changes + // in order to update the padding/margin offset for relative sized + // components + addResizeListener(slot.getCaptionElement(), slotCaptionResizeListener); } - } - private int getStartPadding(boolean isVertical) { - if (isVertical) { - return getLayoutManager().getPaddingTop(getWidget().getElement()); - } else { - return getLayoutManager().getPaddingLeft(getWidget().getElement()); + if (needsExpand()) { + addResizeListener(slot.getWidget().getElement(), childComponentResizeListener); + if (slot.hasSpacing()) { + addResizeListener(slot.getSpacingElement(), spacingResizeListener); + } } - } - private int getEndPadding(boolean isVertical) { - if (isVertical) { - return getLayoutManager() - .getPaddingBottom(getWidget().getElement()); + if (child.isRelativeHeight()) { + hasRelativeHeight.add(child); + needsMeasure.remove(child.getWidget().getElement()); } else { - return getLayoutManager().getPaddingRight(getWidget().getElement()); + hasRelativeHeight.remove(child); + needsMeasure.add(child.getWidget().getElement()); } + } - @Override - public void layoutHorizontally() { - if (getWidget().isVertical) { - layoutSecondaryDirection(); - } else { - layoutPrimaryDirection(); + /** + * Re-calculate the layout height + */ + private void updateLayoutHeight() { + if (needsFixedHeight()) { + int h = getMaxHeight(); + h += getLayoutManager().getBorderHeight(getWidget().getElement()) + + getLayoutManager().getPaddingHeight( + getWidget().getElement()); + getWidget().getElement().getStyle().setHeight(h, Unit.PX); + getLayoutManager().setNeedsMeasure(this); } } - @Override - public void layoutVertically() { - if (getWidget().isVertical) { - layoutPrimaryDirection(); - } else { - layoutSecondaryDirection(); + /** + * Measures the maximum height of the layout in pixels + */ + private int getMaxHeight() { + int highestNonRelative = -1; + int highestRelative = -1; + + for (ComponentConnector child : getChildComponents()) { + // TODO would be more efficient to measure the slot element if both + // caption and child widget elements need to be measured. Keeping + // track of what to measure is the most difficult part of this + // layout. + Element el = child.getWidget().getElement(); + CaptionPosition pos = getWidget().getCaptionPositionFromElement( + (Element) el.getParentElement().cast()); + if (needsMeasure.contains(el)) { + int h = getLayoutManager().getOuterHeight(el); + String sHeight = el.getStyle().getHeight(); + // Only add the caption size to the height of the slot if + // coption position is top or bottom + if (childCaptionElementHeight.containsKey(el) + && (sHeight == null || !sHeight.endsWith("%")) + && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) { + h += childCaptionElementHeight.get(el); + } + if (h > highestNonRelative) { + highestNonRelative = h; + } + } else { + int h = getLayoutManager().getOuterHeight(el); + if (childCaptionElementHeight.containsKey(el) + && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) { + h += childCaptionElementHeight.get(el); + } + if (h > highestRelative) { + highestRelative = h; + } + } } + return highestNonRelative > -1 ? highestNonRelative : highestRelative; } + /* + * (non-Javadoc) + * + * @see com.vaadin.client.ui.AbstractComponentConnector#onUnregister() + */ @Override - public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) { - super.onConnectorHierarchyChange(event); - List previousChildren = event.getOldChildren(); - int currentIndex = 0; - VMeasuringOrderedLayout layout = getWidget(); - + public void onUnregister() { + // Cleanup all ElementResizeListeners for (ComponentConnector child : getChildComponents()) { - Widget childWidget = child.getWidget(); - VLayoutSlot slot = layout.getSlotForChild(childWidget); - - if (childWidget.getParent() != layout) { - // If the child widget was previously attached to another - // AbstractOrderedLayout a slot might be found that belongs to - // another AbstractOrderedLayout. In this case we discard it and - // create a new slot. - slot = new ComponentConnectorLayoutSlot(getWidget() - .getStylePrimaryName(), child, this); - } - layout.addOrMove(slot, currentIndex++); - if (child.isRelativeWidth()) { - slot.getWrapperElement().getStyle().setWidth(100, Unit.PCT); + Slot slot = getWidget().getSlot(child.getWidget()); + if (slot.hasCaption()) { + rmeoveResizeListener(slot.getCaptionElement(), slotCaptionResizeListener); } - } - for (ComponentConnector child : previousChildren) { - if (child.getParent() != this) { - // Remove slot if the connector is no longer a child of this - // layout - layout.removeSlotForWidget(child.getWidget()); + if (slot.getSpacingElement() != null) { + rmeoveResizeListener(slot.getSpacingElement(), spacingResizeListener); } + + rmeoveResizeListener(slot.getWidget().getElement(), + childComponentResizeListener); } - }; + super.onUnregister(); + } + + /** + * Helper method to add a resize listener to an element + * + * @param el + * The element to add the resize listener to + * @param listener + * The listener to add + */ + private void addResizeListener(Element el, ElementResizeListener listener) { + getLayoutManager().addElementResizeListener(el, listener); + } + + /** + * Helper method to remove a resize listener to an element + * + * @param el + * The element from where the resize listener should be removed + * @param listener + * THe listener to remove + */ + private void rmeoveResizeListener(Element el, ElementResizeListener listener) { + getLayoutManager().removeElementResizeListener(el, listener); + } } diff --git a/client/src/com/vaadin/client/ui/orderedlayout/HorizontalBoxLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/HorizontalBoxLayoutConnector.java deleted file mode 100644 index 13e133dfa7..0000000000 --- a/client/src/com/vaadin/client/ui/orderedlayout/HorizontalBoxLayoutConnector.java +++ /dev/null @@ -1,19 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.client.ui.orderedlayout; - -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.Connect.LoadStyle; -import com.vaadin.ui.HorizontalLayout; - -@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER) -public class HorizontalBoxLayoutConnector extends AbstractBoxLayoutConnector { - - @Override - public void init() { - super.init(); - getWidget().setVertical(false); - } - -} diff --git a/client/src/com/vaadin/client/ui/orderedlayout/HorizontalLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/HorizontalLayoutConnector.java index a86baf9cea..aa33e99f45 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/HorizontalLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/HorizontalLayoutConnector.java @@ -15,10 +15,24 @@ */ package com.vaadin.client.ui.orderedlayout; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.Connect.LoadStyle; +import com.vaadin.ui.HorizontalLayout; -//@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER) +/** + * Connects the client widget {@link VHorizontalLayout} with the Vaadin server + * side counterpart {@link HorizontalLayout} + */ +@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER) public class HorizontalLayoutConnector extends AbstractOrderedLayoutConnector { + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.ui.orderedlayout.AbstractOrderedLayoutConnector#getWidget + * () + */ @Override public VHorizontalLayout getWidget() { return (VHorizontalLayout) super.getWidget(); diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VBoxLayout.java b/client/src/com/vaadin/client/ui/orderedlayout/VBoxLayout.java deleted file mode 100644 index 4df4ce6524..0000000000 --- a/client/src/com/vaadin/client/ui/orderedlayout/VBoxLayout.java +++ /dev/null @@ -1,750 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.client.ui.orderedlayout; - -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.ComponentConnector; -import com.vaadin.client.LayoutManager; -import com.vaadin.shared.ui.AlignmentInfo; -import com.vaadin.shared.ui.MarginInfo; -import com.vaadin.ui.themes.BaseTheme; - -public class VBoxLayout extends FlowPanel { - - public static final String CLASSNAME = "v-boxlayout"; - - private static final String ALIGN_CLASS_PREFIX = "v-align-"; - - protected boolean spacing = false; - - protected boolean vertical = true; - - protected boolean definedHeight = false; - - private Map widgetToSlot = new HashMap(); - - private LayoutManager layoutManager; - - public VBoxLayout() { - setStyleName(CLASSNAME); - setVertical(true); - } - - public void setVertical(boolean isVertical) { - vertical = isVertical; - if (vertical) { - addStyleName("v-vertical"); - removeStyleName("v-horizontal"); - } else { - addStyleName("v-horizontal"); - removeStyleName("v-vertical"); - } - } - - public void addOrMoveSlot(Slot slot, int index) { - if (slot.getParent() == this) { - int currentIndex = getWidgetIndex(slot); - if (index == currentIndex) { - return; - } - } - insert(slot, index); - } - - @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) { - DOM.insertChild(container, child.getElement(), - spacing ? beforeIndex * 2 : beforeIndex); - } else { - DOM.appendChild(container, child.getElement()); - } - - // Adopt. - adopt(child); - } - - public Slot removeSlot(Widget widget) { - Slot slot = widgetToSlot.get(widget); - remove(slot); - widgetToSlot.remove(widget); - return slot; - } - - public Slot getSlot(ComponentConnector connector) { - Slot slot = widgetToSlot.get(connector.getWidget()); - if (slot == null) { - slot = new Slot(connector); - widgetToSlot.put(connector.getWidget(), slot); - } - return slot; - } - - public enum CaptionPosition { - TOP, RIGHT, BOTTOM, LEFT - } - - protected class Slot extends SimplePanel { - - private ComponentConnector connector; - - private Element spacer; - - private Element captionWrap; - private Element caption; - private Element captionText; - private Icon icon; - private Element errorIcon; - private Element requiredIcon; - - // 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; - - public Slot(ComponentConnector connector) { - this.connector = connector; - setWidget(connector.getWidget()); - setStylePrimaryName("v-slot"); - } - - public AlignmentInfo getAlignment() { - return alignment; - } - - public void setAlignment(AlignmentInfo alignment) { - this.alignment = alignment; - - if (alignment.isHorizontalCenter()) { - addStyleName(ALIGN_CLASS_PREFIX + "center"); - removeStyleName(ALIGN_CLASS_PREFIX + "right"); - } else if (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.isVerticalCenter()) { - addStyleName(ALIGN_CLASS_PREFIX + "middle"); - removeStyleName(ALIGN_CLASS_PREFIX + "bottom"); - } else if (alignment.isBottom()) { - addStyleName(ALIGN_CLASS_PREFIX + "bottom"); - removeStyleName(ALIGN_CLASS_PREFIX + "middle"); - } else { - removeStyleName(ALIGN_CLASS_PREFIX + "middle"); - removeStyleName(ALIGN_CLASS_PREFIX + "bottom"); - } - } - - public void setExpandRatio(double expandRatio) { - this.expandRatio = expandRatio; - } - - public double getExpandRatio() { - return expandRatio; - } - - public void setSpacing(boolean spacing) { - if (spacing && spacer == null) { - spacer = DOM.createDiv(); - spacer.addClassName("v-spacing"); - getElement().getParentElement().insertBefore(spacer, - getElement()); - } else if (!spacing && spacer != null) { - spacer.removeFromParent(); - spacer = null; - } - } - - public Element getSpacingElement() { - return spacer; - } - - public boolean hasSpacing() { - return getSpacingElement() != null; - } - - protected int getSpacingSize(boolean vertical) { - if (spacer == null) { - return 0; - } - - if (layoutManager != null) { - if (vertical) { - return layoutManager.getOuterHeight(spacer); - } else { - return layoutManager.getOuterWidth(spacer); - } - } - // TODO place for optimization (in expense of theme - // flexibility): only measure one of the elements and cache the - // value - return vertical ? spacer.getOffsetHeight() : spacer - .getOffsetWidth(); - // } - } - - 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()); - } - - public CaptionPosition getCaptionPosition() { - return captionPosition; - } - - // TODO refactor VCaption and use that instead: creates a tight coupling - // between this layout and Vaadin, but it's already coupled - public void setCaption(String captionText, String iconUrl, - List 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(BaseTheme.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(" "); - } 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(); - // icon = DOM.createImg(); - // icon.setClassName("v-icon"); - caption.insertFirst(icon.getElement()); - } - // icon.setAttribute("src", iconUrl); - 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); - } - } - - // TODO theme flexibility: add extra styles to captionWrap as well? - - } - - public boolean hasCaption() { - return caption != null; - } - - public Element getCaptionElement() { - return caption; - } - - public void setRelativeWidth(boolean relativeWidth) { - updateRelativeSize(relativeWidth, "width"); - } - - public void setRelativeHeight(boolean relativeHeight) { - updateRelativeSize(relativeHeight, "height"); - } - - 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(); - } - } - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - if (DOM.eventGetType(event) == Event.ONLOAD - && icon.getElement() == DOM.eventGetTarget(event)) { - if (layoutManager != null) { - layoutManager.layoutLater(); - } else { - updateCaptionOffset(caption); - } - } - } - - @Override - protected Element getContainerElement() { - if (captionWrap == null) { - return getElement(); - } else { - return captionWrap; - } - } - - @Override - protected void onDetach() { - if (spacer != null) { - spacer.removeFromParent(); - } - super.onDetach(); - } - - @Override - protected void onAttach() { - super.onAttach(); - if (spacer != null) { - getElement().getParentElement().insertBefore(spacer, - getElement()); - } - } - - } - - protected class Icon extends UIObject { - public static final String CLASSNAME = "v-icon"; - private String myUrl; - - public Icon() { - setElement(DOM.createImg()); - DOM.setElementProperty(getElement(), "alt", ""); - setStyleName(CLASSNAME); - } - - 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; - } - } - - } - - void setLayoutManager(LayoutManager manager) { - layoutManager = manager; - } - - private static final RegExp captionPositionRegexp = RegExp - .compile("v-caption-on-(\\S+)"); - - CaptionPosition getCaptionPositionFromElement(Element captionWrap) { - // 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; - } - - 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); - } - } - } - } - - void setMargin(MarginInfo marginInfo) { - setStyleName("v-margin-top", marginInfo.hasTop()); - setStyleName("v-margin-right", marginInfo.hasRight()); - setStyleName("v-margin-bottom", marginInfo.hasBottom()); - setStyleName("v-margin-left", marginInfo.hasLeft()); - } - - protected void setSpacing(boolean spacingEnabled) { - spacing = spacingEnabled; - for (Slot slot : widgetToSlot.values()) { - if (getWidgetIndex(slot) > 0) { - slot.setSpacing(spacingEnabled); - } - } - } - - 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.connector.isRelativeHeight()) { - layoutManager.setNeedsMeasure(slot.connector); - } - } else { - slot.setWidth((100 * (slot.getExpandRatio() / total)) + "%"); - if (slot.connector.isRelativeWidth()) { - layoutManager.setNeedsMeasure(slot.connector); - } - } - } - } - } - - private Element expandWrapper; - - 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; - } - } - - 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 = slot.getSpacingSize(vertical); - 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(); - } - } - - 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(); - } - VBoxLayout.this.getElement().getStyle() - .setHeight(newHeight, Unit.PX); - } - - } - - void clearHeight() { - getElement().getStyle().clearHeight(); - } - - @Override - public void setHeight(String height) { - super.setHeight(height); - definedHeight = (height != null && !"".equals(height)); - } -} diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VHorizontalLayout.java b/client/src/com/vaadin/client/ui/orderedlayout/VHorizontalLayout.java index c72c44262b..543eeafe88 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/VHorizontalLayout.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/VHorizontalLayout.java @@ -15,12 +15,15 @@ */ package com.vaadin.client.ui.orderedlayout; -public class VHorizontalLayout extends VMeasuringOrderedLayout { - - public static final String CLASSNAME = "v-horizontallayout"; +/** + * Represents a layout where the children is ordered vertically + */ +public class VHorizontalLayout extends VOrderedLayout { + /** + * Default constructor + */ public VHorizontalLayout() { - super(CLASSNAME, false); + setVertical(false); } - } diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VMeasuringOrderedLayout.java b/client/src/com/vaadin/client/ui/orderedlayout/VMeasuringOrderedLayout.java deleted file mode 100644 index 94dfe2242e..0000000000 --- a/client/src/com/vaadin/client/ui/orderedlayout/VMeasuringOrderedLayout.java +++ /dev/null @@ -1,253 +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.orderedlayout; - -import java.util.HashMap; -import java.util.Map; - -import com.google.gwt.dom.client.DivElement; -import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.Node; -import com.google.gwt.dom.client.Style; -import com.google.gwt.dom.client.Style.Position; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.ComplexPanel; -import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.user.client.ui.WidgetCollection; -import com.vaadin.client.VCaption; -import com.vaadin.client.ui.layout.VLayoutSlot; -import com.vaadin.shared.ui.MarginInfo; - -public class VMeasuringOrderedLayout extends ComplexPanel { - - final boolean isVertical; - - final DivElement spacingMeasureElement; - - private Map widgetToSlot = new HashMap(); - - protected VMeasuringOrderedLayout(String className, boolean isVertical) { - DivElement element = Document.get().createDivElement(); - setElement(element); - - spacingMeasureElement = Document.get().createDivElement(); - Style spacingStyle = spacingMeasureElement.getStyle(); - spacingStyle.setPosition(Position.ABSOLUTE); - getElement().appendChild(spacingMeasureElement); - - setStyleName(className); - this.isVertical = isVertical; - } - - public void addOrMove(VLayoutSlot layoutSlot, int index) { - Widget widget = layoutSlot.getWidget(); - Element wrapperElement = layoutSlot.getWrapperElement(); - - Element containerElement = getElement(); - Node childAtIndex = containerElement.getChild(index); - if (childAtIndex != wrapperElement) { - // Insert at correct location not attached or at wrong location - containerElement.insertBefore(wrapperElement, childAtIndex); - insert(widget, wrapperElement, index, false); - } - - widgetToSlot.put(widget, layoutSlot); - } - - private void togglePrefixedStyleName(String name, boolean enabled) { - if (enabled) { - addStyleDependentName(name); - } else { - removeStyleDependentName(name); - } - } - - void updateMarginStyleNames(MarginInfo marginInfo) { - togglePrefixedStyleName("margin-top", marginInfo.hasTop()); - togglePrefixedStyleName("margin-right", marginInfo.hasRight()); - togglePrefixedStyleName("margin-bottom", marginInfo.hasBottom()); - togglePrefixedStyleName("margin-left", marginInfo.hasLeft()); - } - - void updateSpacingStyleName(boolean spacingEnabled) { - String styleName = getStylePrimaryName(); - if (spacingEnabled) { - spacingMeasureElement.addClassName(styleName + "-spacing-on"); - spacingMeasureElement.removeClassName(styleName + "-spacing-off"); - } else { - spacingMeasureElement.removeClassName(styleName + "-spacing-on"); - spacingMeasureElement.addClassName(styleName + "-spacing-off"); - } - } - - public void removeSlotForWidget(Widget widget) { - VLayoutSlot slot = getSlotForChild(widget); - VCaption caption = slot.getCaption(); - if (caption != null) { - // Must remove using setCaption to ensure dependencies (layout -> - // caption) are unregistered - slot.setCaption(null); - } - - remove(slot.getWidget()); - getElement().removeChild(slot.getWrapperElement()); - widgetToSlot.remove(widget); - } - - public VLayoutSlot getSlotForChild(Widget widget) { - return widgetToSlot.get(widget); - } - - public void setCaption(Widget child, VCaption caption) { - VLayoutSlot slot = getSlotForChild(child); - - if (caption != null) { - // Logical attach. - getChildren().add(caption); - } - - // Physical attach if not null, also removes old caption - slot.setCaption(caption); - - if (caption != null) { - // Adopt. - adopt(caption); - } - } - - public int layoutPrimaryDirection(int spacingSize, int allocatedSize, - int startPadding, int endPadding) { - int actuallyAllocated = 0; - double totalExpand = 0; - - int childCount = 0; - for (Widget child : this) { - if (child instanceof VCaption) { - continue; - } - childCount++; - - VLayoutSlot slot = getSlotForChild(child); - totalExpand += slot.getExpandRatio(); - - if (!slot.isRelativeInDirection(isVertical)) { - actuallyAllocated += slot.getUsedSizeInDirection(isVertical); - } - } - - actuallyAllocated += spacingSize * (childCount - 1); - - if (allocatedSize == -1) { - allocatedSize = actuallyAllocated; - } - - double unallocatedSpace = Math - .max(0, allocatedSize - actuallyAllocated); - - double currentLocation = startPadding; - - WidgetCollection children = getChildren(); - for (int i = 0; i < children.size(); i++) { - Widget child = children.get(i); - if (child instanceof VCaption) { - continue; - } - - VLayoutSlot slot = getSlotForChild(child); - - double childExpandRatio; - if (totalExpand == 0) { - childExpandRatio = 1d / childCount; - } else { - childExpandRatio = slot.getExpandRatio() / totalExpand; - } - - double extraPixels = unallocatedSpace * childExpandRatio; - double endLocation = currentLocation + extraPixels; - if (!slot.isRelativeInDirection(isVertical)) { - endLocation += slot.getUsedSizeInDirection(isVertical); - } - - /* - * currentLocation and allocatedSpace are used with full precision - * to avoid missing pixels in the end. The pixel dimensions passed - * to the DOM are still rounded. Otherwise e.g. 10.5px start - * position + 10.5px space might be cause the component to go 1px - * beyond the edge as the effect of the browser's rounding may cause - * something similar to 11px + 11px. - * - * It's most efficient to use doubles all the way because native - * javascript emulates other number types using doubles. - */ - double roundedLocation = Math.round(currentLocation); - - /* - * Space is calculated as the difference between rounded start and - * end locations. Just rounding the space would cause e.g. 10.5px + - * 10.5px = 21px -> 11px + 11px = 22px but in this way we get 11px + - * 10px = 21px. - */ - double roundedSpace = Math.round(endLocation) - roundedLocation; - - // Reserve room for the padding if we're at the end - double slotEndMargin; - if (i == children.size() - 1) { - slotEndMargin = endPadding; - } else { - slotEndMargin = 0; - } - - slot.positionInDirection(roundedLocation, roundedSpace, - slotEndMargin, isVertical); - - currentLocation = endLocation + spacingSize; - } - - return allocatedSize; - } - - public int layoutSecondaryDirection(int allocatedSize, int startPadding, - int endPadding) { - int maxSize = 0; - for (Widget child : this) { - if (child instanceof VCaption) { - continue; - } - - VLayoutSlot slot = getSlotForChild(child); - if (!slot.isRelativeInDirection(!isVertical)) { - maxSize = Math.max(maxSize, - slot.getUsedSizeInDirection(!isVertical)); - } - } - - if (allocatedSize == -1) { - allocatedSize = maxSize; - } - - for (Widget child : this) { - if (child instanceof VCaption) { - continue; - } - - VLayoutSlot slot = getSlotForChild(child); - slot.positionInDirection(startPadding, allocatedSize, endPadding, - !isVertical); - } - - return allocatedSize; - } -} diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VOrderedLayout.java b/client/src/com/vaadin/client/ui/orderedlayout/VOrderedLayout.java new file mode 100644 index 0000000000..9c45680d92 --- /dev/null +++ b/client/src/com/vaadin/client/ui/orderedlayout/VOrderedLayout.java @@ -0,0 +1,995 @@ +/* + * 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.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.Util; +import com.vaadin.shared.ui.AlignmentInfo; +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.ui.themes.BaseTheme; + +/** + * Base class for ordered layouts + */ +public class VOrderedLayout extends FlowPanel { + + public static final String CLASSNAME = "v-boxlayout"; + + private static final String ALIGN_CLASS_PREFIX = "v-align-"; + + protected boolean spacing = false; + + protected boolean vertical = true; + + protected boolean definedHeight = false; + + private Map widgetToSlot = new HashMap(); + + private Element expandWrapper; + + private LayoutManager layoutManager; + + /** + * Constructor + */ + public VOrderedLayout() { + setStyleName(CLASSNAME); + setVertical(true); + } + + /** + * Does the layout order its children horizontally or vertically + * + * @param vertical + * true to order the childer vertically, false to order them + * horizontally + * + */ + protected void setVertical(boolean vertical) { + this.vertical = vertical; + if (vertical) { + addStyleName("v-vertical"); + removeStyleName("v-horizontal"); + } else { + addStyleName("v-horizontal"); + removeStyleName("v-vertical"); + } + } + + /** + * Add or move a slot to another index + * + * @param slot + * The slot to move or add + * @param index + * The index where the slot should be placed + */ + void addOrMoveSlot(Slot slot, int index) { + if (slot.getParent() == this) { + int currentIndex = getWidgetIndex(slot); + if (index == currentIndex) { + return; + } + } + insert(slot, index); + } + + /** + * {@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) { + DOM.insertChild(container, child.getElement(), + spacing ? beforeIndex * 2 : 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 + * + * @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, this); + widgetToSlot.put(widget, slot); + } + return slot; + } + + /** + * 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 static final class Slot extends SimplePanel { + + private Element spacer; + private Element captionWrap; + private Element caption; + private Element captionText; + private Icon icon; + private Element errorIcon; + private Element requiredIcon; + private final VOrderedLayout layout; + + // 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, VOrderedLayout layout) { + this.layout = layout; + setWidget(widget); + setStylePrimaryName("v-slot"); + } + + /** + * Returns the alignment for the slot + * + */ + public AlignmentInfo getAlignment() { + return 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.isHorizontalCenter()) { + addStyleName(ALIGN_CLASS_PREFIX + "center"); + removeStyleName(ALIGN_CLASS_PREFIX + "right"); + } else if (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.isVerticalCenter()) { + addStyleName(ALIGN_CLASS_PREFIX + "middle"); + removeStyleName(ALIGN_CLASS_PREFIX + "bottom"); + } else if (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"); + 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 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(BaseTheme.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(" "); + } 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; + protected 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; + protected 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 (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()); + } + } + } + + /** + * 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 + * + * @param captionWrap + * The wrapping element + * + * @return The caption position + */ + 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 + * + * @param caption + * The caption element + */ + 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); + } + } + } + + /** + * 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 + */ + 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)); + } +} diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VVerticalLayout.java b/client/src/com/vaadin/client/ui/orderedlayout/VVerticalLayout.java index 76e5910bed..3e597afec1 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/VVerticalLayout.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/VVerticalLayout.java @@ -15,12 +15,15 @@ */ package com.vaadin.client.ui.orderedlayout; -public class VVerticalLayout extends VMeasuringOrderedLayout { - - public static final String CLASSNAME = "v-verticallayout"; +/** + * Represents a layout where the children is ordered vertically + */ +public class VVerticalLayout extends VOrderedLayout { + /** + * Default constructor + */ public VVerticalLayout() { - super(CLASSNAME, true); + setVertical(true); } - } diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VerticalBoxLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/VerticalBoxLayoutConnector.java deleted file mode 100644 index 5d1e1d9eee..0000000000 --- a/client/src/com/vaadin/client/ui/orderedlayout/VerticalBoxLayoutConnector.java +++ /dev/null @@ -1,19 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.client.ui.orderedlayout; - -import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.Connect.LoadStyle; -import com.vaadin.ui.VerticalLayout; - -@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER) -public class VerticalBoxLayoutConnector extends AbstractBoxLayoutConnector { - - @Override - public void init() { - super.init(); - getWidget().setVertical(true); - } - -} diff --git a/client/src/com/vaadin/client/ui/orderedlayout/VerticalLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/VerticalLayoutConnector.java index 455c645144..13e9b3ecf7 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/VerticalLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/VerticalLayoutConnector.java @@ -15,10 +15,24 @@ */ package com.vaadin.client.ui.orderedlayout; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.Connect.LoadStyle; +import com.vaadin.ui.VerticalLayout; -//@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER) +/** + * Connects the client widget {@link VVerticalLayout} with the Vaadin server + * side counterpart {@link VerticalLayout} + */ +@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER) public class VerticalLayoutConnector extends AbstractOrderedLayoutConnector { + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.ui.orderedlayout.AbstractOrderedLayoutConnector#getWidget + * () + */ @Override public VVerticalLayout getWidget() { return (VVerticalLayout) super.getWidget();