From: Leif Åstrand Date: Mon, 30 Jan 2012 10:01:48 +0000 (+0200) Subject: Initial MeasureManager and working Horizontal/VerticalLayout (#8313) X-Git-Tag: 7.0.0.alpha2~434^2~95 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5e14034fcc5e076d1ac4acea6691129d56b22367;p=vaadin-framework.git Initial MeasureManager and working Horizontal/VerticalLayout (#8313) --- diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index 4252ab0aa6..064c2404ad 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -12,11 +12,14 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; +import com.google.gwt.core.client.Duration; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.Style; import com.google.gwt.http.client.Request; import com.google.gwt.http.client.RequestBuilder; import com.google.gwt.http.client.RequestCallback; @@ -34,7 +37,6 @@ import com.google.gwt.user.client.ui.Focusable; 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.RenderInformation.Size; import com.vaadin.terminal.gwt.client.ui.Field; import com.vaadin.terminal.gwt.client.ui.VAbstractPaintableWidget; import com.vaadin.terminal.gwt.client.ui.VContextMenu; @@ -1000,6 +1002,8 @@ public class ApplicationConnection { ArrayList updatedVPaintableWidgets = new ArrayList(); componentCaptionSizeChanges.clear(); + Duration updateDuration = new Duration(); + int length = changes.length(); for (int i = 0; i < length; i++) { try { @@ -1045,25 +1049,10 @@ public class ApplicationConnection { json.getValueMap("dd")); } - // Check which widgets' size has been updated - Set sizeUpdatedWidgets = new HashSet(); - - sizeUpdatedWidgets.addAll(componentCaptionSizeChanges); - - for (VPaintableWidget paintable : updatedVPaintableWidgets) { - Widget widget = paintable.getWidgetForPaintable(); - Size oldSize = paintableMap.getOffsetSize(paintable); - Size newSize = new Size(widget.getOffsetWidth(), - widget.getOffsetHeight()); - - if (oldSize == null || !oldSize.equals(newSize)) { - sizeUpdatedWidgets.add(widget); - paintableMap.setOffsetSize(paintable, newSize); - } - - } + VConsole.log("updateFromUidl: " + + updateDuration.elapsedMillis() + " ms"); - Util.componentSizeUpdated(sizeUpdatedWidgets); + doLayout(false); if (meta != null) { if (meta.containsKey("appError")) { @@ -1806,6 +1795,17 @@ public class ApplicationConnection { Widget component = paintableMap.getWidget(paintable); + Style style = component.getElement().getStyle(); + + // Dirty if either dimension changed between relative and non-relative + if (w.endsWith("%") != style.getWidth().endsWith("%") + || h.endsWith("%") != style.getHeight().endsWith("%")) { + MeasureManager.MeasuredSize measuredSize = getMeasuredSize(paintable); + if (measuredSize != null) { + measuredSize.setDirty(true); + } + } + // Set defined sizes component.setHeight(h); component.setWidth(w); @@ -1838,13 +1838,11 @@ public class ApplicationConnection { * development. Published to JavaScript. */ public void forceLayout() { - Set set = new HashSet(); - for (VPaintable paintable : paintableMap.getPaintables()) { - if (paintable instanceof VPaintableWidget) { - set.add(((VPaintableWidget) paintable).getWidgetForPaintable()); - } - } - Util.componentSizeUpdated(set); + Duration duration = new Duration(); + + doLayout(false); + + VConsole.log("forceLayout in " + duration.elapsedMillis() + " ms"); } private void internalRunDescendentsLayout(HasWidgets container) { @@ -2249,4 +2247,27 @@ public class ApplicationConnection { eventIdentifier); } + private boolean layoutScheduled = false; + private ScheduledCommand layoutCommand = new ScheduledCommand() { + public void execute() { + layoutScheduled = false; + + MeasureManager.get().doLayout(ApplicationConnection.this); + } + }; + + public void doLayout(boolean lazy) { + if (!lazy) { + layoutCommand.execute(); + } else if (!layoutScheduled) { + layoutScheduled = true; + Scheduler.get().scheduleDeferred(layoutCommand); + } + } + + public MeasureManager.MeasuredSize getMeasuredSize( + VPaintableWidget paintable) { + return paintableMap.getMeasuredSize(paintable); + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ComponentDetail.java b/src/com/vaadin/terminal/gwt/client/ComponentDetail.java index 04e990ddea..e70a49fdad 100644 --- a/src/com/vaadin/terminal/gwt/client/ComponentDetail.java +++ b/src/com/vaadin/terminal/gwt/client/ComponentDetail.java @@ -6,11 +6,13 @@ package com.vaadin.terminal.gwt.client; import java.util.HashMap; import com.google.gwt.core.client.JsArrayString; +import com.vaadin.terminal.gwt.client.MeasureManager.MeasuredSize; import com.vaadin.terminal.gwt.client.RenderInformation.Size; class ComponentDetail { private TooltipInfo tooltipInfo = new TooltipInfo(); + private MeasureManager.MeasuredSize measuredSize = new MeasureManager.MeasuredSize(); public ComponentDetail() { @@ -108,4 +110,8 @@ class ComponentDetail { } return false; } + + public MeasureManager.MeasuredSize getMeasuredSize() { + return measuredSize; + } } diff --git a/src/com/vaadin/terminal/gwt/client/FastStringSet.java b/src/com/vaadin/terminal/gwt/client/FastStringSet.java new file mode 100644 index 0000000000..e5ba40da0d --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/FastStringSet.java @@ -0,0 +1,40 @@ +package com.vaadin.terminal.gwt.client; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.JsArrayString; + +public final class FastStringSet extends JavaScriptObject { + protected FastStringSet() { + // JSO constructor + } + + public native boolean contains(String string) + /*-{ + return this.hasOwnProperty(string); + }-*/; + + public native void add(String string) + /*-{ + this[string] = true; + }-*/; + + public native void addAll(JsArrayString array) + /*-{ + for(var i = 0; i < array.length; i++) { + this[array[i]] = true; + } + }-*/; + + public native JsArrayString dump() + /*-{ + var array = []; + for(var string in this) { + array.push(string); + } + return array; + }-*/; + + public static FastStringSet create() { + return JavaScriptObject.createObject().cast(); + } +} \ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/MeasureManager.java b/src/com/vaadin/terminal/gwt/client/MeasureManager.java new file mode 100644 index 0000000000..c2e254b1d0 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/MeasureManager.java @@ -0,0 +1,280 @@ +package com.vaadin.terminal.gwt.client; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +import com.google.gwt.core.client.JsArrayString; +import com.google.gwt.user.client.ui.HasWidgets; +import com.google.gwt.user.client.ui.RequiresResize; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ui.VMeasuringOrderedLayout; + +public class MeasureManager { + + public static final class MeasuredSize { + private int width = -1; + private int height = -1; + private boolean isDirty = true; + private int captionWidth = 0; + private int captionHeight = 0; + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + + public void setHeight(int height) { + if (this.height != height) { + isDirty = true; + } + this.height = height; + } + + public void setWidth(int width) { + if (width != this.width) { + isDirty = true; + } + this.width = width; + } + + public boolean isDirty() { + return isDirty; + } + + public void setDirty(boolean isDirty) { + this.isDirty = isDirty; + } + + public void setCaptionHeight(int captionHeight) { + if (captionHeight != this.captionHeight) { + isDirty = true; + } + this.captionHeight = captionHeight; + } + + public void setCaptionWidth(int captionWidth) { + if (captionWidth != this.captionWidth) { + isDirty = true; + } + this.captionWidth = captionWidth; + } + + public int getCaptionHeight() { + return captionHeight; + } + + public int getCaptionWidth() { + return captionWidth; + } + } + + private static MeasureManager instance = new MeasureManager(); + + public static Collection getChildren( + VPaintableWidget paintable, ApplicationConnection client) { + if (!(paintable instanceof Container)) { + return Collections.emptySet(); + } + Widget widget = paintable.getWidgetForPaintable(); + Collection children = new ArrayList(); + + addDescendantPaintables(widget, children, client); + + return children; + } + + private static void addDescendantPaintables(Widget widget, + Collection paintables, + ApplicationConnection client) { + if (widget instanceof HasWidgets) { + VPaintableMap paintableMap = client.getPaintableMap(); + for (Widget child : (HasWidgets) widget) { + VPaintableWidget paintable = paintableMap.getPaintable(child); + if (paintable != null) { + paintables.add(paintable); + } else { + addDescendantPaintables(child, paintables, client); + } + } + } + } + + private static VPaintableWidget getParentPaintable( + VPaintableWidget paintable, VPaintableMap paintableMap) { + Widget widget = paintable.getWidgetForPaintable(); + while (true) { + widget = widget.getParent(); + if (widget == null) { + return null; + } + VPaintableWidget parentPaintable = paintableMap + .getPaintable(widget); + if (parentPaintable != null) { + return parentPaintable; + } + // Else continue with the parent + } + } + + public void doLayout(ApplicationConnection client) { + VPaintableMap paintableMap = client.getPaintableMap(); + VPaintableWidget[] paintableWidgets = paintableMap + .getRegisteredPaintableWidgets(); + + int passes = 0; + long start = System.currentTimeMillis(); + while (true) { + long passStart = System.currentTimeMillis(); + passes++; + long measureStart = System.currentTimeMillis(); + FastStringSet changedSet = findChangedWidgets(paintableWidgets, + paintableMap); + JsArrayString changed = changedSet.dump(); + long measureEnd = System.currentTimeMillis(); + + VConsole.log("Measure in " + (measureEnd - measureStart) + " ms"); + + if (changed.length() == 0) { + VConsole.log("No more changes in pass " + passes); + break; + } + + if (passes > 100) { + VConsole.log("Aborting layout"); + break; + } + + FastStringSet affectedContainers = FastStringSet.create(); + for (int i = 0; i < changed.length(); i++) { + VPaintableWidget paintable = (VPaintableWidget) paintableMap + .getPaintable(changed.get(i)); + VPaintableWidget parentPaintable = getParentPaintable( + paintable, paintableMap); + if (parentPaintable instanceof Container) { + affectedContainers + .add(paintableMap.getPid(parentPaintable)); + } + } + + long layoutStart = System.currentTimeMillis(); + for (int i = 0; i < changed.length(); i++) { + String pid = changed.get(i); + VPaintableWidget paintable = (VPaintableWidget) paintableMap + .getPaintable(pid); + if (!affectedContainers.contains(pid)) { + Widget widget = paintable.getWidgetForPaintable(); + if (widget instanceof RequiresResize) { + // TODO Do nothing here if parent instanceof + // ProvidesRepaint? + ((RequiresResize) widget).onResize(); + } + } + } + + JsArrayString affectedPids = affectedContainers.dump(); + for (int i = 0; i < affectedPids.length(); i++) { + // Find all changed children + String containerPid = affectedPids.get(i); + VPaintableWidget container = (VPaintableWidget) paintableMap + .getPaintable(containerPid); + Collection children = getChildren(container, + client); + HashSet changedChildren = new HashSet(); + + for (VPaintableWidget child : children) { + if (changedSet.contains(paintableMap.getPid(child))) { + changedChildren.add(child.getWidgetForPaintable()); + } + } + + ((Container) container).requestLayout(changedChildren); + } + + long layoutEnd = System.currentTimeMillis(); + VConsole.log(affectedPids.length() + + " requestLayout invocations in " + + (layoutEnd - layoutStart) + "ms"); + + long passEnd = System.currentTimeMillis(); + StringBuilder b = new StringBuilder(); + b.append(changed.length()); + b.append(" changed widgets in pass "); + b.append(passes); + b.append(" in "); + b.append((passEnd - passStart)); + b.append(" ms: "); + if (changed.length() < 10) { + for (int i = 0; i < changed.length(); i++) { + if (i != 0) { + b.append(", "); + } + b.append(changed.get(i)); + } + } + VConsole.log(b.toString()); + } + long end = System.currentTimeMillis(); + VConsole.log("Total layout time: " + (end - start) + "ms"); + } + + private FastStringSet findChangedWidgets( + VPaintableWidget[] paintableWidgets, VPaintableMap paintableMap) { + + FastStringSet changed = FastStringSet.create(); + for (VPaintableWidget paintableWidget : paintableWidgets) { + Widget widget = paintableWidget.getWidgetForPaintable(); + if (paintableWidget instanceof VMeasuringOrderedLayout) { + VMeasuringOrderedLayout hasCaptions = (VMeasuringOrderedLayout) paintableWidget; + Collection childCaptions = hasCaptions + .getChildCaptions(); + for (VCaption vCaption : childCaptions) { + VPaintableWidget captionOwner = vCaption.getOwner(); + MeasureManager.MeasuredSize measuredSize = paintableMap + .getMeasuredSize(captionOwner); + + int captionHeight = vCaption.getOffsetHeight(); + int captionWidth = vCaption.getOffsetWidth(); + if (captionHeight == 0 || captionWidth == 0) { + // Empty caption is probably detached + if (!Util.isAttachedAndDisplayed(vCaption)) { + // Ignore if it is detached + continue; + } + } + measuredSize.setCaptionHeight(captionHeight); + measuredSize.setCaptionWidth(captionWidth); + if (measuredSize.isDirty()) { + changed.add(paintableMap.getPid(captionOwner)); + measuredSize.setDirty(false); + } + } + } + + MeasureManager.MeasuredSize measuredSize = paintableMap + .getMeasuredSize(paintableWidget); + + measuredSize.setWidth(widget.getOffsetWidth()); + measuredSize.setHeight(widget.getOffsetHeight()); + + if (measuredSize.isDirty()) { + changed.add(paintableMap.getPid(paintableWidget)); + measuredSize.setDirty(false); + } + } + + return changed; + } + + private MeasureManager() { + // Singleton constructor + } + + public static MeasureManager get() { + return instance; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/Util.java b/src/com/vaadin/terminal/gwt/client/Util.java index 0a3d83ff3d..39b44a506c 100644 --- a/src/com/vaadin/terminal/gwt/client/Util.java +++ b/src/com/vaadin/terminal/gwt/client/Util.java @@ -5,11 +5,7 @@ package com.vaadin.terminal.gwt.client; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; +import java.util.List; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; @@ -24,7 +20,6 @@ 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.EventListener; -import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.RootPanel; @@ -65,30 +60,6 @@ public class Util { return el; }-*/; - private static final int LAZY_SIZE_CHANGE_TIMEOUT = 400; - private static Set latelyChangedWidgets = new HashSet(); - - private static Timer lazySizeChangeTimer = new Timer() { - private boolean lazySizeChangeTimerScheduled = false; - - @Override - public void run() { - componentSizeUpdated(latelyChangedWidgets); - latelyChangedWidgets.clear(); - lazySizeChangeTimerScheduled = false; - } - - @Override - public void schedule(int delayMillis) { - if (lazySizeChangeTimerScheduled) { - cancel(); - } else { - lazySizeChangeTimerScheduled = true; - } - super.schedule(delayMillis); - } - }; - /** * This helper method can be called if components size have been changed * outside rendering phase. It notifies components parent about the size @@ -106,58 +77,40 @@ public class Util { * run componentSizeUpdated lazyly */ public static void notifyParentOfSizeChange(Widget widget, boolean lazy) { - if (lazy) { - latelyChangedWidgets.add(widget); - lazySizeChangeTimer.schedule(LAZY_SIZE_CHANGE_TIMEOUT); - } else { - Set widgets = new HashSet(); - widgets.add(widget); - Util.componentSizeUpdated(widgets); + ApplicationConnection applicationConnection = findApplicationConnectionFor(widget); + if (applicationConnection != null) { + applicationConnection.doLayout(lazy); } } - /** - * Called when the size of one or more widgets have changed during - * rendering. Finds parent container and notifies them of the size change. - * - * @param paintables - */ - public static void componentSizeUpdated(Set widgets) { - if (widgets.isEmpty()) { - return; - } + private static boolean findAppConnectionWarningDisplayed = false; - Map> childWidgets = new HashMap>(); + private static ApplicationConnection findApplicationConnectionFor( + Widget widget) { + if (!findAppConnectionWarningDisplayed) { + findAppConnectionWarningDisplayed = true; + VConsole.log("Warning: Using Util.findApplicationConnectionFor which should be eliminated once there is a better way to find the ApplicationConnection for a Paintable"); + } - for (Widget widget : widgets) { - if (!widget.isAttached()) { + List runningApplications = ApplicationConfiguration + .getRunningApplications(); + for (ApplicationConnection applicationConnection : runningApplications) { + VPaintableMap paintableMap = applicationConnection + .getPaintableMap(); + VPaintableWidget paintable = paintableMap.getPaintable(widget); + if (paintable == null) { continue; } - - // ApplicationConnection.getConsole().log( - // "Widget " + Util.getSimpleName(widget) + " size updated"); - Widget parent = widget.getParent(); - while (parent != null && !(parent instanceof Container)) { - parent = parent.getParent(); - } - if (parent != null) { - Set set = childWidgets.get(parent); - if (set == null) { - set = new HashSet(); - childWidgets.put((Container) parent, set); + String pid = paintableMap.getPid(paintable); + if (pid != null) { + VPaintable otherPaintable = paintableMap.getPaintable(pid); + if (otherPaintable == paintable) { + return applicationConnection; } - set.add(widget); - } - } - - Set parentChanges = new HashSet(); - for (Container parent : childWidgets.keySet()) { - if (!parent.requestLayout(childWidgets.get(parent))) { - parentChanges.add(parent.getWidgetForPaintable()); } } - componentSizeUpdated(parentChanges); + return null; } public static float parseRelativeSize(String size) { @@ -614,20 +567,7 @@ public class Util { public static void updateRelativeChildrenAndSendSizeUpdateEvent( ApplicationConnection client, HasWidgets container, Widget widget) { - /* - * Relative sized children must be updated first so the component has - * the correct outer dimensions when signaling a size change to the - * parent. - */ - Iterator childIterator = container.iterator(); - while (childIterator.hasNext()) { - Widget w = childIterator.next(); - client.handleComponentRelativeSize(w); - } - - HashSet widgets = new HashSet(); - widgets.add(widget); - Util.componentSizeUpdated(widgets); + notifyParentOfSizeChange(widget, false); } public static native int getRequiredWidth( diff --git a/src/com/vaadin/terminal/gwt/client/VPaintableMap.java b/src/com/vaadin/terminal/gwt/client/VPaintableMap.java index c810d56e51..31aec87aa0 100644 --- a/src/com/vaadin/terminal/gwt/client/VPaintableMap.java +++ b/src/com/vaadin/terminal/gwt/client/VPaintableMap.java @@ -3,6 +3,7 @@ */ package com.vaadin.terminal.gwt.client; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -16,6 +17,7 @@ import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.Paintable; +import com.vaadin.terminal.gwt.client.MeasureManager.MeasuredSize; import com.vaadin.terminal.gwt.client.RenderInformation.Size; public class VPaintableMap { @@ -208,6 +210,21 @@ public class VPaintableMap { } + public VPaintableWidget[] getRegisteredPaintableWidgets() { + ArrayList result = new ArrayList(); + + for (VPaintable paintable : getPaintables()) { + if (paintable instanceof VPaintableWidget) { + VPaintableWidget paintableWidget = (VPaintableWidget) paintable; + if (!unregistryBag.contains(getPid(paintable))) { + result.add(paintableWidget); + } + } + } + + return result.toArray(new VPaintableWidget[result.size()]); + } + void purgeUnregistryBag(boolean unregisterPaintables) { if (unregisterPaintables) { for (String pid : unregistryBag) { @@ -375,4 +392,17 @@ public class VPaintableMap { return getPid(w) != null; } + /** + * FIXME: Should not be here + */ + @Deprecated + public MeasuredSize getMeasuredSize(VPaintableWidget paintable) { + ComponentDetail componentDetail = getComponentDetail(paintable); + if (componentDetail == null) { + return null; + } + + return componentDetail.getMeasuredSize(); + } + } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java index b3a036f748..30796b1660 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VHorizontalLayout.java @@ -3,12 +3,12 @@ */ package com.vaadin.terminal.gwt.client.ui; -public class VHorizontalLayout extends VOrderedLayout { +public class VHorizontalLayout extends VMeasuringOrderedLayout { public static final String CLASSNAME = "v-horizontallayout"; public VHorizontalLayout() { - super(CLASSNAME, ORIENTATION_HORIZONTAL); + super(CLASSNAME, false); } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VMeasuringOrderedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VMeasuringOrderedLayout.java new file mode 100644 index 0000000000..817cf9bfe0 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/VMeasuringOrderedLayout.java @@ -0,0 +1,539 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client.ui; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Overflow; +import com.google.gwt.dom.client.Style.Position; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.RequiresResize; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.Container; +import com.vaadin.terminal.gwt.client.MeasureManager; +import com.vaadin.terminal.gwt.client.RenderSpace; +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; +import com.vaadin.terminal.gwt.client.ValueMap; + +public class VMeasuringOrderedLayout extends ComplexPanel implements Container, + RequiresResize { + + public static final String CLASSNAME = "v-orderedlayout"; + + private static final int MARGIN_SIZE = 20; + + private final boolean isVertical; + + private ApplicationConnection client; + + private String id; + + private RenderSpace space; + + private ValueMap expandRatios; + + private ValueMap alignments; + + private Map captions = new HashMap(); + + private boolean spacing; + + private VMarginInfo activeMarginsInfo; + + protected VMeasuringOrderedLayout(String className, boolean isVertical) { + DivElement element = Document.get().createDivElement(); + setElement(element); + // TODO These should actually be defined in css + Style style = element.getStyle(); + style.setOverflow(Overflow.HIDDEN); + style.setPosition(Position.RELATIVE); + + setStyleName(className); + this.isVertical = isVertical; + } + + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + this.client = client; + id = uidl.getId(); + if (client.updateComponent(this, uidl, true)) { + return; + } + + long start = System.currentTimeMillis(); + // long childTime = 0; + + HashSet previousChildren = new HashSet(); + for (Widget child : this) { + 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() != this) { + DivElement wrapper = Document.get().createDivElement(); + wrapper.getStyle().setPosition(Position.ABSOLUTE); + getElement().appendChild(wrapper); + add(widget, wrapper); + } + + if (!childUIDL.getBooleanAttribute("cached")) { + child.updateFromUIDL(childUIDL, client); + client.getMeasuredSize(child).setDirty(true); + } + // TODO Update alignments and expand ratios + + previousChildren.remove(widget); + } + + for (Widget widget : previousChildren) { + Element wrapper = getWrapper(widget); + VCaption caption = captions.remove(widget); + if (caption != null) { + remove(caption); + } + remove(widget); + // Remove the wrapper + getElement().removeChild(wrapper); + + client.unregisterPaintable((VPaintableWidget) widget); + } + + int bitMask = uidl.getIntAttribute("margins"); + if (activeMarginsInfo == null + || activeMarginsInfo.getBitMask() != bitMask) { + activeMarginsInfo = new VMarginInfo(bitMask); + } + + spacing = uidl.getBooleanAttribute("spacing"); + expandRatios = uidl.getMapAttribute("expandRatios"); + alignments = uidl.getMapAttribute("alignments"); + client.getMeasuredSize(this).setDirty(true); + } + + private static Element getWrapper(Widget widget) { + return widget.getElement().getParentElement(); + } + + private void add(Widget widget, DivElement wrapper) { + add(widget, (com.google.gwt.user.client.Element) wrapper.cast()); + } + + public void onResize() { + requestLayout(Collections. emptySet()); + } + + private static boolean isUndefinedInDirection(Widget widget, + boolean isVertical) { + String dimension = getDimensionInDirection(widget, isVertical); + return dimension == null || dimension.length() == 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(); + } + } + + public void replaceChildComponent(Widget oldComponent, Widget newComponent) { + throw new UnsupportedOperationException(); + } + + public boolean hasChildComponent(Widget component) { + return component.getParent() == this; + } + + public void updateCaption(VPaintableWidget component, UIDL uidl) { + if (VCaption.isNeeded(uidl)) { + VCaption caption = captions.get(component); + if (caption == null) { + caption = new VCaption(component, client); + + Widget widget = (Widget) component; + Element wrapper = getWrapper(widget); + + // Logical attach. + getChildren().add(caption); + + // Physical attach. + DOM.insertBefore( + (com.google.gwt.user.client.Element) wrapper.cast(), + caption.getElement(), widget.getElement()); + + // Adopt. + adopt(caption); + captions.put(component, caption); + } + caption.updateCaption(uidl); + } else { + VCaption removedCaption = captions.remove(component); + if (removedCaption != null) { + remove(removedCaption); + MeasureManager.MeasuredSize measuredSize = client + .getMeasuredSize(component); + measuredSize.setCaptionHeight(0); + measuredSize.setCaptionWidth(0); + } + } + + } + + private void layoutPrimaryDirection() { + Collection children = MeasureManager.getChildren( + this, client); + + // First pass - get total expand ratio and allocated size + int totalAllocated = 0; + double totalExpand = 0; + for (VPaintableWidget child : children) { + Widget widget = child.getWidgetForPaintable(); + + totalExpand += getExpandRatio(child); + + int captionAllocation; + if (isVertical) { + captionAllocation = getCaptionHeight(child); + getWrapper(widget).getStyle().setPaddingTop(captionAllocation, + Unit.PX); + } else { + captionAllocation = 0; + } + + if (!isRelativeInDirection(widget, isVertical)) { + totalAllocated += getMeasuredInDirection(child, isVertical) + + captionAllocation; + } + } + int startMargin = getStartMarginInDirection(isVertical); + int totalMargins = startMargin + getEndMarginInDirection(isVertical); + + totalAllocated += totalMargins + + (getSpacingInDirection(isVertical) * (children.size() - 1)); + + Style ownStyle = getElement().getStyle(); + double ownSize; + if (isUndefinedInDirection(this, isVertical)) { + ownSize = totalAllocated; + ownStyle.setProperty(getMinPropertyName(isVertical), + totalAllocated, Unit.PX); + } else { + ownSize = getMeasuredInDirection(this, isVertical); + ownStyle.clearProperty(getMinPropertyName(isVertical)); + } + + double unallocatedSpace = Math.max(0, ownSize - totalAllocated); + + double currentLocation = startMargin; + for (VPaintableWidget child : children) { + Widget widget = child.getWidgetForPaintable(); + Element wrapper = getWrapper(widget); + Style wrapperStyle = wrapper.getStyle(); + + double childExpandRatio; + if (totalExpand == 0) { + childExpandRatio = 1d / children.size(); + } else { + childExpandRatio = getExpandRatio(child) / totalExpand; + } + + double extraPixels = unallocatedSpace * childExpandRatio; + + boolean relative = isRelativeInDirection(widget, isVertical); + + double size = getMeasuredInDirection(child, isVertical); + int captionHeight = getCaptionHeight(child); + + if (isVertical) { + size += captionHeight; + } else if (!relative) { + size = Math.max(size, getCaptionWidth(child)); + } + + double allocatedSpace = extraPixels; + if (!relative) { + allocatedSpace += size; + } + + int alignment = getAlignmentInDirection(getAlignment(child), + isVertical); + + if (relative) { + double captionReservation = isVertical ? captionHeight : 0; + wrapperStyle.setProperty(getSizeProperty(isVertical), + allocatedSpace - captionReservation, Unit.PX); + } else { + wrapperStyle.clearProperty(getSizeProperty(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(isVertical), + startPosition, Unit.PX); + + currentLocation += allocatedSpace + + getSpacingInDirection(isVertical); + } + } + + private int getEndMarginInDirection(boolean isVertical) { + if (isVertical) { + return activeMarginsInfo.hasBottom() ? MARGIN_SIZE : 0; + } else { + return activeMarginsInfo.hasRight() ? MARGIN_SIZE : 0; + } + } + + private int getStartMarginInDirection(boolean isVertical) { + if (isVertical) { + return activeMarginsInfo.hasTop() ? MARGIN_SIZE : 0; + } else { + return activeMarginsInfo.hasLeft() ? MARGIN_SIZE : 0; + } + } + + private void layoutSecondaryDirection() { + Collection children = MeasureManager.getChildren( + this, client); + + int maxSize = 0; + for (VPaintableWidget child : children) { + Widget widget = child.getWidgetForPaintable(); + + int captionAllocation; + if (!isVertical) { + captionAllocation = getCaptionHeight(child); + getWrapper(widget).getStyle().setPaddingTop(captionAllocation, + Unit.PX); + } else { + captionAllocation = 0; + } + + if (!isRelativeInDirection(widget, !isVertical)) { + int childSize = getMeasuredInDirection(child, !isVertical) + + captionAllocation; + maxSize = Math.max(maxSize, childSize); + } + } + + int startMargin = getStartMarginInDirection(!isVertical); + int totalMargins = startMargin + getEndMarginInDirection(!isVertical); + + double availableSpace; + Style ownStyle = getElement().getStyle(); + + if (isUndefinedInDirection(this, !isVertical)) { + ownStyle.setProperty(getMinPropertyName(!isVertical), maxSize + + totalMargins, Unit.PX); + availableSpace = maxSize; + } else { + ownStyle.clearProperty(getMinPropertyName(!isVertical)); + availableSpace = getMeasuredInDirection(this, !isVertical) + - totalMargins; + } + + for (VPaintableWidget child : children) { + Widget widget = child.getWidgetForPaintable(); + Element wrapper = getWrapper(widget); + Style wrapperStyle = wrapper.getStyle(); + + boolean relative = isRelativeInDirection(widget, !isVertical); + + int captionHeight = getCaptionHeight(child); + + double allocatedSize = getMeasuredInDirection(child, !isVertical); + if (!isVertical) { + allocatedSize += captionHeight; + } else if (!relative) { + allocatedSize = Math.max(allocatedSize, getCaptionWidth(child)); + } + + int alignment = getAlignmentInDirection(getAlignment(child), + !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(!isVertical), + startPosition, Unit.PX); + + if (relative) { + double captionReservation = !isVertical ? captionHeight : 0; + wrapperStyle.setProperty(getSizeProperty(!isVertical), + availableSpace - captionReservation, Unit.PX); + } else { + wrapperStyle.clearProperty(getSizeProperty(!isVertical)); + } + } + } + + public boolean requestLayout(Set changed) { + layoutPrimaryDirection(); + layoutSecondaryDirection(); + + // Doesn't matter right now... + return true; + } + + 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 int getSpacingInDirection(boolean isVertical) { + if (spacing) { + return 20; + } else { + return 0; + } + } + + private int getCaptionWidth(VPaintableWidget child) { + MeasureManager.MeasuredSize measuredSize = client + .getMeasuredSize(child); + return measuredSize.getCaptionWidth(); + } + + private int getCaptionHeight(VPaintableWidget child) { + MeasureManager.MeasuredSize measuredSize = client + .getMeasuredSize(child); + int captionHeight = measuredSize.getCaptionHeight(); + + VCaption caption = captions.get(child); + if (caption != null) { + caption.getElement().getStyle() + .setMarginTop(-captionHeight, Unit.PX); + } + return captionHeight; + } + + private AlignmentInfo getAlignment(VPaintableWidget child) { + String pid = VPaintableMap.get(client).getPid(child); + if (alignments.containsKey(pid)) { + return new AlignmentInfo(alignments.getInt(pid)); + } else { + return AlignmentInfo.TOP_LEFT; + } + } + + private double getExpandRatio(VPaintableWidget child) { + String pid = VPaintableMap.get(client).getPid(child); + if (expandRatios.containsKey(pid)) { + return expandRatios.getRawNumber(pid); + } else { + return 0; + } + } + + 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 int getMeasuredInDirection(VPaintableWidget paintable, + boolean isVertical) { + MeasureManager.MeasuredSize measuredSize = client + .getMeasuredSize(paintable); + if (isVertical) { + return measuredSize.getHeight(); + } else { + return measuredSize.getWidth(); + } + } + + public Collection getChildCaptions() { + return captions.values(); + } + + public RenderSpace getAllocatedSpace(Widget child) { + // Concept borrowed from CSS layout + if (space == null) { + space = new RenderSpace(-1, -1) { + @Override + public int getWidth() { + return getOffsetWidth(); + } + + @Override + public int getHeight() { + return getOffsetHeight(); + } + }; + } + return space; + } + + public Widget getWidgetForPaintable() { + return this; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java deleted file mode 100644 index f490e5176d..0000000000 --- a/src/com/vaadin/terminal/gwt/client/ui/VOrderedLayout.java +++ /dev/null @@ -1,976 +0,0 @@ -/* -@VaadinApache2LicenseForJavaFiles@ - */ -package com.vaadin.terminal.gwt.client.ui; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Set; - -import com.google.gwt.core.client.JsArrayString; -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.event.dom.client.DomEvent.Type; -import com.google.gwt.event.shared.EventHandler; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.ApplicationConnection; -import com.vaadin.terminal.gwt.client.BrowserInfo; -import com.vaadin.terminal.gwt.client.EventId; -import com.vaadin.terminal.gwt.client.RenderInformation.FloatSize; -import com.vaadin.terminal.gwt.client.RenderInformation.Size; -import com.vaadin.terminal.gwt.client.RenderSpace; -import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; -import com.vaadin.terminal.gwt.client.VPaintableMap; -import com.vaadin.terminal.gwt.client.VPaintableWidget; -import com.vaadin.terminal.gwt.client.ValueMap; -import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout; -import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer; - -public class VOrderedLayout extends CellBasedLayout { - - public static final String CLASSNAME = "v-orderedlayout"; - - private int orientation; - - // Can be removed once OrderedLayout is removed - private boolean allowOrientationUpdate = false; - - /** - * Size of the layout excluding any margins. - */ - private Size activeLayoutSize = new Size(0, 0); - - private boolean isRendering = false; - - private String width = ""; - - private boolean sizeHasChangedDuringRendering = false; - - private ValueMap expandRatios; - - private double expandRatioSum; - - private double defaultExpandRatio; - - private ValueMap alignments; - - private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler( - this, EventId.LAYOUT_CLICK) { - - @Override - protected VPaintableWidget getChildComponent(Element element) { - return getComponent(element); - } - - @Override - protected HandlerRegistration registerHandler( - H handler, Type type) { - return addDomHandler(handler, type); - } - }; - - public VOrderedLayout() { - this(CLASSNAME, ORIENTATION_VERTICAL); - allowOrientationUpdate = true; - } - - protected VOrderedLayout(String className, int orientation) { - setStyleName(className); - this.orientation = orientation; - - STYLENAME_SPACING = className + "-spacing"; - STYLENAME_MARGIN_TOP = className + "-margin-top"; - STYLENAME_MARGIN_RIGHT = className + "-margin-right"; - STYLENAME_MARGIN_BOTTOM = className + "-margin-bottom"; - STYLENAME_MARGIN_LEFT = className + "-margin-left"; - } - - @Override - public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - isRendering = true; - super.updateFromUIDL(uidl, client); - - // Only non-cached, visible UIDL:s can introduce changes - if (uidl.getBooleanAttribute("cached") - || uidl.getBooleanAttribute("invisible")) { - isRendering = false; - return; - } - - clickEventHandler.handleEventHandlerRegistration(client); - - if (allowOrientationUpdate) { - handleOrientationUpdate(uidl); - } - - // IStopWatch w = new IStopWatch("OrderedLayout.updateFromUIDL"); - - ArrayList uidlWidgets = new ArrayList( - uidl.getChildCount()); - ArrayList relativeSizeComponents = new ArrayList(); - ArrayList relativeSizeComponentUIDL = new ArrayList(); - - int pos = 0; - for (final Iterator it = uidl.getChildIterator(); it.hasNext();) { - final UIDL childUIDL = (UIDL) it.next(); - final VPaintableWidget childPaintable = client - .getPaintable(childUIDL); - Widget widget = childPaintable.getWidgetForPaintable(); - - // Create container for component - ChildComponentContainer childComponentContainer = getComponentContainer(widget); - - if (childComponentContainer == null) { - // This is a new component - childComponentContainer = createChildContainer(childPaintable); - } else { - /* - * The widget may be null if the same paintable has been - * rendered in a different component container while this has - * been invisible. Ensure the childComponentContainer has the - * widget attached. See e.g. #5372 - */ - childComponentContainer.setPaintable(childPaintable); - } - - addOrMoveChild(childComponentContainer, pos++); - - /* - * Components which are to be expanded in the same orientation as - * the layout are rendered later when it is clear how much space - * they can use - */ - if (!Util.isCached(childUIDL)) { - FloatSize relativeSize = Util.parseRelativeSize(childUIDL); - childComponentContainer.setRelativeSize(relativeSize); - } - - if (childComponentContainer.isComponentRelativeSized(orientation)) { - relativeSizeComponents.add(childComponentContainer); - relativeSizeComponentUIDL.add(childUIDL); - } else { - if (isDynamicWidth()) { - childComponentContainer.renderChild(childUIDL, client, -1); - } else { - childComponentContainer.renderChild(childUIDL, client, - activeLayoutSize.getWidth()); - } - if (sizeHasChangedDuringRendering && Util.isCached(childUIDL)) { - // notify cached relative sized component about size - // chance - client.handleComponentRelativeSize(childComponentContainer - .getWidget()); - } - } - - uidlWidgets.add(widget); - - } - - // w.mark("Rendering of " - // + (uidlWidgets.size() - relativeSizeComponents.size()) - // + " absolute size components done"); - - /* - * Remove any children after pos. These are the ones that previously - * were in the layout but have now been removed - */ - removeChildrenAfter(pos); - - // w.mark("Old children removed"); - - /* Fetch alignments and expand ratio from UIDL */ - updateAlignmentsAndExpandRatios(uidl, uidlWidgets); - // w.mark("Alignments and expand ratios updated"); - - /* Fetch widget sizes from rendered components */ - updateWidgetSizes(); - // w.mark("Widget sizes updated"); - - recalculateLayout(); - // w.mark("Layout size calculated (" + activeLayoutSize + - // ") offsetSize: " - // + getOffsetWidth() + "," + getOffsetHeight()); - - /* Render relative size components */ - for (int i = 0; i < relativeSizeComponents.size(); i++) { - ChildComponentContainer childComponentContainer = relativeSizeComponents - .get(i); - UIDL childUIDL = relativeSizeComponentUIDL.get(i); - - if (isDynamicWidth()) { - childComponentContainer.renderChild(childUIDL, client, -1); - } else { - childComponentContainer.renderChild(childUIDL, client, - activeLayoutSize.getWidth()); - } - - if (Util.isCached(childUIDL)) { - /* - * We must update the size of the relative sized component if - * the expand ratio or something else in the layout changes - * which affects the size of a relative sized component - */ - client.handleComponentRelativeSize(childComponentContainer - .getWidget()); - } - - // childComponentContainer.updateWidgetSize(); - } - - // w.mark("Rendering of " + (relativeSizeComponents.size()) - // + " relative size components done"); - - /* Fetch widget sizes for relative size components */ - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - - /* Update widget size from DOM */ - childComponentContainer.updateWidgetSize(); - } - - // w.mark("Widget sizes updated"); - - /* - * Components with relative size in main direction may affect the layout - * size in the other direction - */ - if ((isHorizontal() && isDynamicHeight()) - || (isVertical() && isDynamicWidth())) { - layoutSizeMightHaveChanged(); - } - // w.mark("Layout dimensions updated"); - - /* Update component spacing */ - updateContainerMargins(); - - /* - * Update component sizes for components with relative size in non-main - * direction - */ - if (updateRelativeSizesInNonMainDirection()) { - // Sizes updated - might affect the other dimension so we need to - // recheck the widget sizes and recalculate layout dimensions - updateWidgetSizes(); - layoutSizeMightHaveChanged(); - } - calculateAlignments(); - // w.mark("recalculateComponentSizesAndAlignments done"); - - setRootSize(); - - if (BrowserInfo.get().isIE()) { - /* - * This should fix the issue with padding not always taken into - * account for the containers leading to no spacing between - * elements. - */ - root.getStyle().setProperty("zoom", "1"); - } - - // w.mark("runDescendentsLayout done"); - isRendering = false; - sizeHasChangedDuringRendering = false; - } - - private void layoutSizeMightHaveChanged() { - Size oldSize = new Size(activeLayoutSize.getWidth(), - activeLayoutSize.getHeight()); - calculateLayoutDimensions(); - - /* - * If layout dimension changes we must also update container sizes - */ - if (!oldSize.equals(activeLayoutSize)) { - calculateContainerSize(); - } - } - - private void updateWidgetSizes() { - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - - /* - * Update widget size from DOM - */ - childComponentContainer.updateWidgetSize(); - } - } - - private void recalculateLayout() { - - /* Calculate space for relative size components */ - int spaceForExpansion = calculateLayoutDimensions(); - - if (!widgetToComponentContainer.isEmpty()) { - /* Divide expansion space between component containers */ - expandComponentContainers(spaceForExpansion); - - /* Update container sizes */ - calculateContainerSize(); - } - - } - - private void expandComponentContainers(int spaceForExpansion) { - int remaining = spaceForExpansion; - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - remaining -= childComponentContainer.expand(orientation, - spaceForExpansion); - } - - if (remaining > 0) { - - // Some left-over pixels due to rounding errors - - // Add one pixel to each container until there are no pixels left - // FIXME extra pixels should be divided among expanded widgets if - // such a widgets exists - - Iterator widgetIterator = iterator(); - while (widgetIterator.hasNext() && remaining-- > 0) { - ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator - .next(); - childComponentContainer.expandExtra(orientation, 1); - } - } - - } - - private void handleOrientationUpdate(UIDL uidl) { - int newOrientation = ORIENTATION_VERTICAL; - if ("horizontal".equals(uidl.getStringAttribute("orientation"))) { - newOrientation = ORIENTATION_HORIZONTAL; - } - - if (orientation != newOrientation) { - orientation = newOrientation; - - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - childComponentContainer.setOrientation(orientation); - } - } - - } - - /** - * Updated components with relative height in horizontal layouts and - * components with relative width in vertical layouts. This is only needed - * if the height (horizontal layout) or width (vertical layout) has not been - * specified. - */ - private boolean updateRelativeSizesInNonMainDirection() { - int updateDirection = 1 - orientation; - if ((updateDirection == ORIENTATION_HORIZONTAL && !isDynamicWidth()) - || (updateDirection == ORIENTATION_VERTICAL && !isDynamicHeight())) { - return false; - } - - boolean updated = false; - for (ChildComponentContainer componentContainer : widgetToComponentContainer - .values()) { - if (componentContainer.isComponentRelativeSized(updateDirection)) { - client.handleComponentRelativeSize(componentContainer - .getWidget()); - } - - updated = true; - } - - return updated; - } - - private int calculateLayoutDimensions() { - int summedWidgetWidth = 0; - int summedWidgetHeight = 0; - - int maxWidgetWidth = 0; - int maxWidgetHeight = 0; - - // Calculate layout dimensions from component dimensions - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - - int widgetHeight = 0; - int widgetWidth = 0; - if (childComponentContainer.isComponentRelativeSized(orientation)) { - if (orientation == ORIENTATION_HORIZONTAL) { - widgetHeight = getWidgetHeight(childComponentContainer); - } else { - widgetWidth = getWidgetWidth(childComponentContainer); - } - } else { - widgetWidth = getWidgetWidth(childComponentContainer); - widgetHeight = getWidgetHeight(childComponentContainer); - } - - summedWidgetWidth += widgetWidth; - summedWidgetHeight += widgetHeight; - - maxWidgetHeight = Math.max(maxWidgetHeight, widgetHeight); - maxWidgetWidth = Math.max(maxWidgetWidth, widgetWidth); - } - - if (isHorizontal()) { - summedWidgetWidth += activeSpacing.hSpacing - * (widgetToComponentContainer.size() - 1); - } else { - summedWidgetHeight += activeSpacing.vSpacing - * (widgetToComponentContainer.size() - 1); - } - - Size layoutSize = updateLayoutDimensions(summedWidgetWidth, - summedWidgetHeight, maxWidgetWidth, maxWidgetHeight); - - int remainingSpace; - if (isHorizontal()) { - remainingSpace = layoutSize.getWidth() - summedWidgetWidth; - } else { - remainingSpace = layoutSize.getHeight() - summedWidgetHeight; - } - if (remainingSpace < 0) { - remainingSpace = 0; - } - - // ApplicationConnection.getConsole().log( - // "Layout size: " + activeLayoutSize); - return remainingSpace; - } - - private int getWidgetHeight(ChildComponentContainer childComponentContainer) { - Size s = childComponentContainer.getWidgetSize(); - return s.getHeight() - + childComponentContainer.getCaptionHeightAboveComponent(); - } - - private int getWidgetWidth(ChildComponentContainer childComponentContainer) { - Size s = childComponentContainer.getWidgetSize(); - int widgetWidth = s.getWidth() - + childComponentContainer.getCaptionWidthAfterComponent(); - - /* - * If the component does not have a specified size in the main direction - * the caption may determine the space used by the component - */ - if (!childComponentContainer.widgetHasSizeSpecified(orientation)) { - int captionWidth = childComponentContainer - .getCaptionRequiredWidth(); - - if (captionWidth > widgetWidth) { - widgetWidth = captionWidth; - } - } - - return widgetWidth; - } - - private void calculateAlignments() { - int w = 0; - int h = 0; - - if (isHorizontal()) { - // HORIZONTAL - h = activeLayoutSize.getHeight(); - if (!isDynamicWidth()) { - w = -1; - } - - } else { - // VERTICAL - w = activeLayoutSize.getWidth(); - if (!isDynamicHeight()) { - h = -1; - } - } - - for (ChildComponentContainer childComponentContainer : widgetToComponentContainer - .values()) { - childComponentContainer.updateAlignments(w, h); - } - - } - - private void calculateContainerSize() { - - /* - * Container size here means the size the container gets from the - * component. The expansion size is not include in this but taken - * separately into account. - */ - int height = 0, width = 0; - Iterator widgetIterator = iterator(); - if (isHorizontal()) { - height = activeLayoutSize.getHeight(); - int availableWidth = activeLayoutSize.getWidth(); - boolean first = true; - while (widgetIterator.hasNext()) { - ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator - .next(); - if (!childComponentContainer - .isComponentRelativeSized(ORIENTATION_HORIZONTAL)) { - /* - * Only components with non-relative size in the main - * direction has a container size - */ - width = childComponentContainer.getWidgetSize().getWidth() - + childComponentContainer - .getCaptionWidthAfterComponent(); - - /* - * If the component does not have a specified size in the - * main direction the caption may determine the space used - * by the component - */ - if (!childComponentContainer - .widgetHasSizeSpecified(orientation)) { - int captionWidth = childComponentContainer - .getCaptionRequiredWidth(); - // ApplicationConnection.getConsole().log( - // "Component width: " + width - // + ", caption width: " + captionWidth); - if (captionWidth > width) { - width = captionWidth; - } - } - } else { - width = 0; - } - - if (!isDynamicWidth()) { - if (availableWidth == 0) { - /* - * Let the overflowing components overflow. IE has - * problems with zero sizes. - */ - // width = 0; - // height = 0; - } else if (width > availableWidth) { - width = availableWidth; - - if (!first) { - width -= activeSpacing.hSpacing; - } - availableWidth = 0; - } else { - availableWidth -= width; - if (!first) { - availableWidth -= activeSpacing.hSpacing; - } - } - - first = false; - } - - childComponentContainer.setContainerSize(width, height); - } - } else { - width = activeLayoutSize.getWidth(); - while (widgetIterator.hasNext()) { - ChildComponentContainer childComponentContainer = (ChildComponentContainer) widgetIterator - .next(); - - if (!childComponentContainer - .isComponentRelativeSized(ORIENTATION_VERTICAL)) { - /* - * Only components with non-relative size in the main - * direction has a container size - */ - height = childComponentContainer.getWidgetSize() - .getHeight() - + childComponentContainer - .getCaptionHeightAboveComponent(); - } else { - height = 0; - } - - childComponentContainer.setContainerSize(width, height); - } - - } - - } - - private Size updateLayoutDimensions(int totalComponentWidth, - int totalComponentHeight, int maxComponentWidth, - int maxComponentHeight) { - - /* Only need to calculate dynamic dimensions */ - if (!isDynamicHeight() && !isDynamicWidth()) { - return activeLayoutSize; - } - - int activeLayoutWidth = 0; - int activeLayoutHeight = 0; - - // Update layout dimensions - if (isHorizontal()) { - // Horizontal - if (isDynamicWidth()) { - activeLayoutWidth = totalComponentWidth; - } - - if (isDynamicHeight()) { - activeLayoutHeight = maxComponentHeight; - } - - } else { - // Vertical - if (isDynamicWidth()) { - activeLayoutWidth = maxComponentWidth; - } - - if (isDynamicHeight()) { - activeLayoutHeight = totalComponentHeight; - } - } - - if (isDynamicWidth()) { - setActiveLayoutWidth(activeLayoutWidth); - setOuterLayoutWidth(activeLayoutSize.getWidth()); - } - - if (isDynamicHeight()) { - setActiveLayoutHeight(activeLayoutHeight); - setOuterLayoutHeight(activeLayoutSize.getHeight()); - } - - return activeLayoutSize; - } - - private void setActiveLayoutWidth(int activeLayoutWidth) { - if (activeLayoutWidth < 0) { - activeLayoutWidth = 0; - } - activeLayoutSize.setWidth(activeLayoutWidth); - } - - private void setActiveLayoutHeight(int activeLayoutHeight) { - if (activeLayoutHeight < 0) { - activeLayoutHeight = 0; - } - activeLayoutSize.setHeight(activeLayoutHeight); - - } - - private void setOuterLayoutWidth(int activeLayoutWidth) { - // Don't call setWidth to avoid triggering all kinds of recalculations - // Also don't call super.setWidth to avoid messing with the - // dynamicWidth property - int newPixelWidth = (activeLayoutWidth + activeMargins.getHorizontal()); - getElement().getStyle().setWidth(newPixelWidth, Unit.PX); - } - - private void setOuterLayoutHeight(int activeLayoutHeight) { - // Don't call setHeight to avoid triggering all kinds of recalculations - // Also don't call super.setHeight to avoid messing with the - // dynamicHeight property - int newPixelHeight = (activeLayoutHeight + activeMargins.getVertical()); - getElement().getStyle().setHeight(newPixelHeight, Unit.PX); - } - - /** - * Updates the spacing between components. Needs to be done only when - * components are added/removed. - */ - private void updateContainerMargins() { - ChildComponentContainer firstChildComponent = getFirstChildComponentContainer(); - if (firstChildComponent != null) { - firstChildComponent.setMarginLeft(0); - firstChildComponent.setMarginTop(0); - - for (ChildComponentContainer childComponent : widgetToComponentContainer - .values()) { - if (childComponent == firstChildComponent) { - continue; - } - - if (isHorizontal()) { - childComponent.setMarginLeft(activeSpacing.hSpacing); - } else { - childComponent.setMarginTop(activeSpacing.vSpacing); - } - } - } - } - - private boolean isHorizontal() { - return orientation == ORIENTATION_HORIZONTAL; - } - - private boolean isVertical() { - return orientation == ORIENTATION_VERTICAL; - } - - private ChildComponentContainer createChildContainer(VPaintableWidget child) { - - // Create a container DIV for the child - ChildComponentContainer childComponent = new ChildComponentContainer( - child, orientation); - - return childComponent; - - } - - public RenderSpace getAllocatedSpace(Widget child) { - int width = 0; - int height = 0; - ChildComponentContainer childComponentContainer = getComponentContainer(child); - // WIDTH CALCULATION - if (isVertical()) { - width = activeLayoutSize.getWidth(); - width -= childComponentContainer.getCaptionWidthAfterComponent(); - } else if (!isDynamicWidth()) { - // HORIZONTAL - width = childComponentContainer.getContSize().getWidth(); - width -= childComponentContainer.getCaptionWidthAfterComponent(); - } - - // HEIGHT CALCULATION - if (isHorizontal()) { - height = activeLayoutSize.getHeight(); - height -= childComponentContainer.getCaptionHeightAboveComponent(); - } else if (!isDynamicHeight()) { - // VERTICAL - height = childComponentContainer.getContSize().getHeight(); - height -= childComponentContainer.getCaptionHeightAboveComponent(); - } - - // ApplicationConnection.getConsole().log( - // "allocatedSpace for " + Util.getSimpleName(child) + ": " - // + width + "," + height); - RenderSpace space = new RenderSpace(width, height); - return space; - } - - private void recalculateLayoutAndComponentSizes() { - recalculateLayout(); - - if (!(isDynamicHeight() && isDynamicWidth())) { - /* First update relative sized components */ - for (ChildComponentContainer componentContainer : widgetToComponentContainer - .values()) { - client.handleComponentRelativeSize(componentContainer - .getWidget()); - - // Update widget size from DOM - componentContainer.updateWidgetSize(); - } - } - - if (isDynamicHeight()) { - /* - * Height is not necessarily correct anymore as the height of - * components might have changed if the width has changed. - */ - - /* - * Get the new widget sizes from DOM and calculate new container - * sizes - */ - updateWidgetSizes(); - - /* Update layout dimensions based on widget sizes */ - recalculateLayout(); - } - - updateRelativeSizesInNonMainDirection(); - calculateAlignments(); - - setRootSize(); - } - - private void setRootSize() { - root.getStyle().setPropertyPx("width", activeLayoutSize.getWidth()); - root.getStyle().setPropertyPx("height", activeLayoutSize.getHeight()); - } - - public boolean requestLayout(Set children) { - for (Widget p : children) { - /* Update widget size from DOM */ - ChildComponentContainer componentContainer = getComponentContainer(p); - // This should no longer be needed (after #2563) - // if (isDynamicWidth()) { - // componentContainer.setUnlimitedContainerWidth(); - // } else { - // componentContainer.setLimitedContainerWidth(activeLayoutSize - // .getWidth()); - // } - - componentContainer.updateWidgetSize(); - - /* - * If this is the result of an caption icon onload event the caption - * size may have changed - */ - componentContainer.updateCaptionSize(); - } - - Size sizeBefore = new Size(activeLayoutSize.getWidth(), - activeLayoutSize.getHeight()); - - recalculateLayoutAndComponentSizes(); - boolean sameSize = (sizeBefore.equals(activeLayoutSize)); - if (!sameSize) { - /* Must inform child components about possible size updates */ - client.runDescendentsLayout(this); - } - - /* Automatically propagated upwards if the size has changed */ - - return sameSize; - } - - @Override - public void setHeight(String height) { - Size sizeBefore = new Size(activeLayoutSize.getWidth(), - activeLayoutSize.getHeight()); - - super.setHeight(height); - - if (height != null && !height.equals("")) { - setActiveLayoutHeight(getOffsetHeight() - - activeMargins.getVertical()); - } - - if (isRendering) { - sizeHasChangedDuringRendering = true; - } else { - recalculateLayoutAndComponentSizes(); - boolean sameSize = (sizeBefore.equals(activeLayoutSize)); - if (!sameSize) { - /* Must inform child components about possible size updates */ - client.runDescendentsLayout(this); - } - } - } - - @Override - public void setWidth(String width) { - if (this.width.equals(width) || !isVisible()) { - return; - } - Size sizeBefore = new Size(activeLayoutSize.getWidth(), - activeLayoutSize.getHeight()); - - super.setWidth(width); - this.width = width; - if (width != null && !width.equals("")) { - setActiveLayoutWidth(getOffsetWidth() - - activeMargins.getHorizontal()); - } - - if (isRendering) { - sizeHasChangedDuringRendering = true; - } else { - recalculateLayoutAndComponentSizes(); - boolean sameSize = (sizeBefore.equals(activeLayoutSize)); - if (!sameSize) { - /* Must inform child components about possible size updates */ - client.runDescendentsLayout(this); - } - /* - * If the height changes as a consequence of this we must inform the - * parent also - */ - if (isDynamicHeight() - && sizeBefore.getHeight() != activeLayoutSize.getHeight()) { - Util.notifyParentOfSizeChange(this, false); - } - - } - } - - protected void updateAlignmentsAndExpandRatios(UIDL uidl, - ArrayList renderedWidgets) { - - /* - */ - alignments = uidl.getMapAttribute("alignments"); - - /* - * UIDL contains a map of paintable ids to expand ratios - */ - - expandRatios = uidl.getMapAttribute("expandRatios"); - expandRatioSum = -1.0; - - for (int i = 0; i < renderedWidgets.size(); i++) { - Widget widget = renderedWidgets.get(i); - String pid = VPaintableMap.get(client).getPid(widget); - - ChildComponentContainer container = getComponentContainer(widget); - - // Calculate alignment info - container.setAlignment(getAlignment(pid)); - - // Update expand ratio - container.setNormalizedExpandRatio(getExpandRatio(pid)); - } - } - - private AlignmentInfo getAlignment(String pid) { - if (alignments.containsKey(pid)) { - return new AlignmentInfo(alignments.getInt(pid)); - } else { - return AlignmentInfo.TOP_LEFT; - } - } - - private double getExpandRatio(String pid) { - if (expandRatioSum < 0) { - expandRatioSum = 0; - JsArrayString keyArray = expandRatios.getKeyArray(); - int length = keyArray.length(); - for (int i = 0; i < length; i++) { - expandRatioSum += expandRatios.getRawNumber(keyArray.get(i)); - } - if (expandRatioSum == 0) { - // by default split equally among components - defaultExpandRatio = 1.0 / widgetToComponentContainer.size(); - } else { - defaultExpandRatio = 0; - } - } - if (expandRatios.containsKey(pid)) { - return expandRatios.getRawNumber(pid) / expandRatioSum; - } else { - return defaultExpandRatio; - } - } - - public void updateCaption(VPaintableWidget paintable, UIDL uidl) { - Widget widget = paintable.getWidgetForPaintable(); - ChildComponentContainer componentContainer = getComponentContainer(widget); - componentContainer.updateCaption(uidl, client); - if (!isRendering) { - /* - * This was a component-only update and the possible size change - * must be propagated to the layout - */ - client.captionSizeUpdated(widget); - } - } - - /** - * Returns the deepest nested child component which contains "element". The - * child component is also returned if "element" is part of its caption. - * - * @param element - * An element that is a nested sub element of the root element in - * this layout - * @return The Paintable which the element is a part of. Null if the element - * belongs to the layout and not to a child. - */ - private VPaintableWidget getComponent(Element element) { - return Util.getPaintableForElement(client, this, element); - } - - public Widget getWidgetForPaintable() { - return this; - } - -} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java b/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java index fe5749ec8b..03b71321b2 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VVerticalLayout.java @@ -3,12 +3,12 @@ */ package com.vaadin.terminal.gwt.client.ui; -public class VVerticalLayout extends VOrderedLayout { +public class VVerticalLayout extends VMeasuringOrderedLayout { public static final String CLASSNAME = "v-verticallayout"; public VVerticalLayout() { - super(CLASSNAME, ORIENTATION_VERTICAL); + super(CLASSNAME, true); } }