Skip to content

Commit ed322b3

Browse files
authored
fix: use frozen panel size to calculate the visible rows/cols on scroll (#7719)
* fix: use frozen panel size to calculate the visible rows/cols on scroll Use the top and left frozen panel size to calculate the amount of headers and rows to be rendered when the user scrolls. Currently, the size of the frozen panels are not accounted which causes some of the columns and rows to not be rendered even when there's available space for that. Fixes #7507 * test: add tests for the frozen pane * test: add tests for frozen pane with custom editors * test: add tests for frozen pane with custom editors * test: split key commands * test: change selector in expected row headers count * test: align the test execution in EscWithArrowKeys test * fix: adjust top/left shift calculation * test: temporary disable one assertion to check CI * test: undo last commit and disable test for the first column * test: use selectcell instead of clickcell * test: scale down always visible custom editor test * address copilot review comments
1 parent 596f7c1 commit ed322b3

File tree

6 files changed

+445
-44
lines changed

6 files changed

+445
-44
lines changed

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow-client/src/main/java/com/vaadin/addon/spreadsheet/client/SheetWidget.java

Lines changed: 93 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ public void execute() {
686686
updateRowGrouping();
687687

688688
resetCellContents();
689+
relayoutSheet(false);
689690
loaded = true;
690691
}
691692
});
@@ -762,31 +763,29 @@ public void relayoutSheet(boolean triggerRequest) {
762763
final int rightBound = leftFrozenPanelWidth + scrollLeft
763764
+ scrollViewWidth + actionHandler.getColumnBufferSize();
764765

765-
int leftEdgeChange = newFirstColumnPosition - firstColumnPosition;
766766
int rightEdgeChange = newLastColumnPosition - lastColumnPosition;
767767
firstColumnPosition = newFirstColumnPosition;
768768
lastColumnPosition = newLastColumnPosition;
769769

770770
// always call handle scroll left, otherwise
771771
// expanding groups with layouts does not work
772-
handleHorizontalScrollLeft(scrollLeft);
772+
handleHorizontalScroll(scrollLeft, hScrollDiff);
773773
updateCells(0, -1);
774774

775775
if (rightEdgeChange < 0 || hScrollDiff > 0
776776
|| (lastColumnIndex < actionHandler.getMaxColumns()
777777
&& lastColumnPosition < rightBound)) {
778-
handleHorizontalScrollRight(scrollLeft);
779778
updateCells(0, 1);
780779
}
781780

781+
handleVerticalScroll(scrollTop, vScrollDiff);
782+
782783
if (topEdgeChange > 0 || vScrollDiff < 0) {
783-
handleVerticalScrollUp(scrollTop);
784784
updateCells(-1, 0);
785785
}
786786
if (bottomEdgeChange != 0 || vScrollDiff > 0
787787
|| (lastRowIndex < actionHandler.getMaxRows()
788788
&& lastRowPosition < bottomBound)) {
789-
handleVerticalScrollDown(scrollTop);
790789
updateCells(1, 0);
791790
}
792791
resetRowAndColumnStyles();
@@ -2311,15 +2310,15 @@ private void createOverlayStyles(StyleElement stylesheet,
23112310

23122311
private int calculateLeftValueOfScrolledColumns() {
23132312
int left = 0;
2314-
for (int i = 1; i < (firstColumnIndex - horizontalSplitPosition); i++) {
2313+
for (int i = horizontalSplitPosition + 1; i < firstColumnIndex; i++) {
23152314
left += actionHandler.getColWidth(i);
23162315
}
23172316
return left;
23182317
}
23192318

23202319
private int calculateTopValueOfScrolledRows() {
23212320
int top = 0;
2322-
for (int i = 1; i < (firstRowIndex - verticalSplitPosition); i++) {
2321+
for (int i = verticalSplitPosition + 1; i < firstRowIndex; i++) {
23232322
top += definedRowHeights[i - 1];
23242323
}
23252324
return top;
@@ -2992,7 +2991,7 @@ private void clearListOfCells(ArrayList<Cell> row) {
29922991
* handler (if needed).
29932992
*/
29942993
private void onSheetScroll() {
2995-
int scrollTop = topFrozenPanelHeight + sheet.getScrollTop();
2994+
int scrollTop = sheet.getScrollTop();
29962995
int scrollLeft = sheet.getScrollLeft();
29972996
int vScrollDiff = scrollTop - previousScrollTop;
29982997
int hScrollDiff = scrollLeft - previousScrollLeft;
@@ -3007,21 +3006,13 @@ private void onSheetScroll() {
30073006
if (Math.abs(
30083007
hScrollDiff) > (actionHandler.getColumnBufferSize() / 2)) {
30093008
previousScrollLeft = scrollLeft;
3010-
if (hScrollDiff > 0) {
3011-
handleHorizontalScrollRight(scrollLeft);
3012-
} else if (hScrollDiff < 0) {
3013-
handleHorizontalScrollLeft(scrollLeft);
3014-
}
3009+
handleHorizontalScroll(scrollLeft, hScrollDiff);
30153010
}
30163011

30173012
if (Math.abs(
30183013
vScrollDiff) > (actionHandler.getRowBufferSize() / 2)) {
30193014
previousScrollTop = scrollTop;
3020-
if (vScrollDiff > 0) {
3021-
handleVerticalScrollDown(scrollTop);
3022-
} else if (vScrollDiff < 0) {
3023-
handleVerticalScrollUp(scrollTop);
3024-
}
3015+
handleVerticalScroll(scrollTop, vScrollDiff);
30253016
}
30263017
requester.trigger();
30273018
} catch (Throwable t) {
@@ -3310,15 +3301,49 @@ private void updateCells(int vScrollDiff, int hScrollDiff) {
33103301
}
33113302
}
33123303

3313-
private void handleHorizontalScrollLeft(int scrollLeft) {
3304+
/**
3305+
* Handles horizontal scrolling in the spreadsheet. It calculates the
3306+
* visible columns and updates the column headers accordingly.
3307+
*
3308+
* The method takes the current scroll position and the difference since the
3309+
* last update to determine how many columns to display.
3310+
*
3311+
* @param scrollLeft
3312+
* the current scroll position from the left
3313+
* @param scrollDiff
3314+
* the difference in scroll position since the last update
3315+
*/
3316+
private void handleHorizontalScroll(int scrollLeft, int scrollDiff) {
3317+
if (scrollDiff == 0) {
3318+
return; // no scroll
3319+
}
3320+
33143321
int columnBufferSize = actionHandler.getColumnBufferSize();
33153322
int leftBound = scrollLeft - columnBufferSize;
3316-
int rightBound = scrollLeft + scrollViewWidth + columnBufferSize;
3323+
int rightBound = leftFrozenPanelWidth + scrollLeft + scrollViewWidth
3324+
+ columnBufferSize;
33173325

33183326
if (leftBound < 0) {
33193327
leftBound = 0;
33203328
}
33213329

3330+
if (scrollDiff > 0) {
3331+
handleHorizontalScrollRight(leftBound, rightBound);
3332+
} else {
3333+
handleHorizontalScrollLeft(leftBound, rightBound);
3334+
}
3335+
3336+
}
3337+
3338+
/**
3339+
* Handles horizontal scrolling to the left in the sheet.
3340+
*
3341+
* @param leftBound
3342+
* the left bound of the visible area
3343+
* @param rightBound
3344+
* the right bound of the visible area
3345+
*/
3346+
private void handleHorizontalScrollLeft(int leftBound, int rightBound) {
33223347
int maxFirstColumn = horizontalSplitPosition + 1; // hSP is 0 when no
33233348
while (firstColumnPosition > leftBound
33243349
&& firstColumnIndex > maxFirstColumn) {
@@ -3355,15 +3380,7 @@ private void handleHorizontalScrollLeft(int scrollLeft) {
33553380
*
33563381
* @param scrollLeft
33573382
*/
3358-
private void handleHorizontalScrollRight(int scrollLeft) {
3359-
int columnBufferSize = actionHandler.getColumnBufferSize();
3360-
int leftBound = scrollLeft - columnBufferSize;
3361-
int rightBound = scrollLeft + scrollViewWidth + columnBufferSize;
3362-
3363-
if (leftBound < 0) {
3364-
leftBound = 0;
3365-
}
3366-
3383+
private void handleHorizontalScrollRight(int leftBound, int rightBound) {
33673384
final int maximumCols = actionHandler.getMaxColumns();
33683385
while (lastColumnPosition < rightBound
33693386
&& lastColumnIndex < maximumCols) {
@@ -3389,15 +3406,50 @@ private void handleHorizontalScrollRight(int scrollLeft) {
33893406
resetColHeaders();
33903407
}
33913408

3392-
private void handleVerticalScrollDown(int scrollTop) {
3409+
/**
3410+
* Handles vertical scrolling in the spreadsheet. It calculates the visible
3411+
* rows and updates the row headers accordingly.
3412+
*
3413+
* The method takes the current scroll position and the difference since the
3414+
* last update to determine how many rows to display.
3415+
*
3416+
* @param scrollTop
3417+
* the current scroll position from the top
3418+
* @param scrollDiff
3419+
* the difference in scroll position since the last update
3420+
*/
3421+
private void handleVerticalScroll(int scrollTop, int scrollDiff) {
3422+
if (scrollDiff == 0) {
3423+
return; // no scroll
3424+
}
3425+
33933426
int rowBufferSize = actionHandler.getRowBufferSize();
33943427
int topBound = scrollTop - rowBufferSize;
3395-
int bottomBound = scrollTop + scrollViewHeight + rowBufferSize;
3428+
int bottomBound = topFrozenPanelHeight + scrollTop + scrollViewHeight
3429+
+ rowBufferSize;
33963430

33973431
if (topBound < 0) {
33983432
topBound = 0;
33993433
}
34003434

3435+
if (scrollDiff > 0) {
3436+
handleVerticalScrollDown(topBound, bottomBound);
3437+
} else {
3438+
handleVerticalScrollUp(topBound, bottomBound);
3439+
}
3440+
3441+
}
3442+
3443+
/**
3444+
* Calculates viewed cells after a scroll down. Runs the escalator for row
3445+
* headers.
3446+
*
3447+
* @param topBound
3448+
* the top bound of the visible area
3449+
* @param bottomBound
3450+
* the bottom bound of the visible area
3451+
*/
3452+
private void handleVerticalScrollDown(int topBound, int bottomBound) {
34013453
final int maximumRows = actionHandler.getMaxRows();
34023454
while (lastRowPosition < bottomBound && lastRowIndex < maximumRows) {
34033455
if ((firstRowPosition + getRowHeight(firstRowIndex)) < topBound) {
@@ -3417,15 +3469,16 @@ private void handleVerticalScrollDown(int scrollTop) {
34173469
resetRowHeaders();
34183470
}
34193471

3420-
private void handleVerticalScrollUp(int scrollTop) {
3421-
int rowBufferSize = actionHandler.getRowBufferSize();
3422-
int topBound = scrollTop - rowBufferSize;
3423-
int bottomBound = scrollTop + scrollViewHeight + rowBufferSize;
3424-
3425-
if (topBound < 0) {
3426-
topBound = 0;
3427-
}
3428-
3472+
/**
3473+
* Calculates viewed cells after a scroll up. Runs the escalator for row
3474+
* headers.
3475+
*
3476+
* @param topBound
3477+
* the top bound of the visible area
3478+
* @param bottomBound
3479+
* the bottom bound of the visible area
3480+
*/
3481+
private void handleVerticalScrollUp(int topBound, int bottomBound) {
34293482
int maxTopRow = verticalSplitPosition + 1; // vSP is 0 when no split
34303483
while (firstRowPosition > topBound && firstRowIndex > maxTopRow) {
34313484
if ((lastRowPosition - getRowHeight(lastRowIndex)) > bottomBound) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* This program is available under Vaadin Commercial License and Service Terms.
5+
*
6+
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} for the full
7+
* license.
8+
*/
9+
package com.vaadin.flow.component.spreadsheet.tests.fixtures;
10+
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
14+
import org.apache.poi.ss.usermodel.Cell;
15+
import org.apache.poi.ss.usermodel.Sheet;
16+
17+
import com.vaadin.flow.component.Component;
18+
import com.vaadin.flow.component.button.Button;
19+
import com.vaadin.flow.component.spreadsheet.Spreadsheet;
20+
import com.vaadin.flow.component.spreadsheet.SpreadsheetComponentFactory;
21+
import com.vaadin.flow.component.textfield.TextField;
22+
23+
public class CustomEditorRowFixture implements SpreadsheetFixture {
24+
25+
static final int COMPONENT_ROW_INDEX = 1;
26+
27+
@Override
28+
public void loadFixture(final Spreadsheet spreadsheet) {
29+
spreadsheet.setSpreadsheetComponentFactory(new CustomEditorFactory());
30+
spreadsheet.setColumnWidth(0, 200);
31+
spreadsheet.setShowCustomEditorOnFocus(true);
32+
}
33+
34+
private static class CustomEditorFactory
35+
implements SpreadsheetComponentFactory {
36+
37+
private final Map<Integer, TextField> textFields = new HashMap<>();
38+
39+
@Override
40+
public Component getCustomComponentForCell(Cell cell, int rowIndex,
41+
int columnIndex, Spreadsheet spreadsheet, Sheet sheet) {
42+
if (rowIndex != 0 || columnIndex != 0) {
43+
return null;
44+
}
45+
46+
Button toggleCustomEditorVisibilityButton = new Button(
47+
"Toggle custom editor", event -> {
48+
spreadsheet.setShowCustomEditorOnFocus(
49+
!spreadsheet.isShowCustomEditorOnFocus());
50+
});
51+
toggleCustomEditorVisibilityButton
52+
.setId("toggleCustomEditorVisibilityButton");
53+
return toggleCustomEditorVisibilityButton;
54+
}
55+
56+
@Override
57+
public Component getCustomEditorForCell(Cell cell, final int rowIndex,
58+
final int columnIndex, final Spreadsheet spreadsheet,
59+
Sheet sheet) {
60+
if (!sheet.getSheetName().equals("Sheet1")
61+
|| rowIndex != COMPONENT_ROW_INDEX) {
62+
return null;
63+
}
64+
if (!textFields.containsKey(columnIndex)) {
65+
var textField = new TextField();
66+
67+
textField.addValueChangeListener(
68+
e -> spreadsheet.refreshCells(spreadsheet.createCell(
69+
rowIndex, columnIndex, e.getValue())));
70+
textField.setId("textField" + rowIndex + columnIndex);
71+
textFields.put(columnIndex, textField);
72+
}
73+
return textFields.get(columnIndex);
74+
}
75+
76+
@Override
77+
public void onCustomEditorDisplayed(Cell cell, int rowIndex,
78+
int columnIndex, Spreadsheet spreadsheet, Sheet sheet,
79+
Component customEditor) {
80+
if (cell == null) {
81+
return;
82+
}
83+
84+
if (customEditor instanceof TextField editor) {
85+
editor.setValue(cell.getStringCellValue());
86+
}
87+
}
88+
}
89+
90+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright 2000-2025 Vaadin Ltd.
3+
*
4+
* This program is available under Vaadin Commercial License and Service Terms.
5+
*
6+
* See {@literal <https://vaadin.com/commercial-license-and-service-terms>} for the full
7+
* license.
8+
*/
9+
package com.vaadin.flow.component.spreadsheet.tests.fixtures;
10+
11+
import com.vaadin.flow.component.spreadsheet.Spreadsheet;
12+
13+
/**
14+
* Fixture to hide the second column.
15+
*
16+
*/
17+
public class HideSecondColumnFixture implements SpreadsheetFixture {
18+
19+
@Override
20+
public void loadFixture(Spreadsheet spreadsheet) {
21+
spreadsheet.setColumnHidden(1, true);
22+
}
23+
}

vaadin-spreadsheet-flow-parent/vaadin-spreadsheet-flow-integration-tests/src/main/java/com/vaadin/flow/component/spreadsheet/tests/fixtures/TestFixtures.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@ public enum TestFixtures {
2121
AddOrRemoveComment(AddOrRemoveCommentFixture.class),
2222
Formats(FormatsFixture.class),
2323
DisableChartOverlays(DisableChartsFixture.class),
24-
StyleMergeReigions(
25-
com.vaadin.flow.component.spreadsheet.tests.fixtures.StyleMergeReigions.class),
26-
RemoveFixture(
27-
com.vaadin.flow.component.spreadsheet.tests.fixtures.RemoveFixture.class),
24+
StyleMergeReigions(StyleMergeReigions.class),
25+
RemoveFixture(RemoveFixture.class),
2826
DefaultStyleUnlocked(DefaultStyleUnlockedFixture.class),
2927
HideSecondRow(HideSecondRowFixture.class),
28+
HideSecondColumn(HideSecondColumnFixture.class),
3029
LargeSpreadsheet(LargeSpreadsheetFixture.class),
3130
ColumnToggle(ColumnToggleFixture.class),
3231
RowToggle(RowToggleFixture.class),
@@ -37,6 +36,7 @@ public enum TestFixtures {
3736
Rename(RenameFixture.class),
3837
CreateSheet(SheetsFixture.class),
3938
CustomEditor(SimpleCustomEditorFixture.class),
39+
CustomEditorRow(CustomEditorRowFixture.class),
4040
Styles(StylesFixture.class),
4141
LockCell(LockCellFixture.class),
4242
CustomComponent(CustomComponentFixture.class),

0 commit comments

Comments
 (0)