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.layout.MayScrollChildren;
public class LayoutManager {
private static final String LOOP_ABORT_MESSAGE = "Aborting layout after 100 passes. This would probably be an infinite loop.";
private final Collection<ComponentConnector> needsMeasure = new HashSet<ComponentConnector>();
- private final Collection<ComponentConnector> pendingOverflowFixes = 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>();
int measuredConnectorCount = measureConnectors(
currentDependencyTree, everythingNeedsMeasure);
everythingNeedsMeasure = false;
+ if (measuredConnectorCount == 0) {
+ VConsole.log("No more changes in pass " + passes);
+ break;
+ }
int measureTime = passDuration.elapsedMillis();
VConsole.log(" Measured " + measuredConnectorCount
}
FastStringSet updatedSet = FastStringSet.create();
- boolean changed = false;
while (currentDependencyTree.hasHorizontalConnectorToLayout()
|| currentDependencyTree.hasVerticaConnectorToLayout()) {
- changed = true;
for (ManagedLayout layout : currentDependencyTree
.getHorizontalLayoutTargets()) {
if (layout instanceof DirectionalManagedLayout) {
VConsole.log(b.toString());
}
- if (!changed) {
- VConsole.log("No more changes in pass " + passes);
- break;
- }
-
VConsole.log("Pass " + passes + " completed in "
+ passDuration.elapsedMillis() + " ms");
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();
style.setOverflow(Overflow.HIDDEN);
}
+ pendingOverflowFixes.removeAll(delayedOverflowFixes);
+
// Then ensure all scrolling elements are reflowed by measuring
for (ComponentConnector componentConnector : pendingOverflowFixes) {
componentConnector.getWidget().getElement().getParentElement()
originalOverflows.get(parentElement));
layoutDependencyTree.setNeedsMeasure(componentConnector, true);
-
- ComponentContainerConnector parent = componentConnector
- .getParent();
- if (parent instanceof ManagedLayout) {
- ManagedLayout managedParent = (ManagedLayout) parent;
- layoutDependencyTree.setNeedsHorizontalLayout(
- managedParent, true);
- layoutDependencyTree.setNeedsVerticalLayout(managedParent,
- true);
- }
}
- VConsole.log("Did overflow fix for " + pendingOverflowFixes.size()
- + " elements in " + duration.elapsedMillis() + " ms");
- pendingOverflowFixes.clear();
+ if (!pendingOverflowFixes.isEmpty()) {
+ VConsole.log("Did overflow fix for "
+ + pendingOverflowFixes.size() + " elements in "
+ + duration.elapsedMillis() + " ms");
+ }
+ pendingOverflowFixes = delayedOverflowFixes;
}
int measureCount = 0;
private void onConnectorChange(ComponentConnector connector,
boolean widthChanged, boolean heightChanged) {
- doOverflowAutoFix(connector);
+ setNeedsOverflowFix(connector);
if (heightChanged) {
currentDependencyTree.markHeightAsChanged(connector);
}
}
}
- private void doOverflowAutoFix(ComponentConnector connector) {
- // IE9 doesn't need the original fix, but for some reason it needs one
- if (connector.getParent() instanceof MayScrollChildren
- && (BrowserInfo.get().requiresOverflowAutoFix() || BrowserInfo
- .get().isIE9())
- && !"absolute".equals(connector.getWidget().getElement()
- .getStyle().getPosition())) {
- pendingOverflowFixes.add(connector);
+ 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 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 void setNeedsLayout(boolean needsLayout) {
if (!(connector instanceof ManagedLayout)) {
throw new IllegalStateException(
- "Only managed layouts can need layout");
+ "Only managed layouts can need layout, layout attempted for "
+ + Util.getConnectorString(connector));
}
if (needsLayout && !this.needsLayout) {
// If enabling needsLayout
// Should also go through the hierarchy to discover appeared or
// disappeared scrollbars
- LayoutDependency potentiallyChangedScrollbar = findPotentiallyChangedScrollbar();
- if (potentiallyChangedScrollbar != null) {
- potentiallyChangedScrollbar.setNeedsMeasure(true);
+ ComponentConnector scrollingBoundary = getScrollingBoundary(connector);
+ if (scrollingBoundary != null) {
+ getDependency(scrollingBoundary, getOppositeDirection())
+ .setNeedsMeasure(true);
}
}
return s;
}
+ public boolean noMoreChangesExpected() {
+ return !needsLayout && !needsMeasure && layoutBlockers.isEmpty()
+ && measureBlockers.isEmpty();
+ }
+
}
private static final int HORIZONTAL = 0;
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;
+ }
}