diff options
Diffstat (limited to 'src/com/vaadin/terminal/gwt/client/LayoutManager.java')
-rw-r--r-- | src/com/vaadin/terminal/gwt/client/LayoutManager.java | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/src/com/vaadin/terminal/gwt/client/LayoutManager.java b/src/com/vaadin/terminal/gwt/client/LayoutManager.java new file mode 100644 index 0000000000..d125543c2d --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/LayoutManager.java @@ -0,0 +1,334 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client; + +import java.util.HashSet; +import java.util.Set; + +import com.google.gwt.core.client.Duration; +import com.google.gwt.core.client.JsArrayString; +import com.google.gwt.dom.client.Element; +import com.vaadin.terminal.gwt.client.ui.ManagedLayout; +import com.vaadin.terminal.gwt.client.ui.PostLayoutListener; +import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout; + +public class LayoutManager { + private final ApplicationConnection connection; + private final Set<Element> nonPaintableElements = new HashSet<Element>(); + private final MeasuredSize nullSize = new MeasuredSize(); + + public LayoutManager(ApplicationConnection connection) { + this.connection = connection; + } + + public static LayoutManager get(ApplicationConnection connection) { + return connection.getLayoutManager(); + } + + public void registerDependency(ManagedLayout owner, Element element) { + MeasuredSize measuredSize = ensureMeasured(element); + + MeasuredSize ownerSize = getMeasuredSize(owner); + if (measuredSize.isHeightNeedsUpdate()) { + ownerSize.setHeightNeedsUpdate(); + } + if (measuredSize.isWidthNeedsUpdate()) { + ownerSize.setWidthNeedsUpdate(); + } + measuredSize.addDependent(owner.getId()); + } + + private MeasuredSize ensureMeasured(Element element) { + MeasuredSize measuredSize = getMeasuredSize(element, null); + if (measuredSize == null) { + measuredSize = new MeasuredSize(); + + if (VPaintableMap.get(connection).getPaintable(element) == null) { + nonPaintableElements.add(element); + } + setMeasuredSize(element, measuredSize); + } + return measuredSize; + } + + private boolean needsMeasure(Element e) { + if (connection.getPaintableMap().getPid(e) != null) { + return true; + } else if (getMeasuredSize(e, nullSize).hasDependents()) { + return true; + } else { + return false; + } + } + + private static native void setMeasuredSize(Element element, + MeasuredSize measuredSize) + /*-{ + if (measuredSize) { + element.vMeasuredSize = measuredSize; + } else { + delete element.vMeasuredSize; + } + }-*/; + + private static native final MeasuredSize getMeasuredSize(Element element, + MeasuredSize defaultSize) + /*-{ + return element.vMeasuredSize || defaultSize; + }-*/; + + private static final MeasuredSize getMeasuredSize(VPaintableWidget paintable) { + Element element = paintable.getWidgetForPaintable().getElement(); + MeasuredSize measuredSize = getMeasuredSize(element, null); + if (measuredSize == null) { + measuredSize = new MeasuredSize(); + setMeasuredSize(element, measuredSize); + } + return measuredSize; + } + + public void unregisterDependency(ManagedLayout owner, Element element) { + MeasuredSize measuredSize = getMeasuredSize(element, null); + if (measuredSize == null) { + return; + } + measuredSize.removeDependent(owner.getId()); + if (!needsMeasure(element)) { + nonPaintableElements.remove(element); + setMeasuredSize(element, null); + } + } + + public void doLayout() { + VPaintableMap paintableMap = connection.getPaintableMap(); + VPaintableWidget[] paintableWidgets = paintableMap + .getRegisteredPaintableWidgets(); + + int passes = 0; + Duration totalDuration = new Duration(); + + while (true) { + Duration passDuration = new Duration(); + passes++; + measureElements(paintableWidgets); + + FastStringSet needsHeightUpdate = FastStringSet.create(); + FastStringSet needsWidthUpdate = FastStringSet.create(); + + for (VPaintableWidget paintable : paintableWidgets) { + MeasuredSize measuredSize = getMeasuredSize(paintable); + boolean notifiableType = isNotifiableType(paintable); + + VPaintableWidgetContainer parent = paintable.getParent(); + boolean parentNotifiable = parent != null + && isNotifiableType(parent); + + if (measuredSize.isHeightNeedsUpdate()) { + if (notifiableType) { + needsHeightUpdate.add(paintable.getId()); + } + if (!paintable.isRelativeHeight() && parentNotifiable) { + needsHeightUpdate.add(parent.getId()); + } + } + if (measuredSize.isWidthNeedsUpdate()) { + if (notifiableType) { + needsWidthUpdate.add(paintable.getId()); + } + if (!paintable.isRelativeWidth() && parentNotifiable) { + needsWidthUpdate.add(parent.getId()); + } + } + measuredSize.clearDirtyState(); + } + + int measureTime = passDuration.elapsedMillis(); + VConsole.log("Measure in " + measureTime + " ms"); + + FastStringSet updatedSet = FastStringSet.create(); + + JsArrayString needsWidthUpdateArray = needsWidthUpdate.dump(); + + for (int i = 0; i < needsWidthUpdateArray.length(); i++) { + String pid = needsWidthUpdateArray.get(i); + + VPaintable paintable = paintableMap.getPaintable(pid); + if (paintable instanceof DirectionalManagedLayout) { + DirectionalManagedLayout cl = (DirectionalManagedLayout) paintable; + cl.layoutHorizontally(); + } else if (paintable instanceof SimpleManagedLayout) { + SimpleManagedLayout rr = (SimpleManagedLayout) paintable; + rr.layout(); + needsHeightUpdate.remove(pid); + } + updatedSet.add(pid); + } + + JsArrayString needsHeightUpdateArray = needsHeightUpdate.dump(); + for (int i = 0; i < needsHeightUpdateArray.length(); i++) { + String pid = needsHeightUpdateArray.get(i); + + VPaintableWidget paintable = (VPaintableWidget) paintableMap + .getPaintable(pid); + if (paintable instanceof DirectionalManagedLayout) { + DirectionalManagedLayout cl = (DirectionalManagedLayout) paintable; + cl.layoutVertically(); + } else if (paintable instanceof SimpleManagedLayout) { + SimpleManagedLayout rr = (SimpleManagedLayout) paintable; + rr.layout(); + } + updatedSet.add(pid); + } + + JsArrayString changed = updatedSet.dump(); + VConsole.log(changed.length() + " requestLayout invocations in " + + (passDuration.elapsedMillis() - measureTime) + "ms"); + + StringBuilder b = new StringBuilder(); + b.append(changed.length()); + b.append(" changed widgets in pass "); + b.append(passes); + b.append(" in "); + b.append(passDuration.elapsedMillis()); + 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()); + + if (changed.length() == 0) { + VConsole.log("No more changes in pass " + passes); + break; + } + + if (passes > 100) { + VConsole.log("Aborting layout"); + break; + } + } + + for (VPaintableWidget vPaintableWidget : paintableWidgets) { + if (vPaintableWidget instanceof PostLayoutListener) { + ((PostLayoutListener) vPaintableWidget).postLayout(); + } + } + + VConsole.log("Total layout time: " + totalDuration.elapsedMillis() + + "ms"); + } + + private void measureElements(VPaintableWidget[] paintableWidgets) { + + for (VPaintableWidget paintableWidget : paintableWidgets) { + Element element = paintableWidget.getWidgetForPaintable() + .getElement(); + MeasuredSize measuredSize = getMeasuredSize(paintableWidget); + measuredAndUpdate(element, measuredSize); + } + + for (Element element : nonPaintableElements) { + MeasuredSize measuredSize = getMeasuredSize(element, null); + measuredAndUpdate(element, measuredSize); + } + } + + private void measuredAndUpdate(Element element, MeasuredSize measuredSize) { + if (measuredSize.measure(element)) { + JsArrayString dependents = measuredSize.getDependents(); + for (int i = 0; i < dependents.length(); i++) { + String pid = dependents.get(i); + VPaintableWidget dependent = (VPaintableWidget) connection + .getPaintableMap().getPaintable(pid); + if (dependent != null) { + MeasuredSize dependentSize = getMeasuredSize(dependent); + if (measuredSize.isHeightNeedsUpdate()) { + dependentSize.setHeightNeedsUpdate(); + } + if (measuredSize.isWidthNeedsUpdate()) { + dependentSize.setWidthNeedsUpdate(); + } + } + } + } + } + + private static boolean isNotifiableType(VPaintableWidget paintable) { + return paintable instanceof SimpleManagedLayout + || paintable instanceof DirectionalManagedLayout; + } + + public void foceLayout() { + VPaintableMap paintableMap = connection.getPaintableMap(); + VPaintableWidget[] paintableWidgets = paintableMap + .getRegisteredPaintableWidgets(); + for (VPaintableWidget vPaintableWidget : paintableWidgets) { + MeasuredSize measuredSize = getMeasuredSize(vPaintableWidget); + measuredSize.setHeightNeedsUpdate(); + measuredSize.setWidthNeedsUpdate(); + } + doLayout(); + } + + public final void setNeedsUpdate(ManagedLayout layout) { + setWidthNeedsUpdate(layout); + setHeightNeedsUpdate(layout); + } + + public final void setWidthNeedsUpdate(ManagedLayout layout) { + getMeasuredSize(layout).setWidthNeedsUpdate(); + } + + public final void setHeightNeedsUpdate(ManagedLayout layout) { + getMeasuredSize(layout).setHeightNeedsUpdate(); + } + + public boolean isMeasured(Element element) { + return getMeasuredSize(element, nullSize) != nullSize; + } + + public final int getOuterHeight(Element element) { + return getMeasuredSize(element, nullSize).getOuterHeight(); + } + + public final int getOuterWidth(Element element) { + return getMeasuredSize(element, nullSize).getOuterWidth(); + } + + public final int getInnerHeight(Element element) { + return getMeasuredSize(element, nullSize).getInnerHeight(); + } + + public final int getInnerWidth(Element element) { + return getMeasuredSize(element, nullSize).getInnerWidth(); + } + + public final int getBorderHeight(Element element) { + return getMeasuredSize(element, nullSize).getBorderHeight(); + } + + public int getPaddingHeight(Element element) { + return getMeasuredSize(element, nullSize).getPaddingHeight(); + } + + public int getBorderWidth(Element element) { + return getMeasuredSize(element, nullSize).getBorderWidth(); + } + + public int getPaddingWidth(Element element) { + return getMeasuredSize(element, nullSize).getPaddingWidth(); + } + + public int getPaddingTop(Element element) { + return getMeasuredSize(element, nullSize).getPaddingTop(); + } + + public int getPaddingLeft(Element element) { + return getMeasuredSize(element, nullSize).getPaddingLeft(); + } +} |