diff options
author | Leif Åstrand <leif@vaadin.com> | 2012-04-12 10:39:20 +0300 |
---|---|---|
committer | Leif Åstrand <leif@vaadin.com> | 2012-04-12 10:39:20 +0300 |
commit | b65f72265230869b03a09c4e8c353f39b8cc1ecf (patch) | |
tree | 731f563917e0f7316cbf2f5e250d9c7c8c569d26 | |
parent | eff08ecdc070f998d4efd5c1f367173d0321708d (diff) | |
parent | a6e0aed3ba959bc81b2e97ccd9e1098190501ebb (diff) | |
download | vaadin-framework-b65f72265230869b03a09c4e8c353f39b8cc1ecf.tar.gz vaadin-framework-b65f72265230869b03a09c4e8c353f39b8cc1ecf.zip |
Merge branch 'layoutgraph'
Conflicts:
src/com/vaadin/terminal/gwt/client/ApplicationConnection.java
src/com/vaadin/terminal/gwt/client/LayoutManager.java
src/com/vaadin/terminal/gwt/client/ui/AbsoluteLayoutConnector.java
src/com/vaadin/terminal/gwt/client/ui/AbstractOrderedLayoutConnector.java
src/com/vaadin/terminal/gwt/client/ui/AbstractSplitPanelConnector.java
src/com/vaadin/terminal/gwt/client/ui/AccordionConnector.java
src/com/vaadin/terminal/gwt/client/ui/FormConnector.java
src/com/vaadin/terminal/gwt/client/ui/GridLayoutConnector.java
src/com/vaadin/terminal/gwt/client/ui/PanelConnector.java
src/com/vaadin/terminal/gwt/client/ui/RootConnector.java
src/com/vaadin/terminal/gwt/client/ui/TableConnector.java
src/com/vaadin/terminal/gwt/client/ui/TabsheetConnector.java
src/com/vaadin/terminal/gwt/client/ui/TwinColSelectConnector.java
src/com/vaadin/terminal/gwt/client/ui/VAbstractSplitPanel.java
src/com/vaadin/terminal/gwt/client/ui/VAccordion.java
src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java
src/com/vaadin/terminal/gwt/client/ui/VGridLayout.java
src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java
src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java
src/com/vaadin/terminal/gwt/client/ui/VTabsheetPanel.java
src/com/vaadin/terminal/gwt/client/ui/VView.java
src/com/vaadin/terminal/gwt/client/ui/VWindow.java
src/com/vaadin/terminal/gwt/client/ui/WindowConnector.java
32 files changed, 1397 insertions, 329 deletions
diff --git a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index 555112a636..a2816728f9 100644 --- a/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -1130,7 +1130,9 @@ public class ApplicationConnection { + (updateDuration.elapsedMillis() - startProcessing) + " ms"); - doLayout(false); + LayoutManager layoutManager = getLayoutManager(); + layoutManager.setEverythingNeedsMeasure(); + layoutManager.layoutNow(); updateDuration .logDuration(" * Layout processing completed", 10); @@ -1229,7 +1231,8 @@ public class ApplicationConnection { } else if ((cc instanceof RootConnector && cc == getRootConnector())) { // RootConnector for this connection, leave as-is } else if (cc instanceof WindowConnector - && getRootConnector().hasSubWindow((WindowConnector) cc)) { + && getRootConnector().hasSubWindow( + (WindowConnector) cc)) { // Sub window attached to this RootConnector, leave // as-is } else { @@ -1279,8 +1282,10 @@ public class ApplicationConnection { // RootConnector has been created but not // initialized as the connector id has not been // known - connectorMap.registerConnector(connectorId, rootConnector); - rootConnector.doInit(connectorId, ApplicationConnection.this); + connectorMap.registerConnector(connectorId, + rootConnector); + rootConnector.doInit(connectorId, + ApplicationConnection.this); } } catch (final Throwable e) { VConsole.error(e); @@ -1945,7 +1950,7 @@ public class ApplicationConnection { public void forceLayout() { Duration duration = new Duration(); - layoutManager.foceLayout(); + layoutManager.forceLayout(); VConsole.log("forceLayout in " + duration.elapsedMillis() + " ms"); } @@ -2189,40 +2194,8 @@ public class ApplicationConnection { } - /* - * Helper to run layout functions triggered by child components with a - * decent interval. - */ - private final Timer layoutTimer = new Timer() { - - private boolean isPending = false; - - @Override - public void schedule(int delayMillis) { - if (!isPending) { - super.schedule(delayMillis); - isPending = true; - } - } - - @Override - public void run() { - VConsole.log("Running re-layout of " + rootConnector.getClass().getName()); - runDescendentsLayout(rootConnector.getWidget()); - isPending = false; - } - }; - private ConnectorMap connectorMap = GWT.create(ConnectorMap.class); - /** - * Components can call this function to run all layout functions. This is - * usually done, when component knows that its size has changed. - */ - public void requestLayoutPhase() { - layoutTimer.schedule(500); - } - protected String getUidlSecurityKey() { return uidlSecurityKey; } @@ -2384,36 +2357,6 @@ public class ApplicationConnection { eventIdentifier); } - private boolean layoutPending = false; - private ScheduledCommand layoutCommand = new ScheduledCommand() { - public void execute() { - /* - * Layout again if a new layout is requested while the current one - * is running. - */ - while (layoutPending) { - layoutPending = false; - layoutManager.doLayout(); - } - } - }; - - public void doLayout(boolean lazy) { - if (!lazy) { - layoutPending = true; - layoutCommand.execute(); - } else if (!layoutPending) { - layoutPending = true; - /* - * Current layoutCommand will do layouts again if layoutScheduled is - * set to true -> no need to schedule another command - */ - if (!layoutManager.isLayoutRunning()) { - Scheduler.get().scheduleDeferred(layoutCommand); - } - } - } - LayoutManager getLayoutManager() { return layoutManager; } diff --git a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java index 50a59e4975..ef1dc481b1 100644 --- a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java +++ b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java @@ -300,4 +300,16 @@ public class BrowserInfo { return touchDevice; } + /** + * Indicates whether the browser might require juggling to properly update + * sizes inside elements with overflow: auto. + * + * @return <code>true</code> if the browser requires the workaround, + * otherwise <code>false</code> + */ + public boolean requiresOverflowAutoFix() { + return (getWebkitVersion() > 0 || getOperaVersion() >= 11) + && Util.getNativeScrollbarSize() > 0; + } + } diff --git a/src/com/vaadin/terminal/gwt/client/LayoutManager.java b/src/com/vaadin/terminal/gwt/client/LayoutManager.java index 60a2d3543a..4338f1284a 100644 --- a/src/com/vaadin/terminal/gwt/client/LayoutManager.java +++ b/src/com/vaadin/terminal/gwt/client/LayoutManager.java @@ -3,23 +3,57 @@ */ package com.vaadin.terminal.gwt.client; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; 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.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Overflow; +import com.google.gwt.user.client.Timer; +import com.vaadin.terminal.gwt.client.MeasuredSize.MeasureResult; import com.vaadin.terminal.gwt.client.ui.ManagedLayout; import com.vaadin.terminal.gwt.client.ui.PostLayoutListener; import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout; +import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeEvent; +import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeListener; +import com.vaadin.terminal.gwt.client.ui.layout.LayoutDependencyTree; import com.vaadin.terminal.gwt.client.ui.notification.VNotification; public class LayoutManager { private static final String LOOP_ABORT_MESSAGE = "Aborting layout after 100 passes. This would probably be an infinite loop."; + + private static final boolean debugLogging = false; + private ApplicationConnection connection; - private final Set<Element> nonPaintableElements = new HashSet<Element>(); + private final Set<Element> measuredNonPaintableElements = new HashSet<Element>(); private final MeasuredSize nullSize = new MeasuredSize(); - private boolean layoutRunning = false; + + private LayoutDependencyTree currentDependencyTree; + + private final Collection<ManagedLayout> needsHorizontalLayout = new HashSet<ManagedLayout>(); + private final Collection<ManagedLayout> needsVerticalLayout = new HashSet<ManagedLayout>(); + + private final Collection<ComponentConnector> needsMeasure = new HashSet<ComponentConnector>(); + + private Collection<ComponentConnector> pendingOverflowFixes = new HashSet<ComponentConnector>(); + + private final Map<Element, Collection<ElementResizeListener>> elementResizeListeners = new HashMap<Element, Collection<ElementResizeListener>>(); + private final Set<Element> listenersToFire = new HashSet<Element>(); + + private boolean layoutPending = false; + private Timer layoutTimer = new Timer() { + @Override + public void run() { + cancel(); + layoutNow(); + } + }; + private boolean everythingNeedsMeasure = false; public void setConnection(ApplicationConnection connection) { if (this.connection != null) { @@ -35,14 +69,7 @@ public class LayoutManager { 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(); - } + setNeedsUpdate(owner); measuredSize.addDependent(owner.getConnectorId()); } @@ -52,7 +79,7 @@ public class LayoutManager { measuredSize = new MeasuredSize(); if (ConnectorMap.get(connection).getConnector(element) == null) { - nonPaintableElements.add(element); + measuredNonPaintableElements.add(element); } setMeasuredSize(element, measuredSize); } @@ -62,6 +89,8 @@ public class LayoutManager { private boolean needsMeasure(Element e) { if (connection.getConnectorMap().getConnectorId(e) != null) { return true; + } else if (elementResizeListeners.containsKey(e)) { + return true; } else if (getMeasuredSize(e, nullSize).hasDependents()) { return true; } else { @@ -85,8 +114,8 @@ public class LayoutManager { return element.vMeasuredSize || defaultSize; }-*/; - private final MeasuredSize getMeasuredSize(ComponentConnector paintable) { - Element element = paintable.getWidget().getElement(); + private final MeasuredSize getMeasuredSize(ComponentConnector connector) { + Element element = connector.getWidget().getElement(); MeasuredSize measuredSize = getMeasuredSize(element, null); if (measuredSize == null) { measuredSize = new MeasuredSize(); @@ -101,129 +130,187 @@ public class LayoutManager { return; } measuredSize.removeDependent(owner.getConnectorId()); - if (!needsMeasure(element)) { - nonPaintableElements.remove(element); - setMeasuredSize(element, null); - } + stopMeasuringIfUnecessary(element); } public boolean isLayoutRunning() { - return layoutRunning; + return currentDependencyTree != null; + } + + private void countLayout(Map<ManagedLayout, Integer> layoutCounts, + ManagedLayout layout) { + Integer count = layoutCounts.get(layout); + if (count == null) { + count = Integer.valueOf(0); + } else { + count = Integer.valueOf(count.intValue() + 1); + } + layoutCounts.put(layout, count); + if (count.intValue() > 2) { + VConsole.error(Util.getConnectorString(layout) + + " has been layouted " + count.intValue() + " times"); + } } - public void doLayout() { - if (layoutRunning) { + private void layoutLater() { + if (!layoutPending) { + layoutPending = true; + layoutTimer.schedule(100); + } + } + + public void layoutNow() { + if (isLayoutRunning()) { throw new IllegalStateException( "Can't start a new layout phase before the previous layout phase ends."); } + layoutPending = false; + try { + currentDependencyTree = new LayoutDependencyTree(); + doLayout(); + } finally { + currentDependencyTree = null; + } + } + + private void doLayout() { VConsole.log("Starting layout phase"); - layoutRunning = true; - ConnectorMap paintableMap = connection.getConnectorMap(); - ComponentConnector[] paintableWidgets = paintableMap - .getComponentConnectors(); + Map<ManagedLayout, Integer> layoutCounts = new HashMap<ManagedLayout, Integer>(); int passes = 0; Duration totalDuration = new Duration(); + for (ManagedLayout layout : needsHorizontalLayout) { + currentDependencyTree.setNeedsHorizontalLayout(layout, true); + } + for (ManagedLayout layout : needsVerticalLayout) { + currentDependencyTree.setNeedsVerticalLayout(layout, true); + } + needsHorizontalLayout.clear(); + needsVerticalLayout.clear(); + + for (ComponentConnector connector : needsMeasure) { + currentDependencyTree.setNeedsMeasure(connector, true); + } + needsMeasure.clear(); + + measureNonPaintables(); + + VConsole.log("Layout init in " + totalDuration.elapsedMillis() + " ms"); + while (true) { Duration passDuration = new Duration(); passes++; - measureElements(paintableWidgets); - - FastStringSet needsHeightUpdate = FastStringSet.create(); - FastStringSet needsWidthUpdate = FastStringSet.create(); - - for (ComponentConnector paintable : paintableWidgets) { - MeasuredSize measuredSize = getMeasuredSize(paintable); - boolean managed = isManagedLayout(paintable); - ComponentContainerConnector parent = paintable.getParent(); - boolean managedParent = parent != null - && isManagedLayout(parent); + int measuredConnectorCount = measureConnectors( + currentDependencyTree, everythingNeedsMeasure); + everythingNeedsMeasure = false; + if (measuredConnectorCount == 0) { + VConsole.log("No more changes in pass " + passes); + break; + } - if (measuredSize.isHeightNeedsUpdate()) { - if (managed) { - needsHeightUpdate.add(paintable.getConnectorId()); - } - if (!paintable.isRelativeHeight() && managedParent) { - needsHeightUpdate.add(parent.getConnectorId()); - } - } - if (measuredSize.isWidthNeedsUpdate()) { - if (managed) { - needsWidthUpdate.add(paintable.getConnectorId()); - } - if (!paintable.isRelativeWidth() && managedParent) { - needsWidthUpdate.add(parent.getConnectorId()); + int measureTime = passDuration.elapsedMillis(); + VConsole.log(" Measured " + measuredConnectorCount + + " elements in " + measureTime + " ms"); + + if (!listenersToFire.isEmpty()) { + for (Element element : listenersToFire) { + Collection<ElementResizeListener> listeners = elementResizeListeners + .get(element); + ElementResizeListener[] array = listeners + .toArray(new ElementResizeListener[listeners.size()]); + ElementResizeEvent event = new ElementResizeEvent(this, + element); + for (ElementResizeListener listener : array) { + listener.onElementResize(event); } } - measuredSize.clearDirtyState(); + int measureListenerTime = passDuration.elapsedMillis(); + VConsole.log(" Fired resize listeners for " + + listenersToFire.size() + " elements in " + + (measureListenerTime - measureTime) + " ms"); + measureTime = measuredConnectorCount; + listenersToFire.clear(); } - 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); - - ServerConnector paintable = paintableMap.getConnector(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); + while (currentDependencyTree.hasHorizontalConnectorToLayout() + || currentDependencyTree.hasVerticaConnectorToLayout()) { + for (ManagedLayout layout : currentDependencyTree + .getHorizontalLayoutTargets()) { + if (layout instanceof DirectionalManagedLayout) { + currentDependencyTree + .markAsHorizontallyLayouted(layout); + DirectionalManagedLayout cl = (DirectionalManagedLayout) layout; + cl.layoutHorizontally(); + countLayout(layoutCounts, cl); + } else { + currentDependencyTree + .markAsHorizontallyLayouted(layout); + currentDependencyTree.markAsVerticallyLayouted(layout); + SimpleManagedLayout rr = (SimpleManagedLayout) layout; + rr.layout(); + countLayout(layoutCounts, rr); + } + if (debugLogging) { + updatedSet.add(layout.getConnectorId()); + } } - updatedSet.add(pid); - } - JsArrayString needsHeightUpdateArray = needsHeightUpdate.dump(); - for (int i = 0; i < needsHeightUpdateArray.length(); i++) { - String pid = needsHeightUpdateArray.get(i); - - ComponentConnector paintable = (ComponentConnector) paintableMap - .getConnector(pid); - if (paintable instanceof DirectionalManagedLayout) { - DirectionalManagedLayout cl = (DirectionalManagedLayout) paintable; - cl.layoutVertically(); - } else if (paintable instanceof SimpleManagedLayout) { - SimpleManagedLayout rr = (SimpleManagedLayout) paintable; - rr.layout(); + for (ManagedLayout layout : currentDependencyTree + .getVerticalLayoutTargets()) { + if (layout instanceof DirectionalManagedLayout) { + currentDependencyTree.markAsVerticallyLayouted(layout); + DirectionalManagedLayout cl = (DirectionalManagedLayout) layout; + cl.layoutVertically(); + countLayout(layoutCounts, cl); + } else { + currentDependencyTree + .markAsHorizontallyLayouted(layout); + currentDependencyTree.markAsVerticallyLayouted(layout); + SimpleManagedLayout rr = (SimpleManagedLayout) layout; + rr.layout(); + countLayout(layoutCounts, rr); + } + if (debugLogging) { + updatedSet.add(layout.getConnectorId()); + } } - 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(", "); + if (debugLogging) { + JsArrayString changedCids = updatedSet.dump(); + + StringBuilder b = new StringBuilder(" "); + b.append(changedCids.length()); + b.append(" requestLayout invocations in "); + b.append(passDuration.elapsedMillis() - measureTime); + b.append(" ms"); + if (changedCids.length() < 30) { + for (int i = 0; i < changedCids.length(); i++) { + if (i != 0) { + b.append(", "); + } else { + b.append(": "); + } + String connectorString = changedCids.get(i); + if (changedCids.length() < 10) { + ServerConnector connector = ConnectorMap.get( + connection).getConnector(connectorString); + connectorString = Util + .getConnectorString(connector); + } + b.append(connectorString); } - b.append(changed.get(i)); } + VConsole.log(b.toString()); } - VConsole.log(b.toString()); - if (changed.length() == 0) { - VConsole.log("No more changes in pass " + passes); - break; - } + VConsole.log("Pass " + passes + " completed in " + + passDuration.elapsedMillis() + " ms"); if (passes > 100) { VConsole.log(LOOP_ABORT_MESSAGE); @@ -234,82 +321,232 @@ public class LayoutManager { } } - VConsole.log("Layout phase done"); - VConsole.log("Calling post layout listeners"); - - for (ComponentConnector vPaintableWidget : paintableWidgets) { - if (vPaintableWidget instanceof PostLayoutListener) { - ((PostLayoutListener) vPaintableWidget).postLayout(); + int postLayoutStart = totalDuration.elapsedMillis(); + for (ComponentConnector connector : connection.getConnectorMap() + .getComponentConnectors()) { + if (connector instanceof PostLayoutListener) { + ((PostLayoutListener) connector).postLayout(); } } + VConsole.log("Invoke post layout listeners in " + + (totalDuration.elapsedMillis() - postLayoutStart) + " ms"); - layoutRunning = false; VConsole.log("Total layout phase time: " + totalDuration.elapsedMillis() + "ms"); } - private void measureElements(ComponentConnector[] paintableWidgets) { + private void logConnectorStatus(int connectorId) { + currentDependencyTree + .logDependencyStatus((ComponentConnector) ConnectorMap.get( + connection).getConnector(Integer.toString(connectorId))); + } + + private int measureConnectors(LayoutDependencyTree layoutDependencyTree, + boolean measureAll) { + if (!pendingOverflowFixes.isEmpty()) { + Duration duration = new Duration(); + + HashMap<Element, String> originalOverflows = new HashMap<Element, String>(); + + HashSet<ComponentConnector> delayedOverflowFixes = new HashSet<ComponentConnector>(); + + // First set overflow to hidden (and save previous value so it can + // be restored later) + for (ComponentConnector componentConnector : pendingOverflowFixes) { + // Delay the overflow fix if the involved connectors might still + // change + if (!currentDependencyTree + .noMoreChangesExpected(componentConnector) + || !currentDependencyTree + .noMoreChangesExpected(componentConnector + .getParent())) { + delayedOverflowFixes.add(componentConnector); + continue; + } + + if (debugLogging) { + VConsole.log("Doing overflow fix for " + + Util.getConnectorString(componentConnector) + + " in " + + Util.getConnectorString(componentConnector + .getParent())); + } + + Element parentElement = componentConnector.getWidget() + .getElement().getParentElement(); + Style style = parentElement.getStyle(); + String originalOverflow = style.getOverflow(); + + if (originalOverflow != null + && !originalOverflows.containsKey(parentElement)) { + // Store original value for restore, but only the first time + // the value is changed + originalOverflows.put(parentElement, originalOverflow); + } + + style.setOverflow(Overflow.HIDDEN); + } + + pendingOverflowFixes.removeAll(delayedOverflowFixes); + + // Then ensure all scrolling elements are reflowed by measuring + for (ComponentConnector componentConnector : pendingOverflowFixes) { + componentConnector.getWidget().getElement().getParentElement() + .getOffsetHeight(); + } + + // Finally restore old overflow value and update bookkeeping + for (ComponentConnector componentConnector : pendingOverflowFixes) { + Element parentElement = componentConnector.getWidget() + .getElement().getParentElement(); + parentElement.getStyle().setProperty("overflow", + originalOverflows.get(parentElement)); + + layoutDependencyTree.setNeedsMeasure(componentConnector, true); + } + if (!pendingOverflowFixes.isEmpty()) { + VConsole.log("Did overflow fix for " + + pendingOverflowFixes.size() + " elements in " + + duration.elapsedMillis() + " ms"); + } + pendingOverflowFixes = delayedOverflowFixes; + } + + int measureCount = 0; + if (measureAll) { + ComponentConnector[] connectors = ConnectorMap.get(connection) + .getComponentConnectors(); + for (ComponentConnector connector : connectors) { + measueConnector(connector); + } + for (ComponentConnector connector : connectors) { + layoutDependencyTree.setNeedsMeasure(connector, false); + } + measureCount += connectors.length; + } + + while (layoutDependencyTree.hasConnectorsToMeasure()) { + Collection<ComponentConnector> measureTargets = layoutDependencyTree + .getMeasureTargets(); + for (ComponentConnector connector : measureTargets) { + measueConnector(connector); + measureCount++; + } + for (ComponentConnector connector : measureTargets) { + layoutDependencyTree.setNeedsMeasure(connector, false); + } + } + return measureCount; + } + + private void measueConnector(ComponentConnector connector) { + Element element = connector.getWidget().getElement(); + MeasuredSize measuredSize = getMeasuredSize(connector); + MeasureResult measureResult = measuredAndUpdate(element, measuredSize); - for (ComponentConnector paintableWidget : paintableWidgets) { - Element element = paintableWidget.getWidget().getElement(); - MeasuredSize measuredSize = getMeasuredSize(paintableWidget); - measuredAndUpdate(element, measuredSize); + if (measureResult.isChanged()) { + onConnectorChange(connector, measureResult.isWidthChanged(), + measureResult.isHeightChanged()); } + } - for (Element element : nonPaintableElements) { - MeasuredSize measuredSize = getMeasuredSize(element, null); - measuredAndUpdate(element, measuredSize); - measuredSize.clearDirtyState(); + private void onConnectorChange(ComponentConnector connector, + boolean widthChanged, boolean heightChanged) { + setNeedsOverflowFix(connector); + if (heightChanged) { + currentDependencyTree.markHeightAsChanged(connector); + } + if (widthChanged) { + currentDependencyTree.markWidthAsChanged(connector); } } - 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); - ComponentConnector dependent = (ComponentConnector) connection - .getConnectorMap().getConnector(pid); - if (dependent != null) { - MeasuredSize dependentSize = getMeasuredSize(dependent); - if (measuredSize.isHeightNeedsUpdate()) { - dependentSize.setHeightNeedsUpdate(); - } - if (measuredSize.isWidthNeedsUpdate()) { - dependentSize.setWidthNeedsUpdate(); - } + private void setNeedsOverflowFix(ComponentConnector connector) { + // IE9 doesn't need the original fix, but for some reason it needs this + if (BrowserInfo.get().requiresOverflowAutoFix() + || BrowserInfo.get().isIE9()) { + ComponentConnector scrollingBoundary = currentDependencyTree + .getScrollingBoundary(connector); + if (scrollingBoundary != null) { + pendingOverflowFixes.add(scrollingBoundary); + } + } + } + + private void measureNonPaintables() { + for (Element element : measuredNonPaintableElements) { + measuredAndUpdate(element, getMeasuredSize(element, null)); + } + VConsole.log("Measured " + measuredNonPaintableElements.size() + + " non paintable elements"); + } + + private MeasureResult measuredAndUpdate(Element element, + MeasuredSize measuredSize) { + MeasureResult measureResult = measuredSize.measure(element); + if (measureResult.isChanged()) { + notifyListenersAndDepdendents(element, + measureResult.isWidthChanged(), + measureResult.isHeightChanged()); + } + return measureResult; + } + + private void notifyListenersAndDepdendents(Element element, + boolean widthChanged, boolean heightChanged) { + assert widthChanged || heightChanged; + + MeasuredSize measuredSize = getMeasuredSize(element, nullSize); + JsArrayString dependents = measuredSize.getDependents(); + for (int i = 0; i < dependents.length(); i++) { + String pid = dependents.get(i); + ManagedLayout dependent = (ManagedLayout) connection + .getConnectorMap().getConnector(pid); + if (dependent != null) { + if (heightChanged) { + currentDependencyTree.setNeedsVerticalLayout(dependent, + true); + } + if (widthChanged) { + currentDependencyTree.setNeedsHorizontalLayout(dependent, + true); } } } + if (elementResizeListeners.containsKey(element)) { + listenersToFire.add(element); + } } private static boolean isManagedLayout(ComponentConnector paintable) { return paintable instanceof ManagedLayout; } - public void foceLayout() { + public void forceLayout() { ConnectorMap paintableMap = connection.getConnectorMap(); ComponentConnector[] paintableWidgets = paintableMap .getComponentConnectors(); - for (ComponentConnector vPaintableWidget : paintableWidgets) { - MeasuredSize measuredSize = getMeasuredSize(vPaintableWidget); - measuredSize.setHeightNeedsUpdate(); - measuredSize.setWidthNeedsUpdate(); + for (ComponentConnector connector : paintableWidgets) { + if (connector instanceof ManagedLayout) { + setNeedsUpdate((ManagedLayout) connector); + } } - doLayout(); + setEverythingNeedsMeasure(); + layoutNow(); } + // TODO Rename to setNeedsLayout public final void setNeedsUpdate(ManagedLayout layout) { setWidthNeedsUpdate(layout); setHeightNeedsUpdate(layout); } public final void setWidthNeedsUpdate(ManagedLayout layout) { - getMeasuredSize(layout).setWidthNeedsUpdate(); + needsHorizontalLayout.add(layout); } public final void setHeightNeedsUpdate(ManagedLayout layout) { - getMeasuredSize(layout).setHeightNeedsUpdate(); + needsVerticalLayout.add(layout); } public boolean isMeasured(Element element) { @@ -379,4 +616,105 @@ public class LayoutManager { public int getMarginLeft(Element element) { return getMeasuredSize(element, nullSize).getMarginLeft(); } + + public void reportOuterHeight(ComponentConnector component, int outerHeight) { + MeasuredSize measuredSize = getMeasuredSize(component); + if (isLayoutRunning()) { + boolean heightChanged = measuredSize.setOuterHeight(outerHeight); + + if (heightChanged) { + onConnectorChange(component, false, true); + notifyListenersAndDepdendents(component.getWidget() + .getElement(), false, true); + } + currentDependencyTree.setNeedsVerticalMeasure(component, false); + } else if (measuredSize.getOuterHeight() != outerHeight) { + setNeedsMeasure(component); + } + } + + public void reportHeightAssignedToRelative(ComponentConnector component, + int assignedHeight) { + assert component.isRelativeHeight(); + + float percentSize = parsePercent(component.getState().getHeight()); + int effectiveHeight = Math.round(assignedHeight * (percentSize / 100)); + + reportOuterHeight(component, effectiveHeight); + } + + public void reportWidthAssignedToRelative(ComponentConnector component, + int assignedWidth) { + assert component.isRelativeWidth(); + + float percentSize = parsePercent(component.getState().getWidth()); + int effectiveWidth = Math.round(assignedWidth * (percentSize / 100)); + + reportOuterWidth(component, effectiveWidth); + } + + private static float parsePercent(String size) { + return Float.parseFloat(size.substring(0, size.length() - 1)); + } + + public void reportOuterWidth(ComponentConnector component, int outerWidth) { + MeasuredSize measuredSize = getMeasuredSize(component); + if (isLayoutRunning()) { + boolean widthChanged = measuredSize.setOuterWidth(outerWidth); + + if (widthChanged) { + onConnectorChange(component, true, false); + notifyListenersAndDepdendents(component.getWidget() + .getElement(), true, false); + } + currentDependencyTree.setNeedsHorizontalMeasure(component, false); + } else if (measuredSize.getOuterWidth() != outerWidth) { + setNeedsMeasure(component); + } + } + + public void addElementResizeListener(Element element, + ElementResizeListener listener) { + Collection<ElementResizeListener> listeners = elementResizeListeners + .get(element); + if (listeners == null) { + listeners = new HashSet<ElementResizeListener>(); + elementResizeListeners.put(element, listeners); + ensureMeasured(element); + } + listeners.add(listener); + } + + public void removeElementResizeListener(Element element, + ElementResizeListener listener) { + Collection<ElementResizeListener> listeners = elementResizeListeners + .get(element); + if (listeners != null) { + listeners.remove(listener); + if (listeners.isEmpty()) { + elementResizeListeners.remove(element); + stopMeasuringIfUnecessary(element); + } + } + } + + private void stopMeasuringIfUnecessary(Element element) { + if (!needsMeasure(element)) { + measuredNonPaintableElements.remove(element); + setMeasuredSize(element, null); + } + } + + public void setNeedsMeasure(ComponentConnector component) { + if (isLayoutRunning()) { + currentDependencyTree.setNeedsMeasure(component, true); + } else { + needsMeasure.add(component); + layoutLater(); + } + } + + public void setEverythingNeedsMeasure() { + everythingNeedsMeasure = true; + } } diff --git a/src/com/vaadin/terminal/gwt/client/MeasuredSize.java b/src/com/vaadin/terminal/gwt/client/MeasuredSize.java index e8078031e3..97822fa8ec 100644 --- a/src/com/vaadin/terminal/gwt/client/MeasuredSize.java +++ b/src/com/vaadin/terminal/gwt/client/MeasuredSize.java @@ -7,6 +7,28 @@ import com.google.gwt.core.client.JsArrayString; import com.google.gwt.dom.client.Element; public class MeasuredSize { + public static class MeasureResult { + private final boolean widthChanged; + private final boolean heightChanged; + + private MeasureResult(boolean widthChanged, boolean heightChanged) { + this.widthChanged = widthChanged; + this.heightChanged = heightChanged; + } + + public boolean isHeightChanged() { + return heightChanged; + } + + public boolean isWidthChanged() { + return widthChanged; + } + + public boolean isChanged() { + return heightChanged || widthChanged; + } + } + private int width = -1; private int height = -1; @@ -14,9 +36,6 @@ public class MeasuredSize { private int[] borders = new int[4]; private int[] margins = new int[4]; - private boolean heightChanged = true; - private boolean widthChanged = true; - private FastStringSet dependents = FastStringSet.create(); public int getOuterHeight() { @@ -61,17 +80,21 @@ public class MeasuredSize { - sumWidths(paddings); } - public void setOuterHeight(int height) { + public boolean setOuterHeight(int height) { if (this.height != height) { - heightChanged = true; this.height = height; + return true; + } else { + return false; } } - public void setOuterWidth(int width) { + public boolean setOuterWidth(int width) { if (this.width != width) { - widthChanged = true; this.width = width; + return true; + } else { + return false; } } @@ -147,9 +170,9 @@ public class MeasuredSize { return paddings[3]; } - boolean measure(Element element) { - boolean wasHeightChanged = heightChanged; - boolean wasWidthChanged = widthChanged; + public MeasureResult measure(Element element) { + boolean heightChanged = false; + boolean widthChanged = false; ComputedStyle computedStyle = new ComputedStyle(element); int[] paddings = computedStyle.getPadding(); @@ -181,26 +204,17 @@ public class MeasuredSize { int requiredHeight = Util.getRequiredHeight(element); int marginHeight = sumHeights(margins); - setOuterHeight(requiredHeight + marginHeight); + if (setOuterHeight(requiredHeight + marginHeight)) { + heightChanged = true; + } int requiredWidth = Util.getRequiredWidth(element); int marginWidth = sumWidths(margins); - setOuterWidth(requiredWidth + marginWidth); - - return wasHeightChanged != heightChanged - || wasWidthChanged != widthChanged; - } - - void clearDirtyState() { - heightChanged = widthChanged = false; - } - - public boolean isHeightNeedsUpdate() { - return heightChanged; - } + if (setOuterWidth(requiredWidth + marginWidth)) { + widthChanged = true; + } - public boolean isWidthNeedsUpdate() { - return widthChanged; + return new MeasureResult(widthChanged, heightChanged); } private static boolean hasWidthChanged(int[] sizes1, int[] sizes2) { @@ -211,11 +225,4 @@ public class MeasuredSize { return sizes1[0] != sizes2[0] || sizes1[2] != sizes2[2]; } - public void setWidthNeedsUpdate() { - widthChanged = true; - } - - public void setHeightNeedsUpdate() { - heightChanged = true; - } }
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/Util.java b/src/com/vaadin/terminal/gwt/client/Util.java index faaad8c7d2..bfe63caefd 100644 --- a/src/com/vaadin/terminal/gwt/client/Util.java +++ b/src/com/vaadin/terminal/gwt/client/Util.java @@ -79,23 +79,23 @@ public class Util { * @param widget * @param lazy * run componentSizeUpdated lazyly + * + * @deprecated since 7.0, use + * {@link LayoutManager#setNeedsMeasure(ComponentConnector)} + * instead */ + @Deprecated public static void notifyParentOfSizeChange(Widget widget, boolean lazy) { - ApplicationConnection applicationConnection = findApplicationConnectionFor(widget); - if (applicationConnection != null) { - applicationConnection.doLayout(lazy); + ComponentConnector connector = findConnectorFor(widget); + if (connector != null) { + connector.getLayoutManager().setNeedsMeasure(connector); + if (!lazy) { + connector.getLayoutManager().layoutNow(); + } } } - private static boolean findAppConnectionWarningDisplayed = false; - - 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"); - } - + private static ComponentConnector findConnectorFor(Widget widget) { List<ApplicationConnection> runningApplications = ApplicationConfiguration .getRunningApplications(); for (ApplicationConnection applicationConnection : runningApplications) { @@ -105,7 +105,7 @@ public class Util { continue; } if (connector.getConnection() == applicationConnection) { - return applicationConnection; + return connector; } } @@ -433,8 +433,7 @@ public class Util { public static void runWebkitOverflowAutoFix(final Element elem) { // Add max version if fix lands sometime to Webkit // Starting from Opera 11.00, also a problem in Opera - if ((BrowserInfo.get().getWebkitVersion() > 0 || BrowserInfo.get() - .getOperaVersion() >= 11) && getNativeScrollbarSize() > 0) { + if (BrowserInfo.get().requiresOverflowAutoFix()) { final String originalOverflow = elem.getStyle().getProperty( "overflow"); if ("hidden".equals(originalOverflow)) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java index a893657c40..f33d582ef1 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/absolutelayout/AbsoluteLayoutConnector.java @@ -176,6 +176,7 @@ public class AbsoluteLayoutConnector extends - wrapper.getElement().getOffsetTop(); } wrapperStyle.setHeight(h, Unit.PX); + getLayoutManager().reportHeightAssignedToRelative(paintable, h); } else { wrapperStyle.clearHeight(); } @@ -207,6 +208,7 @@ public class AbsoluteLayoutConnector extends - wrapper.getElement().getOffsetLeft(); } wrapperStyle.setWidth(w, Unit.PX); + getLayoutManager().reportWidthAssignedToRelative(paintable, w); } else { wrapperStyle.clearWidth(); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/accordion/AccordionConnector.java b/src/com/vaadin/terminal/gwt/client/ui/accordion/AccordionConnector.java index e5eda7607b..8ab33f615d 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/accordion/AccordionConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/accordion/AccordionConnector.java @@ -13,12 +13,13 @@ import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.ui.Component; import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout; import com.vaadin.terminal.gwt.client.ui.accordion.VAccordion.StackItem; +import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren; import com.vaadin.terminal.gwt.client.ui.tabsheet.TabsheetBaseConnector; import com.vaadin.ui.Accordion; @Component(Accordion.class) public class AccordionConnector extends TabsheetBaseConnector implements - SimpleManagedLayout { + SimpleManagedLayout, MayScrollChildren { @Override public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java b/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java index 71361b79ad..dcd520bbb3 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java +++ b/src/com/vaadin/terminal/gwt/client/ui/accordion/VAccordion.java @@ -254,9 +254,6 @@ public class VAccordion extends VTabsheetBase { super.setWidth(maxWidth + "px"); openTab.setWidth(maxWidth); } - - Util.runWebkitOverflowAutoFix(openTab.getContainerElement()); - } /** diff --git a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java index c5f18ba23c..5cbfabbb11 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java +++ b/src/com/vaadin/terminal/gwt/client/ui/draganddropwrapper/VDragAndDropWrapper.java @@ -25,6 +25,7 @@ import com.google.gwt.xhr.client.XMLHttpRequest; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.ComponentConnector; import com.vaadin.terminal.gwt.client.ConnectorMap; +import com.vaadin.terminal.gwt.client.LayoutManager; import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder; import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VConsole; @@ -565,10 +566,15 @@ public class VDragAndDropWrapper extends VCustomComponent implements + emphasizedHDrop.toString().toLowerCase(), false); } if (doLayout) { - client.doLayout(false); + notifySizePotentiallyChanged(); } } + private void notifySizePotentiallyChanged() { + LayoutManager.get(client).setNeedsMeasure( + ConnectorMap.get(client).getConnector(getElement())); + } + protected void emphasis(VDragEvent drag) { deEmphasis(false); VDragAndDropWrapper.setStyleName(getElement(), OVER_STYLE, true); @@ -581,7 +587,7 @@ public class VDragAndDropWrapper extends VCustomComponent implements // TODO build (to be an example) an emphasis mode where drag image // is fitted before or after the content - client.doLayout(false); + notifySizePotentiallyChanged(); } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/form/FormConnector.java b/src/com/vaadin/terminal/gwt/client/ui/form/FormConnector.java index 34d8461b20..11d5385213 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/form/FormConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/form/FormConnector.java @@ -16,17 +16,41 @@ import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector; import com.vaadin.terminal.gwt.client.ui.Component; import com.vaadin.terminal.gwt.client.ui.Icon; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler; -import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout; +import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeEvent; +import com.vaadin.terminal.gwt.client.ui.layout.ElementResizeListener; +import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren; import com.vaadin.ui.Form; @Component(Form.class) public class FormConnector extends AbstractComponentContainerConnector - implements Paintable, SimpleManagedLayout { + implements Paintable, MayScrollChildren { + + private final ElementResizeListener footerResizeListener = new ElementResizeListener() { + public void onElementResize(ElementResizeEvent e) { + VForm form = getWidget(); + + int footerHeight; + if (form.footer != null) { + LayoutManager lm = getLayoutManager(); + footerHeight = lm.getOuterHeight(form.footer.getElement()); + } else { + footerHeight = 0; + } + + form.fieldContainer.getStyle().setPaddingBottom(footerHeight, + Unit.PX); + form.footerContainer.getStyle() + .setMarginTop(-footerHeight, Unit.PX); + } + }; @Override - public void init() { + public void onUnregister() { VForm form = getWidget(); - getLayoutManager().registerDependency(this, form.footerContainer); + if (form.footer != null) { + getLayoutManager().removeElementResizeListener( + form.footer.getElement(), footerResizeListener); + } } @Override @@ -96,10 +120,16 @@ public class FormConnector extends AbstractComponentContainerConnector .getFooter(); Widget newFooterWidget = newFooter.getWidget(); if (getWidget().footer == null) { + getLayoutManager().addElementResizeListener( + newFooterWidget.getElement(), footerResizeListener); getWidget().add(newFooter.getWidget(), getWidget().footerContainer); getWidget().footer = newFooterWidget; } else if (newFooter != getWidget().footer) { + getLayoutManager().removeElementResizeListener( + getWidget().footer.getElement(), footerResizeListener); + getLayoutManager().addElementResizeListener( + newFooterWidget.getElement(), footerResizeListener); getWidget().remove(getWidget().footer); getWidget().add(newFooter.getWidget(), getWidget().footerContainer); @@ -107,7 +137,10 @@ public class FormConnector extends AbstractComponentContainerConnector getWidget().footer = newFooterWidget; } else { if (getWidget().footer != null) { + getLayoutManager().removeElementResizeListener( + getWidget().footer.getElement(), footerResizeListener); getWidget().remove(getWidget().footer); + getWidget().footer = null; } } @@ -163,17 +196,6 @@ public class FormConnector extends AbstractComponentContainerConnector return GWT.create(VForm.class); } - public void layout() { - VForm form = getWidget(); - - LayoutManager lm = getLayoutManager(); - int footerHeight = lm.getOuterHeight(form.footerContainer) - - lm.getMarginTop(form.footerContainer); - - form.fieldContainer.getStyle().setPaddingBottom(footerHeight, Unit.PX); - form.footerContainer.getStyle().setMarginTop(-footerHeight, Unit.PX); - } - @Override public boolean isReadOnly() { return super.isReadOnly() || getState().isPropertyReadOnly(); diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java index 9858ed362a..0a26fa020a 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/GridLayoutConnector.java @@ -58,6 +58,19 @@ public class GridLayoutConnector extends AbstractComponentContainerConnector } @Override + public void onUnregister() { + VGridLayout layout = getWidget(); + getLayoutManager().unregisterDependency(this, + layout.spacingMeasureElement); + + // Unregister caption size dependencies + for (ComponentConnector child : getChildren()) { + Cell cell = layout.widgetToCell.get(child.getWidget()); + cell.slot.setCaption(null); + } + } + + @Override public GridLayoutState getState() { return (GridLayoutState) super.getState(); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/VGridLayout.java b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/VGridLayout.java index 1949cb191c..6c5d018161 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/gridlayout/VGridLayout.java +++ b/src/com/vaadin/terminal/gwt/client/ui/gridlayout/VGridLayout.java @@ -211,6 +211,8 @@ public class VGridLayout extends ComplexPanel { + layoutManager.getPaddingBottom(element) + layoutManager.getBorderHeight(element); element.getStyle().setHeight(outerHeight, Unit.PX); + getConnector().getLayoutManager().reportOuterHeight(getConnector(), + outerHeight); } } @@ -234,6 +236,8 @@ public class VGridLayout extends ComplexPanel { + layoutManager.getPaddingRight(element) + layoutManager.getBorderWidth(element); element.getStyle().setWidth(outerWidth, Unit.PX); + getConnector().getLayoutManager().reportOuterWidth(getConnector(), + outerWidth); } } @@ -559,6 +563,10 @@ public class VGridLayout extends ComplexPanel { if (slot == null || slot.getChild() != childConnector) { slot = new ComponentConnectorLayoutSlot(CLASSNAME, childConnector, getConnector()); + if (childConnector.isRelativeWidth()) { + slot.getWrapperElement().getStyle() + .setWidth(100, Unit.PCT); + } Element slotWrapper = slot.getWrapperElement(); getElement().appendChild(slotWrapper); diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/ComponentConnectorLayoutSlot.java b/src/com/vaadin/terminal/gwt/client/ui/layout/ComponentConnectorLayoutSlot.java index 8ff012e086..d479e8da9d 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/layout/ComponentConnectorLayoutSlot.java +++ b/src/com/vaadin/terminal/gwt/client/ui/layout/ComponentConnectorLayoutSlot.java @@ -57,6 +57,16 @@ public class ComponentConnectorLayoutSlot extends VLayoutSlot { } @Override + protected void reportActualRelativeHeight(int allocatedHeight) { + getLayoutManager().reportOuterHeight(child, allocatedHeight); + } + + @Override + protected void reportActualRelativeWidth(int allocatedWidth) { + getLayoutManager().reportOuterWidth(child, allocatedWidth); + } + + @Override public int getWidgetHeight() { return getLayoutManager() .getOuterHeight(child.getWidget().getElement()); diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeEvent.java b/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeEvent.java new file mode 100644 index 0000000000..a519f5db87 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeEvent.java @@ -0,0 +1,25 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client.ui.layout; + +import com.google.gwt.dom.client.Element; +import com.vaadin.terminal.gwt.client.LayoutManager; + +public class ElementResizeEvent { + private final Element element; + private final LayoutManager layoutManager; + + public ElementResizeEvent(LayoutManager layoutManager, Element element) { + this.layoutManager = layoutManager; + this.element = element; + } + + public Element getElement() { + return element; + } + + public LayoutManager getLayoutManager() { + return layoutManager; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeListener.java b/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeListener.java new file mode 100644 index 0000000000..d6d3de48b8 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/layout/ElementResizeListener.java @@ -0,0 +1,9 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.terminal.gwt.client.ui.layout; + +public interface ElementResizeListener { + public void onElementResize(ElementResizeEvent e); +}
\ No newline at end of file diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java b/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java new file mode 100644 index 0000000000..1ca8933ff2 --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/layout/LayoutDependencyTree.java @@ -0,0 +1,521 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client.ui.layout; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.vaadin.terminal.gwt.client.ComponentConnector; +import com.vaadin.terminal.gwt.client.ComponentContainerConnector; +import com.vaadin.terminal.gwt.client.ComponentState; +import com.vaadin.terminal.gwt.client.Util; +import com.vaadin.terminal.gwt.client.VConsole; +import com.vaadin.terminal.gwt.client.ui.ManagedLayout; + +public class LayoutDependencyTree { + private class LayoutDependency { + private final ComponentConnector connector; + private final int direction; + + private boolean needsLayout = false; + private boolean needsMeasure = false; + + private boolean scrollingParentCached = false; + private ComponentConnector scrollingBoundary = null; + + private Set<ComponentConnector> measureBlockers = new HashSet<ComponentConnector>(); + private Set<ComponentConnector> layoutBlockers = new HashSet<ComponentConnector>(); + + public LayoutDependency(ComponentConnector connector, int direction) { + this.connector = connector; + this.direction = direction; + } + + private void addLayoutBlocker(ComponentConnector blocker) { + boolean blockerAdded = layoutBlockers.add(blocker); + if (blockerAdded && layoutBlockers.size() == 1) { + if (needsLayout) { + getLayoutQueue(direction).remove(connector); + } else { + // Propagation already done if needsLayout is set + propagatePotentialLayout(); + } + } + } + + private void removeLayoutBlocker(ComponentConnector blocker) { + boolean removed = layoutBlockers.remove(blocker); + if (removed && layoutBlockers.isEmpty()) { + if (needsLayout) { + getLayoutQueue(direction).add((ManagedLayout) connector); + } else { + propagateNoUpcomingLayout(); + } + } + } + + private void addMeasureBlocker(ComponentConnector blocker) { + boolean blockerAdded = measureBlockers.add(blocker); + if (blockerAdded && measureBlockers.size() == 1) { + if (needsMeasure) { + getMeasureQueue(direction).remove(connector); + } else { + propagatePotentialResize(); + } + } + } + + private void removeMeasureBlocker(ComponentConnector blocker) { + boolean removed = measureBlockers.remove(blocker); + if (removed && measureBlockers.isEmpty()) { + if (needsMeasure) { + getMeasureQueue(direction).add(connector); + } else { + propagateNoUpcomingResize(); + } + } + } + + public void setNeedsMeasure(boolean needsMeasure) { + if (needsMeasure && !this.needsMeasure) { + // If enabling needsMeasure + this.needsMeasure = needsMeasure; + + if (measureBlockers.isEmpty()) { + // Add to queue if there are no blockers + getMeasureQueue(direction).add(connector); + // Only need to propagate if not already propagated when + // setting blockers + propagatePotentialResize(); + } + } else if (!needsMeasure && this.needsMeasure + && measureBlockers.isEmpty()) { + // Only disable if there are no blockers (elements gets measured + // in both directions even if there is a blocker in one + // direction) + this.needsMeasure = needsMeasure; + getMeasureQueue(direction).remove(connector); + propagateNoUpcomingResize(); + } + } + + public void setNeedsLayout(boolean needsLayout) { + if (!(connector instanceof ManagedLayout)) { + throw new IllegalStateException( + "Only managed layouts can need layout, layout attempted for " + + Util.getConnectorString(connector)); + } + if (needsLayout && !this.needsLayout) { + // If enabling needsLayout + this.needsLayout = needsLayout; + + if (layoutBlockers.isEmpty()) { + // Add to queue if there are no blockers + getLayoutQueue(direction).add((ManagedLayout) connector); + // Only need to propagate if not already propagated when + // setting blockers + propagatePotentialLayout(); + } + } else if (!needsLayout && this.needsLayout + && layoutBlockers.isEmpty()) { + // Only disable if there are no layout blockers + // (SimpleManagedLayout gets layouted in both directions + // even if there is a blocker in one direction) + this.needsLayout = needsLayout; + getLayoutQueue(direction).remove(connector); + propagateNoUpcomingLayout(); + } + } + + private void propagatePotentialResize() { + for (ComponentConnector needsSize : getNeedsSizeForLayout()) { + LayoutDependency layoutDependency = getDependency(needsSize, + direction); + layoutDependency.addLayoutBlocker(connector); + } + } + + private Collection<ComponentConnector> getNeedsSizeForLayout() { + // Find all connectors that need the size of this connector for + // layouting + + // Parent needs size if it isn't relative? + // Connector itself needs size if it isn't undefined? + // Children doesn't care? + + ArrayList<ComponentConnector> needsSize = new ArrayList<ComponentConnector>(); + + if (!isUndefinedInDirection(connector, direction)) { + needsSize.add(connector); + } + if (!isRelativeInDirection(connector, direction)) { + ComponentConnector parent = connector.getParent(); + if (parent != null) { + needsSize.add(parent); + } + } + + return needsSize; + } + + private void propagateNoUpcomingResize() { + for (ComponentConnector mightNeedLayout : getNeedsSizeForLayout()) { + LayoutDependency layoutDependency = getDependency( + mightNeedLayout, direction); + layoutDependency.removeLayoutBlocker(connector); + } + } + + private void propagatePotentialLayout() { + for (ComponentConnector sizeMightChange : getResizedByLayout()) { + LayoutDependency layoutDependency = getDependency( + sizeMightChange, direction); + layoutDependency.addMeasureBlocker(connector); + } + } + + private Collection<ComponentConnector> getResizedByLayout() { + // Components that might get resized by a layout of this component + + // Parent never resized + // Connector itself resized if undefined + // Children resized if relative + + ArrayList<ComponentConnector> resized = new ArrayList<ComponentConnector>(); + if (isUndefinedInDirection(connector, direction)) { + resized.add(connector); + } + + if (connector instanceof ComponentContainerConnector) { + ComponentContainerConnector container = (ComponentContainerConnector) connector; + for (ComponentConnector child : container.getChildren()) { + if (isRelativeInDirection(child, direction)) { + resized.add(child); + } + } + } + + return resized; + } + + private void propagateNoUpcomingLayout() { + for (ComponentConnector sizeMightChange : getResizedByLayout()) { + LayoutDependency layoutDependency = getDependency( + sizeMightChange, direction); + layoutDependency.removeMeasureBlocker(connector); + } + } + + public void markSizeAsChanged() { + // When the size has changed, all that use that size should be + // layouted + for (ComponentConnector connector : getNeedsSizeForLayout()) { + LayoutDependency layoutDependency = getDependency(connector, + direction); + if (connector instanceof ManagedLayout) { + layoutDependency.setNeedsLayout(true); + } else { + // Should simulate setNeedsLayout(true) + markAsLayouted -> + // propagate needs measure + layoutDependency.propagatePostLayoutMeasure(); + } + } + + // Should also go through the hierarchy to discover appeared or + // disappeared scrollbars + ComponentConnector scrollingBoundary = getScrollingBoundary(connector); + if (scrollingBoundary != null) { + getDependency(scrollingBoundary, getOppositeDirection()) + .setNeedsMeasure(true); + } + + } + + /** + * Go up the hierarchy to find a component whose size might have changed + * in the other direction because changes to this component causes + * scrollbars to appear or disappear. + * + * @return + */ + private LayoutDependency findPotentiallyChangedScrollbar() { + ComponentConnector currentConnector = connector; + while (true) { + ComponentContainerConnector parent = currentConnector + .getParent(); + if (parent == null) { + return null; + } + if (parent instanceof MayScrollChildren) { + return getDependency(currentConnector, + getOppositeDirection()); + } + currentConnector = parent; + } + } + + private int getOppositeDirection() { + return direction == HORIZONTAL ? VERTICAL : HORIZONTAL; + } + + public void markAsLayouted() { + if (!layoutBlockers.isEmpty()) { + // Don't do anything if there are layout blockers (SimpleLayout + // gets layouted in both directions even if one direction is + // blocked) + return; + } + setNeedsLayout(false); + propagatePostLayoutMeasure(); + } + + private void propagatePostLayoutMeasure() { + for (ComponentConnector resized : getResizedByLayout()) { + LayoutDependency layoutDependency = getDependency(resized, + direction); + layoutDependency.setNeedsMeasure(true); + } + + // Special case for e.g. wrapping texts + if (direction == HORIZONTAL && !connector.isUndefinedWidth() + && connector.isUndefinedHeight()) { + LayoutDependency dependency = getDependency(connector, VERTICAL); + dependency.setNeedsMeasure(true); + } + } + + @Override + public String toString() { + String s = getCompactConnectorString(connector) + "\n"; + if (direction == VERTICAL) { + s += "Vertical"; + } else { + s += "Horizontal"; + } + ComponentState state = connector.getState(); + s += " sizing: " + + getSizeDefinition(direction == VERTICAL ? state + .getHeight() : state.getWidth()) + "\n"; + + if (needsLayout) { + s += "Needs layout\n"; + } + if (getLayoutQueue(direction).contains(connector)) { + s += "In layout queue\n"; + } + s += "Layout blockers: " + blockersToString(layoutBlockers) + "\n"; + + if (needsMeasure) { + s += "Needs measure\n"; + } + if (getMeasureQueue(direction).contains(connector)) { + s += "In measure queue\n"; + } + s += "Measure blockers: " + blockersToString(measureBlockers); + + return s; + } + + public boolean noMoreChangesExpected() { + return !needsLayout && !needsMeasure && layoutBlockers.isEmpty() + && measureBlockers.isEmpty(); + } + + } + + private static final int HORIZONTAL = 0; + private static final int VERTICAL = 1; + + private final Map<?, ?>[] dependenciesInDirection = new Map<?, ?>[] { + new HashMap<ComponentConnector, LayoutDependency>(), + new HashMap<ComponentConnector, LayoutDependency>() }; + + private final Collection<?>[] measureQueueInDirection = new HashSet<?>[] { + new HashSet<ComponentConnector>(), + new HashSet<ComponentConnector>() }; + + private final Collection<?>[] layoutQueueInDirection = new HashSet<?>[] { + new HashSet<ComponentConnector>(), + new HashSet<ComponentConnector>() }; + + public void setNeedsMeasure(ComponentConnector connector, + boolean needsMeasure) { + setNeedsHorizontalMeasure(connector, needsMeasure); + setNeedsVerticalMeasure(connector, needsMeasure); + } + + public void setNeedsHorizontalMeasure(ComponentConnector connector, + boolean needsMeasure) { + LayoutDependency dependency = getDependency(connector, HORIZONTAL); + dependency.setNeedsMeasure(needsMeasure); + } + + public void setNeedsVerticalMeasure(ComponentConnector connector, + boolean needsMeasure) { + LayoutDependency dependency = getDependency(connector, VERTICAL); + dependency.setNeedsMeasure(needsMeasure); + } + + private LayoutDependency getDependency(ComponentConnector connector, + int direction) { + @SuppressWarnings("unchecked") + Map<ComponentConnector, LayoutDependency> dependencies = (Map<ComponentConnector, LayoutDependency>) dependenciesInDirection[direction]; + LayoutDependency dependency = dependencies.get(connector); + if (dependency == null) { + dependency = new LayoutDependency(connector, direction); + dependencies.put(connector, dependency); + } + return dependency; + } + + @SuppressWarnings("unchecked") + private Collection<ManagedLayout> getLayoutQueue(int direction) { + return (Collection<ManagedLayout>) layoutQueueInDirection[direction]; + } + + @SuppressWarnings("unchecked") + private Collection<ComponentConnector> getMeasureQueue(int direction) { + return (Collection<ComponentConnector>) measureQueueInDirection[direction]; + } + + public void setNeedsHorizontalLayout(ManagedLayout layout, + boolean needsLayout) { + LayoutDependency dependency = getDependency(layout, HORIZONTAL); + dependency.setNeedsLayout(needsLayout); + } + + public void setNeedsVerticalLayout(ManagedLayout layout, boolean needsLayout) { + LayoutDependency dependency = getDependency(layout, VERTICAL); + dependency.setNeedsLayout(needsLayout); + } + + public void markAsHorizontallyLayouted(ManagedLayout layout) { + LayoutDependency dependency = getDependency(layout, HORIZONTAL); + dependency.markAsLayouted(); + } + + public void markAsVerticallyLayouted(ManagedLayout layout) { + LayoutDependency dependency = getDependency(layout, VERTICAL); + dependency.markAsLayouted(); + } + + public void markHeightAsChanged(ComponentConnector connector) { + LayoutDependency dependency = getDependency(connector, VERTICAL); + dependency.markSizeAsChanged(); + } + + public void markWidthAsChanged(ComponentConnector connector) { + LayoutDependency dependency = getDependency(connector, HORIZONTAL); + dependency.markSizeAsChanged(); + } + + private static boolean isRelativeInDirection(ComponentConnector connector, + int direction) { + if (direction == HORIZONTAL) { + return connector.isRelativeWidth(); + } else { + return connector.isRelativeHeight(); + } + } + + private static boolean isUndefinedInDirection(ComponentConnector connector, + int direction) { + if (direction == VERTICAL) { + return connector.isUndefinedHeight(); + } else { + return connector.isUndefinedWidth(); + } + } + + private static String getCompactConnectorString(ComponentConnector connector) { + return Util.getSimpleName(connector) + " (" + + connector.getConnectorId() + ")"; + } + + private static String getSizeDefinition(String size) { + if (size == null || size.length() == 0) { + return "undefined"; + } else if (size.endsWith("%")) { + return "relative"; + } else { + return "fixed"; + } + } + + private static String blockersToString( + Collection<ComponentConnector> blockers) { + StringBuilder b = new StringBuilder("["); + for (ComponentConnector blocker : blockers) { + if (b.length() != 1) { + b.append(", "); + } + b.append(getCompactConnectorString(blocker)); + } + b.append(']'); + return b.toString(); + } + + public boolean hasConnectorsToMeasure() { + return !measureQueueInDirection[HORIZONTAL].isEmpty() + || !measureQueueInDirection[VERTICAL].isEmpty(); + } + + public boolean hasHorizontalConnectorToLayout() { + return !getLayoutQueue(HORIZONTAL).isEmpty(); + } + + public boolean hasVerticaConnectorToLayout() { + return !getLayoutQueue(VERTICAL).isEmpty(); + } + + public ManagedLayout[] getHorizontalLayoutTargets() { + Collection<ManagedLayout> queue = getLayoutQueue(HORIZONTAL); + return queue.toArray(new ManagedLayout[queue.size()]); + } + + public ManagedLayout[] getVerticalLayoutTargets() { + Collection<ManagedLayout> queue = getLayoutQueue(VERTICAL); + return queue.toArray(new ManagedLayout[queue.size()]); + } + + public Collection<ComponentConnector> getMeasureTargets() { + Collection<ComponentConnector> measureTargets = new HashSet<ComponentConnector>( + getMeasureQueue(HORIZONTAL)); + measureTargets.addAll(getMeasureQueue(VERTICAL)); + return measureTargets; + } + + public void logDependencyStatus(ComponentConnector connector) { + VConsole.log("===="); + VConsole.log(getDependency(connector, HORIZONTAL).toString()); + VConsole.log(getDependency(connector, VERTICAL).toString()); + } + + public boolean noMoreChangesExpected(ComponentConnector connector) { + return getDependency(connector, HORIZONTAL).noMoreChangesExpected() + && getDependency(connector, VERTICAL).noMoreChangesExpected(); + } + + public ComponentConnector getScrollingBoundary(ComponentConnector connector) { + LayoutDependency dependency = getDependency(connector, HORIZONTAL); + if (!dependency.scrollingParentCached) { + ComponentContainerConnector parent = dependency.connector + .getParent(); + if (parent instanceof MayScrollChildren) { + dependency.scrollingBoundary = connector; + } else if (parent != null) { + dependency.scrollingBoundary = getScrollingBoundary(parent); + } else { + // No scrolling parent + } + + dependency.scrollingParentCached = true; + } + return dependency.scrollingBoundary; + } +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/MayScrollChildren.java b/src/com/vaadin/terminal/gwt/client/ui/layout/MayScrollChildren.java new file mode 100644 index 0000000000..62c9937c4c --- /dev/null +++ b/src/com/vaadin/terminal/gwt/client/ui/layout/MayScrollChildren.java @@ -0,0 +1,10 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ +package com.vaadin.terminal.gwt.client.ui.layout; + +import com.vaadin.terminal.gwt.client.ComponentContainerConnector; + +public interface MayScrollChildren extends ComponentContainerConnector { + +} diff --git a/src/com/vaadin/terminal/gwt/client/ui/layout/VLayoutSlot.java b/src/com/vaadin/terminal/gwt/client/ui/layout/VLayoutSlot.java index 1caa87cce5..c02061a2fe 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/layout/VLayoutSlot.java +++ b/src/com/vaadin/terminal/gwt/client/ui/layout/VLayoutSlot.java @@ -94,14 +94,20 @@ public abstract class VLayoutSlot { style.clearProperty("width"); } + double allocatedContentWidth = 0; + if (isRelativeWidth()) { + String percentWidth = getWidget().getElement().getStyle() + .getWidth(); + double percentage = parsePercent(percentWidth); + allocatedContentWidth = availableWidth * (percentage / 100); + reportActualRelativeWidth(Math.round((float) allocatedContentWidth)); + } + AlignmentInfo alignment = getAlignment(); if (!alignment.isLeft()) { double usedWidth; if (isRelativeWidth()) { - String percentWidth = getWidget().getElement().getStyle() - .getWidth(); - double percentage = parsePercent(percentWidth); - usedWidth = availableWidth * (percentage / 100); + usedWidth = allocatedContentWidth; } else { usedWidth = getWidgetWidth(); } @@ -156,13 +162,20 @@ public abstract class VLayoutSlot { style.clearHeight(); } + double allocatedContentHeight = 0; + if (isRelativeHeight()) { + String height = getWidget().getElement().getStyle().getHeight(); + double percentage = parsePercent(height); + allocatedContentHeight = contentHeight * (percentage / 100); + reportActualRelativeHeight(Math + .round((float) allocatedContentHeight)); + } + AlignmentInfo alignment = getAlignment(); if (!alignment.isTop()) { double usedHeight; if (isRelativeHeight()) { - String height = getWidget().getElement().getStyle().getHeight(); - double percentage = parsePercent(height); - usedHeight = captionHeight + contentHeight * (percentage / 100); + usedHeight = captionHeight + allocatedContentHeight; } else { usedHeight = getUsedHeight(); } @@ -176,6 +189,14 @@ public abstract class VLayoutSlot { style.setTop(currentLocation, Unit.PX); } + protected void reportActualRelativeHeight(int allocatedHeight) { + // Default implementation does nothing + } + + protected void reportActualRelativeWidth(int allocatedWidth) { + // Default implementation does nothing + } + public void positionInDirection(double currentLocation, double allocatedSpace, boolean isVertical) { if (isVertical) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java index 486621fae2..4a9190e0f1 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java @@ -6,6 +6,7 @@ package com.vaadin.terminal.gwt.client.ui.orderedlayout; import java.util.List; import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ApplicationConnection; @@ -56,6 +57,20 @@ public abstract class AbstractOrderedLayoutConnector extends } @Override + public void onUnregister() { + LayoutManager lm = getLayoutManager(); + + VMeasuringOrderedLayout layout = getWidget(); + lm.unregisterDependency(this, layout.spacingMeasureElement); + + // Unregister child caption listeners + for (ComponentConnector child : getChildren()) { + VLayoutSlot slot = layout.getSlotForChild(child.getWidget()); + slot.setCaption(null); + } + } + + @Override public AbstractOrderedLayoutState getState() { return (AbstractOrderedLayoutState) super.getState(); } @@ -177,14 +192,23 @@ public abstract class AbstractOrderedLayoutConnector extends Style ownStyle = getWidget().getElement().getStyle(); if (isUndefined) { - ownStyle.setPropertyPx(getSizeProperty(isVertical), - getSizeForInnerSize(allocatedSize, isVertical)); + int outerSize = getSizeForInnerSize(allocatedSize, isVertical); + ownStyle.setPropertyPx(getSizeProperty(isVertical), outerSize); + reportUndefinedSize(outerSize, isVertical); } else { ownStyle.setProperty(getSizeProperty(isVertical), getDefinedSize(isVertical)); } } + private void reportUndefinedSize(int outerSize, boolean isVertical) { + if (isVertical) { + getLayoutManager().reportOuterHeight(this, outerSize); + } else { + getLayoutManager().reportOuterWidth(this, outerSize); + } + } + private int getSpacingInDirection(boolean isVertical) { if (isVertical) { return getLayoutManager().getOuterHeight( @@ -215,8 +239,11 @@ public abstract class AbstractOrderedLayoutConnector extends Style ownStyle = getWidget().getElement().getStyle(); if (isUndefined) { + int outerSize = getSizeForInnerSize(allocatedSize, + !getWidget().isVertical); ownStyle.setPropertyPx(getSizeProperty(!getWidget().isVertical), - getSizeForInnerSize(allocatedSize, !getWidget().isVertical)); + outerSize); + reportUndefinedSize(outerSize, !isVertical); } else { ownStyle.setProperty(getSizeProperty(!getWidget().isVertical), getDefinedSize(!getWidget().isVertical)); @@ -275,6 +302,9 @@ public abstract class AbstractOrderedLayoutConnector extends .getStylePrimaryName(), child, this); } layout.addOrMove(slot, currentIndex++); + if (child.isRelativeWidth()) { + slot.getWrapperElement().getStyle().setWidth(100, Unit.PCT); + } } for (ComponentConnector child : previousChildren) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java index 5fe5c6890e..43f58c10b8 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/panel/PanelConnector.java @@ -15,7 +15,6 @@ import com.vaadin.terminal.gwt.client.LayoutManager; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.communication.RpcProxy; import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector; import com.vaadin.terminal.gwt.client.ui.ClickEventHandler; @@ -23,11 +22,13 @@ import com.vaadin.terminal.gwt.client.ui.Component; import com.vaadin.terminal.gwt.client.ui.PostLayoutListener; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler; import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout; +import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren; import com.vaadin.ui.Panel; @Component(Panel.class) public class PanelConnector extends AbstractComponentContainerConnector - implements Paintable, SimpleManagedLayout, PostLayoutListener { + implements Paintable, SimpleManagedLayout, PostLayoutListener, + MayScrollChildren { private Integer uidlScrollTop; @@ -56,6 +57,16 @@ public class PanelConnector extends AbstractComponentContainerConnector } @Override + public void onUnregister() { + VPanel panel = getWidget(); + LayoutManager layoutManager = getLayoutManager(); + + layoutManager.unregisterDependency(this, panel.captionNode); + layoutManager.unregisterDependency(this, panel.bottomDecoration); + layoutManager.unregisterDependency(this, panel.contentNode); + } + + @Override public boolean delegateCaptionHandling() { return false; } @@ -189,8 +200,6 @@ public class PanelConnector extends AbstractComponentContainerConnector // Read actual value back to ensure update logic is correct panel.scrollTop = panel.contentNode.getScrollTop(); panel.scrollLeft = panel.contentNode.getScrollLeft(); - - Util.runWebkitOverflowAutoFix(panel.contentNode); } public void postLayout() { diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java b/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java index 7e84ee1328..048e3ee94c 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java @@ -29,7 +29,6 @@ import com.vaadin.terminal.gwt.client.Focusable; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VConsole; import com.vaadin.terminal.gwt.client.communication.RpcProxy; import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; @@ -39,13 +38,14 @@ import com.vaadin.terminal.gwt.client.ui.ClickEventHandler; import com.vaadin.terminal.gwt.client.ui.Component; import com.vaadin.terminal.gwt.client.ui.Component.LoadStyle; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler; +import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren; import com.vaadin.terminal.gwt.client.ui.notification.VNotification; import com.vaadin.terminal.gwt.client.ui.window.WindowConnector; import com.vaadin.ui.Root; @Component(value = Root.class, loadStyle = LoadStyle.EAGER) public class RootConnector extends AbstractComponentContainerConnector - implements Paintable { + implements Paintable, MayScrollChildren { private RootServerRPC rpc = RpcProxy.create(RootServerRPC.class, this); @@ -226,12 +226,6 @@ public class RootConnector extends AbstractComponentContainerConnector getWidget().scrollable = false; } - // Safari workaround must be run after scrollTop is updated as it sets - // scrollTop using a deferred command. - if (BrowserInfo.get().isSafari()) { - Util.runWebkitOverflowAutoFix(getWidget().getElement()); - } - if (uidl.hasAttribute("scrollTo")) { final ComponentConnector connector = (ComponentConnector) uidl .getPaintableAttribute("scrollTo", getConnection()); diff --git a/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java b/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java index 8182753ab2..13fc55d7ea 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java +++ b/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java @@ -20,8 +20,9 @@ import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.SimplePanel; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.ComponentConnector; +import com.vaadin.terminal.gwt.client.ConnectorMap; import com.vaadin.terminal.gwt.client.Focusable; -import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VConsole; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; @@ -150,23 +151,27 @@ public class VRoot extends SimplePanel implements ResizeHandler, */ protected void windowSizeMaybeChanged(int newWidth, int newHeight) { boolean changed = false; + ComponentConnector connector = ConnectorMap.get(connection) + .getConnector(this); if (width != newWidth) { width = newWidth; changed = true; + connector.getLayoutManager().reportOuterWidth(connector, newWidth); VConsole.log("New window width: " + width); } if (height != newHeight) { height = newHeight; changed = true; + connector.getLayoutManager() + .reportOuterHeight(connector, newHeight); VConsole.log("New window height: " + height); } if (changed) { VConsole.log("Running layout functions due to window resize"); - Util.runWebkitOverflowAutoFix(getElement()); sendClientResized(); - connection.doLayout(false); + connector.getLayoutManager().layoutNow(); } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java index 66416cf0ec..f8fd2faf41 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/AbstractSplitPanelConnector.java @@ -4,6 +4,7 @@ package com.vaadin.terminal.gwt.client.ui.splitpanel; import java.util.LinkedList; +import java.util.List; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.dom.client.DomEvent; @@ -15,6 +16,7 @@ import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.Widget; import com.vaadin.terminal.gwt.client.ComponentConnector; import com.vaadin.terminal.gwt.client.ConnectorHierarchyChangeEvent; +import com.vaadin.terminal.gwt.client.LayoutManager; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.communication.RpcProxy; import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; @@ -140,6 +142,28 @@ public abstract class AbstractSplitPanelConnector extends VAbstractSplitPanel splitPanel = getWidget(); splitPanel.setSplitPosition(splitPanel.position); splitPanel.updateSizes(); + // Report relative sizes in other direction for quicker propagation + List<ComponentConnector> children = getChildren(); + for (ComponentConnector child : children) { + reportOtherDimension(child); + } + } + + private void reportOtherDimension(ComponentConnector child) { + LayoutManager layoutManager = getLayoutManager(); + if (this instanceof HorizontalSplitPanelConnector) { + if (child.isRelativeHeight()) { + int height = layoutManager.getInnerHeight(getWidget() + .getElement()); + layoutManager.reportHeightAssignedToRelative(child, height); + } + } else { + if (child.isRelativeWidth()) { + int width = layoutManager.getInnerWidth(getWidget() + .getElement()); + layoutManager.reportWidthAssignedToRelative(child, width); + } + } } @Override diff --git a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java index 6aad93dc5c..166e79e92e 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/splitpanel/VAbstractSplitPanel.java @@ -25,6 +25,9 @@ import com.google.gwt.user.client.ui.ComplexPanel; 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.ComponentConnector; +import com.vaadin.terminal.gwt.client.ConnectorMap; +import com.vaadin.terminal.gwt.client.LayoutManager; import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VConsole; import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate; @@ -299,6 +302,28 @@ public class VAbstractSplitPanel extends ComplexPanel { DOM.setStyleAttribute(secondContainer, "left", (pixelPosition + getSplitterSize()) + "px"); + LayoutManager layoutManager = LayoutManager.get(client); + ConnectorMap connectorMap = ConnectorMap.get(client); + if (firstChild != null) { + ComponentConnector connector = connectorMap + .getConnector(firstChild); + if (connector.isRelativeWidth()) { + layoutManager.reportWidthAssignedToRelative(connector, + pixelPosition); + } else { + layoutManager.setNeedsMeasure(connector); + } + } + if (secondChild != null) { + ComponentConnector connector = connectorMap + .getConnector(secondChild); + if (connector.isRelativeWidth()) { + layoutManager.reportWidthAssignedToRelative(connector, + secondContainerWidth); + } else { + layoutManager.setNeedsMeasure(connector); + } + } break; case ORIENTATION_VERTICAL: wholeSize = DOM.getElementPropertyInt(wrapper, "clientHeight"); @@ -326,6 +351,28 @@ public class VAbstractSplitPanel extends ComplexPanel { DOM.setStyleAttribute(secondContainer, "top", (pixelPosition + getSplitterSize()) + "px"); + layoutManager = LayoutManager.get(client); + connectorMap = ConnectorMap.get(client); + if (firstChild != null) { + ComponentConnector connector = connectorMap + .getConnector(firstChild); + if (connector.isRelativeHeight()) { + layoutManager.reportHeightAssignedToRelative(connector, + pixelPosition); + } else { + layoutManager.setNeedsMeasure(connector); + } + } + if (secondChild != null) { + ComponentConnector connector = connectorMap + .getConnector(secondChild); + if (connector.isRelativeHeight()) { + layoutManager.reportHeightAssignedToRelative(connector, + secondContainerHeight); + } else { + layoutManager.setNeedsMeasure(connector); + } + } break; } @@ -463,7 +510,6 @@ public class VAbstractSplitPanel extends ComplexPanel { } setSplitPosition(newX + "px"); - client.doLayout(false); } private void onVerticalMouseMove(int y) { @@ -507,7 +553,6 @@ public class VAbstractSplitPanel extends ComplexPanel { } setSplitPosition(newY + "px"); - client.doLayout(false); } public void onMouseUp(Event event) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java b/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java index 76c6c21571..e0cd3dc4c7 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/table/TableConnector.java @@ -7,6 +7,7 @@ import java.util.Iterator; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Style.Position; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.Widget; @@ -282,7 +283,18 @@ public class TableConnector extends AbstractComponentContainerConnector } public void postLayout() { - getWidget().sizeInit(); + VScrollTable table = getWidget(); + if (table.sizeNeedsInit) { + table.sizeInit(); + Scheduler.get().scheduleFinally(new ScheduledCommand() { + public void execute() { + getLayoutManager().setNeedsMeasure(TableConnector.this); + getLayoutManager() + .setHeightNeedsUpdate(TableConnector.this); + getLayoutManager().layoutNow(); + } + }); + } } @Override diff --git a/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java index fbe1ef2f27..cf6209e312 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java +++ b/src/com/vaadin/terminal/gwt/client/ui/table/VScrollTable.java @@ -465,7 +465,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, int serverCacheFirst = -1; int serverCacheLast = -1; - private boolean sizeNeedsInit = true; + boolean sizeNeedsInit = true; /** * Used to recall the position of an open context menu if we need to close @@ -1629,9 +1629,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets, * * Makes deferred request to get some cache rows */ void sizeInit() { - if (!sizeNeedsInit) { - return; - } sizeNeedsInit = false; scrollBody.setContainerHeight(); @@ -1884,8 +1881,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets, Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement()); } }); - - client.doLayout(true); } /** diff --git a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetConnector.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetConnector.java index 7423a536f2..0518b3a480 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/TabsheetConnector.java @@ -11,11 +11,12 @@ import com.vaadin.terminal.gwt.client.ComponentConnector; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.ui.Component; import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout; +import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren; import com.vaadin.ui.TabSheet; @Component(TabSheet.class) public class TabsheetConnector extends TabsheetBaseConnector implements - SimpleManagedLayout { + SimpleManagedLayout, MayScrollChildren { @Override public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java index 908a984dbb..c97ede1252 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java +++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheet.java @@ -943,7 +943,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable, public void iLayout() { updateTabScroller(); - tp.runWebkitOverflowAutoFix(); } /** diff --git a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java index ee0571d3a7..f2b37c3a1c 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java +++ b/src/com/vaadin/terminal/gwt/client/ui/tabsheet/VTabsheetPanel.java @@ -13,7 +13,6 @@ import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.Widget; -import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.ui.TouchScrollDelegate; /** @@ -194,16 +193,12 @@ public class VTabsheetPanel extends ComplexPanel { getElement().getStyle().setPropertyPx("height", height); // widget wrapper height - wrapperDiv.getStyle().setPropertyPx("height", height); - runWebkitOverflowAutoFix(); - } - - public void runWebkitOverflowAutoFix() { - if (visibleWidget != null) { - Util.runWebkitOverflowAutoFix(DOM.getParent(visibleWidget - .getElement())); + if (dynamicHeight) { + wrapperDiv.getStyle().clearHeight(); + } else { + // widget wrapper height + wrapperDiv.getStyle().setPropertyPx("height", height); } - } public void replaceComponent(Widget oldComponent, Widget newComponent) { diff --git a/src/com/vaadin/terminal/gwt/client/ui/twincolselect/TwinColSelectConnector.java b/src/com/vaadin/terminal/gwt/client/ui/twincolselect/TwinColSelectConnector.java index de1095f664..d8ad511d86 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/twincolselect/TwinColSelectConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/twincolselect/TwinColSelectConnector.java @@ -36,6 +36,12 @@ public class TwinColSelectConnector extends OptionGroupBaseConnector implements } @Override + public void onUnregister() { + getLayoutManager().unregisterDependency(this, + getWidget().captionWrapper.getElement()); + } + + @Override protected Widget createWidget() { return GWT.create(VTwinColSelect.class); } diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java b/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java index 3a1e0e8663..2c5fadff3b 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java +++ b/src/com/vaadin/terminal/gwt/client/ui/window/VWindow.java @@ -711,8 +711,10 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, client.runDescendentsLayout((HasWidgets) layout.getWidget()); } - Util.runWebkitOverflowAutoFix(contentPanel.getElement()); - client.doLayout(false); + LayoutManager layoutManager = LayoutManager.get(client); + layoutManager.setNeedsMeasure(ConnectorMap.get(client).getConnector( + this)); + layoutManager.layoutNow(); } @Override diff --git a/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java b/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java index 57c931fac9..fbb7a3683b 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java +++ b/src/com/vaadin/terminal/gwt/client/ui/window/WindowConnector.java @@ -21,7 +21,6 @@ import com.vaadin.terminal.gwt.client.LayoutManager; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.UIDL; -import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.communication.RpcProxy; import com.vaadin.terminal.gwt.client.ui.AbstractComponentContainerConnector; import com.vaadin.terminal.gwt.client.ui.ClickEventHandler; @@ -30,11 +29,12 @@ import com.vaadin.terminal.gwt.client.ui.PostLayoutListener; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler; import com.vaadin.terminal.gwt.client.ui.ShortcutActionHandler.BeforeShortcutActionListener; import com.vaadin.terminal.gwt.client.ui.SimpleManagedLayout; +import com.vaadin.terminal.gwt.client.ui.layout.MayScrollChildren; @Component(value = com.vaadin.ui.Window.class) public class WindowConnector extends AbstractComponentContainerConnector implements Paintable, BeforeShortcutActionListener, - SimpleManagedLayout, PostLayoutListener { + SimpleManagedLayout, PostLayoutListener, MayScrollChildren { private ClickEventHandler clickEventHandler = new ClickEventHandler(this) { @Override @@ -62,6 +62,15 @@ public class WindowConnector extends AbstractComponentContainerConnector getLayoutManager().registerDependency(this, getWidget().footer); } + @Override + public void onUnregister() { + LayoutManager lm = getLayoutManager(); + VWindow window = getWidget(); + lm.unregisterDependency(this, window.contentPanel.getElement()); + lm.unregisterDependency(this, window.header); + lm.unregisterDependency(this, window.footer); + } + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { getWidget().id = getConnectorId(); getWidget().client = client; @@ -262,8 +271,6 @@ public class WindowConnector extends AbstractComponentContainerConnector } else { childStyle.clearPosition(); } - - Util.runWebkitOverflowAutoFix(window.contentPanel.getElement()); } public void postLayout() { |