diff options
author | jotatu <jotatu@g-10-64-12-127.guest.vaadin.com> | 2015-08-07 14:45:50 +0300 |
---|---|---|
committer | Patrik Lindström <patrik@vaadin.com> | 2015-11-05 12:47:16 +0200 |
commit | 9c1908d0a5fdba09167414e837cc08a2f746584b (patch) | |
tree | fe8fec7d5118134d72af5c7de49890bba8b2bd0c /client | |
parent | 2b8cd659685c48fd6284ba3566009b2285132751 (diff) | |
download | vaadin-framework-9c1908d0a5fdba09167414e837cc08a2f746584b.tar.gz vaadin-framework-9c1908d0a5fdba09167414e837cc08a2f746584b.zip |
Optimize layout performance of Table (#17947)
Adds functions for skipping child component layout measuring.
Removes unnecessary code from VScrollTable.
1. case: no components
- render time without the fix: ~105ms
- render time with fix: ~105ms
2. case: 2 button and 2 textfield cols
- render time without the fix: ~279ms
- render time with fix: ~240ms (~17% faster)
3. case: 3 button and 3 textfield cols
- render time without the fix: ~350ms
- render time with fix: ~281ms (~20% faster)
Change-Id: I6025f8ee2fd438d228ff3b65f43535961cf12c0b
Diffstat (limited to 'client')
6 files changed, 190 insertions, 10 deletions
diff --git a/client/src/com/vaadin/client/HasChildMeasurementHintConnector.java b/client/src/com/vaadin/client/HasChildMeasurementHintConnector.java new file mode 100644 index 0000000000..5ca6317b9b --- /dev/null +++ b/client/src/com/vaadin/client/HasChildMeasurementHintConnector.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client; + +import com.vaadin.client.ui.ManagedLayout; +import com.vaadin.client.ui.layout.ElementResizeListener; + +/** + * Connector with layout measuring hint. Used to improve granularity of control + * over child component measurements. + * + * @since + * @author Vaadin Ltd + */ +public interface HasChildMeasurementHintConnector extends + HasComponentsConnector { + + /** + * Specifies how you would like child components measurements to be handled. + * Since this is a hint, it can be ignored when deemed necessary. + */ + public enum ChildMeasurementHint { + + /** + * Always measure all child components (default). + */ + MEASURE_ALWAYS, + + /** + * Measure child component only if child component is a {@link Layout} + * or implements either {@link ManagedLayout} or + * {@link ElementResizeListener}. + */ + MEASURE_IF_NEEDED, + + /** + * Never measure child components. This can improve rendering speed of + * components with lots of children (e.g. Table), but can cause some + * child components to be rendered incorrectly (e.g. ComboBox). + */ + MEASURE_NEVER + } + + /** + * Sets the child measurement hint for this component. + * + * @param hint + * the value to set + */ + void setChildMeasurementHint(ChildMeasurementHint hint); + + /** + * Returns the current child measurement hint value. + * + * @return a ChildLayoutMeasureMode value + */ + ChildMeasurementHint getChildMeasurementHint(); + +} diff --git a/client/src/com/vaadin/client/LayoutManager.java b/client/src/com/vaadin/client/LayoutManager.java index d39e604aaf..ed83e195d7 100644 --- a/client/src/com/vaadin/client/LayoutManager.java +++ b/client/src/com/vaadin/client/LayoutManager.java @@ -712,7 +712,8 @@ public class LayoutManager { .createArray().cast(); for (int i = 0; i < size; i++) { ComponentConnector candidate = allConnectors.get(i); - if (needsMeasure(candidate.getWidget().getElement())) { + if (!Util.shouldSkipMeasurementOfConnector(candidate) + && needsMeasure(candidate.getWidget().getElement())) { connectors.add(candidate); } } diff --git a/client/src/com/vaadin/client/Util.java b/client/src/com/vaadin/client/Util.java index ab7d344877..8b5caa6051 100644 --- a/client/src/com/vaadin/client/Util.java +++ b/client/src/com/vaadin/client/Util.java @@ -33,8 +33,12 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.HasChildMeasurementHintConnector.ChildMeasurementHint; import com.vaadin.client.RenderInformation.FloatSize; +import com.vaadin.client.ui.AbstractLayoutConnector; +import com.vaadin.client.ui.ManagedLayout; import com.vaadin.client.ui.VOverlay; +import com.vaadin.client.ui.layout.ElementResizeListener; import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.communication.MethodInvocation; @@ -1186,6 +1190,83 @@ public class Util { return +(Math.round(num + "e+" + exp) + "e-" + exp); }-*/; + /** + * Test if we can can skip measuring this connector. We can skip the + * measurement if its parent is a ChildMeasureHintConnector and has an + * appropriate mode set. + * + * For internal use only. May be removed or replaced in the future. + */ + public static boolean shouldSkipMeasurementOfConnector( + ComponentConnector candidate, ComponentConnector parent) { + Profiler.enter("skipMeasureDueLayoutHint"); + boolean skip = false; + + if (parent instanceof HasChildMeasurementHintConnector) { + ChildMeasurementHint measureMode = ((HasChildMeasurementHintConnector) parent) + .getChildMeasurementHint(); + + if (measureMode == ChildMeasurementHint.MEASURE_NEVER) { + skip = true; + } else if (measureMode == ChildMeasurementHint.MEASURE_IF_NEEDED) { + skip = canWeSkipChildMeasurement(candidate); + } + } + Profiler.leave("skipMeasureDueLayoutHint"); + return skip; + } + + /** + * Test if we can can skip measuring this connector. We can skip the + * measurement if its parent is a ChildMeasureHintConnector and has an + * appropriate mode set. + * + * This version of the method tries to recursively locate such a parent. + * + * For internal use only. May be removed or replaced in the future. + */ + public static boolean shouldSkipMeasurementOfConnector( + ComponentConnector candidate) { + Profiler.enter("skipMeasureDueLayoutHint"); + boolean skip = false; + + HasChildMeasurementHintConnector parent = getPossibleChildMeasurementHintParentConnector(candidate); + + if (parent != null) { + ChildMeasurementHint measureMode = parent.getChildMeasurementHint(); + + if (measureMode == ChildMeasurementHint.MEASURE_NEVER) { + skip = true; + } else if (measureMode == ChildMeasurementHint.MEASURE_IF_NEEDED) { + skip = canWeSkipChildMeasurement(candidate); + } + } + Profiler.leave("skipMeasureDueLayoutHint"); + return skip; + } + + /** For internal use only. May be removed or replaced in the future. */ + private static boolean canWeSkipChildMeasurement(ComponentConnector child) { + // common cases when child measuring is possibly needed + if (child instanceof ElementResizeListener + || child instanceof ManagedLayout + || child instanceof AbstractLayoutConnector) { + return false; + } + return true; + } + + /** For internal use only. May be removed or replaced in the future. */ + private static HasChildMeasurementHintConnector getPossibleChildMeasurementHintParentConnector( + ComponentConnector candidate) { + ServerConnector parent = candidate.getParent(); + if (parent != null + && parent instanceof HasChildMeasurementHintConnector) { + return (HasChildMeasurementHintConnector) parent; + } + return null; + } + private static Logger getLogger() { return Logger.getLogger(Util.class.getName()); } diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 6bb8f063a6..3b85356f86 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -84,6 +84,7 @@ import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorMap; import com.vaadin.client.DeferredWorker; import com.vaadin.client.Focusable; +import com.vaadin.client.HasChildMeasurementHintConnector.ChildMeasurementHint; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.StyleConstants; import com.vaadin.client.TooltipInfo; @@ -699,6 +700,11 @@ public class VScrollTable extends FlowPanel implements HasWidgets, private int multiselectmode; + /** + * Hint for how to handle measurement of child components + */ + private ChildMeasurementHint childMeasurementHint = ChildMeasurementHint.MEASURE_ALWAYS; + /** For internal use only. May be removed or replaced in the future. */ public int tabIndex; @@ -1238,11 +1244,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets, uidl.getIntAttribute("rows")); scrollBodyPanel.add(scrollBody); - // New body starts scrolled to the left, make sure the header and footer - // are also scrolled to the left - tHead.setHorizontalScrollPosition(0); - tFoot.setHorizontalScrollPosition(0); - initialContentReceived = true; sizeNeedsInit = true; scrollBody.restoreRowVisibility(); @@ -2620,8 +2621,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, @Override public void run() { - if (client.getMessageSender().hasActiveRequest() - || navKeyDown) { + if (client.getMessageSender().hasActiveRequest() || navKeyDown) { // if client connection is busy, don't bother loading it more VConsole.log("Postponed rowfetch"); schedule(250); @@ -8332,4 +8332,12 @@ public class VScrollTable extends FlowPanel implements HasWidgets, private static Logger getLogger() { return Logger.getLogger(VScrollTable.class.getName()); } + + public ChildMeasurementHint getChildMeasurementHint() { + return childMeasurementHint; + } + + public void setChildMeasurementHint(ChildMeasurementHint hint) { + childMeasurementHint = hint; + } } diff --git a/client/src/com/vaadin/client/ui/layout/LayoutDependencyTree.java b/client/src/com/vaadin/client/ui/layout/LayoutDependencyTree.java index a4c5668948..27733bfbe3 100644 --- a/client/src/com/vaadin/client/ui/layout/LayoutDependencyTree.java +++ b/client/src/com/vaadin/client/ui/layout/LayoutDependencyTree.java @@ -248,7 +248,8 @@ public class LayoutDependencyTree { if (connector instanceof HasComponentsConnector) { HasComponentsConnector container = (HasComponentsConnector) connector; for (ComponentConnector child : container.getChildComponents()) { - if (isRelativeInDirection(child, direction)) { + if (!Util.shouldSkipMeasurementOfConnector(child, connector) + && isRelativeInDirection(child, direction)) { resized.push(child.getConnectorId()); } } diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index 0cce611191..7e7995b5e1 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -28,6 +28,7 @@ import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; +import com.vaadin.client.HasChildMeasurementHintConnector; import com.vaadin.client.ComponentConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler; @@ -55,7 +56,7 @@ import com.vaadin.shared.ui.table.TableState; @Connect(com.vaadin.ui.Table.class) public class TableConnector extends AbstractFieldConnector implements HasComponentsConnector, ConnectorHierarchyChangeHandler, Paintable, - DirectionalManagedLayout, PostLayoutListener { + DirectionalManagedLayout, PostLayoutListener, HasChildMeasurementHintConnector { private List<ComponentConnector> childComponents; @@ -199,6 +200,12 @@ public class TableConnector extends AbstractFieldConnector implements getWidget().updateDragMode(uidl); + // Update child measure hint + int childMeasureHint = uidl.hasAttribute("measurehint") ? uidl + .getIntAttribute("measurehint") : 0; + getWidget().setChildMeasurementHint( + ChildMeasurementHint.values()[childMeasureHint]); + getWidget().updateSelectionProperties(uidl, getState(), isReadOnly()); if (uidl.hasAttribute("alb")) { @@ -532,4 +539,14 @@ public class TableConnector extends AbstractFieldConnector implements ConnectorHierarchyChangeEvent.TYPE, handler); } + @Override + public void setChildMeasurementHint(ChildMeasurementHint hint) { + getWidget().setChildMeasurementHint(hint); + } + + @Override + public ChildMeasurementHint getChildMeasurementHint() { + return getWidget().getChildMeasurementHint(); + } + } |