From: Jouni Koivuviita Date: Tue, 7 Aug 2012 12:35:55 +0000 (+0300) Subject: Boxlayout X-Git-Tag: 7.0.0.beta1~238 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d422eba61ba6e8ef82c72fa661ff4991f8918ec0;p=vaadin-framework.git Boxlayout --- d422eba61ba6e8ef82c72fa661ff4991f8918ec0 diff --cc src/com/vaadin/terminal/gwt/client/ComponentLocator.java index a4df6c6cc4,f88cd6b143..0e7a0c1d1c --- a/src/com/vaadin/terminal/gwt/client/ComponentLocator.java +++ b/src/com/vaadin/terminal/gwt/client/ComponentLocator.java @@@ -12,8 -12,8 +12,9 @@@ import com.google.gwt.user.client.Eleme import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; + import com.vaadin.terminal.gwt.client.communication.SharedState; import com.vaadin.terminal.gwt.client.ui.SubPartAware; +import com.vaadin.terminal.gwt.client.ui.VBoxLayout; import com.vaadin.terminal.gwt.client.ui.gridlayout.VGridLayout; import com.vaadin.terminal.gwt.client.ui.orderedlayout.VMeasuringOrderedLayout; import com.vaadin.terminal.gwt.client.ui.root.VRoot; diff --cc src/com/vaadin/terminal/gwt/client/LayoutManager.java index 239a948a10,74586a6def..41ad0e9f47 --- a/src/com/vaadin/terminal/gwt/client/LayoutManager.java +++ b/src/com/vaadin/terminal/gwt/client/LayoutManager.java @@@ -1032,14 -1019,6 +1019,50 @@@ public class LayoutManager return getMeasuredSize(element, nullSize).getMarginLeft(); } - public int getMarginWidth(Element element) { - return getMeasuredSize(element, nullSize).getMarginWidth(); ++ /** ++ * Gets the combined top & bottom margin of the given element, provided that ++ * they have been measured. These elements are guaranteed to be measured: ++ * ++ * ++ * A negative number is returned if the element has not been measured. If 0 ++ * is returned, it might indicate that the element is not attached to the ++ * DOM. ++ * ++ * @param element ++ * the element to get the measured margin for ++ * @return the measured top+bottom margin of the element in pixels. ++ */ ++ public int getMarginHeight(Element element) { ++ return getMarginTop(element) + getMarginBottom(element); + } + - public int getMarginHeight(Element element) { - return getMeasuredSize(element, nullSize).getMarginHeight(); ++ /** ++ * Gets the combined left & right margin of the given element, provided that ++ * they have been measured. These elements are guaranteed to be measured: ++ * ++ * ++ * A negative number is returned if the element has not been measured. If 0 ++ * is returned, it might indicate that the element is not attached to the ++ * DOM. ++ * ++ * @param element ++ * the element to get the measured margin for ++ * @return the measured left+right margin of the element in pixels. ++ */ ++ public int getMarginWidth(Element element) { ++ return getMarginLeft(element) + getMarginRight(element); + } + /** * Registers the outer height (including margins, borders and paddings) of a * component. This can be used as an optimization by ManagedLayouts; by diff --cc src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java index 854b02018f,0000000000..413c605a88 mode 100644,000000..100644 --- a/src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractBoxLayoutConnector.java @@@ -1,598 -1,0 +1,590 @@@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + - import com.google.gwt.core.client.GWT; +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.terminal.gwt.client.AbstractFieldState; +import com.vaadin.terminal.gwt.client.ComponentConnector; +import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.communication.RpcProxy; +import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; +import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler; +import com.vaadin.terminal.gwt.client.ui.VBoxLayout.CaptionPosition; +import com.vaadin.terminal.gwt.client.ui.VBoxLayout.Slot; +import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeEvent; +import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeListener; +import com.vaadin.terminal.gwt.client.ui.orderedlayout.AbstractOrderedLayoutServerRpc; +import com.vaadin.terminal.gwt.client.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() { + rpc = RpcProxy.create(AbstractOrderedLayoutServerRpc.class, this); + getWidget().setLayoutManager(getLayoutManager()); + } + + @Override + public AbstractOrderedLayoutState getState() { + return (AbstractOrderedLayoutState) super.getState(); + } + - @Override - protected Widget createWidget() { - return GWT.create(VBoxLayout.class); - } - + @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(); + + public void updateCaption(ComponentConnector child) { + Slot slot = getWidget().getSlot(child); + + String caption = child.getState().getCaption(); + String iconUrl = child.getState().getIcon() != null ? child.getState() + .getIcon().getURL() : null; + List styles = child.getState().getStyles(); + String error = child.getState().getErrorMessage(); + boolean showError = error != null; + if (child.getState() instanceof AbstractFieldState) { + AbstractFieldState abstractFieldState = (AbstractFieldState) child + .getState(); + showError = showError && !abstractFieldState.isHideErrors(); + } + boolean required = false; + if (child instanceof AbstractFieldConnector) { + required = ((AbstractFieldConnector) child).isRequired(); + } + boolean enabled = child.getState().isEnabled(); - // TODO Description is handled from somewhere else? + + slot.setCaption(caption, iconUrl, 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 : getChildren()) { ++ 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 VMarginInfo(getState().getMarginsBitmask())); + getWidget().setSpacing(getState().isSpacing()); + + hasExpandRatio.clear(); + hasVerticalAlignment.clear(); + hasRelativeHeight.clear(); + needsMeasure.clear(); + + boolean equalExpandRatio = getWidget().vertical ? !isUndefinedHeight() + : !isUndefinedWidth(); - for (ComponentConnector child : getChildren()) { ++ for (ComponentConnector child : getChildComponents()) { + double expandRatio = getState().getChildData().get(child) + .getExpandRatio(); + if (expandRatio > 0) { + equalExpandRatio = false; + break; + } + } + - for (ComponentConnector child : getChildren()) { ++ for (ComponentConnector child : getChildComponents()) { + Slot slot = getWidget().getSlot(child); + + AlignmentInfo alignment = new AlignmentInfo(getState() + .getChildData().get(child).getAlignmentBitmask()); + slot.setAlignment(alignment); + + double expandRatio = getState().getChildData().get(child) + .getExpandRatio(); + 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() { + 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); + } + }; + + 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 : getChildren()) { ++ 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() { + 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() { + 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() { + public void onElementResize(ElementResizeEvent e) { + if (needsExpand()) { + updateExpand(); + } + } + }; + + private void updateLayoutHeight() { + if (needsFixedHeight() && childElementHeight.size() > 0) { + 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() { + // TODO should use layout manager instead of inner lists of element + // sizes + int highestNonRelative = -1; + int highestRelative = -1; + // System.out.println("Child sizes: " + // + childElementHeight.values().toString()); + for (Element el : childElementHeight.keySet()) { + // 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. + CaptionPosition pos = getWidget().getCaptionPositionFromElement( + (Element) el.getParentElement().cast()); + if (needsMeasure.contains(el)) { + int h = childElementHeight.get(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 = childElementHeight.get(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 : getChildren()) { ++ 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 --cc src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java index 1caec0428e,f0b9d518ca..a621c488be --- a/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/AbstractComponentConnector.java @@@ -98,12 -93,11 +97,12 @@@ public abstract class AbstractComponent .getTabIndex()); } - setWidgetEnabled(isEnabled()); + super.onStateChanged(stateChangeEvent); // Style names - String styleName = getStyleNames(getWidget().getStylePrimaryName()); - getWidget().setStyleName(styleName); + // String styleName = getStyleNames(getWidget().getStylePrimaryName()); + // getWidget().setStyleName(styleName); + updateStyleNames(); // Update tooltip TooltipInfo tooltipInfo = paintableMap.getTooltipInfo(this, null);