mirror of
https://github.com/dream-num/univer.git
synced 2026-09-01 15:29:43 +08:00
fix(sheet): overflow (#1013)
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user