diff --git a/common/shared/eslint/index.js b/common/shared/eslint/index.js index 2ef4db46739..b40f039bd0a 100644 --- a/common/shared/eslint/index.js +++ b/common/shared/eslint/index.js @@ -217,7 +217,7 @@ exports.specPreset = () => { exports.jsdocPreset = () => { return { - files: ['**/*.{ts,tsx}'], + files: ['**/facade/*.{ts,tsx}'], rules: { ...jsdoc.configs.recommended.rules, }, diff --git a/docs/tldr/skeleton.svg b/docs/tldr/skeleton.svg new file mode 100644 index 00000000000..eebe86e8184 --- /dev/null +++ b/docs/tldr/skeleton.svg @@ -0,0 +1,8 @@ +packages/core/src/skeleton.tspackages/core/src/skeleton.tspackages/core/src/sheets/sheet-skeleton.tspackages/core/src/sheets/sheet-skeleton.tspackages/engine-render/.../sheets/sheet.render-skeleton.tspackages/engine-render/.../sheets/sheet.render-skeleton.tspackages/engine-render/.../docs/layout/doc-skeleton.tspackages/engine-render/.../docs/layout/doc-skeleton.ts \ No newline at end of file diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4ce572c3d07..66fa373d9b3 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -196,3 +196,7 @@ export { type BBox, type IRTreeItem, RBush, RTree } from './shared/r-tree'; export { type IUniverConfig, Univer } from './univer'; export { isNodeEnv } from './shared/tools'; +export { Skeleton } from './skeleton.ts'; +export { getCellCoordByIndexSimple as getCellPositionByIndex, getCellWithCoordByIndexCore, SheetSkeleton } from './sheets/sheet-skeleton'; +export type { IGetRowColByPosOptions } from './sheets/sheet-skeleton'; +export type { IPosition } from './sheets/typedef.ts'; diff --git a/packages/core/src/services/resource-manager/resource-manager.service.ts b/packages/core/src/services/resource-manager/resource-manager.service.ts index 31faf391d72..7f4da1d33da 100644 --- a/packages/core/src/services/resource-manager/resource-manager.service.ts +++ b/packages/core/src/services/resource-manager/resource-manager.service.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { Subject } from 'rxjs'; -import { Disposable, toDisposable } from '../../shared/lifecycle'; import type { UniverInstanceType } from '../../common/unit'; import type { IResources } from '../resource-manager/type'; import type { IResourceHook, IResourceManagerService, IResourceName } from './type'; +import { Subject } from 'rxjs'; +import { Disposable, toDisposable } from '../../shared/lifecycle'; export class ResourceManagerService extends Disposable implements IResourceManagerService { private _resourceMap = new Map(); diff --git a/packages/core/src/sheets/sheet-skeleton.ts b/packages/core/src/sheets/sheet-skeleton.ts new file mode 100644 index 00000000000..1a1d0e444d1 --- /dev/null +++ b/packages/core/src/sheets/sheet-skeleton.ts @@ -0,0 +1,1225 @@ +/** + * Copyright 2023-present DreamNum Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint-disable no-param-reassign */ +import type { IObjectArrayPrimitiveType, Nullable } from '../shared'; +import type { + IDocumentData, + IDocumentRenderConfig, + IPaddingData, +} from '../types/interfaces'; +import type { Styles } from './styles'; +import type { + ICellData, + ICellInfo, + ICellWithCoord, + IColumnData, + IPosition, + IRange, + IRowData, + ISelectionCell, + IWorksheetData, +} from './typedef'; +import type { Worksheet } from './worksheet'; +import { Inject, Injector } from '@wendellhu/redi'; +import { IS_ROW_STYLE_PRECEDE_COLUMN_STYLE } from '../common/const'; +import { DocumentDataModel } from '../docs/data-model/document-data-model'; +import { IConfigService } from '../services/config/config.service'; +import { IContextService } from '../services/context/context.service'; +import { LocaleService } from '../services/locale/locale.service'; +import { isCellCoverable, ObjectMatrix, searchArray, Tools } from '../shared'; +import { ImageCacheMap } from '../shared/cache/image-cache'; +import { getIntersectRange } from '../shared/range'; +import { Skeleton } from '../skeleton'; +import { BooleanNumber, HorizontalAlign } from '../types/enum'; + +export interface IGetRowColByPosOptions { + closeFirst?: boolean; + + /** + * For searchArray(rowHeightAccumulation) & searchArray(colWidthAccumulation) + * true means return first matched index in matched sequence. + * default return last index in matched sequence. + */ + firstMatch?: boolean; +} + +export const RENDER_RAW_FORMULA_KEY = 'RENDER_RAW_FORMULA'; +export class SheetSkeleton extends Skeleton { + /** + * @deprecated avoid use `IWorksheetData` directly, use API provided by `Worksheet`, otherwise + * `ViewModel` will be not working. + */ + protected _worksheetData: IWorksheetData; + protected _renderRawFormula = false; + protected _cellData: ObjectMatrix>; + protected _imageCacheMap: ImageCacheMap; + /** + * Whether the row style precedes the column style. + */ + protected _isRowStylePrecedeColumnStyle = false; + constructor( + readonly worksheet: Worksheet, + protected _styles: Styles, + @Inject(LocaleService) _localeService: LocaleService, + @IContextService protected readonly _contextService: IContextService, + @IConfigService protected readonly _configService: IConfigService, + @Inject(Injector) protected _injector: Injector + ) { + super(_localeService); + this._worksheetData = this.worksheet.getConfig(); + this._cellData = this.worksheet.getCellMatrix(); + this._imageCacheMap = new ImageCacheMap(this._injector); + + this.init(); + } + + init() { + this._isRowStylePrecedeColumnStyle = this._configService.getConfig(IS_ROW_STYLE_PRECEDE_COLUMN_STYLE) ?? false; + } + + _resetCache() { + // + } + + /** + * @deprecated should never expose a property that is provided by another module! + */ + getWorksheetConfig(): IWorksheetData { + return this._worksheetData; + } + + /** + * Get which Workbook and Worksheet this skeleton is attached to. + * @returns [unitId, sheetId] + */ + getLocation(): [string, string] { + return [this.worksheet.getUnitId(), this.worksheet.getSheetId()]; + } + + private _rowTotalHeight = 0; + private _columnTotalWidth = 0; + private _rowHeaderWidth = 0; + private _columnHeaderHeight = 0; + private _rowHeightAccumulation: number[] = []; + private _columnWidthAccumulation: number[] = []; + private _marginTop: number = 0; + private _marginLeft: number = 0; + /** Scale of Scene */ + protected _scaleX: number; + protected _scaleY: number; + /** Viewport scrolled value */ + protected _scrollX: number; + /** Viewport scrolled value */ + protected _scrollY: number; + + get rowHeightAccumulation(): number[] { + return this._rowHeightAccumulation; + } + + get rowTotalHeight(): number { + return this._rowTotalHeight; + } + + get columnWidthAccumulation(): number[] { + return this._columnWidthAccumulation; + } + + get columnTotalWidth(): number { + return this._columnTotalWidth; + } + + get rowHeaderWidth(): number { + return this._rowHeaderWidth; + } + + get columnHeaderHeight(): number { + return this._columnHeaderHeight; + } + + setMarginLeft(left: number): void { + this._marginLeft = left; + } + + setMarginTop(top: number): void { + this._marginTop = top; + } + + setScale(value: number, valueY?: number): void { + this._updateLayout(); + this._scaleX = value; + this._scaleY = valueY || value; + this._updateLayout(); + } + + setScroll(scrollX?: number, scrollY?: number) { + if (Tools.isDefine(scrollX)) { + this._scrollX = scrollX; + } + if (Tools.isDefine(scrollY)) { + this._scrollY = scrollY; + } + } + + get scrollX(): number { + return this._scrollX; + } + + get scrollY(): number { + return this._scrollY; + } + + get scaleX(): number { + return this._scaleX; + } + + get scaleY(): number { + return this._scaleY; + } + + get rowHeaderWidthAndMarginLeft(): number { + return this.rowHeaderWidth + this._marginLeft; + } + + get columnHeaderHeightAndMarginTop(): number { + return this.columnHeaderHeight + this._marginTop; + } + + get imageCacheMap(): ImageCacheMap { + return this._imageCacheMap; + } + + private _generateRowMatrixCache( + rowCount: number, + rowData: IObjectArrayPrimitiveType>, + defaultRowHeight: number + ): { rowTotalHeight: number; rowHeightAccumulation: number[] } { + let rowTotalHeight = 0; + const rowHeightAccumulation: number[] = []; + const data = rowData; + for (let r = 0; r < rowCount; r++) { + let rowHeight = defaultRowHeight; + + if (this.worksheet.getRowFiltered(r)) { + rowHeight = 0; + } else if (data[r] != null) { + const rowDataItem = data[r]; + if (!rowDataItem) { + continue; + } + + const { h = defaultRowHeight, ah, ia } = rowDataItem; + if ( + (ia == null || ia === BooleanNumber.TRUE) && + typeof ah === 'number' + ) { + rowHeight = ah; + } else { + rowHeight = h; + } + + if (rowDataItem.hd === BooleanNumber.TRUE) { + rowHeight = 0; + } + } + + rowTotalHeight += rowHeight; + + rowHeightAccumulation.push(rowTotalHeight); + } + + return { + rowTotalHeight, + rowHeightAccumulation, + }; + } + + /** + * Calc columnWidthAccumulation by columnData + * @param colCount + * @param columnData + * @param defaultColumnWidth + */ + private _generateColumnMatrixCache( + colCount: number, + columnData: IObjectArrayPrimitiveType>, + defaultColumnWidth: number + ): { columnTotalWidth: number; columnWidthAccumulation: number[] } { + let columnTotalWidth = 0; + const columnWidthAccumulation: number[] = []; + + const data = columnData; + + for (let c = 0; c < colCount; c++) { + let columnWidth = defaultColumnWidth; + + if (data[c] != null) { + const columnDataItem = data[c]; + + if (!columnDataItem) { + continue; + } + if (columnDataItem.w != null) { + columnWidth = columnDataItem.w; + } + + if (columnDataItem.hd === BooleanNumber.TRUE) { + columnWidth = 0; + } + } + + columnTotalWidth += columnWidth; + columnWidthAccumulation.push(columnTotalWidth); + } + + return { + columnTotalWidth, + columnWidthAccumulation, + }; + } + + intersectMergeRange(row: number, column: number): boolean { + const mergedData = this.worksheet.getMergedCell(row, column); + return Boolean(mergedData); + } + + //eslint-disable-next-line complexity + protected _getOverflowBound( + row: number, + startColumn: number, + endColumn: number, + contentWidth: number, + horizontalAlign = HorizontalAlign.LEFT + ): number { + let cumWidth = 0; + if (startColumn > endColumn) { + const columnCount = this._columnWidthAccumulation.length - 1; + for (let i = startColumn; i >= endColumn; i--) { + const column = i; + const cell = this.worksheet.getCell(row, column); + if ( + (!isCellCoverable(cell) && column !== startColumn) || + this.intersectMergeRange(row, column) + ) { + if (column === startColumn) { + return column; + } + return column + 1 > columnCount ? columnCount : column + 1; + } + const { startX, endX } = getCellWithCoordByIndexCore( + row, + column, + this.rowHeightAccumulation, + this.columnWidthAccumulation + ); + + // For center alignment, the current cell's width needs to be divided in half for comparison. + if ( + horizontalAlign === HorizontalAlign.CENTER && + column === startColumn + ) { + cumWidth += (endX - startX) / 2; + } else { + cumWidth += endX - startX; + } + + if (contentWidth < cumWidth) { + return column; + } + } + return startColumn; + } + for (let i = startColumn; i <= endColumn; i++) { + const column = i; + const cell = this.worksheet.getCell(row, column); + if ( + (!isCellCoverable(cell) && column !== startColumn) || + this.intersectMergeRange(row, column) + ) { + if (column === startColumn) { + return column; + } + + return column - 1 < 0 ? 0 : column - 1; + } + const { startX, endX } = getCellWithCoordByIndexCore( + row, + column, + this.rowHeightAccumulation, + this.columnWidthAccumulation + ); + + if ( + horizontalAlign === HorizontalAlign.CENTER && + column === startColumn + ) { + cumWidth += (endX - startX) / 2; + } else { + cumWidth += endX - startX; + } + + if (contentWidth < cumWidth) { + return column; + } + } + return endColumn; + } + + /** + * Calculate data for row col & cell position. + * This method should be called whenever a sheet is dirty. + * Update position value to this._rowHeaderWidth & this._rowHeightAccumulation & this._columnHeaderHeight & this._columnWidthAccumulation. + */ + protected _updateLayout(): void { + if (!this.dirty) { + return; + } + + const { + rowData, + columnData, + defaultRowHeight, + defaultColumnWidth, + rowCount, + columnCount, + rowHeader, + columnHeader, + } = this._worksheetData; + + const { rowTotalHeight, rowHeightAccumulation } = + this._generateRowMatrixCache(rowCount, rowData, defaultRowHeight); + + const { columnTotalWidth, columnWidthAccumulation } = + this._generateColumnMatrixCache( + columnCount, + columnData, + defaultColumnWidth + ); + + this._rowHeaderWidth = + rowHeader.hidden !== BooleanNumber.TRUE + ? this._dynamicallyUpdateRowHeaderWidth(rowHeader) + : 0; + this._columnHeaderHeight = + columnHeader.hidden !== BooleanNumber.TRUE + ? columnHeader.height + : 0; + + this._rowTotalHeight = rowTotalHeight; + this._rowHeightAccumulation = rowHeightAccumulation; + this._columnTotalWidth = columnTotalWidth; + this._columnWidthAccumulation = columnWidthAccumulation; + + this.makeDirty(false); + } + + /** + * Refresh cache after markDirty by SheetSkeletonManagerService.reCalculate() + * @param bounds + */ + calculate(): Nullable { + this._resetCache(); + this._updateLayout(); + + return this; + } + + private _dynamicallyUpdateRowHeaderWidth(rowHeader: { + width: number; + }): number { + const SIZE_BY_EACH_CHARACTER = 8; + const widthByComputation = + `${this.worksheet.getRowCount()}`.length * SIZE_BY_EACH_CHARACTER; + return Math.max(rowHeader.width, widthByComputation); + } + + protected _hasUnMergedCellInRow( + rowIndex: number, + startColumn: number, + endColumn: number + ): boolean { + const mergeData = this.worksheet.getMergeData(); + if (!mergeData) { + return false; + } + + for (let i = startColumn; i <= endColumn; i++) { + const { isMerged, isMergedMainCell } = + this.worksheet.getCellInfoInMergeData(rowIndex, i); + + if (!isMerged && !isMergedMainCell) { + return true; + } + } + + return false; + } + + /** + * expand curr range if it's intersect with merge range. + * @param range + * @returns {IRange} expanded range because merge info. + */ + expandRangeByMerge(range: IRange): IRange { + let { startRow, startColumn, endRow, endColumn } = range; + const mergeData = this._worksheetData.mergeData; + if (!mergeData) { + return { + startRow, + startColumn, + endRow, + endColumn, + }; + } + + let isSearching = true; + const searchedMarge = new ObjectMatrix(); + + // the loop breaks when there are not merged cells intersect with the current range + // NOTE: what about the performance issue? + while (isSearching) { + isSearching = false; + + for (let i = 0; i < mergeData.length; i++) { + const { + startRow: mainStartRow, + startColumn: mainStartColumn, + endRow: mainEndRow, + endColumn: mainEndColumn, + } = mergeData[i]; + + if (searchedMarge.getValue(mainStartRow, mainStartColumn)) { + continue; + } + + const rect1 = { + startColumn, + startRow, + endColumn, + endRow, + }; + + const rect2 = { + startColumn: mainStartColumn, + startRow: mainStartRow, + endColumn: mainEndColumn, + endRow: mainEndRow, + }; + + if (getIntersectRange(rect1, rect2)) { + startRow = Math.min(startRow, mainStartRow); + startColumn = Math.min(startColumn, mainStartColumn); + endRow = Math.max(endRow, mainEndRow); + endColumn = Math.max(endColumn, mainEndColumn); + searchedMarge.setValue(mainStartRow, mainStartColumn, true); + isSearching = true; + } + } + } + + return { + startRow, + startColumn, + endRow, + endColumn, + } as IRange; + } + + getColumnCount(): number { + return this._columnWidthAccumulation.length; + } + + getRowCount(): number { + return this._rowHeightAccumulation.length; + } + + /** + * New version to get merge data. + * @param row + * @param column + * @returns {ISelectionCell} The cell info with merge data + */ + // protected _getCellMergeInfo(row: number, column: number): ISelectionCell { + // return this.worksheet.getCellInfoInMergeData(row, column); + // } + + /** + * @deprecated use getNoMergeCellWithCoordByIndex instead. + * @param rowIndex + * @param columnIndex + * @param header + * @returns + */ + getNoMergeCellPositionByIndex( + rowIndex: number, + columnIndex: number, + header: boolean = true + ) { + return this.getNoMergeCellWithCoordByIndex(rowIndex, columnIndex, header); + } + + /** + * Original name: getNoMergeCellPositionByIndex + * @param rowIndex + * @param columnIndex + */ + getNoMergeCellWithCoordByIndex( + rowIndex: number, + columnIndex: number, + header: boolean = true + ): IPosition { + const { + rowHeightAccumulation, + columnWidthAccumulation, + rowHeaderWidthAndMarginLeft, + columnHeaderHeightAndMarginTop, + } = this; + + let { startY, endY, startX, endX } = getCellWithCoordByIndexCore( + rowIndex, + columnIndex, + rowHeightAccumulation, + columnWidthAccumulation + ); + + if (header) { + startY += columnHeaderHeightAndMarginTop; + endY += columnHeaderHeightAndMarginTop; + startX += rowHeaderWidthAndMarginLeft; + endX += rowHeaderWidthAndMarginLeft; + } + + return { + startY, + endY, + startX, + endX, + }; + } + + /** + * @deprecated use getNoMergeCellWithCoordByIndex(row, col, false) + * @param rowIndex + * @param columnIndex + */ + getNoMergeCellPositionByIndexWithNoHeader( + rowIndex: number, + columnIndex: number + ): IPosition { + const { rowHeightAccumulation, columnWidthAccumulation } = this; + + const { startY, endY, startX, endX } = getCellWithCoordByIndexCore( + rowIndex, + columnIndex, + rowHeightAccumulation, + columnWidthAccumulation + ); + + return { + startY, + endY, + startX, + endX, + }; + } + + /** + * + * @param offsetY scaled offset y + * @param scaleY scale y + * @param scrollXY + * @param scrollXY.x + * @param scrollXY.y + */ + getRowIndexByOffsetY( + offsetY: number, + scaleY: number, + scrollXY: { x: number; y: number }, + options?: IGetRowColByPosOptions + ): number { + const { rowHeightAccumulation } = this; + offsetY = getTransformOffsetY( + offsetY, + scaleY, + scrollXY, + this.columnHeaderHeightAndMarginTop + ); + + let row = searchArray( + rowHeightAccumulation, + offsetY, + options?.firstMatch + ); + + if (options?.closeFirst) { + // check if upper row was closer than current + if ( + Math.abs(rowHeightAccumulation[row] - offsetY) < + Math.abs(offsetY - (rowHeightAccumulation[row - 1] ?? 0)) + ) { + row = row + 1; + } + } + + return row; + } + + /** + * Get column index by offset x. + * @param offsetX scaled offset x + * @param scaleX scale x + * @param scrollXY scrollXY + * @returns column index + */ + getColumnIndexByOffsetX( + evtOffsetX: number, + scaleX: number, + scrollXY: { x: number; y: number }, + options?: IGetRowColByPosOptions + ): number { + const offsetX = getTransformOffsetX( + evtOffsetX, + scaleX, + scrollXY, + this.rowHeaderWidthAndMarginLeft + ); + const { columnWidthAccumulation } = this; + let column = searchArray( + columnWidthAccumulation, + offsetX, + options?.firstMatch + ); + + if (options?.closeFirst) { + // check if upper column was closer than current + if ( + Math.abs(columnWidthAccumulation[column] - offsetX) < + Math.abs(offsetX - (columnWidthAccumulation[column - 1] ?? 0)) + ) { + column = column + 1; + } + } + + return column; + } + + /** + * Get cell index by offset(o) + * @param offsetX position X in viewport. + * @param offsetY position Y in viewport. + * @param scaleX render scene scale x-axis, scene.getAncestorScale + * @param scaleY render scene scale y-axis, scene.getAncestorScale + * @param scrollXY render viewport scroll {x, y}, scene.getScrollXYByRelativeCoords, scene.getScrollXY + * @param scrollXY.x + * @param scrollXY.y + * @returns {row, col} + */ + getCellIndexByOffset( + offsetX: number, + offsetY: number, + scaleX: number, + scaleY: number, + scrollXY: { x: number; y: number }, + options?: IGetRowColByPosOptions + ): { row: number; column: number } { + const row = this.getRowIndexByOffsetY( + offsetY, + scaleY, + scrollXY, + options + ); + const column = this.getColumnIndexByOffsetX( + offsetX, + scaleX, + scrollXY, + options + ); + + return { + row, + column, + }; + } + + /** + * Unlike getCellWithCoordByOffset, returning data doesn't include coord. + * @param offsetX + * @param offsetY + * @param scaleX + * @param scaleY + * @param scrollXY + */ + getCellByOffset( + offsetX: number, + offsetY: number, + scaleX: number, + scaleY: number, + scrollXY: { x: number; y: number } + ): Nullable { + const cellIndex = this?.getCellIndexByOffset( + offsetX, + offsetY, + scaleX, + scaleY, + scrollXY, + { firstMatch: true } // for visible + ); + if (!cellIndex) return null; + + const selectionCell: ISelectionCell = + this.worksheet.getCellInfoInMergeData( + cellIndex.row, + cellIndex.column + ); + return selectionCell; + } + + /** + * Return cell information corresponding to the current coordinates, including the merged cell object. + * + * @param row Specified Row Coordinate + * @param column Specified Column Coordinate + */ + getCellWithCoordByIndex( + row: number, + column: number, + header: boolean = true + ): ICellWithCoord { + const { + rowHeightAccumulation, + columnWidthAccumulation, + rowHeaderWidthAndMarginLeft, + columnHeaderHeightAndMarginTop, + } = this; + + const primary: ICellWithCoord = getCellWithCoordByIndexCore( + row, + column, + rowHeightAccumulation, + columnWidthAccumulation, + this.worksheet.getCellInfoInMergeData(row, column) + ); + const { isMerged, isMergedMainCell } = primary; + let { startY, endY, startX, endX, mergeInfo } = primary; + + let offsetX = rowHeaderWidthAndMarginLeft; + let offsetY = columnHeaderHeightAndMarginTop; + if (header === false) { + offsetX = 0; + offsetY = 0; + } + + startY += offsetY; + endY += offsetY; + startX += offsetX; + endX += offsetX; + + mergeInfo.startY += offsetY; + mergeInfo.endY += offsetY; + mergeInfo.startX += offsetX; + mergeInfo.endX += offsetX; + + // mergeInfo = mergeInfoOffset(mergeInfo, rowHeaderWidthAndMarginLeft, columnHeaderHeightAndMarginTop); + + return { + actualRow: row, + actualColumn: column, + startX, + startY, + endX, + endY, + isMerged, + isMergedMainCell, + mergeInfo, + }; + } + + /** + * Get cell by pos(offsetX, offsetY). Combine getCellIndexByOffset and then getCellWithCoordByIndex. + * + * options.matchFirst true means get cell would skip all invisible cells. + * @param offsetX position X in viewport. + * @param offsetY position Y in viewport. + * @param scaleX render scene scale x-axis, scene.getAncestorScale + * @param scaleY render scene scale y-axis, scene.getAncestorScale + * @param scrollXY render viewportScroll {x, y} + * @param options {IGetRowColByPosOptions} + * @returns {ICellWithCoord} Selection data with coordinates + */ + getCellWithCoordByOffset( + offsetX: number, + offsetY: number, + scaleX: number, + scaleY: number, + scrollXY: { x: number; y: number }, + options?: IGetRowColByPosOptions + ): ICellWithCoord { + const { row, column } = this.getCellIndexByOffset( + offsetX, + offsetY, + scaleX, + scaleY, + scrollXY, + options + ); + + return this.getCellWithCoordByIndex(row, column); + } + + /** + * Original name: getOffsetByPositionX + * @param column + * @returns + */ + getOffsetByColumn(column: number): number { + const { columnWidthAccumulation, rowHeaderWidthAndMarginLeft } = this; + + const lastColumnIndex = columnWidthAccumulation.length - 1; + const columnValue = columnWidthAccumulation[column]; + if (columnValue != null) { + return columnValue + rowHeaderWidthAndMarginLeft; + } + + if (column < 0) { + return rowHeaderWidthAndMarginLeft; + } + + return ( + columnWidthAccumulation[lastColumnIndex] + + rowHeaderWidthAndMarginLeft + ); + } + + /** + * Original name: getOffsetByPositionY + * @param row + */ + getOffsetByRow(row: number): number { + const { rowHeightAccumulation, columnHeaderHeightAndMarginTop } = this; + const lastRowIndex = rowHeightAccumulation.length - 1; + const rowValue = rowHeightAccumulation[row]; + if (rowValue != null) { + return rowValue + columnHeaderHeightAndMarginTop; + } + + if (row < 0) { + return columnHeaderHeightAndMarginTop; + } + + return ( + rowHeightAccumulation[lastRowIndex] + columnHeaderHeightAndMarginTop + ); + } + + /** + * Original name: getDecomposedOffset + * @param offsetX + * @param offsetY + */ + getOffsetRelativeToRowCol( + offsetX: number, + offsetY: number + ): { + row: number; + column: number; + columnOffset: number; + rowOffset: number; + } { + const column = searchArray(this.columnWidthAccumulation, offsetX); + let columnOffset = 0; + if (column === 0) { + columnOffset = offsetX; + } else { + columnOffset = offsetX - this._columnWidthAccumulation[column - 1]; + } + + const row = searchArray(this.rowHeightAccumulation, offsetY); + let rowOffset = 0; + if (row === 0) { + rowOffset = offsetY; + } else { + rowOffset = offsetY - this._rowHeightAccumulation[row - 1]; + } + return { + row, + column, + columnOffset, + rowOffset, + }; + } + + protected _updateConfigAndGetDocumentModel( + documentData: IDocumentData, + horizontalAlign: HorizontalAlign, + paddingData: IPaddingData, + renderConfig?: IDocumentRenderConfig + ): Nullable { + if (!renderConfig) { + return; + } + + if (!documentData.body?.dataStream) { + return; + } + + if (!documentData.documentStyle) { + documentData.documentStyle = {}; + } + + documentData.documentStyle.marginTop = paddingData.t ?? 0; + documentData.documentStyle.marginBottom = paddingData.b ?? 2; + documentData.documentStyle.marginLeft = paddingData.l ?? 2; + documentData.documentStyle.marginRight = paddingData.r ?? 2; + + // Fix https://github.com/dream-num/univer/issues/1586 + documentData.documentStyle.pageSize = { + width: Number.POSITIVE_INFINITY, + height: Number.POSITIVE_INFINITY, + }; + + documentData.documentStyle.renderConfig = { + ...documentData.documentStyle.renderConfig, + ...renderConfig, + }; + + const paragraphs = documentData.body.paragraphs || []; + + for (const paragraph of paragraphs) { + if (!paragraph.paragraphStyle) { + paragraph.paragraphStyle = {}; + } + + paragraph.paragraphStyle.horizontalAlign = horizontalAlign; + } + + return new DocumentDataModel(documentData); + } + + override dispose(): void { + super.dispose(); + + this._rowHeightAccumulation = []; + this._columnWidthAccumulation = []; + this._rowTotalHeight = 0; + this._columnTotalWidth = 0; + this._rowHeaderWidth = 0; + this._columnHeaderHeight = 0; + + this._worksheetData = null as unknown as IWorksheetData; + this._cellData = null as unknown as ObjectMatrix>; + this._styles = null as unknown as Styles; + } +} + +/** + * Only the coordinates of the corresponding cells in rows and columns are considered, without taking into account the merged data. + * + * Original name: getCellPositionByIndexSimple + * @param row + * @param column + * @param rowHeightAccumulation + * @param columnWidthAccumulation + */ +export function getCellCoordByIndexSimple( + row: number, + column: number, + rowHeightAccumulation: number[], + columnWidthAccumulation: number[] +): IPosition { + const startRow = row - 1; + const startColumn = column - 1; + + const startY = rowHeightAccumulation[startRow] || 0; + let endY = rowHeightAccumulation[row]; + + if (endY == null) { + endY = rowHeightAccumulation[rowHeightAccumulation.length - 1]; + } + + const startX = columnWidthAccumulation[startColumn] || 0; + let endX = columnWidthAccumulation[column]; + + if (endX == null) { + endX = columnWidthAccumulation[columnWidthAccumulation.length - 1]; + } + + return { + startY, + endY, + startX, + endX, + }; +} + +/** + * @description Get the cell position information of the specified row and column, including the position of the cell and the merge info + * @param {number} row The row index of the cell + * @param {number} column The column index of the cell + * @param {number[]} rowHeightAccumulation The accumulated height of each row + * @param {number[]} columnWidthAccumulation The accumulated width of each column + * @param {ICellInfo} mergeData The merge information of the cell + * @returns {ICellWithCoord} The cell position information of the specified row and column, including the position information of the cell and the merge information of the cell + */ +// eslint-disable-next-line max-lines-per-function +export function getCellWithCoordByIndexCore( + row: number, + column: number, + rowHeightAccumulation: number[], + columnWidthAccumulation: number[], + mergeDataInfo: Nullable +): ICellWithCoord { + row = Tools.clamp(row, 0, rowHeightAccumulation.length - 1); + column = Tools.clamp(column, 0, columnWidthAccumulation.length - 1); + // eslint-disable-next-line prefer-const + let { startY, endY, startX, endX } = getCellCoordByIndexSimple( + row, + column, + rowHeightAccumulation, + columnWidthAccumulation + ); + + if (!mergeDataInfo) { + return { + startY, + endY, + startX, + endX, + isMerged: false, + isMergedMainCell: false, + actualRow: row, + actualColumn: column, + mergeInfo: { + startY, + endY, + startX, + endX, + startRow: row, + startColumn: column, + endRow: row, + endColumn: column, + }, + }; + } + + const { + isMerged, + isMergedMainCell, + startRow, + startColumn, + endRow, + endColumn, + } = mergeDataInfo; + + let mergeInfo = { + startRow, + startColumn, + endRow, + endColumn, + startY, + endY, + startX, + endX, + }; + + const rowAccumulationCount = rowHeightAccumulation.length - 1; + const columnAccumulationCount = columnWidthAccumulation.length - 1; + + if (isMerged && startRow !== -1 && startColumn !== -1) { + const mergeStartY = rowHeightAccumulation[startRow - 1] || 0; + const mergeEndY = + rowHeightAccumulation[endRow] || + rowHeightAccumulation[rowAccumulationCount]; + + const mergeStartX = columnWidthAccumulation[startColumn - 1] || 0; + const mergeEndX = + columnWidthAccumulation[endColumn] || + columnWidthAccumulation[columnAccumulationCount]; + mergeInfo = { + ...mergeInfo, + startY: mergeStartY, + endY: mergeEndY, + startX: mergeStartX, + endX: mergeEndX, + }; + } else if (!isMerged && endRow !== -1 && endColumn !== -1) { + const mergeEndY = + rowHeightAccumulation[endRow] || + rowHeightAccumulation[rowAccumulationCount]; + const mergeEndX = + columnWidthAccumulation[endColumn] || + columnWidthAccumulation[columnAccumulationCount]; + + mergeInfo = { + ...mergeInfo, + startY, + endY: mergeEndY, + startX, + endX: mergeEndX, + }; + } + + return { + isMerged, + isMergedMainCell, + actualRow: row, + actualColumn: column, + startY, + endY, + startX, + endX, + mergeInfo, + } as ICellWithCoord; +} + +/** + * Get x in sheet coordinate. Already handled scrolling and zooming. + * @param offsetX Offset value from PointerEvent in canvas element. + * @param scaleX from scene.getAncestorScale + * @param scrollXY Scroll value of viewport + * @returns {number} x in sheet coordinate. + */ +export function getTransformOffsetX( + offsetX: number, + scaleX: number, + scrollXY: { x: number; y: number }, + rowHeaderWidth: number +): number { + const { x: scrollX } = scrollXY; + + // so we should map physical positions to ideal positions + const afterOffsetX = offsetX / scaleX + scrollX - rowHeaderWidth; //this.rowHeaderWidthAndMarginLeft; + + return afterOffsetX; +} + +/** + * @param offsetY + * @param scaleY + * @param scrollXY + */ +export function getTransformOffsetY( + offsetY: number, + scaleY: number, + scrollXY: { x: number; y: number }, + columnHeight: number +): number { + const { y: scrollY } = scrollXY; + + // these values are not affected by zooming (ideal positions) + offsetY = offsetY / scaleY + scrollY - columnHeight; //this.columnHeaderHeightAndMarginTop; + + return offsetY; +} diff --git a/packages/core/src/sheets/util.ts b/packages/core/src/sheets/util.ts index 9258b8d3d91..ecb77b00e13 100644 --- a/packages/core/src/sheets/util.ts +++ b/packages/core/src/sheets/util.ts @@ -24,6 +24,11 @@ import { Rectangle } from '../shared'; import { type CellValueType, HorizontalAlign, type TextDirection, VerticalAlign, WrapStrategy } from '../types/enum'; import { CustomRangeType, type IDocumentData, type IPaddingData, type IStyleBase, type IStyleData, type ITextRotation, type ITextStyle } from '../types/interfaces'; +export interface IFontLocale { + fontList: string[]; + defaultFontSize: number; +} + export const isRangesEqual = (oldRanges: IRange[], ranges: IRange[]): boolean => { return ranges.length === oldRanges.length && !oldRanges.some((oldRange) => ranges.some((range) => !Rectangle.equals(range, oldRange))); }; diff --git a/packages/engine-render/src/components/skeleton.ts b/packages/core/src/skeleton.ts similarity index 90% rename from packages/engine-render/src/components/skeleton.ts rename to packages/core/src/skeleton.ts index c959cc3c85d..7b5479cf353 100644 --- a/packages/engine-render/src/components/skeleton.ts +++ b/packages/core/src/skeleton.ts @@ -14,8 +14,10 @@ * limitations under the License. */ -import type { IFontLocale } from '../basics/interfaces'; -import { Disposable, Inject, LocaleService } from '@univerjs/core'; +import type { IFontLocale } from './sheets/util'; +import { Inject } from '@wendellhu/redi'; +import { LocaleService } from './services/locale/locale.service'; +import { Disposable } from './shared/lifecycle'; export class Skeleton extends Disposable { private _fontLocale!: IFontLocale; diff --git a/packages/engine-render/src/basics/interfaces.ts b/packages/engine-render/src/basics/interfaces.ts index d85dfc23400..acdc27e6380 100644 --- a/packages/engine-render/src/basics/interfaces.ts +++ b/packages/engine-render/src/basics/interfaces.ts @@ -58,6 +58,12 @@ export interface IRect extends ISize, IOffset { points: Vector2[]; } +/** + * width + * height + * scaleX + * scaleY + */ export interface ISceneTransformState extends ISize, IScale {} export enum TRANSFORM_CHANGE_OBSERVABLE_TYPE { diff --git a/packages/engine-render/src/basics/tools.ts b/packages/engine-render/src/basics/tools.ts index 912ebfd8be4..bcbb2a52230 100644 --- a/packages/engine-render/src/basics/tools.ts +++ b/packages/engine-render/src/basics/tools.ts @@ -528,7 +528,7 @@ export function getCellPositionByIndex( * @param {number} column The column index of the cell * @param {number[]} rowHeightAccumulation The accumulated height of each row * @param {number[]} columnWidthAccumulation The accumulated width of each column - * @param {ICellInfo} mergeData The merge information of the cell + * @param {ICellInfo} mergeDataInfo The merge information of the cell * @returns {ICellWithCoord} The cell position information of the specified row and column, including the position information of the cell and the merge information of the cell */ export function getCellWithCoordByIndexCore( @@ -706,13 +706,13 @@ export function pixelToPt(px: number) { } /** - * 当前单元格在任意一个 viewRanges 中 + * Is cell in view ranges. * @param ranges * @param rowIndex * @param colIndex - * @returns + * @returns boolean */ -export function inViewRanges(ranges: IRange[], rowIndex: number, colIndex: number) { +export function inViewRanges(ranges: IRange[], rowIndex: number, colIndex: number): boolean { for (const range of ranges) { if (rowIndex >= range.startRow && rowIndex <= range.endRow && colIndex >= range.startColumn && colIndex <= range.endColumn) { @@ -722,36 +722,6 @@ export function inViewRanges(ranges: IRange[], rowIndex: number, colIndex: numbe return false; } -/** - * 在非下方区域中 - * @param ranges - * @param rowIndex - */ -export function inCurrentAndAboveViewRanges(ranges: IRange[], rowIndex: number) { - for (const range of ranges) { - if (rowIndex > range.endRow) { - return false; - } - } - return true; -} - -/** - * row 在任意一个 Range 中 - * @param ranges - * @param rowIndex - */ -export function inRowViewRanges(ranges: IRange[], rowIndex: number) { - let flag = false; - for (const range of ranges) { - if (rowIndex >= range.startRow && rowIndex <= range.endRow) { - flag = true; - break; - } - } - return flag; -} - /** * If there is an intersection in ranges to the mainRanges, extend it to the first set of ranges. * @param {IRange[]} mainRanges target ranges @@ -778,7 +748,9 @@ export function clampRange(range: IRange, maxRow: number, maxColumn: number) { }; } -// Get system highlight color in rgb format. +/** + * Get system highlight color in rgb format. + */ export function getSystemHighlightColor() { const hiddenEle = document.createElement('div'); hiddenEle.style.width = '0'; diff --git a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts index 90603ef8b0c..a836c290642 100644 --- a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts +++ b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts @@ -25,10 +25,9 @@ import type { IDocsConfig, INodeInfo, INodePosition, INodeSearch } from '../../. import type { IViewportInfo, Vector2 } from '../../../basics/vector2'; import type { DocumentViewModel } from '../view-model/document-view-model'; import type { ILayoutContext } from './tools'; -import { PRESET_LIST_TYPE, SectionType } from '@univerjs/core'; +import { PRESET_LIST_TYPE, SectionType, Skeleton } from '@univerjs/core'; import { Subject } from 'rxjs'; import { DocumentSkeletonPageType, GlyphType, LineType, PageLayoutType } from '../../../basics/i-document-skeleton-cached'; -import { Skeleton } from '../../skeleton'; import { Liquid } from '../liquid'; import { DocumentEditArea } from '../view-model/document-view-model'; import { dealWithSection } from './block/section'; diff --git a/packages/engine-render/src/components/index.ts b/packages/engine-render/src/components/index.ts index 0e4c91ef075..b2490abde2b 100644 --- a/packages/engine-render/src/components/index.ts +++ b/packages/engine-render/src/components/index.ts @@ -17,6 +17,5 @@ export * from './component'; export * from './extension'; export * from './sheets'; -export { RENDER_RAW_FORMULA_KEY } from './sheets/sheet-skeleton'; -export * from './skeleton'; +export { RENDER_RAW_FORMULA_KEY } from './sheets/sheet.render-skeleton'; export * from './slides'; diff --git a/packages/engine-render/src/components/sheets/column-header.ts b/packages/engine-render/src/components/sheets/column-header.ts index b351b956b6d..65c7297ba13 100644 --- a/packages/engine-render/src/components/sheets/column-header.ts +++ b/packages/engine-render/src/components/sheets/column-header.ts @@ -20,7 +20,7 @@ import type { Nullable } from '@univerjs/core'; import type { IViewportInfo, Vector2 } from '../../basics/vector2'; import type { UniverRenderingContext } from '../../context'; import type { ColumnHeaderLayout, IColumnsHeaderCfgParam } from './extensions/column-header-layout'; -import type { SpreadsheetSkeleton } from './sheet-skeleton'; +import type { SpreadsheetSkeleton } from './sheet.render-skeleton'; import { SheetColumnHeaderExtensionRegistry } from '../extension'; import { SpreadsheetHeader } from './sheet-component'; diff --git a/packages/engine-render/src/components/sheets/extensions/background.ts b/packages/engine-render/src/components/sheets/extensions/background.ts index db08e1af7bc..4b3efb904de 100644 --- a/packages/engine-render/src/components/sheets/extensions/background.ts +++ b/packages/engine-render/src/components/sheets/extensions/background.ts @@ -17,7 +17,7 @@ import type { ICellWithCoord, IRange, IScale, ObjectMatrix } from '@univerjs/core'; import type { UniverRenderingContext } from '../../../context'; import type { IDrawInfo } from '../../extension'; -import type { SpreadsheetSkeleton } from '../sheet-skeleton'; +import type { SpreadsheetSkeleton } from '../sheet.render-skeleton'; import type { Spreadsheet } from '../spreadsheet'; import { Range } from '@univerjs/core'; import { fixLineWidthByScale, getColor, inViewRanges } from '../../../basics/tools'; diff --git a/packages/engine-render/src/components/sheets/extensions/border.ts b/packages/engine-render/src/components/sheets/extensions/border.ts index d7280f7e22f..f84e043eee0 100644 --- a/packages/engine-render/src/components/sheets/extensions/border.ts +++ b/packages/engine-render/src/components/sheets/extensions/border.ts @@ -19,7 +19,7 @@ import type { UniverRenderingContext } from '../../../context'; import type { IDrawInfo } from '../../extension'; import type { BorderCache, BorderCacheItem } from '../interfaces'; -import type { SpreadsheetSkeleton } from '../sheet-skeleton'; +import type { SpreadsheetSkeleton } from '../sheet.render-skeleton'; import { BorderStyleTypes, Range } from '@univerjs/core'; import { BORDER_TYPE as BORDER_LTRB, COLOR_BLACK_RGB, FIX_ONE_PIXEL_BLUR_OFFSET } from '../../../basics/const'; import { drawDiagonalLineByBorderType, drawLineByBorderType, getLineWidth, setLineType } from '../../../basics/draw'; diff --git a/packages/engine-render/src/components/sheets/extensions/column-header-layout.ts b/packages/engine-render/src/components/sheets/extensions/column-header-layout.ts index bae1a2b7b2a..08290110a6e 100644 --- a/packages/engine-render/src/components/sheets/extensions/column-header-layout.ts +++ b/packages/engine-render/src/components/sheets/extensions/column-header-layout.ts @@ -18,7 +18,7 @@ import type { IScale } from '@univerjs/core'; import type { UniverRenderingContext } from '../../../context'; import type { IAColumnCfg, IAColumnCfgObj, IColumnStyleCfg } from '../interfaces'; -import type { SpreadsheetSkeleton } from '../sheet-skeleton'; +import type { SpreadsheetSkeleton } from '../sheet.render-skeleton'; import { numberToABC } from '@univerjs/core'; import { DEFAULT_FONTFACE_PLANE, FIX_ONE_PIXEL_BLUR_OFFSET, MIDDLE_CELL_POS_MAGIC_NUMBER } from '../../../basics/const'; import { getColor } from '../../../basics/tools'; diff --git a/packages/engine-render/src/components/sheets/extensions/custom.ts b/packages/engine-render/src/components/sheets/extensions/custom.ts index 5732ddd4bcb..765c2abd44f 100644 --- a/packages/engine-render/src/components/sheets/extensions/custom.ts +++ b/packages/engine-render/src/components/sheets/extensions/custom.ts @@ -16,7 +16,7 @@ import type { IRange, IScale } from '@univerjs/core'; import type { UniverRenderingContext } from '../../../context'; -import type { SpreadsheetSkeleton } from '../sheet-skeleton'; +import type { SpreadsheetSkeleton } from '../sheet.render-skeleton'; import { Range, sortRules } from '@univerjs/core'; import { SpreadsheetExtensionRegistry } from '../../extension'; import { SheetExtension } from './sheet-extension'; @@ -83,7 +83,7 @@ export class Custom extends SheetExtension { const renderInfo = { data: cellData, - style: skeleton.getsStyles().getStyleByCell(cellData), + style: skeleton.getStyles().getStyleByCell(cellData), primaryWithCoord, subUnitId, row, diff --git a/packages/engine-render/src/components/sheets/extensions/font.ts b/packages/engine-render/src/components/sheets/extensions/font.ts index 18233cc7cd1..feab656cd46 100644 --- a/packages/engine-render/src/components/sheets/extensions/font.ts +++ b/packages/engine-render/src/components/sheets/extensions/font.ts @@ -23,14 +23,13 @@ import type { Documents } from '../../docs/document'; import type { IDrawInfo } from '../../extension'; import type { IFontCacheItem } from '../interfaces'; import type { SheetComponent } from '../sheet-component'; -import type { SpreadsheetSkeleton } from '../sheet-skeleton'; import { HorizontalAlign, Range, VerticalAlign, WrapStrategy } from '@univerjs/core'; import { FIX_ONE_PIXEL_BLUR_OFFSET } from '../../../basics'; import { VERTICAL_ROTATE_ANGLE } from '../../../basics/text-rotation'; import { clampRange, inViewRanges } from '../../../basics/tools'; import { SpreadsheetExtensionRegistry } from '../../extension'; import { EXPAND_SIZE_FOR_RENDER_OVERFLOW, FONT_EXTENSION_Z_INDEX } from '../constants'; -import { getDocsSkeletonPageSize } from '../sheet-skeleton'; +import { getDocsSkeletonPageSize, type SpreadsheetSkeleton } from '../sheet.render-skeleton'; import { SheetExtension } from './sheet-extension'; const UNIQUE_KEY = 'DefaultFontExtension'; @@ -139,6 +138,7 @@ export class Font extends SheetExtension { Range.foreach(range, (row, col) => { const index = spreadsheetSkeleton.worksheet.getSpanModel().getMergeDataIndex(row, col); + // put all merged cells to another pass to render. -1 means not merged. if (index !== -1) { return; } diff --git a/packages/engine-render/src/components/sheets/extensions/marker.ts b/packages/engine-render/src/components/sheets/extensions/marker.ts index 36e92240a46..dea84a9b530 100644 --- a/packages/engine-render/src/components/sheets/extensions/marker.ts +++ b/packages/engine-render/src/components/sheets/extensions/marker.ts @@ -16,7 +16,7 @@ import type { IRange, IScale } from '@univerjs/core'; import type { UniverRenderingContext } from '../../../context'; -import type { SpreadsheetSkeleton } from '../sheet-skeleton'; +import type { SpreadsheetSkeleton } from '../sheet.render-skeleton'; import { Range } from '@univerjs/core'; import { SpreadsheetExtensionRegistry } from '../../extension'; import { SheetExtension } from './sheet-extension'; diff --git a/packages/engine-render/src/components/sheets/extensions/row-header-layout.ts b/packages/engine-render/src/components/sheets/extensions/row-header-layout.ts index bcc594a3c60..cd8c9b61dd4 100644 --- a/packages/engine-render/src/components/sheets/extensions/row-header-layout.ts +++ b/packages/engine-render/src/components/sheets/extensions/row-header-layout.ts @@ -18,7 +18,7 @@ import type { IScale } from '@univerjs/core'; import type { UniverRenderingContext } from '../../../context'; import type { IARowCfg, IARowCfgObj, IColumnStyleCfg, IRowStyleCfg } from '../interfaces'; -import type { SpreadsheetSkeleton } from '../sheet-skeleton'; +import type { SpreadsheetSkeleton } from '../sheet.render-skeleton'; import { DEFAULT_FONTFACE_PLANE, FIX_ONE_PIXEL_BLUR_OFFSET, MIDDLE_CELL_POS_MAGIC_NUMBER } from '../../../basics/const'; import { getColor } from '../../../basics/tools'; import { SheetRowHeaderExtensionRegistry } from '../../extension'; diff --git a/packages/engine-render/src/components/sheets/extensions/sheet-extension.ts b/packages/engine-render/src/components/sheets/extensions/sheet-extension.ts index 11410d2c654..9ba399d6525 100644 --- a/packages/engine-render/src/components/sheets/extensions/sheet-extension.ts +++ b/packages/engine-render/src/components/sheets/extensions/sheet-extension.ts @@ -15,7 +15,7 @@ */ import type { IRange } from '@univerjs/core'; -import type { SpreadsheetSkeleton } from '../sheet-skeleton'; +import type { SpreadsheetSkeleton } from '../sheet.render-skeleton'; import { Rectangle } from '@univerjs/core'; import { ComponentExtension } from '../../extension'; diff --git a/packages/engine-render/src/components/sheets/index.ts b/packages/engine-render/src/components/sheets/index.ts index d0f83cfbe9b..9603bde0949 100644 --- a/packages/engine-render/src/components/sheets/index.ts +++ b/packages/engine-render/src/components/sheets/index.ts @@ -20,5 +20,5 @@ export * from './extensions'; export * from './interfaces'; export * from './row-header'; export * from './sheet-component'; -export * from './sheet-skeleton'; +export * from './sheet.render-skeleton'; export * from './spreadsheet'; diff --git a/packages/engine-render/src/components/sheets/row-header.ts b/packages/engine-render/src/components/sheets/row-header.ts index eab3eef615a..e4b4ed0fb85 100644 --- a/packages/engine-render/src/components/sheets/row-header.ts +++ b/packages/engine-render/src/components/sheets/row-header.ts @@ -18,7 +18,7 @@ import type { Nullable } from '@univerjs/core'; import type { IViewportInfo, Vector2 } from '../../basics/vector2'; import type { UniverRenderingContext } from '../../context'; import type { IRowsHeaderCfgParam, RowHeaderLayout } from './extensions/row-header-layout'; -import type { SpreadsheetSkeleton } from './sheet-skeleton'; +import type { SpreadsheetSkeleton } from './sheet.render-skeleton'; import { SheetRowHeaderExtensionRegistry } from '../extension'; import { SpreadsheetHeader } from './sheet-component'; diff --git a/packages/engine-render/src/components/sheets/sheet-component.ts b/packages/engine-render/src/components/sheets/sheet-component.ts index 2cb76d98286..b899d3b3248 100644 --- a/packages/engine-render/src/components/sheets/sheet-component.ts +++ b/packages/engine-render/src/components/sheets/sheet-component.ts @@ -19,7 +19,7 @@ import type { IRange, Nullable } from '@univerjs/core'; import type { IViewportInfo, Vector2 } from '../../basics/vector2'; import type { UniverRenderingContext } from '../../context'; import type { SHEET_EXTENSION_TYPE } from './extensions/sheet-extension'; -import type { SpreadsheetSkeleton } from './sheet-skeleton'; +import type { SpreadsheetSkeleton } from './sheet.render-skeleton'; import { RENDER_CLASS_TYPE } from '../../basics/const'; import { RenderComponent } from '../component'; diff --git a/packages/engine-render/src/components/sheets/sheet-skeleton.ts b/packages/engine-render/src/components/sheets/sheet.render-skeleton.ts similarity index 64% rename from packages/engine-render/src/components/sheets/sheet-skeleton.ts rename to packages/engine-render/src/components/sheets/sheet.render-skeleton.ts index 136d42691a3..cf5524c2a00 100644 --- a/packages/engine-render/src/components/sheets/sheet-skeleton.ts +++ b/packages/engine-render/src/components/sheets/sheet.render-skeleton.ts @@ -16,56 +16,51 @@ import type { BorderStyleTypes, + DocumentDataModel, IBorderStyleData, ICellData, ICellDataForSheetInterceptor, ICellInfo, ICellWithCoord, IColAutoWidthInfo, - IColumnData, IColumnRange, - IDocumentData, - IDocumentRenderConfig, - IObjectArrayPrimitiveType, + IGetRowColByPosOptions, IPaddingData, - IPosition, IRange, IRowAutoHeightInfo, - IRowData, IRowRange, - ISelectionCell, ISize, IStyleData, ITextRotation, - IWorksheetData, Nullable, Styles, VerticalAlign, - Worksheet } from '@univerjs/core'; + + Worksheet, +} from '@univerjs/core'; import type { IDocumentSkeletonColumn } from '../../basics/i-document-skeleton-cached'; -import type { IBoundRectNoAngle, IPoint, IViewportInfo } from '../../basics/vector2'; +import type { ITransformChangeState } from '../../basics/interfaces'; +import type { IBoundRectNoAngle, IViewportInfo } from '../../basics/vector2'; +import type { Scene } from '../../scene'; import { addLinkToDocumentModel, BooleanNumber, CellValueType, composeStyles, DEFAULT_STYLES, - DocumentDataModel, extractPureTextFromCell, getColorStyle, HorizontalAlign, IConfigService, IContextService, - ImageCacheMap, Inject, Injector, - IS_ROW_STYLE_PRECEDE_COLUMN_STYLE, - isCellCoverable, isNullCell, isWhiteColor, LocaleService, ObjectMatrix, searchArray, + SheetSkeleton, Tools, WrapStrategy, } from '@univerjs/core'; @@ -75,74 +70,15 @@ import { getRotateOffsetAndFarthestHypotenuse } from '../../basics/draw'; import { convertTextRotation, VERTICAL_ROTATE_ANGLE } from '../../basics/text-rotation'; import { degToRad, - getCellPositionByIndex, - getCellWithCoordByIndexCore, getFontStyleString, - isRectIntersect, } from '../../basics/tools'; import { DocumentSkeleton } from '../docs/layout/doc-skeleton'; import { columnIterator } from '../docs/layout/tools'; import { DocumentViewModel } from '../docs/view-model/document-view-model'; -import { Skeleton } from '../skeleton'; import { EXPAND_SIZE_FOR_RENDER_OVERFLOW, MEASURE_EXTENT, MEASURE_EXTENT_FOR_PARAGRAPH } from './constants'; import { type BorderCache, type IFontCacheItem, type IStylesCache, SHEET_VIEWPORT_KEY } from './interfaces'; import { createDocumentModelWithStyle, extractOtherStyle, getFontFormat } from './util'; -/** - * Obtain the height and width of a cell's text, taking into account scenarios with rotated text. - * @param documentSkeleton Data of the document's ViewModel - * @param angleInDegree The rotation angle of an Excel cell, it's **degree** - */ -export function getDocsSkeletonPageSize(documentSkeleton: DocumentSkeleton, angleInDegree: number = 0): Nullable> { - const skeletonData = documentSkeleton?.getSkeletonData(); - const angle = degToRad(angleInDegree); - - if (!skeletonData) { - return null; - } - const { pages } = skeletonData; - const lastPage = pages[pages.length - 1]; - const { width, height } = lastPage; - - if (angle === 0) { - return { width, height }; - } - - if (Math.abs(angle) === Math.PI / 2) { - return { width: height, height: width }; - } - - let allRotatedWidth = 0; - let allRotatedHeight = 0; - - const widthArray: Array<{ rotatedWidth: number; spaceWidth: number }> = []; - - columnIterator([lastPage], (column: IDocumentSkeletonColumn) => { - const { lines, width: columnWidth, spaceWidth } = column; - - const { rotatedHeight, rotatedWidth } = getRotateOffsetAndFarthestHypotenuse(lines, columnWidth, angle); - - allRotatedHeight += rotatedHeight; - - widthArray.push({ rotatedWidth, spaceWidth }); - }); - - const widthCount = widthArray.length; - - for (let i = 0; i < widthCount; i++) { - const { rotatedWidth } = widthArray[i]; - - if (i === 0) { - allRotatedWidth += rotatedWidth; - } - } - - return { - width: allRotatedWidth, - height: allRotatedHeight, - }; -} - interface ICellDocumentModelOption { isDeepClone?: boolean; displayRawFormula?: boolean; @@ -181,17 +117,6 @@ export interface ICacheItem { border: boolean; } -export interface IGetRowColByPosOptions { - closeFirst?: boolean; - - /** - * For searchArray(rowHeightAccumulation) & searchArray(colWidthAccumulation) - * true means return first matched index in matched sequence. - * default return last index in matched sequence. - */ - firstMatch?: boolean; -} - export interface IGetPosByRowColOptions { closeFirst?: boolean; @@ -202,15 +127,7 @@ export interface IGetPosByRowColOptions { firstMatch?: boolean; } -export class SpreadsheetSkeleton extends Skeleton { - private _rowHeightAccumulation: number[] = []; - private _columnWidthAccumulation: number[] = []; - - private _rowTotalHeight = 0; - private _columnTotalWidth = 0; - private _rowHeaderWidth = 0; - private _columnHeaderHeight = 0; - +export class SpreadsheetSkeleton extends SheetSkeleton { /** * Range viewBounds. only update by viewBounds. * It would change multiple times in one frame if there is multiple viewport (after freeze row&col) @@ -242,68 +159,50 @@ export class SpreadsheetSkeleton extends Skeleton { private _showGridlines: BooleanNumber = BooleanNumber.TRUE; private _gridlinesColor: string | undefined = undefined; - private _marginTop: number = 0; - private _marginLeft: number = 0; - - private _imageCacheMap: ImageCacheMap; - /** - * Whether the row style precedes the column style. - */ - private _isRowStylePrecedeColumnStyle = false; - - private _renderRawFormula = false; - - /** - * @deprecated avoid use `IWorksheetData` directly, use API provided by `Worksheet`, otherwise - * `ViewModel` will be not working. - */ - private _worksheetData: IWorksheetData; - private _cellData: ObjectMatrix>; + private _scene: Nullable = null; constructor( - readonly worksheet: Worksheet, - private _styles: Styles, + worksheet: Worksheet, + _styles: Styles, @Inject(LocaleService) _localeService: LocaleService, - @IContextService private readonly _contextService: IContextService, - @IConfigService private readonly _configService: IConfigService, - @Inject(Injector) private _injector: Injector + @IContextService _contextService: IContextService, + @IConfigService _configService: IConfigService, + @Inject(Injector) _injector: Injector ) { - super(_localeService); - this._worksheetData = this.worksheet.getConfig(); - this._cellData = this.worksheet.getCellMatrix(); - - this._imageCacheMap = new ImageCacheMap(this._injector); - this._updateLayout(); - this._initContextListener(); - this._isRowStylePrecedeColumnStyle = this._configService.getConfig(IS_ROW_STYLE_PRECEDE_COLUMN_STYLE) ?? false; - } - - get rowHeightAccumulation(): number[] { - return this._rowHeightAccumulation; + super(worksheet, _styles, _localeService, _contextService, _configService, _injector); } - get rowTotalHeight(): number { - return this._rowTotalHeight; - } - - get columnWidthAccumulation(): number[] { - return this._columnWidthAccumulation; + override init() { + this._updateLayout(); + this.disposeWithMe( + this._contextService.subscribeContextValue$(RENDER_RAW_FORMULA_KEY).pipe( + startWith(false), + distinctUntilChanged() + ).subscribe((renderRaw) => { + this._renderRawFormula = renderRaw; + this._resetCache(); + this.makeDirty(true); + }) + ); } - get columnTotalWidth(): number { - return this._columnTotalWidth; - } + setScene(scene: Scene) { + this._scene = scene; - get rowHeaderWidth(): number { - return this._rowHeaderWidth; + this._scene.onTransformChange$.subscribeEvent((param: ITransformChangeState) => { + this.setScale(param.value.scaleX || 1, param.value.scaleY); + }); } - get columnHeaderHeight(): number { - return this._columnHeaderHeight; - } + override _updateLayout() { + const { + showGridlines, + gridlinesColor, + } = this._worksheetData; - get imageCacheMap(): ImageCacheMap { - return this._imageCacheMap; + this._showGridlines = showGridlines; + this._gridlinesColor = gridlinesColor; + super._updateLayout(); // make dirty false } /** @@ -322,10 +221,6 @@ export class SpreadsheetSkeleton extends Skeleton { return this._cacheRangeMap.get(viewportKey); } - // get dataMergeCache(): IRange[] { - // return this._dataMergeCache; - // } - get stylesCache(): IStylesCache { return this._stylesCache; } @@ -342,34 +237,15 @@ export class SpreadsheetSkeleton extends Skeleton { return this._gridlinesColor; } - get mergeData(): IRange[] { - return this._worksheetData.mergeData; - } - - get rowHeaderWidthAndMarginLeft(): number { - return this.rowHeaderWidth + this._marginLeft; - } - - get columnHeaderHeightAndMarginTop(): number { - return this.columnHeaderHeight + this._marginTop; - } - override dispose(): void { super.dispose(); - this._rowHeightAccumulation = []; - this._columnWidthAccumulation = []; - this._rowTotalHeight = 0; - this._columnTotalWidth = 0; - this._rowHeaderWidth = 0; - this._columnHeaderHeight = 0; this._drawingRange = { startRow: -1, endRow: -1, startColumn: -1, endColumn: -1, }; - // this._dataMergeCache = []; this._stylesCache = { background: {}, backgroundPositions: new ObjectMatrix(), @@ -380,52 +256,19 @@ export class SpreadsheetSkeleton extends Skeleton { this._handleBgMatrix.reset(); this._handleBorderMatrix.reset(); this._overflowCache.reset(); - - this._worksheetData = null as unknown as IWorksheetData; - this._cellData = null as unknown as ObjectMatrix>; - this._styles = null as unknown as Styles; } /** * @deprecated should never expose a property that is provided by another module! */ - getsStyles(): Styles { + getStyles(): Styles { return this._styles; } - /** - * Get which Workbook and Worksheet this skeleton is attached to. - * @returns [unitId, sheetId] - */ - getLocation(): [string, string] { - return [this.worksheet.getUnitId(), this.worksheet.getSheetId()]; - } - - private _initContextListener(): void { - this.disposeWithMe( - this._contextService.subscribeContextValue$(RENDER_RAW_FORMULA_KEY).pipe( - startWith(false), - distinctUntilChanged() - ).subscribe((renderRaw) => { - this._renderRawFormula = renderRaw; - this._resetCache(); - this.makeDirty(true); - }) - ); - } - setOverflowCache(value: ObjectMatrix): void { this._overflowCache = value; } - setMarginLeft(left: number): void { - this._marginLeft = left; - } - - setMarginTop(top: number): void { - this._marginTop = top; - } - getFont(rowIndex: number, columnIndex: number): Nullable { const fontCache = this.stylesCache.fontMatrix; if (!fontCache) { @@ -444,7 +287,7 @@ export class SpreadsheetSkeleton extends Skeleton { * @returns boolean */ updateVisibleRange(vpInfo?: IViewportInfo): boolean { - if (!this._worksheetData || !this._rowHeightAccumulation || !this._columnWidthAccumulation) { + if (!this._worksheetData || !this.rowHeightAccumulation || !this.columnWidthAccumulation) { return false; } @@ -474,7 +317,7 @@ export class SpreadsheetSkeleton extends Skeleton { */ setStylesCache(vpInfo?: IViewportInfo): Nullable { if (!this._worksheetData) return; - if (!this._rowHeightAccumulation || !this._columnWidthAccumulation) return; + if (!this.rowHeightAccumulation || !this.columnWidthAccumulation) return; this.updateVisibleRange(vpInfo); @@ -516,34 +359,6 @@ export class SpreadsheetSkeleton extends Skeleton { return this; } - /** - * Refresh cache after markDirty by SheetSkeletonManagerService.reCalculate() - * @param bounds - */ - calculate(): Nullable { - this._resetCache(); - this._updateLayout(); - - return this; - } - - private _hasUnMergedCellInRow(rowIndex: number, startColumn: number, endColumn: number): boolean { - const mergeData = this.worksheet.getMergeData(); - if (!mergeData) { - return false; - } - - for (let i = startColumn; i <= endColumn; i++) { - const { isMerged, isMergedMainCell } = this.worksheet.getCellInfoInMergeData(rowIndex, i); - - if (!isMerged && !isMergedMainCell) { - return true; - } - } - - return false; - } - //#region auto height /** * Calc all auto height by getDocsSkeletonPageSize in ranges @@ -746,14 +561,14 @@ export class SpreadsheetSkeleton extends Skeleton { const rowIdxArr = createRowSequence(checkStartRow, checkEndRow, otherRowIndex); const preColIndex = Math.max(0, colIndex - 1); - let currColWidth = this._columnWidthAccumulation[colIndex] - this._columnWidthAccumulation[preColIndex]; + let currColWidth = this.columnWidthAccumulation[colIndex] - this.columnWidthAccumulation[preColIndex]; if (colIndex === 0) { - currColWidth = this._columnWidthAccumulation[colIndex]; + currColWidth = this.columnWidthAccumulation[colIndex]; } for (let i = 0; i < rowIdxArr.length; i++) { const row = rowIdxArr[i]; - const { isMerged, isMergedMainCell } = this._getCellMergeInfo(colIndex, row); + const { isMerged, isMergedMainCell } = this.worksheet.getCellInfoInMergeData(colIndex, row); if (isMerged && !isMergedMainCell) continue; if (!this.worksheet.getRowVisible(row)) continue; const cell = worksheet.getCell(row, colIndex); @@ -780,7 +595,7 @@ export class SpreadsheetSkeleton extends Skeleton { /** * For _calculateColMaxWidth * @param cell - * @returns {number} width + * @returns {number} currColWidth */ _getMeasuredWidthByCell(cell: ICellDataForSheetInterceptor, currColWidth: number) { let measuredWidth = 0; @@ -841,159 +656,24 @@ export class SpreadsheetSkeleton extends Skeleton { }; //#endregion - /** - * Calculate data for row col & cell position. - * This method should be called whenever a sheet is dirty. - * Update position value to this._rowHeaderWidth & this._rowHeightAccumulation & this._columnHeaderHeight & this._columnWidthAccumulation. - */ - private _updateLayout(): void { - if (!this.dirty) { - return; - } - - const { - rowData, - columnData, - defaultRowHeight, - defaultColumnWidth, - rowCount, - columnCount, - rowHeader, - columnHeader, - showGridlines, - gridlinesColor, - } = this._worksheetData; - - const { rowTotalHeight, rowHeightAccumulation } = this._generateRowMatrixCache( - rowCount, - rowData, - defaultRowHeight - ); - - const { columnTotalWidth, columnWidthAccumulation } = this._generateColumnMatrixCache( - columnCount, - columnData, - defaultColumnWidth - ); - - this._rowHeaderWidth = rowHeader.hidden !== BooleanNumber.TRUE ? this._dynamicallyUpdateRowHeaderWidth(rowHeader) : 0; - this._columnHeaderHeight = columnHeader.hidden !== BooleanNumber.TRUE ? columnHeader.height : 0; - - this._rowTotalHeight = rowTotalHeight; - this._rowHeightAccumulation = rowHeightAccumulation; - this._columnTotalWidth = columnTotalWidth; - this._columnWidthAccumulation = columnWidthAccumulation; - this._showGridlines = showGridlines; - this._gridlinesColor = gridlinesColor; - - this.makeDirty(false); - } - - private _dynamicallyUpdateRowHeaderWidth(rowHeader: { width: number }): number { - const SIZE_BY_EACH_CHARACTER = 8; - const widthByComputation = (`${this.worksheet.getRowCount()}`.length * SIZE_BY_EACH_CHARACTER); - return Math.max(rowHeader.width, widthByComputation); - } - /** * @deprecated use `getRangeByViewport` instead. * @param bounds */ getRangeByBounding(bounds?: IViewportInfo): IRange { - return this._getRangeByViewBounding(this._rowHeightAccumulation, this._columnWidthAccumulation, bounds?.cacheBound); + return this._getRangeByViewBounding(this.rowHeightAccumulation, this.columnWidthAccumulation, bounds?.cacheBound); } getRangeByViewport(vpInfo?: IViewportInfo): IRange { - return this._getRangeByViewBounding(this._rowHeightAccumulation, this._columnWidthAccumulation, vpInfo?.viewBound); + return this._getRangeByViewBounding(this.rowHeightAccumulation, this.columnWidthAccumulation, vpInfo?.viewBound); } getCacheRangeByViewport(vpInfo?: IViewportInfo): IRange { - return this._getRangeByViewBounding(this._rowHeightAccumulation, this._columnWidthAccumulation, vpInfo?.cacheBound); + return this._getRangeByViewBounding(this.rowHeightAccumulation, this.columnWidthAccumulation, vpInfo?.cacheBound); } getRangeByViewBound(bound?: IBoundRectNoAngle): IRange { - return this._getRangeByViewBounding(this._rowHeightAccumulation, this._columnWidthAccumulation, bound); - } - - /** - * @deprecated should never expose a property that is provided by another module! - * @returns - */ - getWorksheetConfig(): IWorksheetData { - return this._worksheetData; - } - - getMergeBounding(startRow: number, startColumn: number, endRow: number, endColumn: number): IRange { - const mergeData = this._worksheetData.mergeData; - if (!mergeData) { - return { - startRow, - startColumn, - endRow, - endColumn, - }; - } - - let isSearching = true; - const searchedMarge = new ObjectMatrix(); - - // the loop breaks when there are not merged cells intersect with the current range - // NOTE: what about the performance issue? - while (isSearching) { - isSearching = false; - - for (let i = 0; i < mergeData.length; i++) { - const { - startRow: mainStartRow, - startColumn: mainStartColumn, - endRow: mainEndRow, - endColumn: mainEndColumn, - } = mergeData[i]; - - if (searchedMarge.getValue(mainStartRow, mainStartColumn)) { - continue; - } - - const rect1 = { - left: startColumn, - top: startRow, - right: endColumn, - bottom: endRow, - }; - - const rect2 = { - left: mainStartColumn, - top: mainStartRow, - right: mainEndColumn, - bottom: mainEndRow, - }; - - if (isRectIntersect(rect1, rect2)) { - startRow = Math.min(startRow, mainStartRow); - startColumn = Math.min(startColumn, mainStartColumn); - endRow = Math.max(endRow, mainEndRow); - endColumn = Math.max(endColumn, mainEndColumn); - searchedMarge.setValue(mainStartRow, mainStartColumn, true); - isSearching = true; - } - } - } - - return { - startRow, - startColumn, - endRow, - endColumn, - }; - } - - /** - * expand curr range if it's intersect with merge range. - * @param range - * @returns {IRange} expanded range because merge info. - */ - expandRangeByMerge(range: IRange): IRange { - return this.getMergeBounding(range.startRow, range.startColumn, range.endRow, range.endColumn); + return this._getRangeByViewBounding(this.rowHeightAccumulation, this.columnWidthAccumulation, bound); } appendToOverflowCache(row: number, column: number, startColumn: number, endColumn: number): void { @@ -1005,14 +685,6 @@ export class SpreadsheetSkeleton extends Skeleton { }); } - getColumnCount(): number { - return this._columnWidthAccumulation.length; - } - - getRowCount(): number { - return this._rowHeightAccumulation.length; - } - getOverflowPosition( contentSize: Required, horizontalAlign: HorizontalAlign, @@ -1039,65 +711,6 @@ export class SpreadsheetSkeleton extends Skeleton { }; } - getNoMergeCellPositionByIndex(rowIndex: number, columnIndex: number): IPosition { - const { - rowHeightAccumulation, - columnWidthAccumulation, - rowHeaderWidthAndMarginLeft, - columnHeaderHeightAndMarginTop, - } = this; - - // const rowCount = this.getRowCount(); - - // const columnCount = this.getColumnCount(); - - // if (rowIndex >= rowCount || rowIndex < 0 || columnIndex >= columnCount || columnIndex < 0) { - // return { - // startY: -100, - // endY: -100, - // startX: -100, - // endX: -100, - // }; - // } - - let { startY, endY, startX, endX } = getCellPositionByIndex( - rowIndex, - columnIndex, - rowHeightAccumulation, - columnWidthAccumulation - ); - - startY += columnHeaderHeightAndMarginTop; - endY += columnHeaderHeightAndMarginTop; - startX += rowHeaderWidthAndMarginLeft; - endX += rowHeaderWidthAndMarginLeft; - - return { - startY, - endY, - startX, - endX, - }; - } - - getNoMergeCellPositionByIndexWithNoHeader(rowIndex: number, columnIndex: number): IPosition { - const { rowHeightAccumulation, columnWidthAccumulation } = this; - - const { startY, endY, startX, endX } = getCellPositionByIndex( - rowIndex, - columnIndex, - rowHeightAccumulation, - columnWidthAccumulation - ); - - return { - startY, - endY, - startX, - endX, - }; - } - /** * Get cell by pos(offsetX, offsetY). * @deprecated Please use `getCellWithCoordByOffset` instead. @@ -1112,31 +725,6 @@ export class SpreadsheetSkeleton extends Skeleton { return this.getCellWithCoordByOffset(offsetX, offsetY, scaleX, scaleY, scrollXY); } - /** - * Get cell by pos(offsetX, offsetY). - * - * options.matchFirst true means get cell would skip all invisible cells. - * @param offsetX position X in viewport. - * @param offsetY position Y in viewport. - * @param scaleX render scene scale x-axis, scene.getAncestorScale - * @param scaleY render scene scale y-axis, scene.getAncestorScale - * @param scrollXY render viewportScroll {x, y} - * @param options {IGetRowColByPosOptions} - * @returns {ICellWithCoord} Selection data with coordinates - */ - getCellWithCoordByOffset( - offsetX: number, - offsetY: number, - scaleX: number, - scaleY: number, - scrollXY: { x: number; y: number }, - options?: IGetRowColByPosOptions - ): ICellWithCoord { - const { row, column } = this.getCellIndexByOffset(offsetX, offsetY, scaleX, scaleY, scrollXY, options); - - return this.getCellWithCoordByIndex(row, column); - } - /** * This method has the same implementation as `getCellIndexByOffset`, * but uses a different name to maintain backward compatibility with previous calls. @@ -1154,55 +742,6 @@ export class SpreadsheetSkeleton extends Skeleton { return this.getCellIndexByOffset(offsetX, offsetY, scaleX, scaleY, scrollXY, options); } - /** - * Get cell index by offset(o) - * @param offsetX position X in viewport. - * @param offsetY position Y in viewport. - * @param scaleX render scene scale x-axis, scene.getAncestorScale - * @param scaleY render scene scale y-axis, scene.getAncestorScale - * @param scrollXY render viewport scroll {x, y}, scene.getScrollXYByRelativeCoords, scene.getScrollXY - * @param scrollXY.x - * @param scrollXY.y - * @returns cell index - */ - getCellIndexByOffset( - offsetX: number, - offsetY: number, - scaleX: number, - scaleY: number, - scrollXY: { x: number; y: number }, - options?: IGetRowColByPosOptions - ): { row: number; column: number } { - const row = this.getRowIndexByOffsetY(offsetY, scaleY, scrollXY, options); - const column = this.getColumnIndexByOffsetX(offsetX, scaleX, scrollXY, options); - - return { - row, - column, - }; - } - - getCellByOffset( - offsetX: number, - offsetY: number, - scaleX: number, - scaleY: number, - scrollXY: { x: number; y: number } - ): Nullable { - const cellIndex = this?.getCellIndexByOffset( - offsetX, - offsetY, - scaleX, - scaleY, - scrollXY, - { firstMatch: true } // for visible - ); - if (!cellIndex) return null; - - const selectionCell = this.worksheet.getCellInfoInMergeData(cellIndex.row, cellIndex.column); - return selectionCell; - } - getCellWithMergeInfoByIndex(row: number, column: number): Nullable { const selectionCell = this.worksheet.getCellInfoInMergeData(row, column); return selectionCell; @@ -1216,28 +755,6 @@ export class SpreadsheetSkeleton extends Skeleton { return this.getColumnIndexByOffsetX(offsetX, scaleX, scrollXY, options); } - /** - * Get column index by offset x. - * @param offsetX scaled offset x - * @param scaleX scale x - * @param scrollXY scrollXY - * @returns column index - */ - getColumnIndexByOffsetX(evtOffsetX: number, scaleX: number, scrollXY: { x: number; y: number }, options?: IGetRowColByPosOptions): number { - const offsetX = this.getTransformOffsetX(evtOffsetX, scaleX, scrollXY); - const { columnWidthAccumulation } = this; - let column = searchArray(columnWidthAccumulation, offsetX, options?.firstMatch); - - if (options?.closeFirst) { - // check if upper column was closer than current - if (Math.abs(columnWidthAccumulation[column] - offsetX) < Math.abs(offsetX - (columnWidthAccumulation[column - 1] ?? 0))) { - column = column + 1; - } - } - - return column; - } - /** * Same as getRowIndexByOffsetY * @deprecated Please use `getRowIndexByOffsetY` method instead. @@ -1246,79 +763,6 @@ export class SpreadsheetSkeleton extends Skeleton { return this.getRowIndexByOffsetY(offsetY, scaleY, scrollXY, options); } - /** - * - * @param offsetY scaled offset y - * @param scaleY scale y - * @param scrollXY - * @param scrollXY.x - * @param scrollXY.y - */ - getRowIndexByOffsetY(offsetY: number, scaleY: number, scrollXY: { x: number; y: number }, options?: IGetRowColByPosOptions): number { - const { rowHeightAccumulation } = this; - offsetY = this.getTransformOffsetY(offsetY, scaleY, scrollXY); - - let row = searchArray(rowHeightAccumulation, offsetY, options?.firstMatch); - - if (options?.closeFirst) { - // check if upper row was closer than current - if (Math.abs(rowHeightAccumulation[row] - offsetY) < Math.abs(offsetY - (rowHeightAccumulation[row - 1] ?? 0))) { - row = row + 1; - } - } - - return row; - } - - getTransformOffsetX(offsetX: number, scaleX: number, scrollXY: { x: number; y: number }): number { - const { x: scrollX } = scrollXY; - - // so we should map physical positions to ideal positions - const afterOffsetX = offsetX / scaleX + scrollX - this.rowHeaderWidthAndMarginLeft; - - return afterOffsetX; - } - - getTransformOffsetY(offsetY: number, scaleY: number, scrollXY: { x: number; y: number }): number { - const { y: scrollY } = scrollXY; - - // these values are not affected by zooming (ideal positions) - offsetY = offsetY / scaleY + scrollY - this.columnHeaderHeightAndMarginTop; - - return offsetY; - } - - getOffsetByPositionX(column: number): number { - const { columnWidthAccumulation, rowHeaderWidthAndMarginLeft } = this; - - const lastColumnIndex = columnWidthAccumulation.length - 1; - const columnValue = columnWidthAccumulation[column]; - if (columnValue != null) { - return columnValue + rowHeaderWidthAndMarginLeft; - } - - if (column < 0) { - return rowHeaderWidthAndMarginLeft; - } - - return columnWidthAccumulation[lastColumnIndex] + rowHeaderWidthAndMarginLeft; - } - - getOffsetByPositionY(row: number): number { - const { rowHeightAccumulation, columnHeaderHeightAndMarginTop } = this; - const lastRowIndex = rowHeightAccumulation.length - 1; - const rowValue = rowHeightAccumulation[row]; - if (rowValue != null) { - return rowValue + columnHeaderHeightAndMarginTop; - } - - if (row < 0) { - return columnHeaderHeightAndMarginTop; - } - - return rowHeightAccumulation[lastRowIndex] + columnHeaderHeightAndMarginTop; - } - /** * Same as getCellWithCoordByIndex, but uses a different name to maintain backward compatibility with previous calls. * @deprecated Please use `getCellWithCoordByIndex` instead. @@ -1336,84 +780,6 @@ export class SpreadsheetSkeleton extends Skeleton { return this.getCellWithCoordByIndex(row, column, false); } - /** - * Return cell information corresponding to the current coordinates, including the merged cell object. - * - * @param row Specified Row Coordinate - * @param column Specified Column Coordinate - */ - getCellWithCoordByIndex(row: number, column: number, header: boolean = true): ICellWithCoord { - const { - rowHeightAccumulation, - columnWidthAccumulation, - rowHeaderWidthAndMarginLeft, - columnHeaderHeightAndMarginTop, - } = this; - - const primary: ICellWithCoord = getCellWithCoordByIndexCore( - row, - column, - rowHeightAccumulation, - columnWidthAccumulation, - this.worksheet.getCellInfoInMergeData(row, column) - ); - const { isMerged, isMergedMainCell } = primary; - let { startY, endY, startX, endX, mergeInfo } = primary; - - let offsetX = rowHeaderWidthAndMarginLeft; - let offsetY = columnHeaderHeightAndMarginTop; - if (header === false) { - offsetX = 0; - offsetY = 0; - } - - startY += offsetY; - endY += offsetY; - startX += offsetX; - endX += offsetX; - - mergeInfo.startY += offsetY; - mergeInfo.endY += offsetY; - mergeInfo.startX += offsetX; - mergeInfo.endX += offsetX; - - // mergeInfo = mergeInfoOffset(mergeInfo, rowHeaderWidthAndMarginLeft, columnHeaderHeightAndMarginTop); - - return { - actualRow: row, - actualColumn: column, - startX, - startY, - endX, - endY, - isMerged, - isMergedMainCell, - mergeInfo, - }; - } - - /** - * convert canvas content position to physical position in screen - * @param offsetX - * @param scaleX - * @param scrollXY - */ - convertTransformToOffsetX(offsetX: number, scaleX: number, scrollXY: { x: number; y: number }): number { - const { x: scrollX } = scrollXY; - return (offsetX - scrollX) * scaleX; - } - - /** - * convert canvas content position to physical position in screen - * @param offsetY - * @param scaleY - * @param scrollXY - */ - convertTransformToOffsetY(offsetY: number, scaleY: number, scrollXY: { x: number; y: number }): number { - const { y: scrollY } = scrollXY; - return (offsetY - scrollY) * scaleY; - } - /** * Only used for cell edit, and no need to rotate text when edit cell content! * @deprecated use same method in worksheet. @@ -1573,30 +939,6 @@ export class SpreadsheetSkeleton extends Skeleton { }; } - getDecomposedOffset(offsetX: number, offsetY: number): { row: number; column: number; columnOffset: number; rowOffset: number } { - const column = searchArray(this._columnWidthAccumulation, offsetX); - let columnOffset = 0; - if (column === 0) { - columnOffset = offsetX; - } else { - columnOffset = offsetX - this._columnWidthAccumulation[column - 1]; - } - - const row = searchArray(this._rowHeightAccumulation, offsetY); - let rowOffset = 0; - if (row === 0) { - rowOffset = offsetY; - } else { - rowOffset = offsetY - this._rowHeightAccumulation[row - 1]; - } - return { - row, - column, - columnOffset, - rowOffset, - }; - } - /** * 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. @@ -1642,13 +984,11 @@ export class SpreadsheetSkeleton extends Skeleton { } if (vertexAngle !== 0) { - const { startY, endY, startX, endX } = getCellWithCoordByIndexCore( + const { startY, endY, startX, endX } = this.getCellWithCoordByIndex( row, - column, - this.rowHeightAccumulation, - this.columnWidthAccumulation, - this.worksheet.getCellInfoInMergeData(row, column) + column ); + const cellWidth = endX - startX; const cellHeight = endY - startY; @@ -1675,12 +1015,9 @@ export class SpreadsheetSkeleton extends Skeleton { return true; } - const { startY, endY } = getCellWithCoordByIndexCore( + const { startY, endY } = this.getCellWithCoordByIndex( row, - column, - this.rowHeightAccumulation, - this.columnWidthAccumulation, - this.worksheet.getCellInfoInMergeData(row, column) + column ); const cellHeight = endY - startY; @@ -1747,167 +1084,6 @@ export class SpreadsheetSkeleton extends Skeleton { } as IRange; } - private _generateRowMatrixCache( - rowCount: number, - rowData: IObjectArrayPrimitiveType>, - defaultRowHeight: number - ): { rowTotalHeight: number; rowHeightAccumulation: number[] } { - let rowTotalHeight = 0; - const rowHeightAccumulation: number[] = []; - const data = rowData; - for (let r = 0; r < rowCount; r++) { - let rowHeight = defaultRowHeight; - - if (this.worksheet.getRowFiltered(r)) { - rowHeight = 0; - } else if (data[r] != null) { - const rowDataItem = data[r]; - if (!rowDataItem) { - continue; - } - - const { h = defaultRowHeight, ah, ia } = rowDataItem; - if ((ia == null || ia === BooleanNumber.TRUE) && typeof ah === 'number') { - rowHeight = ah; - } else { - rowHeight = h; - } - - if (rowDataItem.hd === BooleanNumber.TRUE) { - rowHeight = 0; - } - } - - rowTotalHeight += rowHeight; - - rowHeightAccumulation.push(rowTotalHeight); - } - - return { - rowTotalHeight, - rowHeightAccumulation, - }; - } - - /** - * Calc columnWidthAccumulation by columnData - * @param colCount - * @param columnData - * @param defaultColumnWidth - */ - private _generateColumnMatrixCache( - colCount: number, - columnData: IObjectArrayPrimitiveType>, - defaultColumnWidth: number - ): { columnTotalWidth: number; columnWidthAccumulation: number[] } { - let columnTotalWidth = 0; - const columnWidthAccumulation: number[] = []; - - const data = columnData; - - for (let c = 0; c < colCount; c++) { - let columnWidth = defaultColumnWidth; - - if (data[c] != null) { - const columnDataItem = data[c]; - - if (!columnDataItem) { - continue; - } - if (columnDataItem.w != null) { - columnWidth = columnDataItem.w; - } - - if (columnDataItem.hd === BooleanNumber.TRUE) { - columnWidth = 0; - } - } - - columnTotalWidth += columnWidth; - columnWidthAccumulation.push(columnTotalWidth); - } - - return { - columnTotalWidth, - columnWidthAccumulation, - }; - } - - //eslint-disable-next-line complexity - private _getOverflowBound( - row: number, - startColumn: number, - endColumn: number, - contentWidth: number, - horizontalAlign = HorizontalAlign.LEFT - ): number { - let cumWidth = 0; - if (startColumn > endColumn) { - const columnCount = this._columnWidthAccumulation.length - 1; - for (let i = startColumn; i >= endColumn; i--) { - const column = i; - const cell = this.worksheet.getCell(row, column); - if ((!isCellCoverable(cell) && column !== startColumn) || this.intersectMergeRange(row, column)) { - if (column === startColumn) { - return column; - } - return column + 1 > columnCount ? columnCount : column + 1; - } - const { startX, endX } = getCellPositionByIndex( - row, - column, - this.rowHeightAccumulation, - this.columnWidthAccumulation - ); - - // For center alignment, the current cell's width needs to be divided in half for comparison. - if (horizontalAlign === HorizontalAlign.CENTER && column === startColumn) { - cumWidth += (endX - startX) / 2; - } else { - cumWidth += endX - startX; - } - - if (contentWidth < cumWidth) { - return column; - } - } - return startColumn; - } - for (let i = startColumn; i <= endColumn; i++) { - const column = i; - const cell = this.worksheet.getCell(row, column); - if ((!isCellCoverable(cell) && column !== startColumn) || this.intersectMergeRange(row, column)) { - if (column === startColumn) { - return column; - } - - return column - 1 < 0 ? 0 : column - 1; - } - const { startX, endX } = getCellPositionByIndex( - row, - column, - this.rowHeightAccumulation, - this.columnWidthAccumulation - ); - - if (horizontalAlign === HorizontalAlign.CENTER && column === startColumn) { - cumWidth += (endX - startX) / 2; - } else { - cumWidth += endX - startX; - } - - if (contentWidth < cumWidth) { - return column; - } - } - return endColumn; - } - - intersectMergeRange(row: number, column: number): boolean { - const mergedData = this.worksheet.getMergedCell(row, column); - return Boolean(mergedData); - } - /** * Get the current row and column segment visible merge data. * @returns {IRange} The visible merge data @@ -1936,7 +1112,8 @@ export class SpreadsheetSkeleton extends Skeleton { /** * Any changes to sheet model would reset cache. */ - private _resetCache(): void { + override _resetCache(): void { + super._resetCache(); this._stylesCache = { background: {}, backgroundPositions: new ObjectMatrix(), @@ -1944,9 +1121,10 @@ export class SpreadsheetSkeleton extends Skeleton { fontMatrix: new ObjectMatrix(), border: new ObjectMatrix(), }; - this._handleBgMatrix.reset(); - this._handleBorderMatrix.reset(); - this._overflowCache.reset(); + + this._handleBgMatrix?.reset(); + this._handleBorderMatrix?.reset(); + this._overflowCache?.reset(); } _setBorderStylesCache(row: number, col: number, style: Nullable, options: { @@ -2036,7 +1214,6 @@ export class SpreadsheetSkeleton extends Skeleton { horizontalAlign, wrapStrategy, imageCacheMap: this._imageCacheMap, - fontRenderExtension: cell?.fontRenderExtension, }; this._stylesCache.fontMatrix.setValue(row, col, config); this._calculateOverflowCell(row, col, config); @@ -2099,53 +1276,6 @@ export class SpreadsheetSkeleton extends Skeleton { this._setFontStylesCache(row, col, { ...cell, ...{ s: style } }); } - private _updateConfigAndGetDocumentModel( - documentData: IDocumentData, - horizontalAlign: HorizontalAlign, - paddingData: IPaddingData, - renderConfig?: IDocumentRenderConfig - ): Nullable { - if (!renderConfig) { - return; - } - - if (!documentData.body?.dataStream) { - return; - } - - if (!documentData.documentStyle) { - documentData.documentStyle = {}; - } - - documentData.documentStyle.marginTop = paddingData.t ?? 0; - documentData.documentStyle.marginBottom = paddingData.b ?? 2; - documentData.documentStyle.marginLeft = paddingData.l ?? 2; - documentData.documentStyle.marginRight = paddingData.r ?? 2; - - // Fix https://github.com/dream-num/univer/issues/1586 - documentData.documentStyle.pageSize = { - width: Number.POSITIVE_INFINITY, - height: Number.POSITIVE_INFINITY, - }; - - documentData.documentStyle.renderConfig = { - ...documentData.documentStyle.renderConfig, - ...renderConfig, - }; - - const paragraphs = documentData.body.paragraphs || []; - - for (const paragraph of paragraphs) { - if (!paragraph.paragraphStyle) { - paragraph.paragraphStyle = {}; - } - - paragraph.paragraphStyle.horizontalAlign = horizontalAlign; - } - - return new DocumentDataModel(documentData); - } - /** * pro/issues/344 * In Excel, for the border rendering of merged cells to take effect, the outermost cells need to have the same border style. @@ -2267,43 +1397,6 @@ export class SpreadsheetSkeleton extends Skeleton { }; } - /** - * New version to get merge data. - * @param row - * @param column - * @returns {ISelectionCell} The cell info with merge data - */ - private _getCellMergeInfo(row: number, column: number): ISelectionCell { - return this.worksheet.getCellInfoInMergeData(row, column); - } - - getDistanceFromTopLeft(row: number, col: number): IPoint { - return { - x: this._offsetXToCol(col), - y: this._offsetYToRow(row), - }; - } - - /** - * Distance from top left to row - * @param row - */ - private _offsetYToRow(row: number): number { - const arr = this._rowHeightAccumulation; - const i = Math.max(0, row - 1); - return arr[i]; - } - - /** - * Distance from top left to col - * @param col - */ - private _offsetXToCol(col: number): number { - const arr = this._columnWidthAccumulation; - const i = Math.max(0, col - 1); - return arr[i]; - } - getHiddenRowsInRange(range: IRowRange) { const hiddenRows = []; for (let i = range.startRow; i <= range.endRow; i++) { @@ -2324,3 +1417,88 @@ export class SpreadsheetSkeleton extends Skeleton { return hiddenCols; } } + +/** + * convert canvas content position to physical position in screen + * @param offsetX + * @param scaleX + * @param scrollXY + */ +export function convertTransformToOffsetX( + offsetX: number, + scaleX: number, + scrollXY: { x: number; y: number } +): number { + const { x: scrollX } = scrollXY; + return (offsetX - scrollX) * scaleX; +} + +/** + * convert canvas content position to physical position in screen + * @param offsetY + * @param scaleY + * @param scrollXY + */ +export function convertTransformToOffsetY( + offsetY: number, + scaleY: number, + scrollXY: { x: number; y: number } +): number { + const { y: scrollY } = scrollXY; + return (offsetY - scrollY) * scaleY; +} + +/** + * Obtain the height and width of a cell's text, taking into account scenarios with rotated text. + * @param documentSkeleton Data of the document's ViewModel + * @param angleInDegree The rotation angle of an Excel cell, it's **degree** + */ +export function getDocsSkeletonPageSize(documentSkeleton: DocumentSkeleton, angleInDegree: number = 0): Nullable> { + const skeletonData = documentSkeleton?.getSkeletonData(); + const angle = degToRad(angleInDegree); + + if (!skeletonData) { + return null; + } + const { pages } = skeletonData; + const lastPage = pages[pages.length - 1]; + const { width, height } = lastPage; + + if (angle === 0) { + return { width, height }; + } + + if (Math.abs(angle) === Math.PI / 2) { + return { width: height, height: width }; + } + + let allRotatedWidth = 0; + let allRotatedHeight = 0; + + const widthArray: Array<{ rotatedWidth: number; spaceWidth: number }> = []; + + columnIterator([lastPage], (column: IDocumentSkeletonColumn) => { + const { lines, width: columnWidth, spaceWidth } = column; + + const { rotatedHeight, rotatedWidth } = getRotateOffsetAndFarthestHypotenuse(lines, columnWidth, angle); + + allRotatedHeight += rotatedHeight; + + widthArray.push({ rotatedWidth, spaceWidth }); + }); + + const widthCount = widthArray.length; + + for (let i = 0; i < widthCount; i++) { + const { rotatedWidth } = widthArray[i]; + + if (i === 0) { + allRotatedWidth += rotatedWidth; + } + } + + return { + width: allRotatedWidth, + height: allRotatedHeight, + }; +} diff --git a/packages/engine-render/src/components/sheets/spreadsheet.ts b/packages/engine-render/src/components/sheets/spreadsheet.ts index bd42edc89e6..695b879a5c5 100644 --- a/packages/engine-render/src/components/sheets/spreadsheet.ts +++ b/packages/engine-render/src/components/sheets/spreadsheet.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { IRange, Nullable } from '@univerjs/core'; +import type { IPosition, IRange, Nullable } from '@univerjs/core'; import type { IBoundRectNoAngle, IViewportInfo, Vector2 } from '../../basics/vector2'; import type { Canvas } from '../../canvas'; import type { UniverRenderingContext2D } from '../../context'; @@ -26,10 +26,10 @@ import type { Background } from './extensions/background'; import type { Border } from './extensions/border'; import type { Font } from './extensions/font'; import type { IPaintForRefresh, IPaintForScrolling, SHEET_VIEWPORT_KEY } from './interfaces'; -import type { SpreadsheetSkeleton } from './sheet-skeleton'; +import type { SpreadsheetSkeleton } from './sheet.render-skeleton'; import { BooleanNumber, sortRules, Tools } from '@univerjs/core'; import { FIX_ONE_PIXEL_BLUR_OFFSET, RENDER_CLASS_TYPE } from '../../basics/const'; -import { getCellPositionByIndex, getColor } from '../../basics/tools'; +import { getColor } from '../../basics/tools'; import { Documents } from '../docs/document'; import { SpreadsheetExtensionRegistry } from '../extension'; import { sheetContentViewportKeys, sheetHeaderViewportKeys } from './constants'; @@ -173,32 +173,33 @@ export class Spreadsheet extends SheetComponent { return false; } - override getNoMergeCellPositionByIndex(rowIndex: number, columnIndex: number) { - const spreadsheetSkeleton = this.getSkeleton(); - if (!spreadsheetSkeleton) { - return; + override getNoMergeCellPositionByIndex(rowIndex: number, columnIndex: number): IPosition { + const skeleton = this.getSkeleton(); + if (!skeleton) { + return { startX: 0, startY: 0, endX: 0, endY: 0 }; } - const { rowHeightAccumulation, columnWidthAccumulation, rowHeaderWidth, columnHeaderHeight } = - spreadsheetSkeleton; - - let { startY, endY, startX, endX } = getCellPositionByIndex( - rowIndex, - columnIndex, - rowHeightAccumulation, - columnWidthAccumulation - ); - - startY += columnHeaderHeight; - endY += columnHeaderHeight; - startX += rowHeaderWidth; - endX += rowHeaderWidth; - - return { - startY, - endY, - startX, - endX, - }; + return skeleton.getNoMergeCellWithCoordByIndex(rowIndex, columnIndex); + // const { rowHeightAccumulation, columnWidthAccumulation, rowHeaderWidth, columnHeaderHeight } = + // spreadsheetSkeleton; + + // let { startY, endY, startX, endX } = getCellWithCoordByIndexCore( + // rowIndex, + // columnIndex, + // rowHeightAccumulation, + // columnWidthAccumulation + // ); + + // startY += columnHeaderHeight; + // endY += columnHeaderHeight; + // startX += rowHeaderWidth; + // endX += rowHeaderWidth; + + // return { + // startY, + // endY, + // startX, + // endX, + // }; } override getScrollXYByRelativeCoords(coord: Vector2) { @@ -236,7 +237,7 @@ export class Spreadsheet extends SheetComponent { } override getSelectionBounding(startRow: number, startColumn: number, endRow: number, endColumn: number) { - return this.getSkeleton()?.getMergeBounding(startRow, startColumn, endRow, endColumn); + return this.getSkeleton()?.expandRangeByMerge({ startRow, startColumn, endRow, endColumn }); } /** diff --git a/packages/engine-render/src/components/sheets/util.ts b/packages/engine-render/src/components/sheets/util.ts index 37b933025ce..0c91628e763 100644 --- a/packages/engine-render/src/components/sheets/util.ts +++ b/packages/engine-render/src/components/sheets/util.ts @@ -17,7 +17,7 @@ import type { CellValueType, IDocumentData, IPaddingData, IStyleBase, IStyleData, ITextRotation, ITextStyle, Nullable, TextDirection } from '@univerjs/core'; import { DEFAULT_EMPTY_DOCUMENT_VALUE, DocumentDataModel, HorizontalAlign, VerticalAlign, WrapStrategy } from '@univerjs/core'; import { convertTextRotation } from '../../basics/text-rotation'; -import { DEFAULT_PADDING_DATA } from './sheet-skeleton'; +import { DEFAULT_PADDING_DATA } from './sheet.render-skeleton'; export interface ICellStyle { textRotation?: ITextRotation; diff --git a/packages/engine-render/src/engine.ts b/packages/engine-render/src/engine.ts index c7d1bf5eb75..51dc51212f8 100644 --- a/packages/engine-render/src/engine.ts +++ b/packages/engine-render/src/engine.ts @@ -234,7 +234,7 @@ export class Engine extends Disposable { } addScene(sceneInstance: Scene): Scene { - const sceneKey = (sceneInstance as any).sceneKey; + const sceneKey = sceneInstance.sceneKey; if (this.hasScene(sceneKey)) { console.warn('Scenes has same key, it will be covered'); } diff --git a/packages/engine-render/src/index.ts b/packages/engine-render/src/index.ts index b0e1026aff3..6767213e040 100644 --- a/packages/engine-render/src/index.ts +++ b/packages/engine-render/src/index.ts @@ -34,7 +34,7 @@ export { DocumentViewModel } from './components/docs/view-model/document-view-mo export { DocumentEditArea } from './components/docs/view-model/document-view-model'; export { parseDataStreamToTree } from './components/docs/view-model/document-view-model'; export { getLastColumn } from './components/docs/layout/tools'; -export { DEFAULT_PADDING_DATA } from './components/sheets/sheet-skeleton'; +export { DEFAULT_PADDING_DATA } from './components/sheets/sheet.render-skeleton'; export * from './context'; export * from './custom'; export * from './engine'; @@ -48,5 +48,4 @@ export { type IChangeObserverConfig } from './scene.transformer'; export * from './scene-viewer'; export * from './scroll-timer'; export * from './shape'; -export { ThinEngine } from './thin-engine'; export * from './viewport'; diff --git a/packages/engine-render/src/scene.ts b/packages/engine-render/src/scene.ts index 256d41e5776..cb095f4dc41 100644 --- a/packages/engine-render/src/scene.ts +++ b/packages/engine-render/src/scene.ts @@ -17,7 +17,7 @@ import type { IKeyValue, Nullable } from '@univerjs/core'; import type { BaseObject } from './base-object'; import type { IDragEvent, IKeyboardEvent, IMouseEvent, IPointerEvent, IWheelEvent } from './basics/i-events'; -import type { IObjectFullState, ISceneTransformState, ITransformChangeState } from './basics/interfaces'; +import type { ISceneTransformState, ITransformChangeState } from './basics/interfaces'; import type { ITransformerConfig } from './basics/transformer-config'; import type { Vector2 } from './basics/vector2'; import type { Canvas } from './canvas'; @@ -47,7 +47,13 @@ export interface ISceneInputControlOptions { } export class Scene extends Disposable { private _sceneKey: string = ''; + /** + * Width of scene content, does not affected by zoom. + */ private _width: number = 100; + /** + * Height of scene content, does not affected by zoom. + */ private _height: number = 100; private _scaleX: number = 1; private _scaleY: number = 1; @@ -131,7 +137,7 @@ export class Scene extends Disposable { this.disposeWithMe( toDisposable( this._parent?.onTransformChange$.subscribeEvent((_change: ITransformChangeState) => { - this._setTransForm(); + this._transformHandler(); }) ) ); @@ -189,6 +195,9 @@ export class Scene extends Disposable { this._scaleY = scaleY; } + /** + * ancestorScaleX means this.scaleX * ancestorScaleX + */ get ancestorScaleX() { const p = this.getParent(); let pScale = 1; @@ -198,6 +207,9 @@ export class Scene extends Disposable { return this.scaleX * pScale; } + /** + * ancestorScaleY means this.scaleY * ancestorScaleY + */ get ancestorScaleY() { const p = this.getParent(); let pScale = 1; @@ -207,6 +219,20 @@ export class Scene extends Disposable { return this.scaleY * pScale; } + getAncestorScale() { + // const { scaleX = 1, scaleY = 1 } = this; + // this.classType is always 'Scene', this if is always false + // if (this.classType === RENDER_CLASS_TYPE.SCENE_VIEWER) { + // scaleX = this.ancestorScaleX || 1; + // scaleY = this.ancestorScaleY || 1; + // } + + return { + scaleX: this.ancestorScaleX || 1, + scaleY: this.ancestorScaleY || 1, + }; + } + get ancestorLeft() { const p = this.getParent(); let pOffsetX = 0; @@ -306,6 +332,11 @@ export class Scene extends Disposable { this.resetCursor(); } + /** + * @deprecated use transformByState instead. + * @param width + * @param height + */ resize(width?: number, height?: number) { const preWidth = this.width; if (width !== undefined) { @@ -317,7 +348,7 @@ export class Scene extends Disposable { this.height = height; } - this._setTransForm(); + this._transformHandler(); this.onTransformChange$.emitEvent({ type: TRANSFORM_CHANGE_OBSERVABLE_TYPE.resize, value: { @@ -330,7 +361,12 @@ export class Scene extends Disposable { return this; } - setScaleValue(scaleX: number, scaleY: number) { + /** + * Unlike @scale, this method doesn't emit event. + * @param scaleX + * @param scaleY + */ + setScaleValueOnly(scaleX: number, scaleY: number) { if (scaleX !== undefined) { this.scaleX = scaleX; } @@ -348,16 +384,10 @@ export class Scene extends Disposable { */ scale(scaleX?: number, scaleY?: number) { const preScaleX = this.scaleX; - if (scaleX !== undefined) { - this.scaleX = scaleX; - } - const preScaleY = this.scaleY; - if (scaleY !== undefined) { - this.scaleY = scaleY; - } + this.setScaleValueOnly(scaleX || 1, scaleY || 1); - this._setTransForm(); + this._transformHandler(); this.onTransformChange$.emitEvent({ type: TRANSFORM_CHANGE_OBSERVABLE_TYPE.scale, value: { @@ -370,22 +400,22 @@ export class Scene extends Disposable { } /** - * current scale plus offset, relative + * Apply scaleXY base on current scaleX and scaleY */ - scaleBy(scaleX?: number, scaleY?: number) { + scaleBy(deltaScaleX?: number, deltaScaleY?: number) { const preScaleX = this.scaleX; - if (scaleX !== undefined) { - this.scaleX += scaleX; + if (deltaScaleX !== undefined) { + this.scaleX += deltaScaleX; // @TODO lumixraku why not this.scaleX *= deltaScaleX ??? } const preScaleY = this.scaleY; - if (scaleY !== undefined) { - this.scaleY += scaleY; + if (deltaScaleY !== undefined) { + this.scaleY += deltaScaleY; } this.scaleX = precisionTo(this.scaleX, 1); this.scaleY = precisionTo(this.scaleY, 1); - this._setTransForm(); + this._transformHandler(); this.onTransformChange$.emitEvent({ type: TRANSFORM_CHANGE_OBSERVABLE_TYPE.scale, value: { @@ -402,20 +432,20 @@ export class Scene extends Disposable { * @param state */ transformByState(state: ISceneTransformState) { - const optionKeys = Object.keys(state); - const preKeys: IObjectFullState = {}; - if (optionKeys.length === 0) { + const transformStateKeys = Object.keys(state); + const preKeys: ISceneTransformState = {}; + if (transformStateKeys.length === 0) { return; } - optionKeys.forEach((pKey) => { + transformStateKeys.forEach((pKey) => { if (state[pKey as keyof ISceneTransformState] !== undefined) { (preKeys as IKeyValue)[pKey] = this[pKey as keyof Scene]; (this as IKeyValue)[pKey] = state[pKey as keyof ISceneTransformState]; } }); - this._setTransForm(); + this._transformHandler(); this.onTransformChange$.emitEvent({ type: TRANSFORM_CHANGE_OBSERVABLE_TYPE.all, @@ -871,20 +901,6 @@ export class Scene extends Disposable { this._viewports = []; } - getAncestorScale() { - let { scaleX = 1, scaleY = 1 } = this; - - if (this.classType === RENDER_CLASS_TYPE.SCENE_VIEWER) { - scaleX = this.ancestorScaleX || 1; - scaleY = this.ancestorScaleY || 1; - } - - return { - scaleX, - scaleY, - }; - } - getPrecisionScale() { const pixelRatio = this.getEngine()?.getPixelRatio() || 1; const { scaleX, scaleY } = this.getAncestorScale(); @@ -1193,7 +1209,14 @@ export class Scene extends Disposable { return defaultLayer; } - private _setTransForm() { + /** + * Triggered when scale, resize of scene. + * origin name: _setTransForm + * + */ + private _transformHandler() { + // why not use this.ancestorScaleXY ? + // if parent scale changed, this.ancestorScaleXY will remain same. const composeResult = Transform.create().composeMatrix({ scaleX: this.scaleX, scaleY: this.scaleY, diff --git a/packages/engine-render/src/thin-engine.ts b/packages/engine-render/src/thin-engine.ts deleted file mode 100644 index 8c6fa68add1..00000000000 --- a/packages/engine-render/src/thin-engine.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright 2023-present DreamNum Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { IDisposable } from '@univerjs/core'; -import type { CURSOR_TYPE } from './basics/const'; - -import type { IEvent } from './basics/i-events'; -import type { ITransformChangeState } from './basics/interfaces'; -import type { Canvas } from './canvas'; -import { Disposable, EventSubject } from '@univerjs/core'; -import { RENDER_CLASS_TYPE } from './basics/const'; - -// FIXME: ThinEngine and ThinScene should be removed - -export abstract class ThinEngine extends Disposable { - onInputChanged$ = new EventSubject(); - - onTransformChange$ = new EventSubject(); - - private _scenes: { [sceneKey: string]: T } = {}; - - private _activeScene: T | null = null; - - get classType() { - return RENDER_CLASS_TYPE.ENGINE; - } - - get activeScene() { - return this._activeScene; - } - - get width() { - return 0; - } - - get height() { - return 0; - } - - getScenes() { - return this._scenes; - } - - getScene(sceneKey: string): T | null { - return this._scenes[sceneKey]; - } - - hasScene(sceneKey: string): boolean { - return sceneKey in this._scenes; - } - - addScene(sceneInstance: T): T { - const sceneKey = (sceneInstance as any).sceneKey; - if (this.hasScene(sceneKey)) { - console.warn('Scenes has similar key, it will be covered'); - } - // const newScene = new Scene(this); - this._scenes[sceneKey] = sceneInstance; - return sceneInstance; - } - - setActiveScene(sceneKey: string): T | null { - const scene = this.getScene(sceneKey); - if (scene) { - this._activeScene = scene; - } - return scene; - } - - abstract setRemainCapture(): void; - - hasActiveScene(): boolean { - return this._activeScene != null; - } - - override dispose() { - super.dispose(); - - const scenes = { ...this.getScenes() }; - const sceneKeys = Object.keys(scenes); - sceneKeys.forEach((key) => { - (scenes[key] as any).dispose(); - }); - this._scenes = {}; - } - - getParent() {} - - // TODO @lumixraku, it seems delete scene with key. - remainScene(key: string) { - const scenes = this.getScenes(); - if (scenes[key]) { - const scene = scenes[key]; - delete scenes[key]; - return scene; - } - } - - abstract clearCanvas(): void; - - abstract getCanvas(): Canvas; - - abstract getCanvasElement(): HTMLCanvasElement; - - abstract setCanvasCursor(val: CURSOR_TYPE): void; - - abstract getPixelRatio(): number; -} diff --git a/packages/facade/src/apis/__tests__/create-test-bed.ts b/packages/facade/src/apis/__tests__/create-test-bed.ts index 97ee8fcb46b..aa78352b081 100644 --- a/packages/facade/src/apis/__tests__/create-test-bed.ts +++ b/packages/facade/src/apis/__tests__/create-test-bed.ts @@ -39,6 +39,7 @@ import { RangeProtectionRuleModel, RefRangeService, SheetInterceptorService, + SheetSkeletonService, SheetsSelectionsService, WorkbookPermissionService, WorksheetPermissionService, @@ -166,6 +167,7 @@ export function createFacadeTestBed(workbookData?: IWorkbookData, dependencies?: injector.add([SheetsRenderService]); injector.add([IShortcutService, { useClass: ShortcutService }]); injector.add([IPlatformService, { useClass: PlatformService }]); + injector.add([SheetSkeletonService]); injector.add([SheetSkeletonManagerService]); injector.add([FormulaDataModel]); injector.add([LexerTreeBuilder]); diff --git a/packages/facade/src/apis/sheets/__tests__/create-worksheet-test-bed.ts b/packages/facade/src/apis/sheets/__tests__/create-worksheet-test-bed.ts index d95e471c0b0..270b5be8201 100644 --- a/packages/facade/src/apis/sheets/__tests__/create-worksheet-test-bed.ts +++ b/packages/facade/src/apis/sheets/__tests__/create-worksheet-test-bed.ts @@ -39,6 +39,7 @@ import { RangeProtectionRuleModel, RefRangeService, SheetInterceptorService, + SheetSkeletonService, SheetsSelectionsService, WorkbookPermissionService, WorksheetPermissionService, @@ -159,6 +160,7 @@ export function createWorksheetTestBed(workbookData?: IWorkbookData, dependencie injector.add([SheetsRenderService]); injector.add([IShortcutService, { useClass: ShortcutService }]); injector.add([IPlatformService, { useClass: PlatformService }]); + injector.add([SheetSkeletonService]); injector.add([SheetSkeletonManagerService]); injector.add([FormulaDataModel]); injector.add([LexerTreeBuilder]); diff --git a/packages/sheets-data-validation-ui/src/controllers/dv-render.controller.ts b/packages/sheets-data-validation-ui/src/controllers/dv-render.controller.ts index e383c16ca3d..adf43c224d5 100644 --- a/packages/sheets-data-validation-ui/src/controllers/dv-render.controller.ts +++ b/packages/sheets-data-validation-ui/src/controllers/dv-render.controller.ts @@ -191,7 +191,7 @@ export class SheetsDataValidationRenderController extends RxDisposable { interceptorAutoHeight: () => { const skeleton = this._renderManagerService.getRenderById(unitId) ?.with(SheetSkeletonManagerService) - .getWorksheetSkeleton(subUnitId) + .getSkeletonParam(subUnitId) ?.skeleton; if (!skeleton) { return undefined; @@ -200,7 +200,7 @@ export class SheetsDataValidationRenderController extends RxDisposable { const info: ICellRenderContext = { data: cell, - style: skeleton.getsStyles().getStyleByCell(cell), + style: skeleton.getStyles().getStyleByCell(cell), primaryWithCoord: skeleton.getCellWithCoordByIndex(mergeCell?.startRow ?? row, mergeCell?.startColumn ?? col), unitId, subUnitId, @@ -214,7 +214,7 @@ export class SheetsDataValidationRenderController extends RxDisposable { interceptorAutoWidth: () => { const skeleton = this._renderManagerService.getRenderById(unitId) ?.with(SheetSkeletonManagerService) - .getWorksheetSkeleton(subUnitId) + .getSkeletonParam(subUnitId) ?.skeleton; if (!skeleton) { return undefined; @@ -223,7 +223,7 @@ export class SheetsDataValidationRenderController extends RxDisposable { const info: ICellRenderContext = { data: cell, - style: skeleton.getsStyles().getStyleByCell(cell), + style: skeleton.getStyles().getStyleByCell(cell), primaryWithCoord: skeleton.getCellWithCoordByIndex(mergeCell?.startRow ?? row, mergeCell?.startColumn ?? col), unitId, subUnitId, @@ -341,7 +341,7 @@ export class SheetsDataValidationMobileRenderController extends RxDisposable { interceptorAutoHeight: () => { const skeleton = this._renderManagerService.getRenderById(unitId) ?.with(SheetSkeletonManagerService) - .getWorksheetSkeleton(subUnitId) + .getSkeletonParam(subUnitId) ?.skeleton; if (!skeleton) { return undefined; @@ -350,7 +350,7 @@ export class SheetsDataValidationMobileRenderController extends RxDisposable { const info: ICellRenderContext = { data: cell, - style: skeleton.getsStyles().getStyleByCell(cell), + style: skeleton.getStyles().getStyleByCell(cell), primaryWithCoord: skeleton.getCellWithCoordByIndex(mergeCell?.startRow ?? row, mergeCell?.startColumn ?? col), unitId, subUnitId, diff --git a/packages/sheets-data-validation-ui/src/controllers/dv-rerender.controller.ts b/packages/sheets-data-validation-ui/src/controllers/dv-rerender.controller.ts index 82f10cb8eff..c02edba5f40 100644 --- a/packages/sheets-data-validation-ui/src/controllers/dv-rerender.controller.ts +++ b/packages/sheets-data-validation-ui/src/controllers/dv-rerender.controller.ts @@ -41,7 +41,7 @@ export class SheetsDataValidationReRenderController extends Disposable implement sheetIds.add(value.subUnitId); }); sheetIds.forEach((sheetId) => { - this._sheetSkeletonManagerService.getWorksheetSkeleton(sheetId)?.skeleton.makeDirty(true); + this._sheetSkeletonManagerService.getSkeletonParam(sheetId)?.skeleton.makeDirty(true); }); this._context.mainComponent?.makeForceDirty(); }; diff --git a/packages/sheets-drawing-ui/src/controllers/sheet-drawing-printing.controller.ts b/packages/sheets-drawing-ui/src/controllers/sheet-drawing-printing.controller.ts index f866ec8f95e..b7381371e08 100644 --- a/packages/sheets-drawing-ui/src/controllers/sheet-drawing-printing.controller.ts +++ b/packages/sheets-drawing-ui/src/controllers/sheet-drawing-printing.controller.ts @@ -65,7 +65,7 @@ export class SheetDrawingPrintingController extends Disposable { return next(range); } - const skeleton = renderer.with(SheetSkeletonManagerService).getWorksheetSkeleton(subUnitId); + const skeleton = renderer.with(SheetSkeletonManagerService).getSkeletonParam(subUnitId); if (!skeleton) { return next(range); } diff --git a/packages/sheets-drawing-ui/src/controllers/sheet-drawing-update.controller.ts b/packages/sheets-drawing-ui/src/controllers/sheet-drawing-update.controller.ts index 56e33b54d2e..8a07df03cd8 100644 --- a/packages/sheets-drawing-ui/src/controllers/sheet-drawing-update.controller.ts +++ b/packages/sheets-drawing-ui/src/controllers/sheet-drawing-update.controller.ts @@ -57,7 +57,7 @@ export function getDrawingSizeByCell( return false; } const skeletonManagerService = currentRender.with(SheetSkeletonManagerService); - const skeleton = skeletonManagerService.getWorksheetSkeleton(location.subUnitId)?.skeleton; + const skeleton = skeletonManagerService.getSkeletonParam(location.subUnitId)?.skeleton; if (skeleton == null) { return false; } diff --git a/packages/sheets-drawing-ui/src/services/canvas-float-dom-manager.service.ts b/packages/sheets-drawing-ui/src/services/canvas-float-dom-manager.service.ts index 1937b209f34..592411e4cbb 100644 --- a/packages/sheets-drawing-ui/src/services/canvas-float-dom-manager.service.ts +++ b/packages/sheets-drawing-ui/src/services/canvas-float-dom-manager.service.ts @@ -286,7 +286,7 @@ export class SheetCanvasFloatDomManagerService extends Disposable { return; } - const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getWorksheetSkeleton(subUnitId); + const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getSkeletonParam(subUnitId); if (!skeleton) { return; } @@ -436,7 +436,7 @@ export class SheetCanvasFloatDomManagerService extends Disposable { const map = this._ensureMap(unitId, subUnitId); const ids = Array.from(map.keys()); const target = getSheetCommandTarget(this._univerInstanceService, { unitId, subUnitId }); - const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getWorksheetSkeleton(subUnitId); + const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getSkeletonParam(subUnitId); if (!renderObject || !target || !skeleton) { return; } diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts index 0111c0778db..9399eafc429 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts @@ -61,7 +61,7 @@ export function useSheetHighlight(unitId: string) { const currentSheetId = worksheet.getSheetId(); const getSheetIdByName = (name: string) => workbook?.getSheetBySheetName(name)?.getSheetId(); - const skeleton = sheetSkeletonManagerService?.getWorksheetSkeleton(currentSheetId)?.skeleton; + const skeleton = sheetSkeletonManagerService?.getSkeletonParam(currentSheetId)?.skeleton; if (!skeleton) return; for (let i = 0, len = refSelections.length; i < len; i++) { diff --git a/packages/sheets-hyper-link-ui/src/controllers/popup.controller.ts b/packages/sheets-hyper-link-ui/src/controllers/popup.controller.ts index e3b4aac22ff..99e41fdd675 100644 --- a/packages/sheets-hyper-link-ui/src/controllers/popup.controller.ts +++ b/packages/sheets-hyper-link-ui/src/controllers/popup.controller.ts @@ -122,7 +122,7 @@ export class SheetsHyperLinkPopupController extends Disposable { } const skeleton = renderer?.with(SheetSkeletonManagerService) - .getWorksheetSkeleton(subUnitId) + .getSkeletonParam(subUnitId) ?.skeleton; const currentCol = col; @@ -210,7 +210,7 @@ export class SheetsHyperLinkPopupController extends Disposable { const rect = customRange.rects[customRange.rects.length - 1]; const skeleton = this._renderManagerService.getRenderById(unitId) ?.with(SheetSkeletonManagerService) - .getWorksheetSkeleton(sheetId) + .getSkeletonParam(sheetId) ?.skeleton; if (!skeleton || !rect) { return; diff --git a/packages/sheets-hyper-link/src/controllers/set-range.controller.ts b/packages/sheets-hyper-link/src/controllers/set-range.controller.ts index d95738fce38..09219f02e62 100644 --- a/packages/sheets-hyper-link/src/controllers/set-range.controller.ts +++ b/packages/sheets-hyper-link/src/controllers/set-range.controller.ts @@ -154,7 +154,7 @@ export class SheetHyperLinkSetRangeController extends Disposable { return next(cell); } // const renderer = this._renderManagerService.getRenderById(unitId); - // const skeleton = renderer?.with(SheetSkeletonManagerService).getWorksheetSkeleton(subUnitId); + // const skeleton = renderer?.with(SheetSkeletonManagerService).getSkeletonParam(subUnitId); // if (!skeleton) { // return next(cell); // } diff --git a/packages/sheets-thread-comment-ui/src/controllers/sheets-thread-comment-popup.controller.ts b/packages/sheets-thread-comment-ui/src/controllers/sheets-thread-comment-popup.controller.ts index 9beff43e9d7..68edd9398d2 100644 --- a/packages/sheets-thread-comment-ui/src/controllers/sheets-thread-comment-popup.controller.ts +++ b/packages/sheets-thread-comment-ui/src/controllers/sheets-thread-comment-popup.controller.ts @@ -63,7 +63,7 @@ export class SheetsThreadCommentPopupController extends Disposable { private _handleSelectionChange(selections: ISelectionWithStyle[], unitId: string, subUnitId: string) { const range = selections[0]?.range; const render = this._renderManagerService.getRenderById(unitId); - const skeleton = render?.with(SheetSkeletonManagerService).getWorksheetSkeleton(subUnitId)?.skeleton; + const skeleton = render?.with(SheetSkeletonManagerService).getSkeletonParam(subUnitId)?.skeleton; if (!skeleton) { return; } diff --git a/packages/sheets-ui/src/commands/commands/__tests__/create-selection-command-test-bed.ts b/packages/sheets-ui/src/commands/commands/__tests__/create-selection-command-test-bed.ts index 769a9a445d7..547cf91942b 100644 --- a/packages/sheets-ui/src/commands/commands/__tests__/create-selection-command-test-bed.ts +++ b/packages/sheets-ui/src/commands/commands/__tests__/create-selection-command-test-bed.ts @@ -17,7 +17,7 @@ import type { IWorkbookData } from '@univerjs/core'; import { Disposable, DisposableCollection, ICommandService, LocaleType, UniverInstanceType } from '@univerjs/core'; import { IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; -import { CancelFrozenCommand, SetFrozenMutation, SetSelectionsOperation } from '@univerjs/sheets'; +import { CancelFrozenCommand, SetFrozenMutation, SetSelectionsOperation, SheetSkeletonService } from '@univerjs/sheets'; import { SheetScrollManagerService } from '../../../services/scroll-manager.service'; import { SelectAllService } from '../../../services/select-all/select-all.service'; @@ -193,6 +193,7 @@ export function createFrozenCommandTestBed(workbookData?: IWorkbookData) { const injector = univer.__getInjector(); // NOTE: this is a hack. Please refer to ./services/clipboard/__tests__/clipboard-test-bed.ts + const mockSheetSkService = new SheetSkeletonService(injector); const fakeSheetSkeletonManagerService = new SheetSkeletonManagerService({ unit: sheet, unitId, @@ -203,7 +204,7 @@ export function createFrozenCommandTestBed(workbookData?: IWorkbookData) { mainComponent: null as any, components: null as any, isMainScene: true, - }, injector); + }, injector, mockSheetSkService); injector.add([SheetSkeletonManagerService, { useValue: fakeSheetSkeletonManagerService }]); injector.get(IRenderManagerService).addRender(unitId, { diff --git a/packages/sheets-ui/src/commands/commands/__tests__/set-frozen.command.spec.ts b/packages/sheets-ui/src/commands/commands/__tests__/set-frozen.command.spec.ts index 03e1e61798e..cd06f2aa218 100644 --- a/packages/sheets-ui/src/commands/commands/__tests__/set-frozen.command.spec.ts +++ b/packages/sheets-ui/src/commands/commands/__tests__/set-frozen.command.spec.ts @@ -15,10 +15,11 @@ */ import type { IFreeze, Injector, IWorkbookData, Univer, Workbook } from '@univerjs/core'; +import type { IScrollStateWithSearchParam } from '../../../services/scroll-manager.service'; import { ICommandService, IUniverInstanceService, RANGE_TYPE, UniverInstanceType } from '@univerjs/core'; import { CancelFrozenCommand, SheetsSelectionsService } from '@univerjs/sheets'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { SheetScrollManagerService } from '../../../services/scroll-manager.service'; import { SetColumnFrozenCommand, @@ -34,7 +35,7 @@ describe('Test commands used for change selections', () => { let selectionManagerService: SheetsSelectionsService; let scrollManagerService: SheetScrollManagerService; - const currentInfo = { + const searchParam = { unitId: 'test', sheetId: 'sheet1', }; @@ -68,13 +69,15 @@ describe('Test commands used for change selections', () => { } const scrollTo = (startRow: number, startColumn: number, offsetX = 0, offsetY = 0) => { - scrollManagerService.setScrollInfoAndEmitEvent({ + const scrollInfo = { sheetViewStartRow: startRow, sheetViewStartColumn: startColumn, offsetX, offsetY, - ...currentInfo, - }); + ...searchParam, + }; + scrollManagerService.emitRawScrollParam(scrollInfo); + scrollManagerService.setValidScrollState(scrollInfo); }; const getFreeze = () => { @@ -97,9 +100,15 @@ describe('Test commands used for change selections', () => { commandService = get(ICommandService); selectionManagerService = get(SheetsSelectionsService); scrollManagerService = get(SheetScrollManagerService); - scrollManagerService.setSearchParamAndRefresh({ - ...currentInfo, + scrollManagerService.setSearchParam({ + ...searchParam, }); + const scrollState = scrollManagerService.getScrollStateByParam(searchParam); + const scrollStateWithSheetId: IScrollStateWithSearchParam = { + ...scrollState!, + ...searchParam, + }; + scrollManagerService.emitRawScrollParam(scrollStateWithSheetId); } afterEach(disposeTestBed); diff --git a/packages/sheets-ui/src/commands/operations/scroll.operation.ts b/packages/sheets-ui/src/commands/operations/scroll.operation.ts index 1efd2f66ac3..bf428791759 100644 --- a/packages/sheets-ui/src/commands/operations/scroll.operation.ts +++ b/packages/sheets-ui/src/commands/operations/scroll.operation.ts @@ -39,7 +39,7 @@ export const SetScrollOperation: IOperation = { // const worksheet = workbook!.getSheetBySheetId(sheetId); // const { xSplit, ySplit } = worksheet!.getConfig().freeze; - scrollManagerService.setScrollInfoAndEmitEvent({ + scrollManagerService.emitRawScrollParam({ unitId, sheetId, offsetX, diff --git a/packages/sheets-ui/src/common/utils.ts b/packages/sheets-ui/src/common/utils.ts index 8d09820eae0..98ac1fd4415 100644 --- a/packages/sheets-ui/src/common/utils.ts +++ b/packages/sheets-ui/src/common/utils.ts @@ -188,9 +188,9 @@ export function transformPosition2Offset(x: number, y: number, scene: Scene, ske const freeze = worksheet.getFreeze(); const { startColumn, startRow, xSplit, ySplit } = freeze; // freeze start - const startSheetView = skeleton.getNoMergeCellPositionByIndexWithNoHeader(startRow - ySplit, startColumn - xSplit); + const startSheetView = skeleton.getNoMergeCellWithCoordByIndex(startRow - ySplit, startColumn - xSplit, false); // freeze end - const endSheetView = skeleton.getNoMergeCellPositionByIndexWithNoHeader(startRow, startColumn); + const endSheetView = skeleton.getNoMergeCellWithCoordByIndex(startRow, startColumn, false); const { rowHeaderWidth, columnHeaderHeight } = skeleton; const freezeWidth = endSheetView.startX - startSheetView.startX; const freezeHeight = endSheetView.startY - startSheetView.startY; @@ -301,10 +301,10 @@ export function getHoverCellPosition(currentRender: IRender, workbook: Workbook, }; const position: IPosition = { - startX: (skeleton.getOffsetByPositionX(anchorCell.startColumn - 1) - scrollXY.x) * scaleX, - endX: (skeleton.getOffsetByPositionX(anchorCell.endColumn) - scrollXY.x) * scaleX, - startY: (skeleton.getOffsetByPositionY(anchorCell.startRow - 1) - scrollXY.y) * scaleY, - endY: (skeleton.getOffsetByPositionY(anchorCell.endRow) - scrollXY.y) * scaleY, + startX: (skeleton.getOffsetByColumn(anchorCell.startColumn - 1) - scrollXY.x) * scaleX, + endX: (skeleton.getOffsetByColumn(anchorCell.endColumn) - scrollXY.x) * scaleX, + startY: (skeleton.getOffsetByRow(anchorCell.startRow - 1) - scrollXY.y) * scaleY, + endY: (skeleton.getOffsetByRow(anchorCell.endRow) - scrollXY.y) * scaleY, }; return { diff --git a/packages/sheets-ui/src/controllers/auto-height.controller.ts b/packages/sheets-ui/src/controllers/auto-height.controller.ts index c1a3e32cf94..6b0259d60a6 100644 --- a/packages/sheets-ui/src/controllers/auto-height.controller.ts +++ b/packages/sheets-ui/src/controllers/auto-height.controller.ts @@ -61,13 +61,13 @@ export class AutoHeightController extends Disposable { const subUnitId = worksheet.getSheetId(); const sheetSkeletonService = this._renderManagerService.getRenderById(unitId)!.with(SheetSkeletonManagerService); - if (!subUnitId || !sheetSkeletonService.getCurrent()) { + if (!subUnitId || !sheetSkeletonService.getCurrentParam()) { return { redos: [], undos: [], }; } - const { skeleton } = sheetSkeletonService.getCurrent()!; + const { skeleton } = sheetSkeletonService.getCurrentParam()!; const rowsAutoHeightInfo = skeleton.calculateAutoHeightInRange(ranges); const redoParams: ISetWorksheetRowAutoHeightMutationParams = { diff --git a/packages/sheets-ui/src/controllers/auto-width.controller.ts b/packages/sheets-ui/src/controllers/auto-width.controller.ts index 55efee3f6fc..e7b92b7f7ac 100644 --- a/packages/sheets-ui/src/controllers/auto-width.controller.ts +++ b/packages/sheets-ui/src/controllers/auto-width.controller.ts @@ -72,9 +72,9 @@ export class AutoWidthController extends Disposable { const { unitId, subUnitId, worksheet } = target; const sheetSkeletonService = this._renderManagerService.getRenderById(unitId)!.with(SheetSkeletonManagerService); - if (!sheetSkeletonService.getCurrent()) return defaultValue; + if (!sheetSkeletonService.getCurrentParam()) return defaultValue; - const { skeleton } = sheetSkeletonService.getCurrent()!; + const { skeleton } = sheetSkeletonService.getCurrentParam()!; const colsAutoWidthInfo: IColAutoWidthInfo[] = skeleton.calculateAutoWidthInRange(params.ranges); const colWidthObject: IObjectArrayPrimitiveType> = {}; diff --git a/packages/sheets-ui/src/controllers/cell-custom-render.controller.ts b/packages/sheets-ui/src/controllers/cell-custom-render.controller.ts index 6229004c9b6..f72ec918024 100644 --- a/packages/sheets-ui/src/controllers/cell-custom-render.controller.ts +++ b/packages/sheets-ui/src/controllers/cell-custom-render.controller.ts @@ -119,7 +119,7 @@ export class CellCustomRenderController extends Disposable implements IRenderMod const info: ICellRenderContext = { data: cellData, - style: skeleton.getsStyles().getStyleByCell(cellData), + style: skeleton.getStyles().getStyleByCell(cellData), primaryWithCoord: skeleton.getCellWithCoordByIndex(cellIndex.actualRow, cellIndex.actualCol), unitId, subUnitId, @@ -197,7 +197,7 @@ export class CellCustomRenderController extends Disposable implements IRenderMod }; this.disposeWithMe(this._sheetSkeletonManagerService.currentSkeleton$.subscribe(handleSkeletonChange)); - handleSkeletonChange(this._sheetSkeletonManagerService.getCurrent()); + handleSkeletonChange(this._sheetSkeletonManagerService.getCurrentParam()); this.disposeWithMe(disposableCollection); } } diff --git a/packages/sheets-ui/src/controllers/clipboard/clipboard.controller.ts b/packages/sheets-ui/src/controllers/clipboard/clipboard.controller.ts index 33b7e2268c0..39a51bd9304 100644 --- a/packages/sheets-ui/src/controllers/clipboard/clipboard.controller.ts +++ b/packages/sheets-ui/src/controllers/clipboard/clipboard.controller.ts @@ -553,7 +553,7 @@ export class SheetClipboardController extends RxDisposable { } private _generateDocumentDataModelSnapshot(snapshot: Partial) { - const currentSkeleton = this._renderManagerService.withCurrentTypeOfUnit(UniverInstanceType.UNIVER_SHEET, SheetSkeletonManagerService)?.getCurrent(); + const currentSkeleton = this._renderManagerService.withCurrentTypeOfUnit(UniverInstanceType.UNIVER_SHEET, SheetSkeletonManagerService)?.getCurrentParam(); if (currentSkeleton == null) { return null; } diff --git a/packages/sheets-ui/src/controllers/drag-render.controller.ts b/packages/sheets-ui/src/controllers/drag-render.controller.ts index fbd137abe52..acaa7bb8ebf 100644 --- a/packages/sheets-ui/src/controllers/drag-render.controller.ts +++ b/packages/sheets-ui/src/controllers/drag-render.controller.ts @@ -58,7 +58,7 @@ export class DragRenderController extends Disposable implements IRenderModule, I }); }; - handleSkeletonChange(this._sheetSkeletonManagerService.getCurrent()); + handleSkeletonChange(this._sheetSkeletonManagerService.getCurrentParam()); this.disposeWithMe(this._sheetSkeletonManagerService.currentSkeleton$.subscribe((skeletonParam) => { handleSkeletonChange(skeletonParam); })); diff --git a/packages/sheets-ui/src/controllers/force-string-render.controller.ts b/packages/sheets-ui/src/controllers/force-string-render.controller.ts index 793cd599fae..20c21d73fd8 100644 --- a/packages/sheets-ui/src/controllers/force-string-render.controller.ts +++ b/packages/sheets-ui/src/controllers/force-string-render.controller.ts @@ -46,7 +46,7 @@ export class ForceStringRenderController extends RxDisposable implements IRender priority: 10, effect: InterceptorEffectEnum.Style, handler: (cell, pos, next) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (!skeleton) { return next(cell); } diff --git a/packages/sheets-ui/src/controllers/hover-render.controller.ts b/packages/sheets-ui/src/controllers/hover-render.controller.ts index 575040d21dc..c6df51e70e3 100644 --- a/packages/sheets-ui/src/controllers/hover-render.controller.ts +++ b/packages/sheets-ui/src/controllers/hover-render.controller.ts @@ -79,7 +79,7 @@ export class HoverRenderController extends Disposable implements IRenderModule { })); }; - handleSkeletonChange(this._sheetSkeletonManagerService.getCurrent()); + handleSkeletonChange(this._sheetSkeletonManagerService.getCurrentParam()); this.disposeWithMe(this._sheetSkeletonManagerService.currentSkeleton$.subscribe((skeletonParam) => { handleSkeletonChange(skeletonParam); })); diff --git a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts index 6ffe1e4d6c5..949e3da9939 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts @@ -102,7 +102,7 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende const unitId = this._context.unitId; const sheetId = this._context.unit.getActiveSheet()?.getSheetId(); if (!sheetId) return; - const mergeInfo = this._sheetSkeletonManagerService.getWorksheetSkeleton(sheetId)?.skeleton.getCellWithCoordByIndex(primary.actualRow, primary.actualColumn); + const mergeInfo = this._sheetSkeletonManagerService.getSkeletonParam(sheetId)?.skeleton.getCellWithCoordByIndex(primary.actualRow, primary.actualColumn); const newPrimary: ISelectionCell = mergeInfo ? { actualRow: mergeInfo.actualRow, diff --git a/packages/sheets-ui/src/controllers/render-controllers/freeze.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/freeze.render-controller.ts index cbbfaa4123b..53c4c208897 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/freeze.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/freeze.render-controller.ts @@ -14,6 +14,22 @@ * limitations under the License. */ +import type { ICommandInfo, IFreeze, IRange, IStyleSheet, IWorksheetData, Nullable, Workbook } from '@univerjs/core'; +import type { IMouseEvent, IPointerEvent, IRenderContext, IRenderModule, Viewport } from '@univerjs/engine-render'; +import type { + IInsertColCommandParams, + IInsertRowCommandParams, + IMoveColsCommandParams, + IMoveRowsCommandParams, + IRemoveRowColCommandParams, + ISetColHiddenMutationParams, + ISetFrozenMutationParams, + ISetRowHiddenMutationParams, + ISetWorksheetColWidthMutationParams, + ISetWorksheetRowAutoHeightMutationParams, + ISetWorksheetRowHeightMutationParams, +} from '@univerjs/sheets'; +import type { IViewportScrollState } from '../../services/scroll-manager.service'; import { ColorKit, createInterceptorKey, @@ -28,6 +44,7 @@ import { toDisposable, } from '@univerjs/core'; import { CURSOR_TYPE, Rect, SHEET_VIEWPORT_KEY, TRANSFORM_CHANGE_OBSERVABLE_TYPE, Vector2 } from '@univerjs/engine-render'; + import { InsertColCommand, InsertRangeMoveDownCommand, @@ -52,30 +69,13 @@ import { SheetsSelectionsService, } from '@univerjs/sheets'; import { Subscription } from 'rxjs'; -import type { ICommandInfo, IFreeze, IRange, IStyleSheet, IWorksheetData, Nullable, Workbook } from '@univerjs/core'; -import type { IMouseEvent, IPointerEvent, IRenderContext, IRenderModule, Viewport } from '@univerjs/engine-render'; - -import type { - IInsertColCommandParams, - IInsertRowCommandParams, - IMoveColsCommandParams, - IMoveRowsCommandParams, - IRemoveRowColCommandParams, - ISetColHiddenMutationParams, - ISetFrozenMutationParams, - ISetRowHiddenMutationParams, - ISetWorksheetColWidthMutationParams, - ISetWorksheetRowAutoHeightMutationParams, - ISetWorksheetRowHeightMutationParams, -} from '@univerjs/sheets'; import { ScrollCommand } from '../../commands/commands/set-scroll.command'; import { SetZoomRatioOperation } from '../../commands/operations/set-zoom-ratio.operation'; -import { SHEET_COMPONENT_HEADER_LAYER_INDEX } from '../../common/keys'; +import { SHEET_COMPONENT_HEADER_LAYER_INDEX } from '../../common/keys'; import { SheetScrollManagerService } from '../../services/scroll-manager.service'; import { SheetSkeletonManagerService } from '../../services/sheet-skeleton-manager.service'; import { getCoordByOffset, getSheetObject } from '../utils/component-tools'; -import type { IViewportScrollState } from '../../services/scroll-manager.service'; enum FREEZE_DIRECTION_TYPE { ROW, @@ -197,7 +197,7 @@ export class HeaderFreezeRenderController extends Disposable implements IRenderM const config = freezeConfig ?? this._getFreeze(); if (config == null) return null; - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; const { startRow: freezeRow, startColumn: freezeColumn } = config; const position = this._getPositionByIndex(freezeRow, freezeColumn); @@ -398,7 +398,7 @@ export class HeaderFreezeRenderController extends Disposable implements IRenderM return; } - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } @@ -469,7 +469,7 @@ export class HeaderFreezeRenderController extends Disposable implements IRenderM freezeObjectMainRect: Rect, freezeDirectionType: FREEZE_DIRECTION_TYPE = FREEZE_DIRECTION_TYPE.ROW ) { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } @@ -794,7 +794,7 @@ export class HeaderFreezeRenderController extends Disposable implements IRenderM xSplit: number = 0, resetScroll = ResetScrollType.ALL ) { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } @@ -854,9 +854,9 @@ export class HeaderFreezeRenderController extends Disposable implements IRenderM } // freeze start - const startSheetView = skeleton.getNoMergeCellPositionByIndexWithNoHeader(row - ySplit, column - xSplit); + const startSheetView = skeleton.getNoMergeCellWithCoordByIndex(row - ySplit, column - xSplit, false); // freeze end - const endSheetView = skeleton.getNoMergeCellPositionByIndexWithNoHeader(row, column); + const endSheetView = skeleton.getNoMergeCellWithCoordByIndex(row, column, false); viewMainLeftTop.disable(); viewMainTop.disable(); @@ -1545,8 +1545,8 @@ export class HeaderFreezeRenderController extends Disposable implements IRenderM return; } - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; - const position = skeleton?.getNoMergeCellPositionByIndex(row, column); + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; + const position = skeleton?.getNoMergeCellWithCoordByIndex(row, column); if (skeleton == null) { return; } @@ -1566,7 +1566,7 @@ export class HeaderFreezeRenderController extends Disposable implements IRenderM private _getFreeze() { const config: IWorksheetData | undefined = this._sheetSkeletonManagerService - .getCurrent() + .getCurrentParam() ?.skeleton .getWorksheetConfig(); diff --git a/packages/sheets-ui/src/controllers/render-controllers/header-menu.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/header-menu.render-controller.ts index bae867efe02..db4349aa8d9 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/header-menu.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/header-menu.render-controller.ts @@ -124,7 +124,7 @@ export class HeaderMenuRenderController extends Disposable implements IRenderMod initialType === HEADER_HOVER_TYPE.ROW ? spreadsheetRowHeader : spreadsheetColumnHeader; const pointerMoveHandler = (evt: IPointerEvent | IMouseEvent) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } diff --git a/packages/sheets-ui/src/controllers/render-controllers/header-move.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/header-move.render-controller.ts index f2f7f47ffb1..c1f75764556 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/header-move.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/header-move.render-controller.ts @@ -126,7 +126,7 @@ export class HeaderMoveRenderController extends Disposable implements IRenderMod // only style cursor style when pointer move const pointerMoveHandler = (evt: IPointerEvent | IMouseEvent) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } @@ -166,7 +166,7 @@ export class HeaderMoveRenderController extends Disposable implements IRenderMod const pointerDownHandler = (evt: IPointerEvent | IMouseEvent, state: EventState) => { if (state.isStopPropagation) return; - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } @@ -356,7 +356,7 @@ export class HeaderMoveRenderController extends Disposable implements IRenderMod initialType: RANGE_TYPE.ROW | RANGE_TYPE.COLUMN ) { const scene = this._context.scene; - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } @@ -377,7 +377,7 @@ export class HeaderMoveRenderController extends Disposable implements IRenderMod const { row, column } = moveActualSelection; - const startCell = skeleton.getNoMergeCellPositionByIndex(row, column); + const startCell = skeleton.getNoMergeCellWithCoordByIndex(row, column); const { startX: cellStartX, startY: cellStartY, endX: cellEndX, endY: cellEndY } = startCell; diff --git a/packages/sheets-ui/src/controllers/render-controllers/header-resize.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/header-resize.render-controller.ts index 98285680ed9..8b6dc0aac44 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/header-resize.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/header-resize.render-controller.ts @@ -160,7 +160,7 @@ export class HeaderResizeRenderController extends Disposable implements IRenderM }; const pointerMoveEvent = (evt: IPointerEvent | IMouseEvent, _state: EventState) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null || this._rowResizeRect == null || this._columnResizeRect == null) { return; } @@ -307,7 +307,7 @@ export class HeaderResizeRenderController extends Disposable implements IRenderM this.disposeWithMe( eventBindingObject.onPointerDown$.subscribeEvent((evt: IPointerEvent | IMouseEvent) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) return; const scene = this._context.scene; @@ -326,9 +326,9 @@ export class HeaderResizeRenderController extends Disposable implements IRenderM this._startOffsetY = transformCoord.y; - const currentOffsetX = skeleton.getOffsetByPositionX(this._currentColumn); - const currentOffsetY = skeleton.getOffsetByPositionY(this._currentRow); - const cell = skeleton.getNoMergeCellPositionByIndex(this._currentRow, this._currentColumn); + const currentOffsetX = skeleton.getOffsetByColumn(this._currentColumn); + const currentOffsetY = skeleton.getOffsetByRow(this._currentRow); + const cell = skeleton.getNoMergeCellWithCoordByIndex(this._currentRow, this._currentColumn); let isStartMove = false; let moveChangeX = 0; @@ -488,7 +488,7 @@ export class HeaderResizeRenderController extends Disposable implements IRenderM const scene = this._context.scene; scene.resetCursor(); - const sk = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const sk = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (!sk) return; const startRow = 0; diff --git a/packages/sheets-ui/src/controllers/render-controllers/header-unhide.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/header-unhide.render-controller.ts index 55ef3b54fce..b5998a53ab5 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/header-unhide.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/header-unhide.render-controller.ts @@ -116,7 +116,7 @@ export class HeaderUnhideRenderController extends RxDisposable { } private _update(workbook: Workbook, worksheet: Worksheet): void { - const skeleton = this._sheetSkeletonManagerService.getWorksheetSkeleton(worksheet.getSheetId())?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getSkeletonParam(worksheet.getSheetId())?.skeleton; if (!skeleton) return; // steps to render the unhide button for the current worksheet diff --git a/packages/sheets-ui/src/controllers/render-controllers/mobile/mobile-contextmenu.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/mobile/mobile-contextmenu.render-controller.ts index 473c11c536f..c1f327811a2 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/mobile/mobile-contextmenu.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/mobile/mobile-contextmenu.render-controller.ts @@ -46,7 +46,7 @@ export class SheetContextMenuMobileRenderController extends Disposable implement let listenToSelectionChangeEvent = false; this.disposeWithMe(this._selectionManagerService.selectionMoveStart$.subscribe(() => listenToSelectionChangeEvent = true)); this.disposeWithMe(this._selectionManagerService.selectionMoveEnd$.subscribe((selectionWithStyleList: ISelectionWithStyle[]) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()!.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()!.skeleton; if (!skeleton || !selectionWithStyleList || listenToSelectionChangeEvent === false) { return; } diff --git a/packages/sheets-ui/src/controllers/render-controllers/mobile/mobile-scroll.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/mobile/mobile-scroll.render-controller.ts index 1f7a23d77db..d41e8e07abe 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/mobile/mobile-scroll.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/mobile/mobile-scroll.render-controller.ts @@ -150,7 +150,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR } private _getFreeze(): Nullable { - const snapshot: IWorksheetData | undefined = this._sheetSkeletonManagerService.getCurrent()?.skeleton.getWorksheetConfig(); + const snapshot: IWorksheetData | undefined = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton.getWorksheetConfig(); if (snapshot == null) { return; } @@ -181,7 +181,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR return; } - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (!skeleton) return; // data source: scroll-manager.service.ts@_scrollInfo @@ -208,7 +208,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR this.disposeWithMe( viewportMain.onScrollAfter$.subscribeEvent((scrollAfterParam: IScrollObserverParam) => { if (!scrollAfterParam) return; - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) return; const sheetObject = this._getSheetObject(); if (skeleton == null || sheetObject == null) return; @@ -216,19 +216,22 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR const { viewportScrollX, viewportScrollY, scrollX, scrollY } = scrollAfterParam; // according to the actual scroll position, the most suitable row, column and offset combination is recalculated. - const { row, column, rowOffset, columnOffset } = skeleton.getDecomposedOffset( + const { row, column, rowOffset, columnOffset } = skeleton.getOffsetRelativeToRowCol( viewportScrollX, viewportScrollY ); - const scrollInfo = { + const scrollInfo: IViewportScrollState = { sheetViewStartRow: row, sheetViewStartColumn: column, offsetX: columnOffset, offsetY: rowOffset, + viewportScrollX, + viewportScrollY, + scrollX, scrollY, }; // lastestScrollInfo derived from viewportScrollX, viewportScrollY from onScrollAfter$ - this._scrollManagerService.setScrollStateToCurrSheet(scrollInfo); + this._scrollManagerService.setValidScrollStateToCurrSheet(scrollInfo); this._scrollManagerService.validViewportScrollInfo$.next({ ...scrollInfo, scrollX, @@ -245,7 +248,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR //#region scroll by bar this.disposeWithMe( viewportMain.onScrollByBar$.subscribeEvent((param) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null || param.isTrigger === false) { return; } @@ -258,7 +261,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR const freeze = this._getFreeze(); - const { row, column, rowOffset, columnOffset } = skeleton.getDecomposedOffset( + const { row, column, rowOffset, columnOffset } = skeleton.getOffsetRelativeToRowCol( viewportScrollX, viewportScrollY ); @@ -422,7 +425,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR if (!worksheet) return; const zoomRatio = worksheet.getZoomRatio() || 1; - scene?.setScaleValue(zoomRatio, zoomRatio); + scene?.setScaleValueOnly(zoomRatio, zoomRatio); scene?.transformByState({ width: rowHeaderWidthAndMarginLeft + columnTotalWidth, height: columnHeaderHeightAndMarginTop + rowTotalHeight, @@ -493,7 +496,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR return; } - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } @@ -503,7 +506,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR // eslint-disable-next-line max-lines-per-function, complexity private _scrollToCell(row: number, column: number): boolean { - const { rowHeightAccumulation, columnWidthAccumulation } = this._sheetSkeletonManagerService.getCurrent()?.skeleton ?? {}; + const { rowHeightAccumulation, columnWidthAccumulation } = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton ?? {}; if (rowHeightAccumulation == null || columnWidthAccumulation == null) return false; @@ -513,7 +516,7 @@ export class MobileSheetsScrollRenderController extends Disposable implements IR const viewport = scene.getViewport(SHEET_VIEWPORT_KEY.VIEW_MAIN); if (viewport == null) return false; - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) return false; const worksheet = this._context.unit.getActiveSheet(); diff --git a/packages/sheets-ui/src/controllers/render-controllers/scroll.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/scroll.render-controller.ts index 3cbda7bb85f..195876aa1f5 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/scroll.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/scroll.render-controller.ts @@ -126,7 +126,7 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM this.disposeWithMe( toDisposable( this._scrollManagerService.rawScrollInfo$.subscribe((rawScrollInfo: Nullable) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (!skeleton) return; if (rawScrollInfo == null) { @@ -159,7 +159,7 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM this.disposeWithMe( // set scrollInfo, the event is triggered in viewport@_scrollToScrollbarPos viewportMain.onScrollAfter$.subscribeEvent((scrollAfterParam: IScrollObserverParam) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null || scrollAfterParam.isTrigger === false) { return; } @@ -173,18 +173,22 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM const { viewportScrollX, viewportScrollY, scrollX, scrollY } = scrollAfterParam; // according to the actual scroll position, the most suitable row, column and offset combination is recalculated. - const { row, column, rowOffset, columnOffset } = skeleton.getDecomposedOffset( + const { row, column, rowOffset, columnOffset } = skeleton.getOffsetRelativeToRowCol( viewportScrollX, viewportScrollY ); - const scrollInfo = { + const scrollInfo: IViewportScrollState = { sheetViewStartRow: row, sheetViewStartColumn: column, offsetX: columnOffset, offsetY: rowOffset, + viewportScrollX, + viewportScrollY, + scrollX, + scrollY, }; - this._scrollManagerService.setScrollStateToCurrSheet(scrollInfo); + this._scrollManagerService.setValidScrollStateToCurrSheet(scrollInfo); //#endregion this._scrollManagerService.validViewportScrollInfo$.next({ @@ -194,7 +198,7 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM scrollX, scrollY, }); - // snapshot is diff by diff people! + // snapshot is diff by diff users! // this._scrollManagerService.setScrollInfoToSnapshot({ ...lastestScrollInfo, viewportScrollX, viewportScrollY }); }) ); @@ -204,7 +208,7 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM this.disposeWithMe( // get scrollByBar event from viewport and exec ScrollCommand.id. viewportMain.onScrollByBar$.subscribeEvent((param) => { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null || param.isTrigger === false) { return; } @@ -215,7 +219,9 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM } const { viewportScrollX = 0, viewportScrollY = 0 } = param; - const { row, column, rowOffset, columnOffset } = skeleton.getDecomposedOffset( + const freeze = this._getFreeze(); + + const { row, column, rowOffset, columnOffset } = skeleton.getOffsetRelativeToRowCol( viewportScrollX, viewportScrollY ); @@ -255,7 +261,8 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM viewportMain.viewportScrollX = 0; viewportMain.viewportScrollY = 0; } - this._updateSceneSize(param as unknown as ISheetSkeletonManagerParam); + // why handle size in scroll controller? + // this._updateSceneSize(param as unknown as ISheetSkeletonManagerParam); } }))); } @@ -369,7 +376,7 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM } private _getFreeze(): Nullable { - const snapshot: IWorksheetData | undefined = this._sheetSkeletonManagerService.getCurrent()?.skeleton.getWorksheetConfig(); + const snapshot: IWorksheetData | undefined = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton.getWorksheetConfig(); if (snapshot == null) { return; } @@ -396,8 +403,9 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM const worksheet = workbook.getActiveSheet(); if (!worksheet) return; - const zoomRatio = worksheet.getZoomRatio() || 1; - scene?.setScaleValue(zoomRatio, zoomRatio); + // @TODO lumixraku why handle zoom in scroll.render-controller ??? + // const zoomRatio = worksheet.getZoomRatio() || 1; + // scene?.setScaleValueOnly(zoomRatio, zoomRatio); scene?.transformByState({ width: rowHeaderWidthAndMarginLeft + columnTotalWidth, height: columnHeaderHeightAndMarginTop + rowTotalHeight, @@ -469,7 +477,7 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM return; } - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) { return; } @@ -481,7 +489,7 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM // For arrow key to active cell cause scrolling. // eslint-disable-next-line max-lines-per-function, complexity private _scrollToCell(row: number, column: number, forceTop = false, forceLeft = false) { - const { rowHeightAccumulation, columnWidthAccumulation } = this._sheetSkeletonManagerService.getCurrent()?.skeleton ?? {}; + const { rowHeightAccumulation, columnWidthAccumulation } = this._sheetSkeletonManagerService.getCurrentSkeleton() ?? {}; if (rowHeightAccumulation == null || columnWidthAccumulation == null) return false; @@ -491,7 +499,7 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM const viewport = scene.getViewport(SHEET_VIEWPORT_KEY.VIEW_MAIN); if (viewport == null) return false; - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; if (skeleton == null) return false; const worksheet = this._context.unit.getActiveSheet(); diff --git a/packages/sheets-ui/src/controllers/render-controllers/sheet.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/sheet.render-controller.ts index fc4527f0485..6a5c550bf64 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/sheet.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/sheet.render-controller.ts @@ -238,6 +238,7 @@ export class SheetRenderController extends RxDisposable implements IRenderModule private _initViewports(scene: Scene, rowHeader: { width: number }, columnHeader: { height: number }) { const bufferEdgeX = 100; const bufferEdgeY = 100; + const viewMain = new Viewport(SHEET_VIEWPORT_KEY.VIEW_MAIN, scene, { left: rowHeader.width, top: columnHeader.height, @@ -488,7 +489,7 @@ export class SheetRenderController extends RxDisposable implements IRenderModule } private _rangeToBounds(ranges: IRange[]) { - const skeleton = this._sheetSkeletonManagerService.getCurrent()!.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()!.skeleton; const { rowHeightAccumulation, columnWidthAccumulation, rowHeaderWidth, columnHeaderHeight } = skeleton; const dirtyBounds: IViewportInfos[] = []; diff --git a/packages/sheets-ui/src/controllers/render-controllers/skeleton.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/skeleton.render-controller.ts index abbdeff9fa1..6134f0b4409 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/skeleton.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/skeleton.render-controller.ts @@ -14,22 +14,51 @@ * limitations under the License. */ -import type { Workbook } from '@univerjs/core'; -import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { Nullable, Workbook } from '@univerjs/core'; +import type { ISheetSkeletonManagerParam } from '../../services/sheet-skeleton-manager.service'; import { Disposable, Inject } from '@univerjs/core'; +import { type IRenderContext, IRenderManagerService, type IRenderModule } from '@univerjs/engine-render'; import { SheetSkeletonManagerService } from '../../services/sheet-skeleton-manager.service'; export class SheetSkeletonRenderController extends Disposable implements IRenderModule { constructor( private readonly _context: IRenderContext, - @Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService + @Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService, + @IRenderManagerService private readonly _renderManagerService: IRenderManagerService ) { super(); this.disposeWithMe(this._context.unit.sheetDisposed$.subscribe((sheet) => { - this._sheetSkeletonManagerService.disposeSkeleton({ - sheetId: sheet.getSheetId(), - }); + this._sheetSkeletonManagerService.disposeSkeleton(sheet.getSheetId()); })); + + this._sheetSkeletonManagerService.currentSkeletonBefore$.subscribe((param: Nullable) => { + this._updateSceneSize(param); + }); + } + + private _updateSceneSize(param: Nullable) { + if (param == null) { + return; + } + + const { unitId } = this._context; + const { skeleton } = param; + const scene = this._renderManagerService.getRenderById(unitId)?.scene; + + if (skeleton == null || scene == null) { + return; + } + + const { rowTotalHeight, columnTotalWidth, rowHeaderWidthAndMarginLeft, columnHeaderHeightAndMarginTop } = + skeleton; + const workbook = this._context.unit; + const worksheet = workbook.getActiveSheet(); + if (!worksheet) return; + + scene?.transformByState({ + width: rowHeaderWidthAndMarginLeft + columnTotalWidth, + height: columnHeaderHeightAndMarginTop + rowTotalHeight, + }); } } diff --git a/packages/sheets-ui/src/controllers/render-controllers/zoom.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/zoom.render-controller.ts index 7ca1bead4dc..2e12f382160 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/zoom.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/zoom.render-controller.ts @@ -52,6 +52,7 @@ export class SheetsZoomRenderController extends Disposable implements IRenderMod const scene = this._getSheetObject().scene; this.disposeWithMe( + // hold ctrl & mousewheel ---> zoom scene.onMouseWheel$.subscribeEvent((e: IWheelEvent) => { if (!e.ctrlKey || !this._contextService.getContextValue(FOCUSING_SHEET)) { return; @@ -106,12 +107,12 @@ export class SheetsZoomRenderController extends Disposable implements IRenderMod } /** - * Zoom scene, resize viewport and then setScrollInfo + * Triggered when zoom and switch sheet. * @param zoomRatio */ private _updateViewZoom(zoomRatio: number) { const sheetObject = this._getSheetObject(); - sheetObject?.scene.scale(zoomRatio, zoomRatio); + sheetObject?.scene.transformByState({ scaleX: zoomRatio, scaleY: zoomRatio }); sheetObject?.spreadsheet.makeForceDirty(); } diff --git a/packages/sheets-ui/src/controllers/utils/component-tools.ts b/packages/sheets-ui/src/controllers/utils/component-tools.ts index 8604bd03717..a53b8f3465c 100644 --- a/packages/sheets-ui/src/controllers/utils/component-tools.ts +++ b/packages/sheets-ui/src/controllers/utils/component-tools.ts @@ -127,7 +127,7 @@ export function getCoordByOffset( const { row, column } = moveActualSelection; - const startCell = skeleton.getNoMergeCellPositionByIndex(row, column); + const startCell = skeleton.getNoMergeCellWithCoordByIndex(row, column); const { startX, startY, endX, endY } = startCell; diff --git a/packages/sheets-ui/src/facade/f-range.ts b/packages/sheets-ui/src/facade/f-range.ts index 85a2f020446..0ac25708954 100644 --- a/packages/sheets-ui/src/facade/f-range.ts +++ b/packages/sheets-ui/src/facade/f-range.ts @@ -124,7 +124,7 @@ class FRangeSheetsUIMixin extends FRange implements IFRangeSheetsUIMixin { const subUnitId = this._worksheet.getSheetId(); const skeleton = renderManagerService.getRenderById(unitId)! .with(SheetSkeletonManagerService) - .getWorksheetSkeleton(subUnitId)!.skeleton; + .getSkeletonParam(subUnitId)!.skeleton; return skeleton.getCellWithCoordByIndex(this._range.startRow, this._range.startColumn); } diff --git a/packages/sheets-ui/src/plugin.ts b/packages/sheets-ui/src/plugin.ts index 0b92202e1de..0a0b425e0f4 100644 --- a/packages/sheets-ui/src/plugin.ts +++ b/packages/sheets-ui/src/plugin.ts @@ -57,6 +57,7 @@ import { HeaderResizeRenderController } from './controllers/render-controllers/h import { HeaderUnhideRenderController } from './controllers/render-controllers/header-unhide.render-controller'; import { SheetsScrollRenderController } from './controllers/render-controllers/scroll.render-controller'; import { SheetRenderController } from './controllers/render-controllers/sheet.render-controller'; +import { SheetSkeletonRenderController } from './controllers/render-controllers/skeleton.render-controller'; import { SheetsZoomRenderController } from './controllers/render-controllers/zoom.render-controller'; import { SheetUIController } from './controllers/sheet-ui.controller'; import { StatusBarController } from './controllers/status-bar.controller'; @@ -209,6 +210,7 @@ export class UniverSheetsUIPlugin extends Plugin { private _registerRenderBasics(): void { ([ [SheetSkeletonManagerService], + [SheetSkeletonRenderController], [SheetRenderController], [ISheetSelectionRenderService, { useClass: SheetSelectionRenderService }], ] as Dependency[]).forEach((m) => { diff --git a/packages/sheets-ui/src/services/auto-fill/auto-fill.service.ts b/packages/sheets-ui/src/services/auto-fill/auto-fill.service.ts index 548014729db..f541e5fb597 100644 --- a/packages/sheets-ui/src/services/auto-fill/auto-fill.service.ts +++ b/packages/sheets-ui/src/services/auto-fill/auto-fill.service.ts @@ -482,7 +482,7 @@ export class AutoFillService extends Disposable implements IAutoFillService { private _getAutoHeightUndoRedos(unitId: string, subUnitId: string, ranges: IRange[]) { const sheetSkeletonService = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService); - const skeleton = sheetSkeletonService?.getCurrent()?.skeleton; + const skeleton = sheetSkeletonService?.getCurrentParam()?.skeleton; if (!skeleton) return { redos: [], undos: [] }; const rowsAutoHeightInfo = skeleton.calculateAutoHeightInRange(ranges); diff --git a/packages/sheets-ui/src/services/canvas-pop-manager.service.ts b/packages/sheets-ui/src/services/canvas-pop-manager.service.ts index 14096787981..c508a49667a 100644 --- a/packages/sheets-ui/src/services/canvas-pop-manager.service.ts +++ b/packages/sheets-ui/src/services/canvas-pop-manager.service.ts @@ -225,9 +225,7 @@ export class SheetCanvasPopManagerService extends Disposable { const unitId = workbook.getUnitId(); const subUnitId = worksheet.getSheetId(); - const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getOrCreateSkeleton({ - sheetId: subUnitId, - }); + const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).ensureSkeleton(subUnitId); const currentRender = this._renderManagerService.getRenderById(unitId); if (!currentRender || !skeleton) { @@ -283,9 +281,7 @@ export class SheetCanvasPopManagerService extends Disposable { return null; } - const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getOrCreateSkeleton({ - sheetId: subUnitId, - }); + const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).ensureSkeleton(subUnitId); const currentRender = this._renderManagerService.getRenderById(unitId); if (!currentRender || !skeleton) { @@ -336,9 +332,7 @@ export class SheetCanvasPopManagerService extends Disposable { return null; } - const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).getOrCreateSkeleton({ - sheetId: subUnitId, - }); + const skeleton = this._renderManagerService.getRenderById(unitId)?.with(SheetSkeletonManagerService).ensureSkeleton(subUnitId); const currentRender = this._renderManagerService.getRenderById(unitId); if (!currentRender || !skeleton) { @@ -391,9 +385,7 @@ export class SheetCanvasPopManagerService extends Disposable { return null; } const currentRender = this._renderManagerService.getRenderById(unitId); - const skeleton = currentRender?.with(SheetSkeletonManagerService).getOrCreateSkeleton({ - sheetId: subUnitId, - }); + const skeleton = currentRender?.with(SheetSkeletonManagerService).ensureSkeleton(subUnitId); const sheetSelectionRenderService = currentRender?.with(ISheetSelectionRenderService); if (!currentRender || !skeleton || !sheetSelectionRenderService) { diff --git a/packages/sheets-ui/src/services/clipboard/__tests__/clipboard-test-bed.ts b/packages/sheets-ui/src/services/clipboard/__tests__/clipboard-test-bed.ts index 83a4b012f6f..d4deb4bc0d1 100644 --- a/packages/sheets-ui/src/services/clipboard/__tests__/clipboard-test-bed.ts +++ b/packages/sheets-ui/src/services/clipboard/__tests__/clipboard-test-bed.ts @@ -20,7 +20,7 @@ import type { Dependency, IDisposable, IWorkbookData } from '@univerjs/core'; import { DisposableCollection, ILogService, Inject, Injector, IUniverInstanceService, LocaleService, LocaleType, LogLevel, Plugin, Univer, UniverInstanceType } from '@univerjs/core'; import { CalculateFormulaService, DefinedNamesService, FormulaCurrentConfigService, FormulaDataModel, FormulaRuntimeService, ICalculateFormulaService, IDefinedNamesService, IFormulaCurrentConfigService, IFormulaRuntimeService, LexerTreeBuilder } from '@univerjs/engine-formula'; import { IRenderManagerService, RenderManagerService } from '@univerjs/engine-render'; -import { SheetInterceptorService, SheetsSelectionsService } from '@univerjs/sheets'; +import { SheetInterceptorService, SheetSkeletonService, SheetsSelectionsService } from '@univerjs/sheets'; import { BrowserClipboardService, @@ -594,6 +594,7 @@ export function clipboardTestBed(workbookData?: IWorkbookData, dependencies?: De // NOTE: This is pretty hack for the test. But with these hacks we can avoid to create // real canvas-environment in univerjs/sheets-ui. If some we have to do that, this hack could be removed. + const mockSheetSkService = new SheetSkeletonService(injector); const fakeSheetSkeletonManagerService = new SheetSkeletonManagerService({ unit: sheet, unitId: 'test', @@ -603,7 +604,7 @@ export function clipboardTestBed(workbookData?: IWorkbookData, dependencies?: De mainComponent: null as any, components: null as any, isMainScene: true, - }, injector); + }, injector, mockSheetSkService); injector.add([SheetSkeletonManagerService, { useValue: fakeSheetSkeletonManagerService }]); injector.get(IRenderManagerService).addRender('test', { diff --git a/packages/sheets-ui/src/services/clipboard/__tests__/sheet-paste.spec.ts b/packages/sheets-ui/src/services/clipboard/__tests__/sheet-paste.spec.ts index a719d71e9c4..1683674f622 100644 --- a/packages/sheets-ui/src/services/clipboard/__tests__/sheet-paste.spec.ts +++ b/packages/sheets-ui/src/services/clipboard/__tests__/sheet-paste.spec.ts @@ -107,7 +107,7 @@ describe('Test clipboard', () => { const worksheet = get(IUniverInstanceService).getUnit('test')?.getSheetBySheetId('sheet1'); if (!worksheet) return false; const htmlToUSM = new HtmlToUSMService({ - getCurrentSkeleton: () => sheetSkeletonManagerService.getCurrent(), + getCurrentSkeleton: () => sheetSkeletonManagerService.getCurrentParam(), }); const htmlPath = path.join(__dirname, 'assets', 'html', 'excel-base-sample.html'); const html = getHTMLString(htmlPath); @@ -119,7 +119,7 @@ describe('Test clipboard', () => { const worksheet = get(IUniverInstanceService).getUnit('test')?.getSheetBySheetId('sheet1'); if (!worksheet) return false; const htmlToUSM = new HtmlToUSMService({ - getCurrentSkeleton: () => sheetSkeletonManagerService.getCurrent(), + getCurrentSkeleton: () => sheetSkeletonManagerService.getCurrentParam(), }); const htmlPath = path.join(__dirname, 'assets', 'html', 'google-base-sample.html'); const html = getHTMLString(htmlPath); diff --git a/packages/sheets-ui/src/services/clipboard/clipboard.service.ts b/packages/sheets-ui/src/services/clipboard/clipboard.service.ts index f0d9fcba86b..e920fa17486 100644 --- a/packages/sheets-ui/src/services/clipboard/clipboard.service.ts +++ b/packages/sheets-ui/src/services/clipboard/clipboard.service.ts @@ -142,7 +142,7 @@ export class SheetClipboardService extends Disposable implements ISheetClipboard ) { super(); this._htmlToUSM = new HtmlToUSMService({ - getCurrentSkeleton: () => this._renderManagerService.withCurrentTypeOfUnit(UniverInstanceType.UNIVER_SHEET, SheetSkeletonManagerService)?.getCurrent(), + getCurrentSkeleton: () => this._renderManagerService.withCurrentTypeOfUnit(UniverInstanceType.UNIVER_SHEET, SheetSkeletonManagerService)?.getCurrentParam(), }); this._usmToHtml = new USMToHtmlService(); diff --git a/packages/sheets-ui/src/services/drag-manager.service.ts b/packages/sheets-ui/src/services/drag-manager.service.ts index a1476a9d75a..26ed5c0d2b9 100644 --- a/packages/sheets-ui/src/services/drag-manager.service.ts +++ b/packages/sheets-ui/src/services/drag-manager.service.ts @@ -78,7 +78,7 @@ export class DragManagerService extends Disposable { const currentRender = this._renderManagerService.getRenderById(workbook.getUnitId()); if (!currentRender) return; - const skeletonParam = currentRender.with(SheetSkeletonManagerService).getCurrent(); + const skeletonParam = currentRender.with(SheetSkeletonManagerService).getCurrentParam(); const scrollManagerService = currentRender.with(SheetScrollManagerService); const scrollInfo = scrollManagerService.getCurrentScrollState(); diff --git a/packages/sheets-ui/src/services/editor-bridge.service.ts b/packages/sheets-ui/src/services/editor-bridge.service.ts index ce7b4161a3e..40190033b77 100644 --- a/packages/sheets-ui/src/services/editor-bridge.service.ts +++ b/packages/sheets-ui/src/services/editor-bridge.service.ts @@ -36,7 +36,7 @@ import { UniverInstanceType, } from '@univerjs/core'; import { getCanvasOffsetByEngine, IEditorService } from '@univerjs/docs-ui'; -import { convertTextRotation, DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; +import { convertTextRotation, convertTransformToOffsetX, convertTransformToOffsetY, DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; import { BEFORE_CELL_EDIT, IRefSelectionsService, SheetInterceptorService } from '@univerjs/sheets'; import { BehaviorSubject, map, switchMap } from 'rxjs'; import { ISheetSelectionRenderService } from './selection/base-selection-render.service'; @@ -188,7 +188,7 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ const ru = this._renderManagerService.getCurrentTypeOfRenderer(UniverInstanceType.UNIVER_SHEET); if (!ru) return; - const skeleton = ru.with(SheetSkeletonManagerService).getWorksheetSkeleton(currentEditCell.sheetId)?.skeleton; + const skeleton = ru.with(SheetSkeletonManagerService).getSkeletonParam(currentEditCell.sheetId)?.skeleton; const selectionRenderService = ru.with(ISheetSelectionRenderService); if (!skeleton) return; if (!this._currentEditCellState) return; @@ -207,10 +207,10 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ const { scaleX, scaleY } = scene.getAncestorScale(); const scrollXY = scene.getViewportScrollXY(selectionRenderService.getViewPort()); - startX = skeleton.convertTransformToOffsetX(startX, scaleX, scrollXY); - startY = skeleton.convertTransformToOffsetY(startY, scaleY, scrollXY); - endX = skeleton.convertTransformToOffsetX(endX, scaleX, scrollXY); - endY = skeleton.convertTransformToOffsetY(endY, scaleY, scrollXY); + startX = convertTransformToOffsetX(startX, scaleX, scrollXY); + startY = convertTransformToOffsetY(startY, scaleY, scrollXY); + endX = convertTransformToOffsetX(endX, scaleX, scrollXY); + endY = convertTransformToOffsetY(endY, scaleY, scrollXY); if (resetSizeOnly && this._currentEditCellLayout) { endX = endX - startX + this._currentEditCellLayout.position.startX; @@ -322,10 +322,10 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ const { scaleX, scaleY } = scene.getAncestorScale(); const scrollXY = scene.getViewportScrollXY(selectionRenderService.getViewPort()); - startX = skeleton.convertTransformToOffsetX(startX, scaleX, scrollXY); - startY = skeleton.convertTransformToOffsetY(startY, scaleY, scrollXY); - endX = skeleton.convertTransformToOffsetX(endX, scaleX, scrollXY); - endY = skeleton.convertTransformToOffsetY(endY, scaleY, scrollXY); + startX = convertTransformToOffsetX(startX, scaleX, scrollXY); + startY = convertTransformToOffsetY(startY, scaleY, scrollXY); + endX = convertTransformToOffsetX(endX, scaleX, scrollXY); + endY = convertTransformToOffsetY(endY, scaleY, scrollXY); const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET)!; const worksheet = workbook.getActiveSheet(); diff --git a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts index 967b9d7687b..047729b8e76 100644 --- a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts +++ b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts @@ -358,7 +358,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM const editCellState = this._editorBridgeService.getEditCellState(); if (!editCellState) return; - const skeleton = this._sheetSkeletonManagerService.getWorksheetSkeleton(editCellState.sheetId)?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getSkeletonParam(editCellState.sheetId)?.skeleton; if (!skeleton) return; const { row, column, scaleX, scaleY, position, canvasOffset } = editCellState; const maxSize = this._getEditorMaxSize(position, canvasOffset); diff --git a/packages/sheets-ui/src/services/hover-manager.service.ts b/packages/sheets-ui/src/services/hover-manager.service.ts index 1aa3750b368..298e1991022 100644 --- a/packages/sheets-ui/src/services/hover-manager.service.ts +++ b/packages/sheets-ui/src/services/hover-manager.service.ts @@ -208,7 +208,7 @@ export class HoverManagerService extends Disposable { const currentRender = this._renderManagerService.getRenderById(workbook.getUnitId()); if (!currentRender) return null; - const skeletonParam = currentRender.with(SheetSkeletonManagerService).getWorksheetSkeleton(worksheet.getSheetId()); + const skeletonParam = currentRender.with(SheetSkeletonManagerService).getSkeletonParam(worksheet.getSheetId()); if (!skeletonParam) return null; const scrollManagerService = currentRender.with(SheetScrollManagerService); diff --git a/packages/sheets-ui/src/services/scroll-manager.service.ts b/packages/sheets-ui/src/services/scroll-manager.service.ts index 2d667ab851b..a7805ff9dd6 100644 --- a/packages/sheets-ui/src/services/scroll-manager.service.ts +++ b/packages/sheets-ui/src/services/scroll-manager.service.ts @@ -75,7 +75,7 @@ export type IScrollStateMap = Map>; */ export class SheetScrollManagerService implements IRenderModule { /** - * a map holds all scroll info for each sheet + * a map holds all scroll info for each sheet(valid value) */ private readonly _scrollStateMap: IScrollStateMap = new Map(); /** @@ -111,16 +111,35 @@ export class SheetScrollManagerService implements IRenderModule { this._rawScrollInfo$.complete(); } - setSearchParam(param: IScrollStateSearchParam) { - this._searchParamForScroll = param; + calcViewportScrollFromRowColOffset(scrollInfo: Nullable) { + if (!scrollInfo) { + return { + viewportScrollX: 0, + viewportScrollY: 0, + }; + } + + let { sheetViewStartColumn, sheetViewStartRow, offsetX, offsetY } = scrollInfo; + sheetViewStartRow = sheetViewStartRow || 0; + offsetY = offsetY || 0; + + const skeleton = this._sheetSkeletonManagerService.getCurrentSkeleton(); + const rowAcc = skeleton?.rowHeightAccumulation[sheetViewStartRow - 1] || 0; + const colAcc = skeleton?.columnWidthAccumulation[sheetViewStartColumn - 1] || 0; + const viewportScrollX = colAcc + offsetX; + const viewportScrollY = rowAcc + offsetY; + + return { + viewportScrollX, + viewportScrollY, + }; } - setSearchParamAndRefresh(param: IScrollStateSearchParam) { + setSearchParam(param: IScrollStateSearchParam) { this._searchParamForScroll = param; - this._scrollStateNext(param); } - getScrollStateByParam(param: IScrollStateSearchParam): Readonly { + getScrollStateByParam(param: IScrollStateSearchParam): Readonly> { return this._getCurrentScroll(param); } @@ -128,20 +147,24 @@ export class SheetScrollManagerService implements IRenderModule { return this._getCurrentScroll(this._searchParamForScroll); } + setValidScrollState(param: IScrollStateWithSearchParam) { + this._setScrollState(param); + } + /** - * Set scrollInfo by SetScrollOperation, call by ScrollCommand.id. + * emit raw scrollInfo by SetScrollOperation, call by ScrollCommand.id. + * raw scrollInfo means not handled by limit scroll method. * @param param */ - setScrollInfoAndEmitEvent(param: IScrollStateWithSearchParam) { - this._setScrollState(param); - this._scrollStateNext(param); + emitRawScrollParam(param: IScrollStateWithSearchParam) { + this._emitRawScroll(param); } /** - * Set _scrollStateMap but no _scrollInfo$.next + * Set _scrollStateMap * @param scroll */ - setScrollStateToCurrSheet(scroll: IScrollState) { + setValidScrollStateToCurrSheet(scroll: IViewportScrollState) { if (this._searchParamForScroll == null) { return; } @@ -150,6 +173,12 @@ export class SheetScrollManagerService implements IRenderModule { ...this._searchParamForScroll, ...scroll, }); + + const sheetId = this._searchParamForScroll.sheetId; + const sheetSkeleton = this._sheetSkeletonManagerService.getSkeleton(sheetId); + if (sheetSkeleton) { + sheetSkeleton.setScroll(scroll.viewportScrollX, scroll.viewportScrollY); + } } clear(): void { @@ -159,30 +188,10 @@ export class SheetScrollManagerService implements IRenderModule { this._clearByParamAndNotify(this._searchParamForScroll); } - calcViewportScrollFromRowColOffset(scrollInfo: Nullable) { - if (!scrollInfo) { - return { - viewportScrollX: 0, - viewportScrollY: 0, - }; - } - - let { sheetViewStartColumn, sheetViewStartRow, offsetX, offsetY } = scrollInfo; - sheetViewStartRow = sheetViewStartRow || 0; - offsetY = offsetY || 0; - - const skeleton = this._sheetSkeletonManagerService.getCurrentSkeleton(); - const rowAcc = skeleton?.rowHeightAccumulation[sheetViewStartRow - 1] || 0; - const colAcc = skeleton?.columnWidthAccumulation[sheetViewStartColumn - 1] || 0; - const viewportScrollX = colAcc + offsetX; - const viewportScrollY = rowAcc + offsetY; - - return { - viewportScrollX, - viewportScrollY, - }; - } - + /** + * scroll + * @param scrollInfo + */ private _setScrollState(scrollInfo: IScrollStateWithSearchParam): void { const { unitId, sheetId, sheetViewStartColumn, sheetViewStartRow, offsetX, offsetY } = scrollInfo; @@ -209,7 +218,12 @@ export class SheetScrollManagerService implements IRenderModule { offsetY: 0, }); - this._scrollStateNext(param); + this._emitRawScroll({ + sheetViewStartRow: 0, + sheetViewStartColumn: 0, + offsetX: 0, + offsetY: 0, + }); } private _getCurrentScroll(param: Nullable): IScrollState { @@ -227,10 +241,7 @@ export class SheetScrollManagerService implements IRenderModule { return currScrollState || emptyState; } - private _scrollStateNext(param: IScrollStateSearchParam): void { - const scrollInfo = this._getCurrentScroll(param); - - // subscriber can be found in scrollManagerService.rawScrollInfo$.subscribe - this._rawScrollInfo$.next(scrollInfo); + private _emitRawScroll(param: IScrollState): void { + this._rawScrollInfo$.next(param); } } diff --git a/packages/sheets-ui/src/services/selection/base-selection-render.service.ts b/packages/sheets-ui/src/services/selection/base-selection-render.service.ts index dd019402736..8bd18b01260 100644 --- a/packages/sheets-ui/src/services/selection/base-selection-render.service.ts +++ b/packages/sheets-ui/src/services/selection/base-selection-render.service.ts @@ -373,7 +373,7 @@ export class BaseSelectionRenderService extends Disposable implements ISheetSele } protected _getFreeze(): Nullable { - const freeze = this._sheetSkeletonManagerService.getCurrent()?.skeleton.getWorksheetConfig().freeze; + const freeze = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton.getWorksheetConfig().freeze; return freeze; } @@ -808,7 +808,7 @@ export class BaseSelectionRenderService extends Disposable implements ISheetSele //@region other range type const { row, column } = skeleton.getCellIndexByOffset(offsetX, offsetY, scaleX, scaleY, scrollXY); - const startCell = skeleton.getNoMergeCellPositionByIndex(row, column); + const startCell = skeleton.getNoMergeCellWithCoordByIndex(row, column); const { startX, startY, endX, endY } = startCell; const rangeWithCoord: IRangeWithCoord = { diff --git a/packages/sheets-ui/src/services/selection/mobile-selection-render.service.ts b/packages/sheets-ui/src/services/selection/mobile-selection-render.service.ts index 32e6d4432bd..10ead8ba9a9 100644 --- a/packages/sheets-ui/src/services/selection/mobile-selection-render.service.ts +++ b/packages/sheets-ui/src/services/selection/mobile-selection-render.service.ts @@ -148,7 +148,7 @@ export class MobileSheetsSelectionRenderService extends BaseSelectionRenderServi spreadsheetRowHeader?.onPointerUp$.subscribeEvent((evt: IPointerEvent | IMouseEvent, _state: EventState) => { if (this._normalSelectionDisabled()) return; - const skeleton = this._sheetSkeletonManagerService.getCurrent()!.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()!.skeleton; const { row } = getCoordByOffset(evt.offsetX, evt.offsetY, scene, skeleton); const matchSelectionData = isThisRowSelected(this._workbookSelections.getCurrentSelections(), row); if (matchSelectionData) return; @@ -162,7 +162,7 @@ export class MobileSheetsSelectionRenderService extends BaseSelectionRenderServi spreadsheetColumnHeader?.onPointerUp$.subscribeEvent((evt: IPointerEvent | IMouseEvent, _state: EventState) => { if (this._normalSelectionDisabled()) return; - const skeleton = this._sheetSkeletonManagerService.getCurrent()!.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()!.skeleton; const { column } = getCoordByOffset(evt.offsetX, evt.offsetY, scene, skeleton); const matchSelectionData = isThisColSelected(this._workbookSelections.getCurrentSelections(), column); if (matchSelectionData) return; @@ -179,7 +179,7 @@ export class MobileSheetsSelectionRenderService extends BaseSelectionRenderServi this._reset(); // remove all other selections - const skeleton = this._sheetSkeletonManagerService.getCurrent()!.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()!.skeleton; const selectionWithStyle = selectionDataForSelectAll(skeleton); this._addSelectionControlByModelData(selectionWithStyle); // pointerup --> create selection @@ -707,7 +707,7 @@ export class MobileSheetsSelectionRenderService extends BaseSelectionRenderServi if (activeControl == null) { return; } - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()?.skeleton; const sheetContentHeight = skeleton?.rowTotalHeight; const sheetContentWidth = skeleton?.columnTotalWidth; const rangeType = activeControl.rangeType; diff --git a/packages/sheets-ui/src/services/selection/selection-render.service.ts b/packages/sheets-ui/src/services/selection/selection-render.service.ts index f130ebbc191..a0a8620c314 100644 --- a/packages/sheets-ui/src/services/selection/selection-render.service.ts +++ b/packages/sheets-ui/src/services/selection/selection-render.service.ts @@ -92,7 +92,7 @@ export class SheetSelectionRenderService extends BaseSelectionRenderService impl if (this.isSelectionDisabled()) return; if (this.inRefSelectionMode()) return; - const skeleton = this._sheetSkeletonManagerService.getCurrent()!.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()!.skeleton; const { row } = getCoordByOffset(evt.offsetX, evt.offsetY, scene, skeleton); const matchSelectionData = isThisRowSelected(this._workbookSelections.getCurrentSelections(), row); if (matchSelectionData) return; @@ -108,7 +108,7 @@ export class SheetSelectionRenderService extends BaseSelectionRenderService impl if (this.isSelectionDisabled()) return; if (this.inRefSelectionMode()) return; - const skeleton = this._sheetSkeletonManagerService.getCurrent()!.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()!.skeleton; const { column } = getCoordByOffset(evt.offsetX, evt.offsetY, scene, skeleton); const matchSelectionData = isThisColSelected(this._workbookSelections.getCurrentSelections(), column); if (matchSelectionData) return; @@ -126,7 +126,7 @@ export class SheetSelectionRenderService extends BaseSelectionRenderService impl this._reset(); // remove all other selections - const skeleton = this._sheetSkeletonManagerService.getCurrent()!.skeleton; + const skeleton = this._sheetSkeletonManagerService.getCurrentParam()!.skeleton; const selectionWithStyle = selectionDataForSelectAll(skeleton); this._addSelectionControlByModelData(selectionWithStyle); this.refreshSelectionMoveEnd(); diff --git a/packages/sheets-ui/src/services/selection/selection-shape-extension.ts b/packages/sheets-ui/src/services/selection/selection-shape-extension.ts index e5246d168cd..30d280cc5a8 100644 --- a/packages/sheets-ui/src/services/selection/selection-shape-extension.ts +++ b/packages/sheets-ui/src/services/selection/selection-shape-extension.ts @@ -129,7 +129,7 @@ export class SelectionShapeExtension { private _getFreeze() { const renderManagerService = this._injector.get(IRenderManagerService); const freeze = renderManagerService.withCurrentTypeOfUnit(UniverInstanceType.UNIVER_SHEET, SheetSkeletonManagerService) - ?.getCurrent() + ?.getCurrentParam() ?.skeleton .getWorksheetConfig() .freeze; @@ -243,8 +243,8 @@ export class SelectionShapeExtension { style: null, }; const selectionWithCoord = attachSelectionWithCoord(selection, this._skeleton); - const startCell = this._skeleton.getNoMergeCellPositionByIndex(startRow, startColumn); - const endCell = this._skeleton.getNoMergeCellPositionByIndex(endRow, endColumn); + const startCell = this._skeleton.getNoMergeCellWithCoordByIndex(startRow, startColumn); + const endCell = this._skeleton.getNoMergeCellWithCoordByIndex(endRow, endColumn); const startY = startCell?.startY || 0; const endY = endCell?.endY || 0; const startX = startCell?.startX || 0; @@ -709,8 +709,8 @@ export class SelectionShapeExtension { isLighten = rulerValue.isLighten; } - const startCell = this._skeleton.getNoMergeCellPositionByIndex(startRow, startColumn); - const endCell = this._skeleton.getNoMergeCellPositionByIndex(endRow, endColumn); + const startCell = this._skeleton.getNoMergeCellWithCoordByIndex(startRow, startColumn); + const endCell = this._skeleton.getNoMergeCellWithCoordByIndex(endRow, endColumn); const startY = startCell?.startY || 0; const endY = endCell?.endY || 0; diff --git a/packages/sheets-ui/src/services/selection/util.ts b/packages/sheets-ui/src/services/selection/util.ts index 969201bbd3e..1ae962a3c05 100644 --- a/packages/sheets-ui/src/services/selection/util.ts +++ b/packages/sheets-ui/src/services/selection/util.ts @@ -35,8 +35,8 @@ export function attachRangeWithCoord(skeleton: SpreadsheetSkeleton, range: IRang const _startColumn = endColumn < startColumn ? endColumn : startColumn; const _endColumn = endColumn < startColumn ? startColumn : endColumn; - const startCell = skeleton.getNoMergeCellPositionByIndex(_startRow, _startColumn); - const endCell = skeleton.getNoMergeCellPositionByIndex(_endRow, _endColumn); + const startCell = skeleton.getNoMergeCellWithCoordByIndex(_startRow, _startColumn); + const endCell = skeleton.getNoMergeCellWithCoordByIndex(_endRow, _endColumn); return { startRow, @@ -71,9 +71,9 @@ export function attachSelectionWithCoord(selection: ISelectionWithStyle, skeleto export function attachPrimaryWithCoord(skeleton: SpreadsheetSkeleton, primary: ISelectionCell): ICellWithCoord { const { actualRow, actualColumn, isMerged, isMergedMainCell, startRow, startColumn, endRow, endColumn } = primary; - const cellPosition = skeleton.getNoMergeCellPositionByIndex(actualRow, actualColumn); - const startCell = skeleton.getNoMergeCellPositionByIndex(startRow, startColumn); - const endCell = skeleton.getNoMergeCellPositionByIndex(endRow, endColumn); + const cellPosition = skeleton.getNoMergeCellWithCoordByIndex(actualRow, actualColumn); + const startCell = skeleton.getNoMergeCellWithCoordByIndex(startRow, startColumn); + const endCell = skeleton.getNoMergeCellWithCoordByIndex(endRow, endColumn); return { actualRow, diff --git a/packages/sheets-ui/src/services/sheet-skeleton-manager.service.ts b/packages/sheets-ui/src/services/sheet-skeleton-manager.service.ts index 350d755d3a7..cc6284e12fc 100644 --- a/packages/sheets-ui/src/services/sheet-skeleton-manager.service.ts +++ b/packages/sheets-ui/src/services/sheet-skeleton-manager.service.ts @@ -15,12 +15,14 @@ */ import type { IRange, IRangeWithCoord, Nullable, Workbook, Worksheet } from '@univerjs/core'; -import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { IRenderContext, IRenderModule, Scene } from '@univerjs/engine-render'; import { Disposable, Inject, Injector } from '@univerjs/core'; import { SpreadsheetSkeleton } from '@univerjs/engine-render'; +import { SheetSkeletonService } from '@univerjs/sheets'; import { BehaviorSubject } from 'rxjs'; import { attachRangeWithCoord } from './selection/util'; + // Why need this SkParam? what is Param used for? Could unitId & sheetId & dirty in skeleton itself ? export interface ISheetSkeletonManagerParam { unitId: string; sheetId: string; @@ -35,21 +37,13 @@ export interface ISheetSkeletonManagerSearch { } /** - * This service manages the drawing of the sheet's viewModel (skeleton). - * - * Each time there is a content change, it will trigger the viewModel of the render to recalculate. - * - * Each application and sub-table has its own viewModel (skeleton). - * - * The viewModel is also a temporary storage variable, which does not need to be persisted, - * so it is managed uniformly through the service. + * SheetSkeletonManagerService is registered in a render unit */ export class SheetSkeletonManagerService extends Disposable implements IRenderModule { - private _currentSkeletonSearchParam: ISheetSkeletonManagerSearch = { - sheetId: '', - }; + private _sheetId: string = ''; - private _sheetSkeletonParam: ISheetSkeletonManagerParam[] = []; + // @TODO lumixraku, why need this? How about put dirty & sheetId & unitId in skeleton itself z? + private _sheetSkeletonParamStore: Map = new Map(); private readonly _currentSkeleton$ = new BehaviorSubject>(null); readonly currentSkeleton$ = this._currentSkeleton$.asObservable(); @@ -62,7 +56,8 @@ export class SheetSkeletonManagerService extends Disposable implements IRenderMo constructor( private readonly _context: IRenderContext, - @Inject(Injector) private readonly _injector: Injector + @Inject(Injector) private readonly _injector: Injector, + @Inject(SheetSkeletonService) private readonly _sheetSkService: SheetSkeletonService ) { // empty super(); @@ -70,7 +65,7 @@ export class SheetSkeletonManagerService extends Disposable implements IRenderMo this.disposeWithMe(() => { this._currentSkeletonBefore$.complete(); this._currentSkeleton$.complete(); - this._sheetSkeletonParam = []; + this._sheetSkeletonParamStore = new Map(); }); this._initRemoveSheet(); @@ -78,54 +73,89 @@ export class SheetSkeletonManagerService extends Disposable implements IRenderMo private _initRemoveSheet() { this.disposeWithMe(this._context.unit.sheetDisposed$.subscribe((sheet) => { - this.disposeSkeleton({ - sheetId: sheet.getSheetId(), - }); + this.disposeSkeleton(sheet.getSheetId()); })); } getCurrentSkeleton(): Nullable { - return this.getCurrent()?.skeleton; + return this.getCurrentParam()?.skeleton; } + /** + * @deprecated use `getCurrentSkeleton` instead. + */ getCurrent(): Nullable { - return this._getSkeleton(this._currentSkeletonSearchParam); + return this.getCurrentParam(); + } + + /** + * get ISheetSkeletonManagerParam from _currentSkeletonSearchParam + * @returns + */ + getCurrentParam(): Nullable { + return this._getSkeletonParam(this._sheetId); } + /** + * Get skeleton by sheetId + * @param sheetId + */ + getSkeleton(sheetId: string): Nullable { + return this._getSkeleton(sheetId); + } + + /** + * Get SkeletonParam by sheetId + * @param sheetId + */ + getSkeletonParam(sheetId: string): Nullable { + return this._getSkeletonParam(sheetId); + } + + /** + * @deprecated use `getSkeleton` instead. + */ getWorksheetSkeleton(sheetId: string): Nullable { - return this._getSkeleton({ sheetId }); + return this.getSkeletonParam(sheetId); } /** * unitId is never read? + * why ?? what does unitId for ??? no need unitId this service is registered in render unit already. */ - getUnitSkeleton(unitId: string, sheetId: string): Nullable { - const param = this._getSkeleton({ sheetId }); - if (param != null) { - param.unitId = unitId; - } - return param; - } + // getUnitSkeleton(sheetId: string): Nullable { + // const param = this._getSkeleton(sheetId); + // // if (param) { + // // param.unitId = unitId; + // // } + // return param; + // } setCurrent(searchParam: ISheetSkeletonManagerSearch): Nullable { - this._setCurrent(searchParam); + this._setCurrent(searchParam.sheetId); + } + + setSkeletonParam(sheetId: string, skp: ISheetSkeletonManagerParam) { + this._sheetSkService.setSkeleton(skp.unitId, sheetId, skp.skeleton); + this._sheetSkeletonParamStore.set(sheetId, skp); } - private _setCurrent(searchParam: ISheetSkeletonManagerSearch): Nullable { - const param = this._getSkeleton(searchParam); + private _setCurrent(sheetId: string): Nullable { + this._sheetId = sheetId; + const skParam = this._getSkeletonParam(sheetId); const unitId = this._context.unitId; - if (param != null) { - this._reCalculate(param); + if (skParam != null) { + this.reCalculate(skParam); } else { - const { sheetId } = searchParam; const workbook = this._context.unit; - const worksheet = workbook.getSheetBySheetId(searchParam.sheetId); + const worksheet = workbook.getSheetBySheetId(sheetId); if (worksheet == null) { return; } - const skeleton = this._buildSkeleton(worksheet); - this._sheetSkeletonParam.push({ + const scene = this._context.scene; + const skeleton = this._buildSkeleton(worksheet, scene); + this.setSkeletonParam(sheetId, { unitId, sheetId, skeleton, @@ -133,22 +163,19 @@ export class SheetSkeletonManagerService extends Disposable implements IRenderMo }); } - this._currentSkeletonSearchParam = searchParam; - const sheetId = this._currentSkeletonSearchParam.sheetId; - const sheetSkeletonManagerParam = this.getUnitSkeleton(unitId, sheetId); + const sheetSkeletonManagerParam = this._getSkeletonParam(sheetId); this._currentSkeletonBefore$.next(sheetSkeletonManagerParam); this._currentSkeleton$.next(sheetSkeletonManagerParam); } - reCalculate() { - const param = this.getCurrent(); + // @TODO why need this function? How about caller get skeleton and call sk.calculate()? + reCalculate(param?: Nullable) { + if (!param) { + param = this.getCurrentParam(); + } if (param == null) { return; } - this._reCalculate(param); - } - - private _reCalculate(param: ISheetSkeletonManagerParam) { if (param.dirty) { param.skeleton.makeDirty(true); param.dirty = false; @@ -162,29 +189,37 @@ export class SheetSkeletonManagerService extends Disposable implements IRenderMo * @param state */ makeDirty(searchParm: ISheetSkeletonManagerSearch, state: boolean = true) { - const param = this._getSkeleton(searchParm); + const param = this._getSkeletonParam(searchParm.sheetId); if (param == null) { return; } param.dirty = state; } + /** + * @deprecated Use function `ensureSkeleton` instead. + * @param searchParam + */ getOrCreateSkeleton(searchParam: ISheetSkeletonManagerSearch) { - const skeleton = this._getSkeleton(searchParam); + return this.ensureSkeleton(searchParam.sheetId); + } + + ensureSkeleton(sheetId: string) { + const skeleton = this._getSkeletonParam(sheetId); if (skeleton) { return skeleton.skeleton; } const workbook = this._context.unit; - const worksheet = workbook.getSheetBySheetId(searchParam.sheetId); + const worksheet = workbook.getSheetBySheetId(sheetId); if (!worksheet) { return; } const newSkeleton = this._buildSkeleton(worksheet); - this._sheetSkeletonParam.push({ + this.setSkeletonParam(sheetId, { unitId: this._context.unitId, - sheetId: searchParam.sheetId, + sheetId, skeleton: newSkeleton, dirty: false, }); @@ -192,12 +227,12 @@ export class SheetSkeletonManagerService extends Disposable implements IRenderMo return newSkeleton; } - disposeSkeleton(searchParm: ISheetSkeletonManagerSearch) { - const index = this._sheetSkeletonParam.findIndex((param) => param.sheetId === searchParm.sheetId); - if (index > -1) { - const skeleton = this._sheetSkeletonParam[index]; - skeleton.skeleton.dispose(); - this._sheetSkeletonParam.splice(index, 1); + disposeSkeleton(sheetId: string) { + const skParam = this.getSkeletonParam(sheetId); + if (skParam) { + skParam.skeleton.dispose(); + this._sheetSkeletonParamStore.delete(sheetId); + this._sheetSkService.deleteSkeleton(skParam.unitId, sheetId); } } @@ -209,22 +244,25 @@ export class SheetSkeletonManagerService extends Disposable implements IRenderMo return attachRangeWithCoord(skeleton, range); } - private _getSkeleton(searchParm: ISheetSkeletonManagerSearch): Nullable { - const item = this._sheetSkeletonParam.find((param) => param.sheetId === searchParm.sheetId); - if (item != null) { - item.commandId = searchParm.commandId; - } - + private _getSkeletonParam(sheetId: string): Nullable { + const item = this._sheetSkeletonParamStore.get(sheetId); return item; } - private _buildSkeleton(worksheet: Worksheet) { + private _getSkeleton(sheetId: string): Nullable { + const param = this._getSkeletonParam(sheetId); + return param ? param.skeleton : null; + } + + private _buildSkeleton(worksheet: Worksheet, scene: Nullable) { const spreadsheetSkeleton = this._injector.createInstance( SpreadsheetSkeleton, worksheet, this._context.unit.getStyles() ); - + if (scene) { + spreadsheetSkeleton.setScene(scene); + } return spreadsheetSkeleton; } } diff --git a/packages/sheets-ui/src/services/utils/doc-skeleton-util.ts b/packages/sheets-ui/src/services/utils/doc-skeleton-util.ts index 2f0aca24ac9..ed466c818bf 100644 --- a/packages/sheets-ui/src/services/utils/doc-skeleton-util.ts +++ b/packages/sheets-ui/src/services/utils/doc-skeleton-util.ts @@ -212,7 +212,7 @@ export const getCustomRangePosition = (injector: Injector, unitId: string, subUn } const currentRender = renderManagerService.getRenderById(workbook.getUnitId()); - const skeletonParam = currentRender?.with(SheetSkeletonManagerService).getWorksheetSkeleton(worksheet.getSheetId()); + const skeletonParam = currentRender?.with(SheetSkeletonManagerService).getSkeletonParam(worksheet.getSheetId()); const skeleton = skeletonParam?.skeleton; @@ -282,7 +282,7 @@ export const getEditingCustomRangePosition = (injector: Injector, unitId: string } const docSkeleton = renderer.with(DocSkeletonManagerService).getSkeleton(); - const sheetSkeleton = sheetRenderer.with(SheetSkeletonManagerService).getWorksheetSkeleton(sheetId)?.skeleton; + const sheetSkeleton = sheetRenderer.with(SheetSkeletonManagerService).getSkeletonParam(sheetId)?.skeleton; if (!docSkeleton || !sheetSkeleton) { return null; diff --git a/packages/sheets-ui/src/services/utils/drawing-position-util.ts b/packages/sheets-ui/src/services/utils/drawing-position-util.ts index 9840745963b..33d93d75f7b 100644 --- a/packages/sheets-ui/src/services/utils/drawing-position-util.ts +++ b/packages/sheets-ui/src/services/utils/drawing-position-util.ts @@ -26,7 +26,7 @@ export function convertPositionSheetOverGridToAbsolute(unitId: string, subUnitId const { column: fromColumn, columnOffset: fromColumnOffset, row: fromRow, rowOffset: fromRowOffset } = from; const { column: toColumn, columnOffset: toColumnOffset, row: toRow, rowOffset: toRowOffset } = to; - const skeleton = sheetSkeletonManagerService.getOrCreateSkeleton({ sheetId: subUnitId }); + const skeleton = sheetSkeletonManagerService.ensureSkeleton(subUnitId); if (skeleton == null) { throw new Error('No current skeleton'); @@ -77,7 +77,7 @@ export function convertPositionSheetOverGridToAbsolute(unitId: string, subUnitId export function convertPositionCellToSheetOverGrid(unitId: string, subUnitId: string, cellOverGridPosition: ICellOverGridPosition, width: number, height: number, selectionRenderService: ISheetSelectionRenderService, sheetSkeletonManagerService: SheetSkeletonManagerService) { const { column: fromColumn, columnOffset: fromColumnOffset, row: fromRow, rowOffset: fromRowOffset } = cellOverGridPosition; - const skeleton = sheetSkeletonManagerService.getOrCreateSkeleton({ sheetId: subUnitId }); + const skeleton = sheetSkeletonManagerService.ensureSkeleton(subUnitId); if (skeleton == null) { throw new Error('No current skeleton'); diff --git a/packages/sheets-ui/src/views/operate-container/AutoFillPopupMenu.tsx b/packages/sheets-ui/src/views/operate-container/AutoFillPopupMenu.tsx index 9fd35cd61a5..699b5ed790d 100644 --- a/packages/sheets-ui/src/views/operate-container/AutoFillPopupMenu.tsx +++ b/packages/sheets-ui/src/views/operate-container/AutoFillPopupMenu.tsx @@ -17,7 +17,7 @@ import type { ICommandInfo, IExecutionOptions } from '@univerjs/core'; import { ICommandService, IUniverInstanceService, LocaleService, toDisposable, useDependency } from '@univerjs/core'; import { DropdownLegacy } from '@univerjs/design'; -import { IRenderManagerService } from '@univerjs/engine-render'; +import { convertTransformToOffsetX, convertTransformToOffsetY, IRenderManagerService } from '@univerjs/engine-render'; import { Autofill, CheckMarkSingle, MoreDownSingle } from '@univerjs/icons'; import clsx from 'clsx'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; @@ -161,10 +161,10 @@ export const AutoFillPopupMenu: React.FC<{}> = () => { const scaleY = scene?.scaleY; const scrollXY = scene?.getViewportScrollXY(viewport); if (!scaleX || !scene || !scaleX || !scaleY || !scrollXY) return null; - const x = skeleton?.getNoMergeCellPositionByIndex(anchor.row, anchor.col).endX || 0; - const y = skeleton?.getNoMergeCellPositionByIndex(anchor.row, anchor.col).endY || 0; - const relativeX = skeleton?.convertTransformToOffsetX(x, scaleX, scrollXY); - const relativeY = skeleton?.convertTransformToOffsetY(y, scaleY, scrollXY); + const x = skeleton?.getNoMergeCellWithCoordByIndex(anchor.row, anchor.col).endX || 0; + const y = skeleton?.getNoMergeCellWithCoordByIndex(anchor.row, anchor.col).endY || 0; + const relativeX = convertTransformToOffsetX(x, scaleX, scrollXY); + const relativeY = convertTransformToOffsetY(y, scaleY, scrollXY); if (relativeX == null || relativeY == null) return null; const onVisibleChange = (visible: boolean) => { diff --git a/packages/sheets/src/index.ts b/packages/sheets/src/index.ts index 4b517076aca..deca27470e5 100644 --- a/packages/sheets/src/index.ts +++ b/packages/sheets/src/index.ts @@ -434,4 +434,6 @@ export { type ISetWorksheetActiveOperationParams, SetWorksheetActiveOperation } export { type IToggleCellCheckboxCommandParams, ToggleCellCheckboxCommand } from './commands/commands/toggle-checkbox.command'; export { SCOPE_WORKBOOK_VALUE_DEFINED_NAME } from './controllers/defined-name-data.controller'; export type { ICellOverGridPosition, ISheetOverGridPosition } from './basics/cell-position'; + +export { SheetSkeletonService } from './skeleton/skeleton.service'; // #endregion diff --git a/packages/sheets/src/sheets-plugin.ts b/packages/sheets/src/sheets-plugin.ts index 47981e1a079..6d6282e2eb7 100644 --- a/packages/sheets/src/sheets-plugin.ts +++ b/packages/sheets/src/sheets-plugin.ts @@ -46,6 +46,7 @@ import { SheetRangeThemeService } from './services/range-theme-service'; import { RefRangeService } from './services/ref-range/ref-range.service'; import { SheetsSelectionsService } from './services/selections/selection.service'; import { SheetInterceptorService } from './services/sheet-interceptor/sheet-interceptor.service'; +import { SheetSkeletonService } from './skeleton/skeleton.service'; const PLUGIN_NAME = 'SHEET_PLUGIN'; @@ -92,6 +93,7 @@ export class UniverSheetsPlugin extends Plugin { [INumfmtService, { useClass: NumfmtService }], [SheetInterceptorService], [SheetRangeThemeService], + [SheetSkeletonService], // controllers [BasicWorksheetController], @@ -142,6 +144,7 @@ export class UniverSheetsPlugin extends Plugin { [WorkbookPermissionService], [WorksheetPermissionService], [SheetPermissionViewModelController], + [SheetSkeletonService], ]); } diff --git a/packages/sheets/src/skeleton/skeleton.service.ts b/packages/sheets/src/skeleton/skeleton.service.ts new file mode 100644 index 00000000000..344d6c336e1 --- /dev/null +++ b/packages/sheets/src/skeleton/skeleton.service.ts @@ -0,0 +1,54 @@ +/** + * Copyright 2023-present DreamNum Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Nullable, SheetSkeleton } from '@univerjs/core'; +import { Disposable, Inject, Injector } from '@univerjs/core'; + +export class SheetSkeletonService extends Disposable { + private _sheetSkeletonStore: Map> = new Map(); + + constructor( + @Inject(Injector) readonly _injector: Injector + ) { + // empty + super(); + + this.disposeWithMe(() => { + this._sheetSkeletonStore = new Map(); + }); + } + + getSkeleton(unitId: string, subUnitId: string): Nullable { + if (!this._sheetSkeletonStore.has(unitId)) { + return undefined; + } + return this._sheetSkeletonStore.get(unitId)!.get(subUnitId); + } + + setSkeleton(unitId: string, subUnitId: string, skeleton: SheetSkeleton) { + if (!this._sheetSkeletonStore.has(unitId)) { + this._sheetSkeletonStore.set(unitId, new Map()); + } + this._sheetSkeletonStore.get(unitId)!.set(subUnitId, skeleton); + } + + deleteSkeleton(unitId: string, subUnitId: string) { + if (!this._sheetSkeletonStore.has(unitId)) { + return; + } + this._sheetSkeletonStore.get(unitId)!.delete(subUnitId); + } +}