From: Leif Åstrand Date: Wed, 8 Feb 2012 07:21:24 +0000 (+0200) Subject: Merge remote branch 'origin/master' into layoutperformance X-Git-Tag: 7.0.0.alpha2~434^2~82 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2be547521ac5497e15fc8f2b997611e8f8dfd9fe;p=vaadin-framework.git Merge remote branch 'origin/master' into layoutperformance Conflicts: src/com/vaadin/terminal/gwt/client/ApplicationConnection.java src/com/vaadin/terminal/gwt/client/ui/VAbstractPaintableWidget.java src/com/vaadin/terminal/gwt/client/ui/VOrderedLayoutPaintable.java --- 2be547521ac5497e15fc8f2b997611e8f8dfd9fe diff --cc src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index afd8130ada,65827fac0c..9408879b35 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@@ -32,12 -29,12 +32,9 @@@ 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.Timer; - import com.google.gwt.user.client.ui.FocusWidget; - import com.google.gwt.user.client.ui.Focusable; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage; - import com.vaadin.terminal.gwt.client.ui.Field; -import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize; -import com.vaadin.terminal.gwt.client.RenderInformation.Size; import com.vaadin.terminal.gwt.client.ui.VContextMenu; import com.vaadin.terminal.gwt.client.ui.VNotification; import com.vaadin.terminal.gwt.client.ui.VNotification.HideEvent; diff --cc src/com/vaadin/terminal/gwt/client/ui/VAbstractPaintableWidget.java index 1555371e47,355516ccd1..e45387a116 --- a/src/com/vaadin/terminal/gwt/client/ui/VAbstractPaintableWidget.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VAbstractPaintableWidget.java @@@ -3,9 -3,13 +3,14 @@@ */ package com.vaadin.terminal.gwt.client.ui; + import com.google.gwt.user.client.DOM; + import com.google.gwt.user.client.ui.FocusWidget; + import com.google.gwt.user.client.ui.Focusable; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.MeasureManager.MeasuredSize; + import com.vaadin.terminal.gwt.client.TooltipInfo; + import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.VPaintableMap; import com.vaadin.terminal.gwt.client.VPaintableWidget; import com.vaadin.terminal.gwt.client.VPaintableWidgetContainer; @@@ -15,12 -22,10 +23,12 @@@ public abstract class VAbstractPaintabl private Widget widget; private ApplicationConnection connection; private String id; - private VPaintableWidgetContainer parent; + private final MeasuredSize measuredSize = new MeasuredSize(); + /* State variables */ private boolean enabled = true; + private boolean visible = true; /** * Default constructor @@@ -110,7 -105,209 +108,212 @@@ } } + protected static boolean isRealUpdate(UIDL uidl) { + return !isCachedUpdate(uidl) && !uidl.getBooleanAttribute("invisible"); + } + + protected static boolean isCachedUpdate(UIDL uidl) { + return uidl.getBooleanAttribute("cached"); + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + if (isCachedUpdate(uidl)) { + return; + } + + VPaintableMap paintableMap = VPaintableMap.get(getConnection()); + // register the listened events by the server-side to the event-handler + // of the component + paintableMap.registerEventListenersFromUIDL(getId(), uidl); + + // Visibility + setVisible(!uidl.getBooleanAttribute("invisible"), uidl); + + if (uidl.getId().startsWith("PID_S")) { + DOM.setElementProperty(getWidgetForPaintable().getElement(), "id", + uidl.getId().substring(5)); + } + + if (!isVisible()) { + // component is invisible, delete old size to notify parent, if + // later made visible + paintableMap.setOffsetSize(this, null); + return; + } + + /* + * Disabled state may affect (override) tabindex so the order must be + * first setting tabindex, then enabled state. + */ + if (uidl.hasAttribute("tabindex") + && getWidgetForPaintable() instanceof Focusable) { + ((Focusable) getWidgetForPaintable()).setTabIndex(uidl + .getIntAttribute("tabindex")); + } + setEnabled(!uidl.getBooleanAttribute("disabled")); + + // Style names + String styleName = getStyleNameFromUIDL(getWidgetForPaintable() + .getStylePrimaryName(), uidl, + getWidgetForPaintable() instanceof Field); + getWidgetForPaintable().setStyleName(styleName); + + // Update tooltip + TooltipInfo tooltipInfo = paintableMap.getTooltipInfo(this, null); + if (uidl.hasAttribute(ATTRIBUTE_DESCRIPTION)) { + tooltipInfo + .setTitle(uidl.getStringAttribute(ATTRIBUTE_DESCRIPTION)); + } else { + tooltipInfo.setTitle(null); + } + // add error info to tooltip if present + if (uidl.hasAttribute(ATTRIBUTE_ERROR)) { + tooltipInfo.setErrorUidl(uidl.getErrors()); + } else { + tooltipInfo.setErrorUidl(null); + } + + // Set captions + if (delegateCaptionHandling()) { + getParent().updateCaption(this, uidl); + } + + /* + * updateComponentSize need to be after caption update so caption can be + * taken into account + */ + + getConnection().updateComponentSize(this, uidl); + } + + /** + * Sets the enabled state of this paintable + * + * @param enabled + * true if the paintable is enabled, false otherwise + */ + protected void setEnabled(boolean enabled) { + this.enabled = enabled; + + if (getWidgetForPaintable() instanceof FocusWidget) { + FocusWidget fw = (FocusWidget) getWidgetForPaintable(); + fw.setEnabled(enabled); + } + + } + + public boolean isEnabled() { + return enabled; + } + + /** + * Return true if parent handles caption, false if the paintable handles the + * caption itself. + * + * + * @deprecated This should always return true and all components should let + * the parent handle the caption and use other attributes for + * internal texts in the component + * @return + */ + @Deprecated + protected boolean delegateCaptionHandling() { + return true; + } + + /** + * Sets the visible state for this paintable. + * + * @param visible + * true if the paintable should be made visible, false otherwise + * @param captionUidl + * The UIDL that is passed to the parent and onwards to VCaption + * if the caption needs to be updated as a result of the + * visibility change. + */ + protected void setVisible(boolean visible, UIDL captionUidl) { + boolean wasVisible = this.visible; + this.visible = visible; + + getWidgetForPaintable().setVisible(visible); + if (wasVisible != visible) { + // Changed invisibile <-> visible + if (wasVisible && delegateCaptionHandling()) { + // Must hide caption when component is hidden + getParent().updateCaption(this, captionUidl); + } + } + } + + protected boolean isVisible() { + return visible; + } + + /** + * Generates the style name for the widget based on the given primary style + * name (typically returned by Widget.getPrimaryStyleName()) and the UIDL. + * An additional "modified" style name can be added if the field parameter + * is set to true. + * + * @param primaryStyleName + * @param uidl + * @param isField + * @return + */ + protected static String getStyleNameFromUIDL(String primaryStyleName, + UIDL uidl, boolean field) { + boolean enabled = !uidl.getBooleanAttribute("disabled"); + + StringBuffer styleBuf = new StringBuffer(); + styleBuf.append(primaryStyleName); + + // first disabling and read-only status + if (!enabled) { + styleBuf.append(" "); + styleBuf.append(ApplicationConnection.DISABLED_CLASSNAME); + } + if (uidl.getBooleanAttribute("readonly")) { + styleBuf.append(" "); + styleBuf.append("v-readonly"); + } + + // add additional styles as css classes, prefixed with component default + // stylename + if (uidl.hasAttribute("style")) { + final String[] styles = uidl.getStringAttribute("style").split(" "); + for (int i = 0; i < styles.length; i++) { + styleBuf.append(" "); + styleBuf.append(primaryStyleName); + styleBuf.append("-"); + styleBuf.append(styles[i]); + styleBuf.append(" "); + styleBuf.append(styles[i]); + } + } + + // add modified classname to Fields + if (field && uidl.hasAttribute("modified")) { + styleBuf.append(" "); + styleBuf.append(ApplicationConnection.MODIFIED_CLASSNAME); + } + + // add error classname to components w/ error + if (uidl.hasAttribute(ATTRIBUTE_ERROR)) { + styleBuf.append(" "); + styleBuf.append(primaryStyleName); + styleBuf.append(ApplicationConnection.ERROR_CLASSNAME_EXT); + } + // add required style to required components + if (uidl.hasAttribute("required")) { + styleBuf.append(" "); + styleBuf.append(primaryStyleName); + styleBuf.append(ApplicationConnection.REQUIRED_CLASSNAME_EXT); + } + + return styleBuf.toString(); + } + + public MeasuredSize getMeasuredSize() { + return measuredSize; + } } diff --cc src/com/vaadin/terminal/gwt/client/ui/VMeasuringOrderedLayoutPaintable.java index c13ebc87f3,0000000000..48375d38cd mode 100644,000000..100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VMeasuringOrderedLayoutPaintable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VMeasuringOrderedLayoutPaintable.java @@@ -1,434 -1,0 +1,437 @@@ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.CalculatingLayout; +import com.vaadin.terminal.gwt.client.MeasureManager; +import com.vaadin.terminal.gwt.client.UIDL; +import com.vaadin.terminal.gwt.client.VCaption; +import com.vaadin.terminal.gwt.client.VPaintableMap; +import com.vaadin.terminal.gwt.client.VPaintableWidget; + +public abstract class VMeasuringOrderedLayoutPaintable extends + VAbstractPaintableWidgetContainer implements CalculatingLayout { + + public void updateCaption(VPaintableWidget component, UIDL uidl) { + if (VCaption.isNeeded(uidl)) { + VCaption caption = getWidgetForPaintable().captions.get(component); + if (caption == null) { + caption = new VCaption(component, + getWidgetForPaintable().client); + + Widget widget = component.getWidgetForPaintable(); + + getWidgetForPaintable().addCaption(caption, widget); + getWidgetForPaintable().captions.put(component, caption); + + getMeasuredSize().registerDependency(caption.getElement()); + } + caption.updateCaption(uidl); + } else { + VCaption removedCaption = getWidgetForPaintable().captions + .remove(component); + if (removedCaption != null) { + getWidgetForPaintable().remove(removedCaption); + getMeasuredSize().deRegisterDependency( + removedCaption.getElement()); + } + } + } + + @Override + public VMeasuringOrderedLayout getWidgetForPaintable() { + return (VMeasuringOrderedLayout) super.getWidgetForPaintable(); + } + ++ @Override + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + getWidgetForPaintable().client = client; + getWidgetForPaintable().id = uidl.getId(); - if (client.updateComponent(this, uidl, true)) { ++ ++ super.updateFromUIDL(uidl, client); ++ if (!isRealUpdate(uidl)) { + return; + } + + long start = System.currentTimeMillis(); + // long childTime = 0; + + HashSet previousChildren = new HashSet(); + for (Widget child : getWidgetForPaintable()) { + if (!(child instanceof VCaption)) { + previousChildren.add(child); + } + } + // TODO Support reordering elements! + for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { + final UIDL childUIDL = (UIDL) it.next(); + final VPaintableWidget child = client.getPaintable(childUIDL); + Widget widget = child.getWidgetForPaintable(); + + if (widget.getParent() != getWidgetForPaintable()) { + getWidgetForPaintable().addChildWidget(widget); + } + + if (!childUIDL.getBooleanAttribute("cached")) { + child.updateFromUIDL(childUIDL, client); + child.getMeasuredSize().setDirty(true); + } + // TODO Update alignments and expand ratios + + previousChildren.remove(widget); + } + + for (Widget widget : previousChildren) { + Element wrapper = getWidgetForPaintable().getWrapper(widget); + VCaption caption = getWidgetForPaintable().captions.remove(widget); + if (caption != null) { + getWidgetForPaintable().remove(caption); + } + getWidgetForPaintable().remove(widget); + // Remove the wrapper + getWidgetForPaintable().getElement().removeChild(wrapper); + + client.unregisterPaintable(VPaintableMap.get(client).getPaintable( + widget)); + } + + int bitMask = uidl.getIntAttribute("margins"); + if (getWidgetForPaintable().activeMarginsInfo == null + || getWidgetForPaintable().activeMarginsInfo.getBitMask() != bitMask) { + getWidgetForPaintable().activeMarginsInfo = new VMarginInfo(bitMask); + } + + getWidgetForPaintable().spacing = uidl.getBooleanAttribute("spacing"); + getWidgetForPaintable().expandRatios = uidl + .getMapAttribute("expandRatios"); + getWidgetForPaintable().alignments = uidl.getMapAttribute("alignments"); + getMeasuredSize().setDirty(true); + } + + private int getCaptionWidth(VPaintableWidget child) { + VCaption caption = getWidgetForPaintable().captions.get(child); + if (caption == null) { + return 0; + } else { + return getMeasuredSize().getDependencyWidth(caption.getElement()); + } + } + + private int getCaptionHeight(VPaintableWidget child) { + VCaption caption = getWidgetForPaintable().captions.get(child); + if (caption != null) { + int captionHeight = getMeasuredSize().getDependencyHeight( + caption.getElement()); + + caption.getElement().getStyle() + .setMarginTop(-captionHeight, Unit.PX); + return captionHeight; + } else { + return 0; + } + } + + private static boolean isRelativeInDirection(Widget widget, + boolean isVertical) { + String dimension = getDimensionInDirection(widget, isVertical); + return dimension != null && dimension.endsWith("%"); + } + + private static String getDimensionInDirection(Widget widget, + boolean vertical) { + com.google.gwt.user.client.Element element = widget.getElement(); + Style style = element.getStyle(); + if (vertical) { + return style.getHeight(); + } else { + return style.getWidth(); + } + } + + private static String getSizeProperty(boolean isVertical) { + return isVertical ? "height" : "width"; + } + + private static String getStartProperty(boolean isVertical) { + return isVertical ? "top" : "left"; + } + + private static String getMinPropertyName(boolean isVertical) { + return isVertical ? "minHeight" : "minWidth"; + } + + private static boolean isUndefinedInDirection(Widget widget, + boolean isVertical) { + String dimension = getDimensionInDirection(widget, isVertical); + return dimension == null || dimension.length() == 0; + } + + private int getMeasuredInDirection(VPaintableWidget paintable, + boolean isVertical) { + MeasureManager.MeasuredSize measuredSize = paintable.getMeasuredSize(); + if (isVertical) { + return measuredSize.getHeight(); + } else { + return measuredSize.getWidth(); + } + } + + private static int getAlignmentInDirection(AlignmentInfo alignment, + boolean isVertical) { + if (alignment == null) { + return -1; + } + if (isVertical) { + if (alignment.isTop()) { + return -1; + } else if (alignment.isBottom()) { + return 1; + } else { + return 0; + } + } else { + if (alignment.isLeft()) { + return -1; + } else if (alignment.isRight()) { + return 1; + } else { + return 0; + } + } + } + + private void layoutPrimaryDirection() { + Collection children = getChildren(); + + // First pass - get total expand ratio and allocated size + int totalAllocated = 0; + double totalExpand = 0; + for (VPaintableWidget child : children) { + Widget widget = child.getWidgetForPaintable(); + + totalExpand += getWidgetForPaintable().getExpandRatio(child); + + int captionAllocation; + if (getWidgetForPaintable().isVertical) { + captionAllocation = getCaptionHeight(child); + getWidgetForPaintable().getWrapper(widget).getStyle() + .setPaddingTop(captionAllocation, Unit.PX); + } else { + captionAllocation = 0; + } + + if (!isRelativeInDirection(widget, + getWidgetForPaintable().isVertical)) { + totalAllocated += getMeasuredInDirection(child, + getWidgetForPaintable().isVertical) + captionAllocation; + } + } + int startMargin = getWidgetForPaintable().getStartMarginInDirection( + getWidgetForPaintable().isVertical); + int totalMargins = startMargin + + getWidgetForPaintable().getEndMarginInDirection( + getWidgetForPaintable().isVertical); + + totalAllocated += totalMargins + + (getWidgetForPaintable().getSpacingInDirection( + getWidgetForPaintable().isVertical) * (children.size() - 1)); + + Style ownStyle = getWidgetForPaintable().getElement().getStyle(); + double ownSize; + if (isUndefinedInDirection(getWidgetForPaintable(), + getWidgetForPaintable().isVertical)) { + ownSize = totalAllocated; + ownStyle.setProperty( + getMinPropertyName(getWidgetForPaintable().isVertical), + totalAllocated, Unit.PX); + } else { + ownSize = getMeasuredInDirection(this, + getWidgetForPaintable().isVertical); + ownStyle.clearProperty(getMinPropertyName(getWidgetForPaintable().isVertical)); + } + + double unallocatedSpace = Math.max(0, ownSize - totalAllocated); + + double currentLocation = startMargin; + for (VPaintableWidget child : children) { + Widget widget = child.getWidgetForPaintable(); + Element wrapper = getWidgetForPaintable().getWrapper(widget); + Style wrapperStyle = wrapper.getStyle(); + + double childExpandRatio; + if (totalExpand == 0) { + childExpandRatio = 1d / children.size(); + } else { + childExpandRatio = getWidgetForPaintable() + .getExpandRatio(child) / totalExpand; + } + + double extraPixels = unallocatedSpace * childExpandRatio; + + boolean relative = isRelativeInDirection(widget, + getWidgetForPaintable().isVertical); + + double size = getMeasuredInDirection(child, + getWidgetForPaintable().isVertical); + int captionHeight = getCaptionHeight(child); + + if (getWidgetForPaintable().isVertical) { + size += captionHeight; + } else if (!relative) { + size = Math.max(size, getCaptionWidth(child)); + } + + double allocatedSpace = extraPixels; + if (!relative) { + allocatedSpace += size; + } + + int alignment = getAlignmentInDirection(getWidgetForPaintable() + .getAlignment(child), getWidgetForPaintable().isVertical); + + if (relative) { + double captionReservation = getWidgetForPaintable().isVertical ? captionHeight + : 0; + wrapperStyle.setProperty( + getSizeProperty(getWidgetForPaintable().isVertical), + allocatedSpace - captionReservation, Unit.PX); + } else { + wrapperStyle + .clearProperty(getSizeProperty(getWidgetForPaintable().isVertical)); + } + + double startPosition = currentLocation; + if (alignment == 0) { + // Centered + startPosition += (allocatedSpace - size) / 2; + } else if (alignment == 1) { + // Right or bottom + startPosition += allocatedSpace - size; + } + + wrapperStyle.setProperty( + getStartProperty(getWidgetForPaintable().isVertical), + startPosition, Unit.PX); + + currentLocation += allocatedSpace + + getWidgetForPaintable().getSpacingInDirection( + getWidgetForPaintable().isVertical); + } + } + + private void layoutSecondaryDirection() { + Collection children = getChildren(); + + int maxSize = 0; + for (VPaintableWidget child : children) { + Widget widget = child.getWidgetForPaintable(); + + int captionAllocation; + if (!getWidgetForPaintable().isVertical) { + captionAllocation = getCaptionHeight(child); + getWidgetForPaintable().getWrapper(widget).getStyle() + .setPaddingTop(captionAllocation, Unit.PX); + } else { + captionAllocation = 0; + } + + if (!isRelativeInDirection(widget, + !getWidgetForPaintable().isVertical)) { + int childSize = getMeasuredInDirection(child, + !getWidgetForPaintable().isVertical) + + captionAllocation; + maxSize = Math.max(maxSize, childSize); + } + } + + int startMargin = getWidgetForPaintable().getStartMarginInDirection( + !getWidgetForPaintable().isVertical); + int totalMargins = startMargin + + getWidgetForPaintable().getEndMarginInDirection( + !getWidgetForPaintable().isVertical); + + double availableSpace; + Style ownStyle = getWidgetForPaintable().getElement().getStyle(); + + if (isUndefinedInDirection(getWidgetForPaintable(), + !getWidgetForPaintable().isVertical)) { + ownStyle.setProperty( + getMinPropertyName(!getWidgetForPaintable().isVertical), + maxSize + totalMargins, Unit.PX); + availableSpace = maxSize; + } else { + ownStyle.clearProperty(getMinPropertyName(!getWidgetForPaintable().isVertical)); + availableSpace = getMeasuredInDirection(this, + !getWidgetForPaintable().isVertical) - totalMargins; + } + + for (VPaintableWidget child : children) { + Widget widget = child.getWidgetForPaintable(); + Element wrapper = getWidgetForPaintable().getWrapper(widget); + Style wrapperStyle = wrapper.getStyle(); + + boolean relative = isRelativeInDirection(widget, + !getWidgetForPaintable().isVertical); + + int captionHeight = getCaptionHeight(child); + + double allocatedSize = getMeasuredInDirection(child, + !getWidgetForPaintable().isVertical); + if (!getWidgetForPaintable().isVertical) { + allocatedSize += captionHeight; + } else if (!relative) { + allocatedSize = Math.max(allocatedSize, getCaptionWidth(child)); + } + + int alignment = getAlignmentInDirection(getWidgetForPaintable() + .getAlignment(child), !getWidgetForPaintable().isVertical); + + double startPosition = startMargin; + if (alignment == 0) { + startPosition += (availableSpace - allocatedSize) / 2; + // Centered + } else if (alignment == 1) { + // Right or bottom + startPosition += (availableSpace - allocatedSize); + } + + wrapperStyle.setProperty( + getStartProperty(!getWidgetForPaintable().isVertical), + startPosition, Unit.PX); + + if (relative) { + double captionReservation = !getWidgetForPaintable().isVertical ? captionHeight + : 0; + wrapperStyle.setProperty( + getSizeProperty(!getWidgetForPaintable().isVertical), + availableSpace - captionReservation, Unit.PX); + } else { + wrapperStyle + .clearProperty(getSizeProperty(!getWidgetForPaintable().isVertical)); + } + } + } + + public void updateHorizontalSizes() { + if (getWidgetForPaintable().isVertical) { + layoutSecondaryDirection(); + } else { + layoutPrimaryDirection(); + } + } + + public void updateVerticalSizes() { + if (getWidgetForPaintable().isVertical) { + layoutPrimaryDirection(); + } else { + layoutSecondaryDirection(); + } + } +}