fix(sheet): overflow (#1013)

This commit is contained in:
Univer
2024-01-06 15:59:00 +08:00
committed by GitHub
parent eeabcfade9
commit 38f1af8769
2 changed files with 115 additions and 171 deletions
@@ -38,6 +38,7 @@ import type {
} from '@univerjs/core';
import {
BooleanNumber,
CellValueType,
DEFAULT_EMPTY_DOCUMENT_VALUE,
DocumentDataModel,
getColorStyle,
@@ -69,7 +70,7 @@ import { columnIterator } from '../docs/common/tools';
import { DocumentSkeleton } from '../docs/doc-skeleton';
import { DocumentViewModel } from '../docs/view-model/document-view-model';
import { Skeleton } from '../skeleton';
import type { BorderCache, IStylesCache } from './interfaces';
import type { BorderCache, IFontCacheItem, IStylesCache } from './interfaces';
/**
* Obtain the height and width of a cell's text, taking into account scenarios with rotated text.
@@ -1098,6 +1099,103 @@ export class SpreadsheetSkeleton extends Skeleton {
};
}
/**
* Calculate the overflow of cell text. If there is no value on either side of the cell,
* the text content of this cell can be drawn to both sides, not limited by the cell's width.
* Overflow on the left or right is aligned according to the text's horizontal alignment.
*/
private _calculateOverflowCell(row: number, column: number, docsConfig: IFontCacheItem) {
// wrap and angle handler
const { documentSkeleton, angle = 0, horizontalAlign, wrapStrategy } = docsConfig;
const cell = this.getCellData().getValue(row, column);
const { t: cellValueType = CellValueType.STRING } = cell || {};
/**
* Numerical and Boolean values are not displayed with overflow.
*/
if (
(wrapStrategy === WrapStrategy.OVERFLOW || wrapStrategy === WrapStrategy.UNSPECIFIED) &&
cellValueType !== CellValueType.NUMBER &&
cellValueType !== CellValueType.BOOLEAN &&
horizontalAlign !== HorizontalAlign.JUSTIFIED
) {
// Merged cells do not support overflow.
if (this.intersectMergeRange(row, column)) {
return true;
}
let contentSize = getDocsSkeletonPageSize(documentSkeleton, angle);
if (!contentSize) {
return true;
}
if (angle !== 0) {
const { startY, endY, startX, endX } = getCellByIndex(
row,
column,
this.rowHeightAccumulation,
this.columnWidthAccumulation,
this.mergeData
);
const cellWidth = endX - startX;
const cellHeight = endY - startY;
if (contentSize.height > cellHeight) {
contentSize = {
width: cellHeight / Math.tan(Math.abs(angle)) + cellWidth,
height: cellHeight,
};
// if (angle > 0) {
// horizontalAlign = HorizontalAlign.LEFT;
// } else {
// horizontalAlign = HorizontalAlign.RIGHT;
// }
}
}
const position = this.getOverflowPosition(contentSize, horizontalAlign, row, column, this.getColumnCount());
const { startColumn, endColumn } = position;
this.appendToOverflowCache(row, column, startColumn, endColumn);
} else if (wrapStrategy === WrapStrategy.WRAP && angle !== 0) {
// Merged cells do not support overflow.
if (this.intersectMergeRange(row, column)) {
return true;
}
const { startY, endY } = getCellByIndex(
row,
column,
this.rowHeightAccumulation,
this.columnWidthAccumulation,
this.mergeData
);
const cellHeight = endY - startY;
documentSkeleton.getViewModel().getDataModel().updateDocumentDataPageSize(cellHeight);
documentSkeleton.calculate();
const contentSize = getDocsSkeletonPageSize(documentSkeleton, angle);
if (!contentSize) {
return true;
}
const { startColumn, endColumn } = this.getOverflowPosition(
contentSize,
horizontalAlign,
row,
column,
this.getColumnCount()
);
this.appendToOverflowCache(row, column, startColumn, endColumn);
}
}
/**
*
* @param rowHeightAccumulation Row layout information
@@ -1375,10 +1473,14 @@ export class SpreadsheetSkeleton extends Skeleton {
const columnWidthAccumulation = this.columnWidthAccumulation;
const { startRow, endRow, startColumn, endColumn } = rowColumnSegment;
if (endColumn === -1) {
if (endColumn === -1 || endRow === -1) {
return;
}
for (const data of dataMergeCache) {
this._setCellCache(data.startRow, data.startColumn, false);
}
for (let r = startRow; r <= endRow; r++) {
for (let c = startColumn; c <= endColumn; c++) {
this._setCellCache(r, c, false);
@@ -1399,9 +1501,7 @@ export class SpreadsheetSkeleton extends Skeleton {
}
}
for (const data of dataMergeCache) {
this._setCellCache(data.startRow, data.startColumn, false);
}
// this.calculateOverflow();
}
private _resetCache() {
@@ -1413,6 +1513,8 @@ export class SpreadsheetSkeleton extends Skeleton {
};
this._renderedCellCache = new ObjectMatrix<boolean>();
this._overflowCache.reset();
}
private _makeDocumentSkeletonDirty(r: number, c: number) {
@@ -1530,13 +1632,17 @@ export class SpreadsheetSkeleton extends Skeleton {
const documentSkeleton = DocumentSkeleton.create(documentViewModel, this._localService);
documentSkeleton.calculate();
fontCache.setValue(r, c, {
const config = {
documentSkeleton,
angle,
verticalAlign,
horizontalAlign,
wrapStrategy,
});
};
fontCache.setValue(r, c, config);
this._calculateOverflowCell(r, c, config);
}
if (!skipBackgroundAndBorder) {
@@ -15,12 +15,12 @@
*/
import type { IRange, ISelectionCellWithCoord, Nullable } from '@univerjs/core';
import { BooleanNumber, CellValueType, HorizontalAlign, ObjectMatrix, sortRules, WrapStrategy } from '@univerjs/core';
import { BooleanNumber, ObjectMatrix, sortRules } from '@univerjs/core';
import type { BaseObject } from '../../base-object';
import { FIX_ONE_PIXEL_BLUR_OFFSET, RENDER_CLASS_TYPE } from '../../basics/const';
import { clearLineByBorderType } from '../../basics/draw';
import { getCellByIndex, getCellPositionByIndex, getColor } from '../../basics/tools';
import { getCellPositionByIndex, getColor } from '../../basics/tools';
import type { IViewportBound, Vector2 } from '../../basics/vector2';
import { Canvas } from '../../canvas';
import type { UniverRenderingContext } from '../../context';
@@ -36,7 +36,6 @@ import type { Font } from './extensions/font';
import type { BorderCacheItem } from './interfaces';
import { SheetComponent } from './sheet-component';
import type { SpreadsheetSkeleton } from './sheet-skeleton';
import { getDocsSkeletonPageSize } from './sheet-skeleton';
const OBJECT_KEY = '__SHEET_EXTENSION_FONT_DOCUMENT_INSTANCE__';
@@ -122,15 +121,6 @@ export class Spreadsheet extends SheetComponent {
for (const extension of extensions) {
extension.draw(ctx, parentScale, spreadsheetSkeleton, diffRanges);
}
/**
* Caching overflow during scrolling and clearing it after waiting for 1 second without scrolling can significantly improve performance.
*/
clearTimeout(this._overflowCacheRuntimeTimeout);
this._overflowCacheRuntimeTimeout = setTimeout(() => {
this._overflowCacheRuntimeRange.reset();
this._overflowCacheRuntime = {};
}, 1000);
}
override isHit(coord: Vector2) {
@@ -452,155 +442,6 @@ export class Spreadsheet extends SheetComponent {
return newViewports;
}
/**
* Calculate the overflow of cell text. If there is no value on either side of the cell,
* the text content of this cell can be drawn to both sides, not limited by the cell's width.
* Overflow on the left or right is aligned according to the text's horizontal alignment.
*/
private _calculateOverflow() {
// const overflowCache = new ObjectMatrix<IRange>();
const spreadsheetSkeleton = this.getSkeleton();
if (!spreadsheetSkeleton) {
return;
}
const columnCount = spreadsheetSkeleton.getColumnCount();
const { stylesCache, rowHeightAccumulation, columnWidthAccumulation, mergeData } = spreadsheetSkeleton;
const { font: fontList } = stylesCache;
fontList &&
Object.keys(fontList).forEach((fontFormat: string) => {
const fontObjectArray = fontList[fontFormat];
fontObjectArray.forRow((row, columns) => {
if (this._overflowCacheRuntime[row] != null) {
return;
}
columns.forEach((column) => {
const docsConfig = fontObjectArray.getValue(row, column);
// wrap and angle handler
const { documentSkeleton, angle = 0, horizontalAlign, wrapStrategy } = docsConfig;
const cell = spreadsheetSkeleton.getCellData().getValue(row, column);
const sheetSkeleton = this.getSkeleton();
if (!sheetSkeleton) {
return true;
}
const { t: cellValueType = CellValueType.STRING } = cell || {};
/**
* Numerical and Boolean values are not displayed with overflow.
*/
if (
wrapStrategy === WrapStrategy.OVERFLOW &&
cellValueType !== CellValueType.NUMBER &&
cellValueType !== CellValueType.BOOLEAN &&
horizontalAlign !== HorizontalAlign.JUSTIFIED
) {
// Merged cells do not support overflow.
if (spreadsheetSkeleton.intersectMergeRange(row, column)) {
return true;
}
let contentSize = getDocsSkeletonPageSize(documentSkeleton, angle);
if (!contentSize) {
return true;
}
if (angle !== 0) {
const { startY, endY, startX, endX } = getCellByIndex(
row,
column,
rowHeightAccumulation,
columnWidthAccumulation,
mergeData
);
const cellWidth = endX - startX;
const cellHeight = endY - startY;
if (contentSize.height > cellHeight) {
contentSize = {
width: cellHeight / Math.tan(Math.abs(angle)) + cellWidth,
height: cellHeight,
};
// if (angle > 0) {
// horizontalAlign = HorizontalAlign.LEFT;
// } else {
// horizontalAlign = HorizontalAlign.RIGHT;
// }
}
}
const position = spreadsheetSkeleton.getOverflowPosition(
contentSize,
horizontalAlign,
row,
column,
columnCount
);
const { startColumn, endColumn } = position;
this._overflowCacheRuntimeRange.setValue(row, column, {
startRow: row,
endRow: row,
startColumn,
endColumn,
});
} else if (wrapStrategy === WrapStrategy.WRAP && angle !== 0) {
// Merged cells do not support overflow.
if (spreadsheetSkeleton.intersectMergeRange(row, column)) {
return true;
}
const { startY, endY } = getCellByIndex(
row,
column,
rowHeightAccumulation,
columnWidthAccumulation,
mergeData
);
const cellHeight = endY - startY;
documentSkeleton.getViewModel().getDataModel().updateDocumentDataPageSize(cellHeight);
documentSkeleton.calculate();
const contentSize = getDocsSkeletonPageSize(documentSkeleton, angle);
if (!contentSize) {
return true;
}
const { startColumn, endColumn } = sheetSkeleton.getOverflowPosition(
contentSize,
horizontalAlign,
row,
column,
sheetSkeleton.getColumnCount()
);
this._overflowCacheRuntimeRange.setValue(row, column, {
startRow: row,
endRow: row,
startColumn,
endColumn,
});
}
});
this._overflowCacheRuntime[row] = true;
});
fontObjectArray.forValue((row, column, docsConfig) => {
// console.log('appendToOverflowCache', cellHeight, angle, contentSize, { rowIndex, columnIndex, startColumn, endColumn });
});
});
spreadsheetSkeleton.setOverflowCache(this._overflowCacheRuntimeRange);
}
private _drawAuxiliary(ctx: UniverRenderingContext) {
const spreadsheetSkeleton = this.getSkeleton();
@@ -608,9 +449,6 @@ export class Spreadsheet extends SheetComponent {
return;
}
// insert overflow cache
this._calculateOverflow();
const { rowColumnSegment, dataMergeCache, overflowCache, stylesCache, showGridlines } = spreadsheetSkeleton;
const { border, backgroundPositions } = stylesCache;
const { startRow, endRow, startColumn, endColumn } = rowColumnSegment;