diff options
author | Henrik Paul <henrik@vaadin.com> | 2015-01-28 13:40:00 +0200 |
---|---|---|
committer | Henrik Paul <henrik@vaadin.com> | 2015-02-04 15:09:28 +0200 |
commit | 7cffb158ceab46df983e9ff81326e360f9e5235d (patch) | |
tree | 45cfa3cc89520513278820a4ed914f04e42d8c00 /client/src/com/vaadin | |
parent | 103b177475e0029abed00eec68e752492feafa61 (diff) | |
download | vaadin-framework-7cffb158ceab46df983e9ff81326e360f9e5235d.tar.gz vaadin-framework-7cffb158ceab46df983e9ff81326e360f9e5235d.zip |
Speeds up column adding in Grid (#16474)
Grid.onStateChange is now about 40% faster when adding columns,
and setting several column widths has now way less overhead.
Change-Id: I7bd900324207bfb2543a1a90390665b90206aefd
Diffstat (limited to 'client/src/com/vaadin')
3 files changed, 120 insertions, 119 deletions
diff --git a/client/src/com/vaadin/client/widget/escalator/ColumnConfiguration.java b/client/src/com/vaadin/client/widget/escalator/ColumnConfiguration.java index af49dcd64f..76f6a55b8a 100644 --- a/client/src/com/vaadin/client/widget/escalator/ColumnConfiguration.java +++ b/client/src/com/vaadin/client/widget/escalator/ColumnConfiguration.java @@ -16,7 +16,7 @@ package com.vaadin.client.widget.escalator; -import com.vaadin.client.widgets.Escalator; +import java.util.Map; /** * A representation of the columns in an instance of {@link Escalator}. @@ -141,6 +141,23 @@ public interface ColumnConfiguration { public double getColumnWidth(int index) throws IllegalArgumentException; /** + * Sets widths for a set of columns. + * + * @param indexWidthMap + * a map from column index to its respective width to be set. If + * the given width for a column index is negative, the column is + * resized-to-fit. + * @throws IllegalArgumentException + * if {@code indexWidthMap} is {@code null} + * @throws IllegalArgumentException + * if any column index in {@code indexWidthMap} is invalid + * @throws NullPointerException + * If any value in the map is <code>null</code> + */ + public void setColumnWidths(Map<Integer, Double> indexWidthMap) + throws IllegalArgumentException; + + /** * Returns the actual width of a column. * * @param index diff --git a/client/src/com/vaadin/client/widgets/Escalator.java b/client/src/com/vaadin/client/widgets/Escalator.java index 6a8fb311b1..94b6efc0d7 100644 --- a/client/src/com/vaadin/client/widgets/Escalator.java +++ b/client/src/com/vaadin/client/widgets/Escalator.java @@ -16,11 +16,13 @@ package com.vaadin.client.widgets; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; @@ -1161,47 +1163,6 @@ public class Escalator extends Widget implements RequiresResize, DeferredWorker } } - private class ColumnAutoWidthAssignScheduler { - private boolean isScheduled = false; - private final ScheduledCommand widthCommand = new ScheduledCommand() { - @Override - public void execute() { - if (!isScheduled) { - return; - } - - isScheduled = false; - - ColumnConfigurationImpl cc = columnConfiguration; - for (int col = 0; col < cc.getColumnCount(); col++) { - ColumnConfigurationImpl.Column column = cc.columns.get(col); - if (!column.isWidthFinalized()) { - cc.setColumnWidth(col, -1); - column.widthIsFinalized(); - } - } - } - }; - - /** - * Calculates the widths of all uncalculated cells once the javascript - * execution is done. - * <p> - * This method makes sure that any duplicate requests in the same cycle - * are ignored. - */ - public void reschedule() { - if (!isScheduled) { - isScheduled = true; - Scheduler.get().scheduleFinally(widthCommand); - } - } - - public void cancel() { - isScheduled = false; - } - } - protected abstract class AbstractRowContainer implements RowContainer { private EscalatorUpdater updater = EscalatorUpdater.NULL; @@ -1422,14 +1383,18 @@ public class Escalator extends Widget implements RequiresResize, DeferredWorker if (rows == numberOfRows) { /* * We are inserting the first rows in this container. We - * potentially need to autocalculate the widths for the - * cells for the first time. - * - * To make sure that can take the entire dataset into - * account, we'll do this deferredly, so that each container - * section gets populated before we start calculating. + * potentially need to set the widths for the cells for the + * first time. */ - columnAutoWidthAssignScheduler.reschedule(); + Map<Integer, Double> colWidths = new HashMap<Integer, Double>(); + Double width = Double + .valueOf(ColumnConfigurationImpl.Column.DEFAULT_COLUMN_WIDTH_PX); + for (int i = 0; i < getColumnConfiguration() + .getColumnCount(); i++) { + Integer col = Integer.valueOf(i); + colWidths.put(col, width); + } + getColumnConfiguration().setColumnWidths(colWidths); } } } @@ -3808,19 +3773,12 @@ public class Escalator extends Widget implements RequiresResize, DeferredWorker private class ColumnConfigurationImpl implements ColumnConfiguration { public class Column { - private static final int DEFAULT_COLUMN_WIDTH_PX = 100; + public static final double DEFAULT_COLUMN_WIDTH_PX = 100; private double definedWidth = -1; private double calculatedWidth = DEFAULT_COLUMN_WIDTH_PX; private boolean measuringRequested = false; - /** - * If a column has been created (either via insertRow or - * insertColumn), it will be given an arbitrary width, and only then - * a width will be defined. - */ - private boolean widthHasBeenFinalized = false; - public void setWidth(double px) { definedWidth = px; @@ -3884,15 +3842,6 @@ public class Escalator extends Widget implements RequiresResize, DeferredWorker private void calculateWidth() { calculatedWidth = getMaxCellWidth(columns.indexOf(this)); } - - public void widthIsFinalized() { - columnAutoWidthAssignScheduler.cancel(); - widthHasBeenFinalized = true; - } - - public boolean isWidthFinalized() { - return widthHasBeenFinalized; - } } private final List<Column> columns = new ArrayList<Column>(); @@ -4082,13 +4031,17 @@ public class Escalator extends Widget implements RequiresResize, DeferredWorker body.paintInsertColumns(index, numberOfColumns, frozen); footer.paintInsertColumns(index, numberOfColumns, frozen); - // fix autowidth + // fix initial width if (header.getRowCount() > 0 || body.getRowCount() > 0 || footer.getRowCount() > 0) { - for (int col = index; col < index + numberOfColumns; col++) { - getColumnConfiguration().setColumnWidth(col, -1); - columnConfiguration.columns.get(col).widthIsFinalized(); + + Map<Integer, Double> colWidths = new HashMap<Integer, Double>(); + Double width = Double.valueOf(Column.DEFAULT_COLUMN_WIDTH_PX); + for (int i = index; i < index + numberOfColumns; i++) { + Integer col = Integer.valueOf(i); + colWidths.put(col, width); } + getColumnConfiguration().setColumnWidths(colWidths); } // Adjust scrollbar @@ -4170,16 +4123,30 @@ public class Escalator extends Widget implements RequiresResize, DeferredWorker @Override public void setColumnWidth(int index, double px) throws IllegalArgumentException { - checkValidColumnIndex(index); + setColumnWidths(Collections.singletonMap(Integer.valueOf(index), + Double.valueOf(px))); + } - columns.get(index).setWidth(px); - columns.get(index).widthIsFinalized(); - widthsArray = null; + @Override + public void setColumnWidths(Map<Integer, Double> indexWidthMap) + throws IllegalArgumentException { - /* - * TODO [[optimize]]: only modify the elements that are actually - * modified. - */ + if (indexWidthMap == null) { + throw new IllegalArgumentException("indexWidthMap was null"); + } + + if (indexWidthMap.isEmpty()) { + return; + } + + for (Entry<Integer, Double> entry : indexWidthMap.entrySet()) { + int index = entry.getKey().intValue(); + double width = entry.getValue().doubleValue(); + checkValidColumnIndex(index); + columns.get(index).setWidth(width); + } + + widthsArray = null; header.reapplyColumnWidths(); body.reapplyColumnWidths(); footer.reapplyColumnWidths(); @@ -4373,8 +4340,6 @@ public class Escalator extends Widget implements RequiresResize, DeferredWorker } }; - private final ColumnAutoWidthAssignScheduler columnAutoWidthAssignScheduler = new ColumnAutoWidthAssignScheduler(); - /** * Creates a new Escalator widget instance. */ @@ -5144,9 +5109,7 @@ public class Escalator extends Widget implements RequiresResize, DeferredWorker @Override public boolean isWorkPending() { - return body.domSorter.waiting - || columnAutoWidthAssignScheduler.isScheduled - || verticalScrollbar.isWorkPending() + return body.domSorter.waiting || verticalScrollbar.isWorkPending() || horizontalScrollbar.isWorkPending(); } diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index ea69968a75..e112d868b4 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -2217,21 +2218,67 @@ public class Grid<T> extends ResizeComposite implements rescheduleCount = 0; assert !dataIsBeingFetched : "Trying to calculate column widths even though data is still being fetched."; - /* - * At this point we assume that no data is being fetched anymore. - * Everything's rendered in the DOM. Now we just make sure - * everything fits as it should. - */ + + if (columnsAreGuaranteedToBeWiderThanGrid()) { + applyColumnWidths(); + } else { + applyColumnWidthsWithExpansion(); + } + } + + private boolean columnsAreGuaranteedToBeWiderThanGrid() { + double freeSpace = escalator.getInnerWidth(); + for (Column<?, ?> column : getColumns()) { + if (column.getWidth() >= 0) { + freeSpace -= column.getWidth(); + } else if (column.getMinimumWidth() >= 0) { + freeSpace -= column.getMinimumWidth(); + } + } + return freeSpace < 0; + } + + @SuppressWarnings("boxing") + private void applyColumnWidths() { + + /* Step 1: Apply all column widths as they are. */ + + Map<Integer, Double> selfWidths = new LinkedHashMap<Integer, Double>(); + List<Column<?, T>> columns = getColumns(); + for (int index = 0; index < columns.size(); index++) { + selfWidths.put(index, columns.get(index).getWidth()); + } + Grid.this.escalator.getColumnConfiguration().setColumnWidths( + selfWidths); /* - * Quick optimization: if the sum of fixed widths and minimum widths - * is greater than the grid can display, we already know that things - * will be squeezed and no expansion will happen. + * Step 2: Make sure that each column ends up obeying their min/max + * width constraints if defined as autowidth. If constraints are + * violated, fix it. */ - if (gridWasTooNarrowAndEverythingWasFixedAlready()) { - return; + + Map<Integer, Double> constrainedWidths = new LinkedHashMap<Integer, Double>(); + for (int index = 0; index < columns.size(); index++) { + Column<?, T> column = columns.get(index); + + boolean hasAutoWidth = column.getWidth() < 0; + if (!hasAutoWidth) { + continue; + } + + // TODO: bug: these don't honor the CSS max/min. :( + double actualWidth = column.getWidthActual(); + if (actualWidth < getMinWidth(column)) { + constrainedWidths.put(index, column.getMinimumWidth()); + } else if (actualWidth > getMaxWidth(column)) { + constrainedWidths.put(index, column.getMaximumWidth()); + } } + Grid.this.escalator.getColumnConfiguration().setColumnWidths( + constrainedWidths); + } + private void applyColumnWidthsWithExpansion() { boolean someColumnExpands = false; int totalRatios = 0; double reservedPixels = 0; @@ -2416,32 +2463,6 @@ public class Grid<T> extends ResizeComposite implements } while (minWidthsCausedReflows); } - private boolean gridWasTooNarrowAndEverythingWasFixedAlready() { - double freeSpace = escalator.getInnerWidth(); - for (Column<?, ?> column : getColumns()) { - if (column.getWidth() >= 0) { - freeSpace -= column.getWidth(); - } else if (column.getMinimumWidth() >= 0) { - freeSpace -= column.getMinimumWidth(); - } - } - - if (freeSpace < 0) { - for (Column<?, ?> column : getColumns()) { - column.doSetWidth(column.getWidth()); - - boolean wasFixedWidth = column.getWidth() <= 0; - boolean newWidthIsSmallerThanMinWidth = column - .getWidthActual() < getMinWidth(column); - if (wasFixedWidth && newWidthIsSmallerThanMinWidth) { - column.doSetWidth(column.getMinimumWidth()); - } - } - } - - return freeSpace < 0; - } - private int getExpandRatio(Column<?, ?> column, boolean someColumnExpands) { int expandRatio = column.getExpandRatio(); |