From 0c9ef57b2158c23b3d6e7aff3e6e26e4515f1604 Mon Sep 17 00:00:00 2001 From: Anna Koskinen Date: Fri, 23 Jul 2021 18:56:52 +0300 Subject: Divide too long Grid and Escalator methods into smaller units. (#12340) --- .../java/com/vaadin/client/widgets/Escalator.java | 222 ++++++------ .../main/java/com/vaadin/client/widgets/Grid.java | 385 +++++++++++++-------- 2 files changed, 348 insertions(+), 259 deletions(-) diff --git a/client/src/main/java/com/vaadin/client/widgets/Escalator.java b/client/src/main/java/com/vaadin/client/widgets/Escalator.java index 0c289b0ef0..27c512fa77 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Escalator.java +++ b/client/src/main/java/com/vaadin/client/widgets/Escalator.java @@ -4729,7 +4729,6 @@ public class Escalator extends Widget return; } - int oldTopRowLogicalIndex = getTopRowLogicalIndex(); int oldVisualRangeLength = visualRowOrder.size(); final int maxVisibleRowCount = getMaxVisibleRowCount(); @@ -4740,132 +4739,141 @@ public class Escalator extends Widget if (rowDiff > 0) { // more rows are needed + handleAddingRequiredRows(rowDiff); + } else if (rowDiff < 0) { + // rows need to be removed + handleRemovingExcessRows(rowDiff); + } - // calculate the indexes for adding rows below the last row of - // the visual range - final int visualTargetIndex = oldVisualRangeLength; - final int logicalTargetIndex; - if (!visualRowOrder.isEmpty()) { - logicalTargetIndex = oldTopRowLogicalIndex - + visualTargetIndex; - } else { - logicalTargetIndex = 0; - } - - // prioritise adding to the bottom so that there's less chance - // for a gap if a details row is later closed (e.g. by user) - final int addToBottom = Math.min(rowDiff, - getRowCount() - logicalTargetIndex); - final int addToTop = Math.max(rowDiff - addToBottom, 0); + Profiler.leave("Escalator.BodyRowContainer.verifyEscalatorCount"); + } - if (addToTop > 0) { - fillAndPopulateEscalatorRowsIfNeeded(0, - oldTopRowLogicalIndex - addToTop, addToTop); + private void handleAddingRequiredRows(final int rowDiff) { + int oldTopRowLogicalIndex = getTopRowLogicalIndex(); - updateTopRowLogicalIndex(-addToTop); - } - if (addToBottom > 0) { - // take into account that rows may have got added to top as - // well, affects visual but not logical indexing - fillAndPopulateEscalatorRowsIfNeeded( - visualTargetIndex + addToTop, logicalTargetIndex, - addToBottom); - - // adding new rows due to resizing may have created a gap in - // the middle, check whether the existing rows need moving - double rowTop = getRowTop(oldTopRowLogicalIndex); - if (rowTop > getRowTop(visualRowOrder.get(addToTop))) { - for (int i = addToTop; i < visualTargetIndex; i++) { - - final TableRowElement tr = visualRowOrder.get(i); - - setRowPosition(tr, 0, rowTop); - rowTop += getDefaultRowHeight(); - SpacerContainer.SpacerImpl spacer = spacerContainer - .getSpacer(oldTopRowLogicalIndex + i); - if (spacer != null) { - spacer.setPosition(0, rowTop); - rowTop += spacer.getHeight(); - } + // calculate the indexes for adding rows below the last row of + // the visual range + final int visualTargetIndex = visualRowOrder.size(); + final int logicalTargetIndex; + if (!visualRowOrder.isEmpty()) { + logicalTargetIndex = oldTopRowLogicalIndex + visualTargetIndex; + } else { + logicalTargetIndex = 0; + } + + // prioritise adding to the bottom so that there's less chance + // for a gap if a details row is later closed (e.g. by user) + final int addToBottom = Math.min(rowDiff, + getRowCount() - logicalTargetIndex); + final int addToTop = Math.max(rowDiff - addToBottom, 0); + + if (addToTop > 0) { + fillAndPopulateEscalatorRowsIfNeeded(0, + oldTopRowLogicalIndex - addToTop, addToTop); + + updateTopRowLogicalIndex(-addToTop); + } + if (addToBottom > 0) { + // take into account that rows may have got added to top as + // well, affects visual but not logical indexing + fillAndPopulateEscalatorRowsIfNeeded( + visualTargetIndex + addToTop, logicalTargetIndex, + addToBottom); + + // adding new rows due to resizing may have created a gap in + // the middle, check whether the existing rows need moving + double rowTop = getRowTop(oldTopRowLogicalIndex); + if (rowTop > getRowTop(visualRowOrder.get(addToTop))) { + for (int i = addToTop; i < visualTargetIndex; i++) { + + final TableRowElement tr = visualRowOrder.get(i); + + setRowPosition(tr, 0, rowTop); + rowTop += getDefaultRowHeight(); + SpacerContainer.SpacerImpl spacer = spacerContainer + .getSpacer(oldTopRowLogicalIndex + i); + if (spacer != null) { + spacer.setPosition(0, rowTop); + rowTop += spacer.getHeight(); } } } - } else if (rowDiff < 0) { - // rows need to be removed + } - // prioritise removing rows from above the viewport as they are - // less likely to be needed in a hurry -- the rows below are - // more likely to slide into view when spacer contents are - // updated - - // top of visible area before any rows are actually added - double scrollTop = getScrollTop(); - - // visual index of the first actually visible row, including - // spacer - int oldFirstVisibleVisualIndex = -1; - ListIterator iter = visualRowOrder - .listIterator(0); - for (int i = 0; i < visualRowOrder.size(); ++i) { - if (positions.getTop(iter.next()) > scrollTop) { - break; - } - oldFirstVisibleVisualIndex = i; - } + scroller.recalculateScrollbarsForVirtualViewport(); + fireRowVisibilityChangeEvent(); + } - int rowsToRemoveFromAbove = Math.max(0, Math - .min(Math.abs(rowDiff), oldFirstVisibleVisualIndex)); + private void handleRemovingExcessRows(final int rowDiff) { + // prioritise removing rows from above the viewport as they are + // less likely to be needed in a hurry -- the rows below are + // more likely to slide into view when spacer contents are + // updated - boolean spacersRemovedFromAbove = false; - if (rowsToRemoveFromAbove > 0) { - double initialSpacerHeightSum = spacerContainer - .getSpacerHeightsSum(); - iter = visualRowOrder.listIterator(0); - for (int i = 0; i < rowsToRemoveFromAbove; ++i) { - final Element first = iter.next(); - first.removeFromParent(); - iter.remove(); + int oldTopRowLogicalIndex = getTopRowLogicalIndex(); + final int oldVisualRangeLength = visualRowOrder.size(); - spacerContainer.removeSpacer(oldTopRowLogicalIndex + i); - } - spacersRemovedFromAbove = initialSpacerHeightSum != spacerContainer - .getSpacerHeightsSum(); - } + // top of visible area before any rows are actually added + double scrollTop = getScrollTop(); - // if there weren't enough rows above, remove the rest from - // below - int rowsToRemoveFromBelow = Math.abs(rowDiff) - - rowsToRemoveFromAbove; - if (rowsToRemoveFromBelow > 0) { - iter = visualRowOrder.listIterator(visualRowOrder.size()); - for (int i = 1; i <= rowsToRemoveFromBelow; ++i) { - final Element last = iter.previous(); - last.removeFromParent(); - iter.remove(); - - spacerContainer.removeSpacer(oldTopRowLogicalIndex - + oldVisualRangeLength - i); - } + // visual index of the first actually visible row, including + // spacer + int oldFirstVisibleVisualIndex = -1; + ListIterator iter = visualRowOrder.listIterator(0); + for (int i = 0; i < visualRowOrder.size(); ++i) { + if (positions.getTop(iter.next()) > scrollTop) { + break; } + oldFirstVisibleVisualIndex = i; + } - updateTopRowLogicalIndex(rowsToRemoveFromAbove); + int rowsToRemoveFromAbove = Math.max(0, + Math.min(Math.abs(rowDiff), oldFirstVisibleVisualIndex)); - if (spacersRemovedFromAbove) { - updateRowPositions(oldTopRowLogicalIndex, 0, - visualRowOrder.size()); - } + boolean spacersRemovedFromAbove = false; + if (rowsToRemoveFromAbove > 0) { + double initialSpacerHeightSum = spacerContainer + .getSpacerHeightsSum(); + iter = visualRowOrder.listIterator(0); + for (int i = 0; i < rowsToRemoveFromAbove; ++i) { + final Element first = iter.next(); + first.removeFromParent(); + iter.remove(); - // removing rows might cause a gap at the bottom - adjustScrollPositionIfNeeded(); + spacerContainer.removeSpacer(oldTopRowLogicalIndex + i); + } + spacersRemovedFromAbove = initialSpacerHeightSum != spacerContainer + .getSpacerHeightsSum(); + } + + // if there weren't enough rows above, remove the rest from + // below + int rowsToRemoveFromBelow = Math.abs(rowDiff) + - rowsToRemoveFromAbove; + if (rowsToRemoveFromBelow > 0) { + iter = visualRowOrder.listIterator(visualRowOrder.size()); + for (int i = 1; i <= rowsToRemoveFromBelow; ++i) { + final Element last = iter.previous(); + last.removeFromParent(); + iter.remove(); + + spacerContainer.removeSpacer( + oldTopRowLogicalIndex + oldVisualRangeLength - i); + } } - if (rowDiff != 0) { - scroller.recalculateScrollbarsForVirtualViewport(); + updateTopRowLogicalIndex(rowsToRemoveFromAbove); - fireRowVisibilityChangeEvent(); + if (spacersRemovedFromAbove) { + updateRowPositions(oldTopRowLogicalIndex, 0, + visualRowOrder.size()); } - Profiler.leave("Escalator.BodyRowContainer.verifyEscalatorCount"); + // removing rows might cause a gap at the bottom + adjustScrollPositionIfNeeded(); + + scroller.recalculateScrollbarsForVirtualViewport(); + fireRowVisibilityChangeEvent(); } @Override diff --git a/client/src/main/java/com/vaadin/client/widgets/Grid.java b/client/src/main/java/com/vaadin/client/widgets/Grid.java index 3188f70b09..14bf795e2e 100755 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -2129,21 +2129,19 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, updateVerticalScrollPosition(); }); + addEditorAndContents(gridElement, tr); + updateSizeAndPosition(gridElement, tr); + } + + private void addEditorAndContents(DivElement gridElement, + TableRowElement tr) { gridElement.appendChild(editorOverlay); editorOverlay.appendChild(frozenCellWrapper); editorOverlay.appendChild(cellWrapper); editorOverlay.appendChild(messageAndButtonsWrapper); updateBufferedStyleName(); - - // Add class name with selected modifier if the editor is being - // opened on selected row, see #11634 - String selectedStylename = styleName + "-selected"; - if (grid.isSelected(grid.getDataSource().getRow(getRow()))) { - cellWrapper.addClassName(selectedStylename); - } else { - cellWrapper.removeClassName(selectedStylename); - } + updateSelectedStyleName(); int frozenColumns = grid.getVisibleFrozenColumnCount(); double frozenColumnsWidth = 0; @@ -2248,6 +2246,10 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, } setMessageAndButtonsWrapperVisible(isBuffered()); + } + + private void updateSizeAndPosition(DivElement gridElement, + TableRowElement tr) { updateHorizontalScrollPosition(); @@ -2362,6 +2364,17 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, } } + private void updateSelectedStyleName() { + // Add class name with selected modifier if the editor is being + // opened on selected row, see #11634 + String selectedStylename = styleName + "-selected"; + if (grid.isSelected(grid.getDataSource().getRow(getRow()))) { + cellWrapper.addClassName(selectedStylename); + } else { + cellWrapper.removeClassName(selectedStylename); + } + } + /** * Sets the editor's primary style name and updates all dependent style * names. @@ -3913,6 +3926,7 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, .setColumnWidths(constrainedWidths, true); } + @SuppressWarnings("unchecked") private void applyColumnWidthsWithExpansion() { boolean defaultExpandRatios = true; int totalRatios = 0; @@ -4019,6 +4033,46 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, return; } + Object[] result = handleMaximumWidths(totalRatios, + pixelsToDistribute, defaultExpandRatios, columnsToExpand, + visibleColumns, columnSizes); + if (result.length != 3) { + // everything handled already + return; + } + // update values based on maximum width handling + totalRatios = (int) result[0]; + pixelsToDistribute = (double) result[1]; + columnSizes = (Map) result[2]; + + assert pixelsToDistribute > 0 : "We've run out of pixels to distribute (" + + pixelsToDistribute + "px to " + totalRatios + + " ratios between " + columnsToExpand.size() + " columns)"; + assert totalRatios > 0 && !columnsToExpand + .isEmpty() : "Bookkeeping out of sync. Ratios: " + + totalRatios + " Columns: " + + columnsToExpand.size(); + + /* + * If we still have anything left, distribute the remaining pixels + * to the remaining columns. + */ + columnSizes = distributeRemainingPixels(totalRatios, + pixelsToDistribute, defaultExpandRatios, columnsToExpand, + visibleColumns, columnSizes); + + columnSizes = handleMinimumWidths(defaultExpandRatios, + columnsToExpand, visibleColumns, columnSizes); + + // Finally set all the column sizes. + setColumnSizes(columnSizes, true); + } + + private Object[] handleMaximumWidths(int totalRatios, + double pixelsToDistribute, final boolean defaultExpandRatios, + final Set> columnsToExpand, + final List> visibleColumns, + final Map columnSizes) { /* * Check for columns that hit their max width. Adjust * pixelsToDistribute and totalRatios accordingly. Recheck. Stop @@ -4051,20 +4105,20 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, if (totalRatios <= 0 && columnsToExpand.isEmpty()) { setColumnSizes(columnSizes, true); - return; + // nothing left to handle + return new Object[] {}; } - assert pixelsToDistribute > 0 : "We've run out of pixels to distribute (" - + pixelsToDistribute + "px to " + totalRatios - + " ratios between " + columnsToExpand.size() + " columns)"; - assert totalRatios > 0 && !columnsToExpand - .isEmpty() : "Bookkeeping out of sync. Ratios: " - + totalRatios + " Columns: " - + columnsToExpand.size(); + // this must return exactly 3 objects in this precise order + return new Object[] { totalRatios, pixelsToDistribute, + columnSizes }; + } - /* - * If we still have anything left, distribute the remaining pixels - * to the remaining columns. - */ + private Map distributeRemainingPixels(int totalRatios, + final double pixelsToDistribute, + final boolean defaultExpandRatios, + final Set> columnsToExpand, + final List> visibleColumns, + final Map columnSizes) { final double widthPerRatio; int leftOver = 0; if (BrowserInfo.getBrowserString().contains("PhantomJS")) { @@ -4093,6 +4147,14 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, assert totalRatios == 0 : "Bookkeeping error: there were still some ratios left undistributed: " + totalRatios; + return columnSizes; + } + + private Map handleMinimumWidths( + final boolean defaultExpandRatios, + final Set> columnsToExpand, + final List> visibleColumns, + final Map columnSizes) { /* * Check the guarantees for minimum width and scoot back the columns * that don't care. @@ -4138,7 +4200,7 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, * Now we need to shrink the remaining columns according to * their ratios. Recalculate the sum of remaining ratios. */ - totalRatios = 0; + int totalRatios = 0; for (Column column : columnsToExpand) { totalRatios += getExpandRatio(column, defaultExpandRatios); } @@ -4154,8 +4216,7 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, } while (minWidthsCausedReflows); - // Finally set all the column sizes. - setColumnSizes(columnSizes, true); + return columnSizes; } private void setColumnSizes(Map columnSizes, @@ -6533,161 +6594,181 @@ public class Grid extends ResizeComposite implements HasSelectionHandlers, resizeHandleWidth = dragger.getElement().getOffsetWidth() + WidgetUtil.getBorderLeftAndRightThickness(td); - // Common functionality for drag handle callback - // implementations - abstract class AbstractDHCallback - implements DragHandleCallback { - protected Column col = getVisibleColumn(column); - protected double initialWidth = 0; - protected double minCellWidth; - protected double width; - - protected void dragStarted() { - initialWidth = col.getWidthActual(); - width = initialWidth; - - minCellWidth = escalator.getMinCellWidth( - getVisibleColumns().indexOf(col)); - for (Column c : getVisibleColumns()) { - if (selectionColumn == c) { - // Don't modify selection column. - continue; - } + final DragHandleCallback simpleResizeMode = createSimpleResizeModeCallback( + column, dragger, resizeElement); - if (c.getWidth() < 0) { - c.setWidth(c.getWidthActual()); - fireEvent(new ColumnResizeEvent<>(c)); - } - } + final DragHandleCallback animatedResizeMode = createAnimatedResizeModeCallback( + column); - WidgetUtil.setTextSelectionEnabled(getElement(), - false); - } + // DragHandle gets assigned a 'master callback' that + // delegates functionality to the correct case-specific + // implementation + dragger.setCallback(createResizeModeAwareDragHandleCallback( + simpleResizeMode, animatedResizeMode)); + } - protected void dragEnded() { - WidgetUtil.setTextSelectionEnabled(getElement(), - true); - } + cellFocusHandler.updateFocusedCellStyle(cell, container); + } + } + + // Common functionality for drag handle callback + // implementations + private abstract class AbstractDHCallback + implements DragHandleCallback { + protected Column col; + protected double initialWidth = 0; + protected double minCellWidth; + protected double width; + + AbstractDHCallback(int column) { + col = getVisibleColumn(column); + } + + protected void dragStarted() { + initialWidth = col.getWidthActual(); + width = initialWidth; + + minCellWidth = escalator + .getMinCellWidth(getVisibleColumns().indexOf(col)); + for (Column c : getVisibleColumns()) { + if (selectionColumn == c) { + // Don't modify selection column. + continue; } - final DragHandleCallback simpleResizeMode = new AbstractDHCallback() { - @Override - protected void dragEnded() { - super.dragEnded(); - dragger.getElement().removeChild(resizeElement); - } + if (c.getWidth() < 0) { + c.setWidth(c.getWidthActual()); + fireEvent(new ColumnResizeEvent<>(c)); + } + } - @Override - public void onStart() { - dragStarted(); - dragger.getElement().appendChild(resizeElement); - resizeElement.getStyle().setLeft( - (dragger.getElement().getOffsetWidth() - - resizeElement.getOffsetWidth()) - * .5, + WidgetUtil.setTextSelectionEnabled(getElement(), false); + } + + protected void dragEnded() { + WidgetUtil.setTextSelectionEnabled(getElement(), true); + } + } + + private DragHandleCallback createSimpleResizeModeCallback( + final int column, final DragHandle dragger, + final DivElement resizeElement) { + return new AbstractDHCallback(column) { + @Override + protected void dragEnded() { + super.dragEnded(); + dragger.getElement().removeChild(resizeElement); + } + + @Override + public void onStart() { + dragStarted(); + dragger.getElement().appendChild(resizeElement); + resizeElement.getStyle() + .setLeft((dragger.getElement().getOffsetWidth() + - resizeElement.getOffsetWidth()) * .5, Unit.PX); - resizeElement.getStyle().setHeight( - col.grid.getOffsetHeight(), Unit.PX); - } + resizeElement.getStyle() + .setHeight(col.grid.getOffsetHeight(), Unit.PX); + } - @Override - public void onUpdate(double deltaX, double deltaY) { - width = Math.max(minCellWidth, - initialWidth + deltaX); - resizeElement.getStyle().setLeft( + @Override + public void onUpdate(double deltaX, double deltaY) { + width = Math.max(minCellWidth, initialWidth + deltaX); + resizeElement.getStyle() + .setLeft( (dragger.getElement().getOffsetWidth() - resizeElement.getOffsetWidth()) * .5 + (width - initialWidth), Unit.PX); - } + } - @Override - public void onCancel() { - dragEnded(); - } + @Override + public void onCancel() { + dragEnded(); + } - @Override - public void onComplete() { - dragEnded(); - col.setWidth(width); - - // Need to wait for column width recalculation - // scheduled by setWidth() before firing the event - Scheduler.get().scheduleDeferred(() -> fireEvent( - new ColumnResizeEvent<>(col))); - } - }; + @Override + public void onComplete() { + dragEnded(); + col.setWidth(width); - final DragHandleCallback animatedResizeMode = new AbstractDHCallback() { - @Override - public void onStart() { - dragStarted(); - } + // Need to wait for column width recalculation + // scheduled by setWidth() before firing the event + Scheduler.get().scheduleDeferred( + () -> fireEvent(new ColumnResizeEvent<>(col))); + } + }; + } - @Override - public void onUpdate(double deltaX, double deltaY) { - width = Math.max(minCellWidth, - initialWidth + deltaX); - col.setWidth(width); - } + private DragHandleCallback createAnimatedResizeModeCallback( + final int column) { + return new AbstractDHCallback(column) { + @Override + public void onStart() { + dragStarted(); + } - @Override - public void onCancel() { - dragEnded(); - col.setWidth(initialWidth); - } + @Override + public void onUpdate(double deltaX, double deltaY) { + width = Math.max(minCellWidth, initialWidth + deltaX); + col.setWidth(width); + } - @Override - public void onComplete() { - dragEnded(); - col.setWidth(width); - fireEvent(new ColumnResizeEvent<>(col)); - } - }; + @Override + public void onCancel() { + dragEnded(); + col.setWidth(initialWidth); + } - // DragHandle gets assigned a 'master callback' that - // delegates - // functionality to the correct case-specific implementation - dragger.setCallback(new DragHandleCallback() { + @Override + public void onComplete() { + dragEnded(); + col.setWidth(width); + fireEvent(new ColumnResizeEvent<>(col)); + } + }; + } - private DragHandleCallback currentCallback; + private DragHandleCallback createResizeModeAwareDragHandleCallback( + final DragHandleCallback simpleResizeMode, + final DragHandleCallback animatedResizeMode) { + return new DragHandleCallback() { - @Override - public void onStart() { - switch (getColumnResizeMode()) { - case SIMPLE: - currentCallback = simpleResizeMode; - break; - case ANIMATED: - currentCallback = animatedResizeMode; - break; - default: - throw new UnsupportedOperationException( - "Support for current column resize mode is not yet implemented"); - } + private DragHandleCallback currentCallback; - currentCallback.onStart(); - } + @Override + public void onStart() { + switch (getColumnResizeMode()) { + case SIMPLE: + currentCallback = simpleResizeMode; + break; + case ANIMATED: + currentCallback = animatedResizeMode; + break; + default: + throw new UnsupportedOperationException( + "Support for current column resize mode is not yet implemented"); + } - @Override - public void onUpdate(double deltaX, double deltaY) { - currentCallback.onUpdate(deltaX, deltaY); - } + currentCallback.onStart(); + } - @Override - public void onCancel() { - currentCallback.onCancel(); - } + @Override + public void onUpdate(double deltaX, double deltaY) { + currentCallback.onUpdate(deltaX, deltaY); + } - @Override - public void onComplete() { - currentCallback.onComplete(); - } - }); + @Override + public void onCancel() { + currentCallback.onCancel(); } - cellFocusHandler.updateFocusedCellStyle(cell, container); - } + @Override + public void onComplete() { + currentCallback.onComplete(); + } + }; } private void addAriaLabelToHeaderRow(FlyweightCell cell) { -- cgit v1.2.3