From ce068bb6bf116f96798cbe0a17786b4a4b802e89 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Wed, 25 Sep 2024 21:01:33 +0800 Subject: [PATCH] chore: better code --- eslint.config.js | 4 + packages/engine-render/src/basics/const.ts | 2 +- packages/engine-render/src/basics/draw.ts | 26 +- .../src/components/docs/document.ts | 30 +- .../src/components/docs/extensions/border.ts | 20 +- .../src/components/sheets/constants.ts | 22 ++ .../sheets/extensions/background.ts | 57 ++-- .../components/sheets/extensions/border.ts | 57 ++-- .../src/components/sheets/extensions/font.ts | 221 +++++++------- .../src/components/sheets/interfaces.ts | 13 +- .../src/components/sheets/sheet-skeleton.ts | 272 ++++++++++-------- .../engine-render/src/components/skeleton.ts | 4 +- .../src/controllers/dv-render.controller.ts | 1 - .../commands/commands/set-border-command.ts | 2 - 14 files changed, 362 insertions(+), 369 deletions(-) create mode 100644 packages/engine-render/src/components/sheets/constants.ts diff --git a/eslint.config.js b/eslint.config.js index 2d91596110b5..b11e7a2115d3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -25,6 +25,10 @@ export default antfu({ html: true, }, rules: baseRules, + ignores: [ + 'examples/**/*.json', + // ...globs + ], }, { files: ['**/*.ts', '**/*.tsx'], ignores: [ diff --git a/packages/engine-render/src/basics/const.ts b/packages/engine-render/src/basics/const.ts index e674fafcbd9e..3e6f08ab8fad 100644 --- a/packages/engine-render/src/basics/const.ts +++ b/packages/engine-render/src/basics/const.ts @@ -99,7 +99,7 @@ export enum DOCUMENT_CONTEXT_CLIP_TYPE { export const COLOR_BLACK_RGB = 'rgb(0,0,0)'; -export enum BORDER_TYPE { +export enum BORDER_LTRB { TOP = 't', BOTTOM = 'b', LEFT = 'l', diff --git a/packages/engine-render/src/basics/draw.ts b/packages/engine-render/src/basics/draw.ts index ba3b3db6a3c5..3491c6f767ee 100644 --- a/packages/engine-render/src/basics/draw.ts +++ b/packages/engine-render/src/basics/draw.ts @@ -15,11 +15,11 @@ */ import type { IPosition } from '@univerjs/core'; -import { BorderStyleTypes } from '@univerjs/core'; - import type { UniverRenderingContext } from '../context'; -import { BORDER_TYPE, ORIENTATION_TYPE } from './const'; + import type { IDocumentSkeletonLine } from './i-document-skeleton-cached'; +import { BorderStyleTypes } from '@univerjs/core'; +import { BORDER_LTRB, ORIENTATION_TYPE } from './const'; import { createCanvasElement } from './tools'; import { Vector2 } from './vector2'; @@ -61,28 +61,28 @@ export function getDevicePixelRatio(): number { * @param lineWidthBuffer Solving the problem of mitered corners in the drawing of borders thicker than 2 pixels, caused by the line segments being centered. * @param position border draw position */ -export function drawLineByBorderType(ctx: UniverRenderingContext, type: BORDER_TYPE, lineWidthBuffer: number, position: IPosition) { +export function drawLineByBorderType(ctx: UniverRenderingContext, type: BORDER_LTRB, lineWidthBuffer: number, position: IPosition) { let drawStartX = 0; let drawStartY = 0; let drawEndX = 0; let drawEndY = 0; const { startX, startY, endX, endY } = position; - if (type === BORDER_TYPE.TOP) { + if (type === BORDER_LTRB.TOP) { drawStartX = startX - lineWidthBuffer; drawStartY = startY; drawEndX = endX + lineWidthBuffer; drawEndY = startY; - } else if (type === BORDER_TYPE.BOTTOM) { + } else if (type === BORDER_LTRB.BOTTOM) { drawStartX = startX - lineWidthBuffer; drawStartY = endY; drawEndX = endX - lineWidthBuffer; drawEndY = endY; - } else if (type === BORDER_TYPE.LEFT) { + } else if (type === BORDER_LTRB.LEFT) { drawStartX = startX; drawStartY = startY - lineWidthBuffer; drawEndX = startX; drawEndY = endY + lineWidthBuffer; - } else if (type === BORDER_TYPE.RIGHT) { + } else if (type === BORDER_LTRB.RIGHT) { drawStartX = endX; drawStartY = startY - lineWidthBuffer; drawEndX = endX; @@ -90,14 +90,14 @@ export function drawLineByBorderType(ctx: UniverRenderingContext, type: BORDER_T } // ctx.clearRect(drawStartX - 1, drawStartY - 1, drawEndX - drawStartX + 2, drawEndY - drawStartY + 2); - ctx.beginPath(); + // ctx.beginPath(); ctx.moveToByPrecision(drawStartX, drawStartY); ctx.lineToByPrecision(drawEndX, drawEndY); - ctx.closePathByEnv(); + // ctx.closePathByEnv(); ctx.stroke(); } -export function drawDiagonalLineByBorderType(ctx: UniverRenderingContext, type: BORDER_TYPE, position: IPosition) { +export function drawDiagonalLineByBorderType(ctx: UniverRenderingContext, type: BORDER_LTRB, position: IPosition) { let drawStartX = 0; let drawStartY = 0; let drawEndX = 0; @@ -150,7 +150,7 @@ export function drawDiagonalLineByBorderType(ctx: UniverRenderingContext, type: ctx.stroke(); } -export function clearLineByBorderType(ctx: UniverRenderingContext, type: BORDER_TYPE, position: IPosition) { +export function clearLineByBorderType(ctx: UniverRenderingContext, type: BORDER_LTRB, position: IPosition) { let drawStartX = 0; let drawStartY = 0; let drawEndX = 0; @@ -264,7 +264,7 @@ export function getRotateOrientation(angle: number) { } // rotate calculate logic https://www.processon.com/view/link/630df928f346fb0714c9c4ec -// eslint-disable-next-line max-lines-per-function +// eslint-disable-next-line max-lines-per-function, complexity export function getRotateOffsetAndFarthestHypotenuse( lines: IDocumentSkeletonLine[], rectWidth: number, diff --git a/packages/engine-render/src/components/docs/document.ts b/packages/engine-render/src/components/docs/document.ts index 9e0a59945b81..e7cf0070be94 100644 --- a/packages/engine-render/src/components/docs/document.ts +++ b/packages/engine-render/src/components/docs/document.ts @@ -14,11 +14,19 @@ * limitations under the License. */ -import { CellValueType, HorizontalAlign, VerticalAlign, WrapStrategy } from '@univerjs/core'; +import type { IDocumentRenderConfig, IScale, Nullable } from '@univerjs/core'; +import type { IDocumentSkeletonGlyph, IDocumentSkeletonLine, IDocumentSkeletonPage, IDocumentSkeletonTable } from '../../basics/i-document-skeleton-cached'; +import type { Transform } from '../../basics/transform'; +import type { IBoundRectNoAngle, IViewportInfo } from '../../basics/vector2'; +import type { UniverRenderingContext } from '../../context'; +import type { Scene } from '../../scene'; +import type { ComponentExtension, IExtensionConfig } from '../extension'; +import type { IDocumentsConfig, IPageMarginLayout } from './doc-component'; +import type { DocumentSkeleton } from './layout/doc-skeleton'; +import { CellValueType, HorizontalAlign, VerticalAlign, WrapStrategy } from '@univerjs/core'; import { Subject } from 'rxjs'; -import type { IDocumentRenderConfig, IScale, Nullable } from '@univerjs/core'; -import { BORDER_TYPE, drawLineByBorderType } from '../../basics'; +import { BORDER_LTRB, drawLineByBorderType } from '../../basics'; import { calculateRectRotate, getRotateOffsetAndFarthestHypotenuse } from '../../basics/draw'; import { LineType } from '../../basics/i-document-skeleton-cached'; import { VERTICAL_ROTATE_ANGLE } from '../../basics/text-rotation'; @@ -28,14 +36,6 @@ import { DocumentsSpanAndLineExtensionRegistry } from '../extension'; import { DocComponent } from './doc-component'; import { DOCS_EXTENSION_TYPE } from './doc-extension'; import { Liquid } from './liquid'; -import type { IDocumentSkeletonGlyph, IDocumentSkeletonLine, IDocumentSkeletonPage, IDocumentSkeletonTable } from '../../basics/i-document-skeleton-cached'; -import type { Transform } from '../../basics/transform'; -import type { IBoundRectNoAngle, IViewportInfo } from '../../basics/vector2'; -import type { UniverRenderingContext } from '../../context'; -import type { Scene } from '../../scene'; -import type { ComponentExtension, IExtensionConfig } from '../extension'; -import type { IDocumentsConfig, IPageMarginLayout } from './doc-component'; -import type { DocumentSkeleton } from './layout/doc-skeleton'; import './extensions'; export interface IPageRenderConfig { @@ -723,28 +723,28 @@ export class Documents extends DocComponent { x += marginLeft; y += marginTop; - drawLineByBorderType(ctx, BORDER_TYPE.LEFT, 0, { + drawLineByBorderType(ctx, BORDER_LTRB.LEFT, 0, { startX: x, startY: y, endX: x + pageWidth, endY: y + pageHeight, }); - drawLineByBorderType(ctx, BORDER_TYPE.TOP, 0, { + drawLineByBorderType(ctx, BORDER_LTRB.TOP, 0, { startX: x, startY: y, endX: x + pageWidth, endY: y + pageHeight, }); - drawLineByBorderType(ctx, BORDER_TYPE.RIGHT, 0, { + drawLineByBorderType(ctx, BORDER_LTRB.RIGHT, 0, { startX: x, startY: y, endX: x + pageWidth, endY: y + pageHeight, }); - drawLineByBorderType(ctx, BORDER_TYPE.BOTTOM, 0, { + drawLineByBorderType(ctx, BORDER_LTRB.BOTTOM, 0, { startX: x, startY: y, endX: x + pageWidth, diff --git a/packages/engine-render/src/components/docs/extensions/border.ts b/packages/engine-render/src/components/docs/extensions/border.ts index 75465e510e91..92c2ac6623a1 100644 --- a/packages/engine-render/src/components/docs/extensions/border.ts +++ b/packages/engine-render/src/components/docs/extensions/border.ts @@ -15,13 +15,13 @@ */ import type { BorderStyleTypes, IBorderData, IBorderStyleData, IScale, Nullable } from '@univerjs/core'; -import { getColorStyle } from '@univerjs/core'; +import type { IDocumentSkeletonGlyph } from '../../../basics/i-document-skeleton-cached'; -import { BORDER_TYPE, COLOR_BLACK_RGB, FIX_ONE_PIXEL_BLUR_OFFSET } from '../../../basics/const'; +import type { UniverRenderingContext } from '../../../context'; +import { getColorStyle } from '@univerjs/core'; +import { BORDER_LTRB, COLOR_BLACK_RGB, FIX_ONE_PIXEL_BLUR_OFFSET } from '../../../basics/const'; import { drawLineByBorderType, getLineWidth, setLineType } from '../../../basics/draw'; -import type { IDocumentSkeletonGlyph } from '../../../basics/i-document-skeleton-cached'; import { Vector2 } from '../../../basics/vector2'; -import type { UniverRenderingContext } from '../../../context'; import { DocumentsSpanAndLineExtensionRegistry } from '../../extension'; import { docExtension } from '../doc-extension'; @@ -85,7 +85,7 @@ export class Border extends docExtension { this._preBorderColor = color; } - drawLineByBorderType(ctx, type as BORDER_TYPE, (lineWidth - 1) / 2 / precisionScale, { + drawLineByBorderType(ctx, type as BORDER_LTRB, (lineWidth - 1) / 2 / precisionScale, { startX: spanStartPoint.x, startY: spanStartPoint.y, endX: spanStartPoint.x + spanWidth, @@ -103,11 +103,11 @@ export class Border extends docExtension { private _createBorderCache(borderData: IBorderData) { const { t, b, l, r } = borderData; - const borderCache = new Map>(); - t && borderCache.set(BORDER_TYPE.TOP, t); - b && borderCache.set(BORDER_TYPE.BOTTOM, b); - l && borderCache.set(BORDER_TYPE.LEFT, l); - r && borderCache.set(BORDER_TYPE.RIGHT, r); + const borderCache = new Map>(); + t && borderCache.set(BORDER_LTRB.TOP, t); + b && borderCache.set(BORDER_LTRB.BOTTOM, b); + l && borderCache.set(BORDER_LTRB.LEFT, l); + r && borderCache.set(BORDER_LTRB.RIGHT, r); return borderCache; } } diff --git a/packages/engine-render/src/components/sheets/constants.ts b/packages/engine-render/src/components/sheets/constants.ts new file mode 100644 index 000000000000..bb761c77f775 --- /dev/null +++ b/packages/engine-render/src/components/sheets/constants.ts @@ -0,0 +1,22 @@ +/** + * 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. + */ + +export const BORDER_Z_INDEX = 50; +export const FONT_EXTENSION_Z_INDEX = 45; +export const BG_Z_INDEX = 21; +export const PRINTING_BG_Z_INDEX = 21; + +export const EXPAND_SIZE_FOR_RENDER_OVERFLOW = 20; diff --git a/packages/engine-render/src/components/sheets/extensions/background.ts b/packages/engine-render/src/components/sheets/extensions/background.ts index ea78dc38a659..9b6aea175bcb 100644 --- a/packages/engine-render/src/components/sheets/extensions/background.ts +++ b/packages/engine-render/src/components/sheets/extensions/background.ts @@ -14,25 +14,19 @@ * limitations under the License. */ -import { Range } from '@univerjs/core'; import type { IRange, IScale, ISelectionCellWithMergeInfo, ObjectMatrix } from '@univerjs/core'; -import { fixLineWidthByScale, getColor, inViewRanges } from '../../../basics/tools'; -import { SpreadsheetExtensionRegistry } from '../../extension'; -import { SheetExtension } from './sheet-extension'; import type { UniverRenderingContext } from '../../../context'; import type { IDrawInfo } from '../../extension'; import type { SpreadsheetSkeleton } from '../sheet-skeleton'; import type { Spreadsheet } from '../spreadsheet'; +import { Range } from '@univerjs/core'; +import { fixLineWidthByScale, getColor, inViewRanges } from '../../../basics/tools'; +import { SpreadsheetExtensionRegistry } from '../../extension'; +import { BG_Z_INDEX, PRINTING_BG_Z_INDEX } from '../constants'; +import { SheetExtension } from './sheet-extension'; const UNIQUE_KEY = 'DefaultBackgroundExtension'; -/** - * in prev version background ext is higher than font ext. now turing back lower than font ext. - * font ext z-index is 30. - */ -const DOC_EXTENSION_Z_INDEX = 21; -const PRINTING_Z_INDEX = 21; - interface IRenderBGContext { ctx: UniverRenderingContext; spreadsheetSkeleton: SpreadsheetSkeleton; @@ -48,12 +42,8 @@ interface IRenderBGContext { export class Background extends SheetExtension { override uKey = UNIQUE_KEY; - override Z_INDEX = DOC_EXTENSION_Z_INDEX; - - PRINTING_Z_INDEX = PRINTING_Z_INDEX; - override get zIndex() { - return (this.parent as Spreadsheet)?.isPrinting ? this.PRINTING_Z_INDEX : this.Z_INDEX; + return (this.parent as Spreadsheet)?.isPrinting ? PRINTING_BG_Z_INDEX : BG_Z_INDEX; } override draw( @@ -64,8 +54,8 @@ export class Background extends SheetExtension { { viewRanges, checkOutOfViewBound }: IDrawInfo ) { const { stylesCache, worksheet, rowHeightAccumulation, columnTotalWidth, columnWidthAccumulation, rowTotalHeight } = spreadsheetSkeleton; - const { background: bgMatrixCacheByColor, backgroundPositions } = stylesCache; - if (!worksheet || !bgMatrixCacheByColor) return; + const { bgGroupMatrix } = stylesCache; + if (!worksheet || !bgGroupMatrix) return; if ( !rowHeightAccumulation || !columnWidthAccumulation || @@ -78,7 +68,6 @@ export class Background extends SheetExtension { const { scaleX, scaleY } = ctx.getScale(); const renderBGContext = { ctx, - backgroundPositions, scaleX, scaleY, checkOutOfViewBound, @@ -87,18 +76,17 @@ export class Background extends SheetExtension { spreadsheetSkeleton, } as IRenderBGContext; const renderBGCore = (rgb: string) => { - const bgColorMatrix = bgMatrixCacheByColor[rgb]; + const bgColorMatrix = bgGroupMatrix[rgb]; ctx.fillStyle = rgb || getColor([255, 255, 255])!; const backgroundPaths = new Path2D(); renderBGContext.backgroundPaths = backgroundPaths; ctx.beginPath(); - // bgColorMatrix.forValue(renderBGByCell); viewRanges.forEach((range) => { Range.foreach(range, (row, col) => { const bgConfig = bgColorMatrix.getValue(row, col); if (bgConfig) { - this.renderBGByCell(renderBGContext, row, col); + this.renderBGEachCell(renderBGContext, row, col); } }); }); @@ -106,23 +94,18 @@ export class Background extends SheetExtension { ctx.closePath(); }; - Object.keys(bgMatrixCacheByColor).forEach(renderBGCore); + Object.keys(bgGroupMatrix).forEach(renderBGCore); ctx.restore(); } - renderBGByCell(bgContext: IRenderBGContext, row: number, col: number) { - const { spreadsheetSkeleton, backgroundPositions, backgroundPaths, scaleX, scaleY, viewRanges, diffRanges } = bgContext; - // if (!checkOutOfViewBound && !inViewRanges(viewRanges, row, col)) { - // return true; - // } + renderBGEachCell(bgContext: IRenderBGContext, row: number, col: number) { + const { spreadsheetSkeleton, backgroundPaths, scaleX, scaleY, viewRanges, diffRanges } = bgContext; - const cellInfo = backgroundPositions?.getValue(row, col); - if (cellInfo == null) { - return true; - } + const calcHeader = false; + const cellMergeInfo = spreadsheetSkeleton.getMergedCellInfo(row, col, calcHeader); - let { startY, endY, startX, endX } = cellInfo; - const { isMerged, isMergedMainCell, mergeInfo } = cellInfo; + let { startY, endY, startX, endX } = cellMergeInfo; + const { isMerged, isMergedMainCell, mergeInfo } = cellMergeInfo; const renderRange = diffRanges && diffRanges.length > 0 ? diffRanges : viewRanges; // isMerged isMergedMainCell are mutually exclusive. isMerged true then isMergedMainCell false. @@ -140,12 +123,6 @@ export class Background extends SheetExtension { endX = mergeInfo.endX; } - // in merge range , but not top-left cell. - // if (isMerged) return true; - - // const combineWithMergeRanges = mergeTo; - //expandRangeIfIntersects([...mergeTo], [mergeInfo]); - // If curr cell is not in the viewrange (viewport + merged cells), exit early. if ((!isMerged && !isMergedMainCell) && !inViewRanges(renderRange!, row, col)) { return true; diff --git a/packages/engine-render/src/components/sheets/extensions/border.ts b/packages/engine-render/src/components/sheets/extensions/border.ts index 1c07c9b90ae5..51f78be1e517 100644 --- a/packages/engine-render/src/components/sheets/extensions/border.ts +++ b/packages/engine-render/src/components/sheets/extensions/border.ts @@ -14,24 +14,23 @@ * limitations under the License. */ -import { BorderStyleTypes, Range } from '@univerjs/core'; import type { IRange, IScale, ObjectMatrix } from '@univerjs/core'; - -import { BORDER_TYPE, COLOR_BLACK_RGB, FIX_ONE_PIXEL_BLUR_OFFSET } from '../../../basics/const'; -import { drawDiagonalLineByBorderType, drawLineByBorderType, getLineWidth, setLineType } from '../../../basics/draw'; -import { SpreadsheetExtensionRegistry } from '../../extension'; -import { SheetExtension } from './sheet-extension'; import type { UniverRenderingContext } from '../../../context'; + import type { IDrawInfo } from '../../extension'; import type { BorderCache, BorderCacheItem } from '../interfaces'; import type { SpreadsheetSkeleton } from '../sheet-skeleton'; +import { BorderStyleTypes, Range } from '@univerjs/core'; +import { BORDER_LTRB, COLOR_BLACK_RGB, FIX_ONE_PIXEL_BLUR_OFFSET } from '../../../basics/const'; +import { drawDiagonalLineByBorderType, drawLineByBorderType, getLineWidth, setLineType } from '../../../basics/draw'; +import { SpreadsheetExtensionRegistry } from '../../extension'; +import { BORDER_Z_INDEX } from '../constants'; +import { SheetExtension } from './sheet-extension'; const UNIQUE_KEY = 'DefaultBorderExtension'; -const BORDER_Z_INDEX = 50; interface IRenderBorderContext { ctx: UniverRenderingContext; - // border: BorderCacheItem; overflowCache: ObjectMatrix; precisionScale: number; spreadsheetSkeleton: SpreadsheetSkeleton; @@ -49,7 +48,7 @@ export class Border extends SheetExtension { _parentScale: IScale, spreadsheetSkeleton: SpreadsheetSkeleton, diffRanges: IRange[], - { viewRanges, checkOutOfViewBound }: IDrawInfo + { viewRanges }: IDrawInfo ) { const { stylesCache, overflowCache, worksheet, rowHeightAccumulation, columnTotalWidth, columnWidthAccumulation, rowTotalHeight } = spreadsheetSkeleton; if (!worksheet) return; @@ -61,13 +60,13 @@ export class Border extends SheetExtension { ) { return; } - ctx.save(); + const { borderMatrix } = stylesCache; + if (!borderMatrix) return; + ctx.save(); ctx.translateWithPrecisionRatio(FIX_ONE_PIXEL_BLUR_OFFSET, FIX_ONE_PIXEL_BLUR_OFFSET); - const precisionScale = this._getScale(ctx.getScale()); - const { border } = stylesCache; - if (!border) return; + const renderBorderContext = { ctx, precisionScale, @@ -77,26 +76,27 @@ export class Border extends SheetExtension { spreadsheetSkeleton, } as IRenderBorderContext; + ctx.beginPath(); viewRanges.forEach((range) => { Range.foreach(range, (row, col) => { - const borderConfig = border!.getValue(row, col); + const borderConfig = borderMatrix!.getValue(row, col); if (borderConfig) { - this.renderBorderByCell(renderBorderContext, row, col, borderConfig); + this.renderBorderEachCell(renderBorderContext, row, col, borderConfig); } }); }); - ctx.closePath(); ctx.restore(); } - renderBorderByCell(renderBorderContext: IRenderBorderContext, row: number, col: number, borderCacheItem: BorderCache) { + renderBorderEachCell(renderBorderContext: IRenderBorderContext, row: number, col: number, borderCacheItem: BorderCache) { const { ctx, precisionScale, overflowCache, spreadsheetSkeleton, diffRanges } = renderBorderContext; - const cellInfo = spreadsheetSkeleton.getCellByIndexWithNoHeader(row, col); + const calcHeader = false; + const cellMergeInfo = spreadsheetSkeleton.getMergedCellInfo(row, col, calcHeader); - const { startY: cellStartY, endY: cellEndY, startX: cellStartX, endX: cellEndX } = cellInfo; - const { isMerged, isMergedMainCell, mergeInfo } = cellInfo; + const { startY: cellStartY, endY: cellEndY, startX: cellStartX, endX: cellEndX } = cellMergeInfo; + const { isMerged, isMergedMainCell, mergeInfo } = cellMergeInfo; if (!isMerged) { const visibleRow = spreadsheetSkeleton.worksheet.getRowVisible(row); @@ -120,7 +120,7 @@ export class Border extends SheetExtension { let startX = cellStartX; let endX = cellEndX; - if (type !== BORDER_TYPE.TOP && type !== BORDER_TYPE.BOTTOM && type !== BORDER_TYPE.LEFT && type !== BORDER_TYPE.RIGHT) { + if (type !== BORDER_LTRB.TOP && type !== BORDER_LTRB.BOTTOM && type !== BORDER_LTRB.LEFT && type !== BORDER_LTRB.RIGHT) { if (isMerged) { return true; } @@ -135,16 +135,9 @@ export class Border extends SheetExtension { const lineWidth = getLineWidth(style); - // if (style !== preStyle) { setLineType(ctx, style); ctx.setLineWidthByPrecision(lineWidth); - // preStyle = style; - // } - - // if (color !== preColor) { ctx.strokeStyle = color || COLOR_BLACK_RGB; - // preColor = color; - // } drawDiagonalLineByBorderType(ctx, type, { startX, @@ -168,12 +161,12 @@ export class Border extends SheetExtension { private _getOverflowExclusion( overflowCache: ObjectMatrix, - type: BORDER_TYPE, + type: BORDER_LTRB, borderRow: number, borderColumn: number ) { let isDraw = false; - if (type === BORDER_TYPE.TOP || type === BORDER_TYPE.BOTTOM) { + if (type === BORDER_LTRB.TOP || type === BORDER_LTRB.BOTTOM) { return isDraw; } @@ -184,12 +177,12 @@ export class Border extends SheetExtension { rowArray.forEach((column) => { const rectangle = overflowCache.getValue(row, column)!; const { startColumn, endColumn } = rectangle; - if (type === BORDER_TYPE.LEFT && borderColumn > startColumn && borderColumn <= endColumn) { + if (type === BORDER_LTRB.LEFT && borderColumn > startColumn && borderColumn <= endColumn) { isDraw = true; return false; } - if (type === BORDER_TYPE.RIGHT && borderColumn >= startColumn && borderColumn < endColumn) { + if (type === BORDER_LTRB.RIGHT && borderColumn >= startColumn && borderColumn < endColumn) { isDraw = true; return false; } diff --git a/packages/engine-render/src/components/sheets/extensions/font.ts b/packages/engine-render/src/components/sheets/extensions/font.ts index 4d4cacfe5e86..4018879c171f 100644 --- a/packages/engine-render/src/components/sheets/extensions/font.ts +++ b/packages/engine-render/src/components/sheets/extensions/font.ts @@ -17,25 +17,23 @@ /* eslint-disable max-lines-per-function */ /* eslint-disable complexity */ +import type { ICellDataForSheetInterceptor, IRange, IScale, Nullable, ObjectMatrix } from '@univerjs/core'; +import type { UniverRenderingContext } from '../../../context'; +import type { Documents } from '../../docs/document'; +import type { IDrawInfo } from '../../extension'; +import type { IFontCacheItem } from '../interfaces'; +import type { SheetComponent } from '../sheet-component'; import { HorizontalAlign, Range, WrapStrategy } from '@univerjs/core'; -import { clampRange } from '@univerjs/engine-render'; -import type { ICellDataForSheetInterceptor, IRange, IScale, ObjectMatrix } from '@univerjs/core'; import { FIX_ONE_PIXEL_BLUR_OFFSET } from '../../../basics'; import { VERTICAL_ROTATE_ANGLE } from '../../../basics/text-rotation'; -import { inViewRanges } from '../../../basics/tools'; +import { clampRange, inViewRanges } from '../../../basics/tools'; import { SpreadsheetExtensionRegistry } from '../../extension'; +import { EXPAND_SIZE_FOR_RENDER_OVERFLOW, FONT_EXTENSION_Z_INDEX } from '../constants'; import { getDocsSkeletonPageSize, type SpreadsheetSkeleton } from '../sheet-skeleton'; import { SheetExtension } from './sheet-extension'; -import type { UniverRenderingContext } from '../../../context'; -import type { Documents } from '../../docs/document'; -import type { IDrawInfo } from '../../extension'; -import type { IFontCacheItem } from '../interfaces'; -import type { SheetComponent } from '../sheet-component'; const UNIQUE_KEY = 'DefaultFontExtension'; -const EXTENSION_Z_INDEX = 45; - interface IRenderFontContext { ctx: UniverRenderingContext; scale: number; @@ -47,12 +45,17 @@ interface IRenderFontContext { checkOutOfViewBound: boolean; diffRanges: IRange[]; spreadsheetSkeleton: SpreadsheetSkeleton; + overflowRectangle: Nullable; + cellData: ICellDataForSheetInterceptor; + startY: number; + endY: number; + startX: number; + endX: number; } export class Font extends SheetExtension { override uKey = UNIQUE_KEY; - - override Z_INDEX = EXTENSION_Z_INDEX; + override Z_INDEX = FONT_EXTENSION_Z_INDEX; getDocuments() { const parent = this.parent as SheetComponent; @@ -84,7 +87,7 @@ export class Font extends SheetExtension { const scale = this._getScale(parentScale); const { viewRanges = [], checkOutOfViewBound } = moreBoundsInfo; - const renderFontContext: IRenderFontContext = { + const renderFontContext = { ctx, scale, rowHeightAccumulation, @@ -95,106 +98,30 @@ export class Font extends SheetExtension { checkOutOfViewBound: checkOutOfViewBound || true, diffRanges, spreadsheetSkeleton, - }; + } as IRenderFontContext; ctx.save(); // fontMatrix.forValue((row: number, col: number, fontsConfig: IFontCacheItem) => { // this.renderFontByCellMatrix(renderFontContext, row, col, fontsConfig); // }); viewRanges.forEach((range) => { - range.startColumn -= 20; - range.endColumn += 20; + range.startColumn -= EXPAND_SIZE_FOR_RENDER_OVERFLOW; + range.endColumn += EXPAND_SIZE_FOR_RENDER_OVERFLOW; range = clampRange(range); Range.foreach(range, (row, col) => { - this.renderFontByCellMatrix2(renderFontContext, row, col, fontMatrix); + this.renderFontEachCell(renderFontContext, row, col, fontMatrix); }); }); ctx.restore(); } - renderFontByCellMatrix2(renderFontContext: IRenderFontContext, row: number, col: number, fontMatrix: ObjectMatrix) { - const { ctx, scale, rowHeightAccumulation, columnWidthAccumulation, viewRanges, checkOutOfViewBound, diffRanges, spreadsheetSkeleton } = renderFontContext; - - if (!checkOutOfViewBound) { - // if (!inViewRanges(viewRanges!, row, col)) { - // return true; - // } - } - - //#region merged cell - const cellInfo = spreadsheetSkeleton.getCellByIndexWithNoHeader(row, col); - // if (row === 6 && col === 1) debugger; - let { startY, endY, startX, endX } = cellInfo; - const { isMerged, isMergedMainCell, mergeInfo } = cellInfo; - - // if (isMerged && !isMergedMainCell) { - if (isMerged) { - // return true; - const mergeMainCellRow = mergeInfo.startRow; - const mergeMainCellCol = mergeInfo.startColumn; - row = mergeMainCellRow; - col = mergeMainCellCol; - - startY = mergeInfo.startY; - endY = mergeInfo.endY; - startX = mergeInfo.startX; - endX = mergeInfo.endX; - } - - // If the merged cell area intersects with the current viewRange, - // then merge it into the current viewRange. - // After the merge, the font extension within the current viewBounds - // also needs to be drawn once. - // But at this moment, we cannot assume that it is not within the viewRanges and exit, because there may still be horizontal overflow. - // At this moment, we can only exclude the cells that are not within the current row. - - // const combineWithMergeRanges = expandRangeIfIntersects([...mergeTo], [mergeInfo]); - // if (!inRowViewRanges(combineWithMergeRanges, row)) { - // return true; - // } - - if (isMergedMainCell) { - startY = mergeInfo.startY; - endY = mergeInfo.endY; - startX = mergeInfo.startX; - endX = mergeInfo.endX; - } - //#endregion - - /** - * Incremental content rendering for texture mapping - * If the diffRanges do not intersect with the startRow and endRow on the row, then exit early. - * - * If this cell is not within a merged region, the mergeInfo start and end values are just the cell itself. - */ - // if (diffRanges) { - // if (!this.isRowInRanges(mergeInfo.startRow, mergeInfo.endRow, diffRanges)) { - // return true; - // } - // } - - // If the cell is overflowing, but the overflowRectangle has not been set, - // then overflowRectangle is set to undefined. - const overflowRectangle = spreadsheetSkeleton.overflowCache.getValue(row, col); - - // If it's neither an overflow nor within the current range, - // then we can exit early (taking into account the range extension - // caused by the merged cells). - // if (!overflowRectangle && !inViewRanges(renderRange, row, col)) { - const renderRange = diffRanges && diffRanges.length > 0 ? diffRanges : viewRanges; - if (!overflowRectangle && (!isMergedMainCell && !isMerged) && !inViewRanges(renderRange, row, col)) { - return true; - } + clipTextOverflow(renderFontContext: IRenderFontContext, row: number, col: number, fontMatrix: ObjectMatrix) { + const { ctx, scale, overflowRectangle, rowHeightAccumulation, columnWidthAccumulation, cellData } = renderFontContext; + let { startX, endX, startY, endY } = renderFontContext; - const visibleRow = spreadsheetSkeleton.worksheet.getRowVisible(row); - const visibleCol = spreadsheetSkeleton.worksheet.getColVisible(col); - if (!visibleRow || !visibleCol) return true; - /** - * https://github.com/dream-num/univer-pro/issues/334 - * When horizontal alignment is not set, the default alignment for rotation angles varies to accommodate overflow scenarios. - */ - const fontsConfig = fontMatrix.getValue(row, col); - if (!fontsConfig) return true; + // https://github.com/dream-num/univer-pro/issues/334 + // When horizontal alignment is not set, the default alignment for rotation angles varies to accommodate overflow scenarios. + const fontsConfig = fontMatrix.getValue(row, col)!; const { horizontalAlign, vertexAngle = 0, centerAngle = 0 } = fontsConfig; let horizontalAlignOverFlow = horizontalAlign; if (horizontalAlign === HorizontalAlign.UNSPECIFIED) { @@ -205,14 +132,6 @@ export class Font extends SheetExtension { } } - const cellData = spreadsheetSkeleton.worksheet.getCell(row, col) as ICellDataForSheetInterceptor || {}; - if (cellData.fontRenderExtension?.isSkip) { - return true; - } - - ctx.save(); - ctx.beginPath(); - const rightOffset = cellData.fontRenderExtension?.rightOffset ?? 0; const leftOffset = cellData.fontRenderExtension?.leftOffset ?? 0; let isOverflow = true; @@ -225,7 +144,6 @@ export class Font extends SheetExtension { isOverflow = false; } } - const cellWidth = endX - startX; const cellHeight = endY - startY; @@ -242,12 +160,6 @@ export class Font extends SheetExtension { cellHeight - 2 / scale ); ctx.clip(); - // ctx.clearRectForTexture( - // startX + 1 / scale, - // startY + 1 / scale, - // cellWidth - 2 / scale, - // cellHeight - 2 / scale - // ); } else { if (horizontalAlignOverFlow === HorizontalAlign.CENTER) { this._clipRectangleForOverflow( @@ -288,13 +200,82 @@ export class Font extends SheetExtension { ctx.rectByPrecision(startX + 1 / scale, startY + 1 / scale, cellWidth - 2 / scale, cellHeight - 2 / scale); // for normal cell, forbid text overflow cellarea ctx.clip(); - // ctx.clearRectForTexture( - // startX + 1 / scale, - // startY + 1 / scale, - // cellWidth - 2 / scale, - // cellHeight - 2 / scale - // ); } + } + + renderFontEachCell(renderFontContext: IRenderFontContext, row: number, col: number, fontMatrix: ObjectMatrix) { + const { ctx, viewRanges, diffRanges, spreadsheetSkeleton } = renderFontContext; + + //#region merged cell + const calcHeader = false; + const cellInfo = spreadsheetSkeleton.getMergedCellInfo(row, col, calcHeader); + let { startY, endY, startX, endX } = cellInfo; + const { isMerged, isMergedMainCell, mergeInfo } = cellInfo; + + // merged, but not primary cell + if (isMerged && !isMergedMainCell) { + // return true; + const mergeMainCellRow = mergeInfo.startRow; + const mergeMainCellCol = mergeInfo.startColumn; + row = mergeMainCellRow; + col = mergeMainCellCol; + + startY = mergeInfo.startY; + endY = mergeInfo.endY; + startX = mergeInfo.startX; + endX = mergeInfo.endX; + } + + // merged and primary cell + if (isMergedMainCell) { + startY = mergeInfo.startY; + endY = mergeInfo.endY; + startX = mergeInfo.startX; + endX = mergeInfo.endX; + } + //#endregion + + const fontsConfig = fontMatrix.getValue(row, col); + if (!fontsConfig) return true; + + //#region overflow + // If the cell is overflowing, but the overflowRectangle has not been set, + // then overflowRectangle is set to undefined. + const overflowRange = spreadsheetSkeleton.overflowCache.getValue(row, col); + + // If it's neither an overflow nor within the current range, + // then we can exit early + const renderRange = diffRanges && diffRanges.length > 0 ? diffRanges : viewRanges; + const notInMergeRange = !isMergedMainCell && !isMerged; + if (!overflowRange && notInMergeRange) { + if (!inViewRanges(renderRange, row, col)) { + return true; + } + } + //#endregion + + const visibleRow = spreadsheetSkeleton.worksheet.getRowVisible(row); + const visibleCol = spreadsheetSkeleton.worksheet.getColVisible(col); + if (!visibleRow || !visibleCol) return true; + + const cellData = spreadsheetSkeleton.worksheet.getCell(row, col) as ICellDataForSheetInterceptor || {}; + if (cellData.fontRenderExtension?.isSkip) { + return true; + } + + ctx.save(); + ctx.beginPath(); + + //#region text overflow + renderFontContext.overflowRectangle = overflowRange; + renderFontContext.cellData = cellData; + renderFontContext.startX = startX; + renderFontContext.startY = startY; + renderFontContext.endX = endX; + renderFontContext.endY = endY; + this.clipTextOverflow(renderFontContext, row, col, fontMatrix); + //#endregion + ctx.translate(startX + FIX_ONE_PIXEL_BLUR_OFFSET, startY + FIX_ONE_PIXEL_BLUR_OFFSET); this._renderDocuments(ctx, fontsConfig, startX, startY, endX, endY, row, col, spreadsheetSkeleton.overflowCache); diff --git a/packages/engine-render/src/components/sheets/interfaces.ts b/packages/engine-render/src/components/sheets/interfaces.ts index 86ead765c565..ab12dd5f5a52 100644 --- a/packages/engine-render/src/components/sheets/interfaces.ts +++ b/packages/engine-render/src/components/sheets/interfaces.ts @@ -17,13 +17,12 @@ import type { BorderStyleTypes, HorizontalAlign, - ISelectionCellWithMergeInfo, ObjectMatrix, VerticalAlign, WrapStrategy, } from '@univerjs/core'; -import type { BORDER_TYPE } from '../../basics/const'; +import type { BORDER_LTRB } from '../../basics/const'; import type { Canvas } from '../../canvas'; import type { UniverRenderingContext } from '../../context'; import type { DocumentSkeleton } from '../docs/layout/doc-skeleton'; @@ -33,7 +32,7 @@ export interface BorderCache { } export interface BorderCacheItem { - type: BORDER_TYPE; + type: BORDER_LTRB; style: BorderStyleTypes; color: string; } @@ -52,13 +51,11 @@ export interface IFontCacheItem { // content?: string; } -type colorString = string; +export type colorString = string; export interface IStylesCache { - background?: Record>; - backgroundPositions?: ObjectMatrix; - font?: Record>; + bgGroupMatrix: Record>; fontMatrix: ObjectMatrix; - border?: ObjectMatrix; + borderMatrix: ObjectMatrix; } export enum ShowGridlinesState { diff --git a/packages/engine-render/src/components/sheets/sheet-skeleton.ts b/packages/engine-render/src/components/sheets/sheet-skeleton.ts index 140854384d08..b34b03247a8b 100644 --- a/packages/engine-render/src/components/sheets/sheet-skeleton.ts +++ b/packages/engine-render/src/components/sheets/sheet-skeleton.ts @@ -74,7 +74,7 @@ import { WrapStrategy, } from '@univerjs/core'; import { distinctUntilChanged, startWith } from 'rxjs'; -import { BORDER_TYPE, COLOR_BLACK_RGB, MAXIMUM_ROW_HEIGHT } from '../../basics/const'; +import { BORDER_LTRB, COLOR_BLACK_RGB, MAXIMUM_ROW_HEIGHT } from '../../basics/const'; import { getRotateOffsetAndFarthestHypotenuse } from '../../basics/draw'; import { convertTextRotation, VERTICAL_ROTATE_ANGLE } from '../../basics/text-rotation'; import { @@ -89,6 +89,7 @@ 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 } from './constants'; function addLinkToDocumentModel(documentModel: DocumentDataModel, linkUrl: string, linkId: string): void { const body = documentModel.getBody()!; @@ -239,6 +240,11 @@ export interface ICacheItem { border: boolean; } +export interface IStylesCacheOptions { + mergeRange?: IRange; + cacheItem?: ICacheItem; +} + export class SpreadsheetSkeleton extends Skeleton { private _rowHeightAccumulation: number[] = []; private _columnWidthAccumulation: number[] = []; @@ -251,7 +257,7 @@ export class SpreadsheetSkeleton extends Skeleton { /** * Range of visible area(range in viewBounds) */ - private _rowColumnSegment: IRowColumnRange = { + private _visibleRange: IRowColumnRange = { startRow: -1, endRow: -1, startColumn: -1, @@ -261,11 +267,10 @@ export class SpreadsheetSkeleton extends Skeleton { // private _dataMergeCache: IRange[] = []; private _overflowCache: ObjectMatrix = new ObjectMatrix(); private _stylesCache: IStylesCache = { - background: {}, - backgroundPositions: new ObjectMatrix(), - font: {} as Record>, + // background render background by color, it's better we put cells share same bg color in a group. + bgGroupMatrix: {}, fontMatrix: new ObjectMatrix(), - border: new ObjectMatrix(), + borderMatrix: new ObjectMatrix(), }; /** A matrix to store if a (row, column) position has render cache. */ @@ -327,7 +332,7 @@ export class SpreadsheetSkeleton extends Skeleton { * Range of visible area(range in viewBounds) */ get rowColumnSegment(): IRowColumnRange { - return this._rowColumnSegment; + return this._visibleRange; } // get dataMergeCache(): IRange[] { @@ -367,23 +372,14 @@ export class SpreadsheetSkeleton extends Skeleton { this._columnTotalWidth = 0; this._rowHeaderWidth = 0; this._columnHeaderHeight = 0; - this._rowColumnSegment = { + this._visibleRange = { startRow: -1, endRow: -1, startColumn: -1, endColumn: -1, }; // this._dataMergeCache = []; - this._stylesCache = { - background: {}, - backgroundPositions: new ObjectMatrix(), - font: {} as Record>, - fontMatrix: new ObjectMatrix(), - border: new ObjectMatrix(), - }; - this._handleBgMatrix.reset(); - this._handleBorderMatrix.reset(); - this._overflowCache.reset(); + this._resetCache(); this._worksheetData = null as unknown as IWorksheetData; this._cellData = null as unknown as ObjectMatrix>; @@ -459,7 +455,7 @@ export class SpreadsheetSkeleton extends Skeleton { } if (bounds != null) { - this._rowColumnSegment = this.getRowColumnSegment(bounds); + this._visibleRange = this.getRowColumnSegment(bounds); } return true; @@ -483,9 +479,7 @@ export class SpreadsheetSkeleton extends Skeleton { calculate(bounds?: IViewportInfo): Nullable { this._resetCache(); - this.calculateWithoutClearingCache(bounds); - return this; } @@ -781,8 +775,6 @@ export class SpreadsheetSkeleton extends Skeleton { let startColumn = column; let endColumn = column; - // console.log('documentSkeleton', cell?.v, column, endColumn, row, column, columnCount, contentWidth); - if (horizontalAlign === HorizontalAlign.CENTER) { startColumn = this._getOverflowBound(row, column, 0, contentWidth / 2, horizontalAlign); endColumn = this._getOverflowBound(row, column, columnCount - 1, contentWidth / 2, horizontalAlign); @@ -1021,8 +1013,9 @@ export class SpreadsheetSkeleton extends Skeleton { /** * Return cell information corresponding to the current coordinates, including the merged cell object. - * @param row Specified Row Coordinate - * @param column Specified Column Coordinate + * @param row {number} row index + * @param column {number} Specified Column Coordinate + * @returns {ISelectionCellWithMergeInfo} cellInfo */ getCellByIndex(row: number, column: number): ISelectionCellWithMergeInfo { const { @@ -1062,6 +1055,12 @@ export class SpreadsheetSkeleton extends Skeleton { }; } + /** + * Nearly same as getCellByIndex, but startXY endXY use corrdinates without rowheader & colheader. + * @param row + * @param column + * @returns {ISelectionCellWithMergeInfo} cellInfo + */ getCellByIndexWithNoHeader(row: number, column: number): ISelectionCellWithMergeInfo { const { rowHeightAccumulation, columnWidthAccumulation } = this; @@ -1091,6 +1090,22 @@ export class SpreadsheetSkeleton extends Skeleton { }; } + /** + * TODO @lumixraku + * Only one coordinate system in Sheet is better. + * @param row + * @param column + * @param header + * @returns {ISelectionCellWithMergeInfo} cellInfo + */ + getMergedCellInfo(row: number, column: number, header: boolean): ISelectionCellWithMergeInfo { + if (header) { + return this.getCellByIndex(row, column); + } else { + return this.getCellByIndexWithNoHeader(row, column); + } + } + // convert canvas content position to physical position in screen convertTransformToOffsetX(offsetX: number, scaleX: number, scrollXY: { x: number; y: number }): number { const { x: scrollX } = scrollXY; @@ -1216,7 +1231,6 @@ export class SpreadsheetSkeleton extends Skeleton { wrapStrategy, } ); - // console.log(cell.p); } else if (cell.v != null) { const textStyle = this._getFontFormat(style); fontString = getFontStyleString(textStyle, this._localService).fontCache; @@ -1677,9 +1691,9 @@ export class SpreadsheetSkeleton extends Skeleton { } private _calculateStylesCache(): void { - const rowColumnSegment = this._rowColumnSegment; + const visibleRange = this._visibleRange; const columnWidthAccumulation = this.columnWidthAccumulation; - const { startRow, endRow, startColumn, endColumn } = rowColumnSegment; + const { startRow, endRow, startColumn, endColumn } = visibleRange; if (endColumn === -1 || endRow === -1) return; @@ -1701,13 +1715,13 @@ export class SpreadsheetSkeleton extends Skeleton { } // Calculate the text length for overflow situations, focusing on the leftmost column within the visible range. - for (let c = 0; c < startColumn; c++) { + for (let c = Math.max(startRow - EXPAND_SIZE_FOR_RENDER_OVERFLOW); c < startColumn; c++) { this._setStylesCache(r, c, { cacheItem: { bg: false, border: false } }); } if (endColumn === 0) continue; // Calculate the text length for overflow situations, focusing on the rightmost column within the visible range. - for (let c = endColumn + 1; c < columnWidthAccumulation.length; c++) { + for (let c = endColumn + 1; c < Math.min(endColumn + EXPAND_SIZE_FOR_RENDER_OVERFLOW, columnWidthAccumulation.length); c++) { this._setStylesCache(r, c, { cacheItem: { bg: false, border: false } }); } } @@ -1721,16 +1735,13 @@ export class SpreadsheetSkeleton extends Skeleton { * Any changes to sheet model would reset cache. */ private _resetCache(): void { - this._stylesCache = { - background: {}, - backgroundPositions: new ObjectMatrix(), - font: {}, - fontMatrix: new ObjectMatrix(), - border: new ObjectMatrix(), - }; + this._stylesCache.bgGroupMatrix = {}; + this._stylesCache.fontMatrix.reset(); + this._stylesCache.borderMatrix.reset(); this._handleBgMatrix.reset(); this._handleBorderMatrix.reset(); this._overflowCache.reset(); + this._renderCellCacheMatrix.reset(); } private _makeDocumentSkeletonDirty(row: number, col: number): void { @@ -1752,23 +1763,23 @@ export class SpreadsheetSkeleton extends Skeleton { if (style && style.bd) { const mergeRange = options?.mergeRange; if (mergeRange) { - this._setMergeBorderProps(BORDER_TYPE.TOP, this._stylesCache, mergeRange); - this._setMergeBorderProps(BORDER_TYPE.BOTTOM, this._stylesCache, mergeRange); - this._setMergeBorderProps(BORDER_TYPE.LEFT, this._stylesCache, mergeRange); - this._setMergeBorderProps(BORDER_TYPE.RIGHT, this._stylesCache, mergeRange); + this._setMergeBorderProps(BORDER_LTRB.TOP, mergeRange, style); + this._setMergeBorderProps(BORDER_LTRB.BOTTOM, mergeRange, style); + this._setMergeBorderProps(BORDER_LTRB.LEFT, mergeRange, style); + this._setMergeBorderProps(BORDER_LTRB.RIGHT, mergeRange, style); } else if (!this.intersectMergeRange(row, col)) { - this._setBorderProps(row, col, BORDER_TYPE.TOP, style, this._stylesCache); - this._setBorderProps(row, col, BORDER_TYPE.BOTTOM, style, this._stylesCache); - this._setBorderProps(row, col, BORDER_TYPE.LEFT, style, this._stylesCache); - this._setBorderProps(row, col, BORDER_TYPE.RIGHT, style, this._stylesCache); + this._setBorderProps(row, col, BORDER_LTRB.TOP, style); + this._setBorderProps(row, col, BORDER_LTRB.BOTTOM, style); + this._setBorderProps(row, col, BORDER_LTRB.LEFT, style); + this._setBorderProps(row, col, BORDER_LTRB.RIGHT, style); } - this._setBorderProps(row, col, BORDER_TYPE.TL_BR, style, this._stylesCache); - this._setBorderProps(row, col, BORDER_TYPE.TL_BC, style, this._stylesCache); - this._setBorderProps(row, col, BORDER_TYPE.TL_MR, style, this._stylesCache); - this._setBorderProps(row, col, BORDER_TYPE.BL_TR, style, this._stylesCache); - this._setBorderProps(row, col, BORDER_TYPE.ML_TR, style, this._stylesCache); - this._setBorderProps(row, col, BORDER_TYPE.BC_TR, style, this._stylesCache); + this._setBorderProps(row, col, BORDER_LTRB.TL_BR, style); + this._setBorderProps(row, col, BORDER_LTRB.TL_BC, style); + this._setBorderProps(row, col, BORDER_LTRB.TL_MR, style); + this._setBorderProps(row, col, BORDER_LTRB.BL_TR, style); + this._setBorderProps(row, col, BORDER_LTRB.ML_TR, style); + this._setBorderProps(row, col, BORDER_LTRB.BC_TR, style); } } @@ -1785,14 +1796,10 @@ export class SpreadsheetSkeleton extends Skeleton { this._handleBgMatrix.setValue(row, col, true); if (style && style.bg && style.bg.rgb) { const rgb = style.bg.rgb; - if (!this._stylesCache.background![rgb]) { - this._stylesCache.background![rgb] = new ObjectMatrix(); + if (!this._stylesCache.bgGroupMatrix[rgb]) { + this._stylesCache.bgGroupMatrix[rgb] = new ObjectMatrix(); } - - const bgCache = this._stylesCache.background![rgb]; - bgCache.setValue(row, col, rgb); - const cellInfo = this.getCellByIndexWithNoHeader(row, col); - this._stylesCache.backgroundPositions?.setValue(row, col, cellInfo); + this._stylesCache.bgGroupMatrix[rgb].setValue(row, col, rgb); } } @@ -1811,13 +1818,6 @@ export class SpreadsheetSkeleton extends Skeleton { const { fontString: _fontString, textRotation, wrapStrategy, verticalAlign, horizontalAlign } = modelObject; - // if (!this._stylesCache.font![fontString]) { - // this._stylesCache.font![fontString] = new ObjectMatrix(); - // } - - // const fontFamilyMatrix: ObjectMatrix = this._stylesCache.font![fontString]; - // if (fontFamilyMatrix.getValue(row, col)) return; - const documentViewModel = new DocumentViewModel(documentModel); if (documentViewModel) { const { vertexAngle, centerAngle } = convertTextRotation(textRotation); @@ -1833,22 +1833,47 @@ export class SpreadsheetSkeleton extends Skeleton { wrapStrategy, }; this._stylesCache.fontMatrix.setValue(row, col, config); - // fontFamilyMatrix.setValue(row, col, config); this._calculateOverflowCell(row, col, config); } } /** - * Set border background and font to this._stylesCache + * This cache is for the @getCell, used to reduce the time it takes in @getCell, and is only utilized during the rendering phase. + * Any changes will clear the cache. + */ + private _renderCellCacheMatrix: ObjectMatrix> = new ObjectMatrix(); + + /** + * Same as getCell but only for render(see @_setStylesCache). + * Every change in sheet would clear this matrix (see @_resetCache) + * @param row + * @param col + * @returns {Nullable} cellData + */ + getCellForRender(row: number, col: number) { + if (!this._renderCellCacheMatrix.getValue(row, col)) { + const v = this.worksheet.getCell(row, col) || this.worksheet.getCellRaw(row, col); + this._renderCellCacheMatrix.setValue(row, col); + return v; + } + return this._renderCellCacheMatrix.getValue(row, col); + } + + /** + * Set border & background and font to this._stylesCache * @param row {number} * @param col {number} * @param options {{ mergeRange: IRange; cacheItem: ICacheItem } | undefined} */ - private _setStylesCache(row: number, col: number, options?: { mergeRange?: IRange; cacheItem?: ICacheItem }): void { + private _setStylesCache(row: number, col: number, options?: IStylesCacheOptions): void { if (row === -1 || col === -1) { return; } + if (!options) { + options = { cacheItem: { bg: true, border: true } }; + } + // return in advance if we already handled this cell. const handledBgCell = Tools.isDefine(this._handleBgMatrix.getValue(row, col)); const handledBorderCell = Tools.isDefine(this._handleBorderMatrix.getValue(row, col)); @@ -1857,18 +1882,9 @@ export class SpreadsheetSkeleton extends Skeleton { return; } - if (!options) { - options = { cacheItem: { bg: true, border: true } }; - } - const { isMerged, isMergedMainCell, startRow, startColumn, endRow, endColumn } = this._getCellMergeInfo(row, col); - if (options) { - options.mergeRange = { startRow, startColumn, endRow, endColumn }; - } - + // if hidden and not in mergeRange, then return. const hidden = this.worksheet.getColVisible(col) === false || this.worksheet.getRowVisible(row) === false; - - // hiddene and not in mergeRange return. if (hidden) { // If the cell is merged and is not the main cell, the cell is not rendered. if (isMerged && !isMergedMainCell) { @@ -1879,7 +1895,10 @@ export class SpreadsheetSkeleton extends Skeleton { } } - const cell = this.worksheet.getCell(row, col) || this.worksheet.getCellRaw(row, col); + if (options) { + options.mergeRange = { startRow, startColumn, endRow, endColumn }; + } + const cell = this.getCellForRender(row, col); const style = this._styles.getStyleByCell(cell); this._setBgStylesCache(row, col, style, options); this._setBorderStylesCache(row, col, style, options); @@ -1997,8 +2016,9 @@ export class SpreadsheetSkeleton extends Skeleton { * 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. */ - private _setMergeBorderProps(type: BORDER_TYPE, cache: IStylesCache, mergeRange: IRange): void { - if (!this.worksheet || !cache.border) { + private _setMergeBorderProps(ltrb: BORDER_LTRB, mergeRange: IRange, style: IStyleData): void { + const cache = this._stylesCache; + if (!this.worksheet || !cache.borderMatrix) { return; } @@ -2007,59 +2027,60 @@ export class SpreadsheetSkeleton extends Skeleton { let forStart = mergeRange.startRow; let forEnd = mergeRange.endRow; - let row = mergeRange.startRow; + let col = mergeRange.startColumn; - let column = mergeRange.startColumn; - - if (type === BORDER_TYPE.TOP) { - row = mergeRange.startRow; - forStart = mergeRange.startColumn; - forEnd = mergeRange.endColumn; - } else if (type === BORDER_TYPE.BOTTOM) { - row = mergeRange.endRow; - forStart = mergeRange.startColumn; - forEnd = mergeRange.endColumn; - } else if (type === BORDER_TYPE.LEFT) { - column = mergeRange.startColumn; - forStart = mergeRange.startRow; - forEnd = mergeRange.endRow; - } else if (type === BORDER_TYPE.RIGHT) { - column = mergeRange.endColumn; - forStart = mergeRange.startRow; - forEnd = mergeRange.endRow; + switch (ltrb) { + case BORDER_LTRB.TOP: + row = mergeRange.startRow; + forStart = mergeRange.startColumn; + forEnd = mergeRange.endColumn; + break; + case BORDER_LTRB.BOTTOM: + row = mergeRange.endRow; + forStart = mergeRange.startColumn; + forEnd = mergeRange.endColumn; + break; + case BORDER_LTRB.LEFT: + col = mergeRange.startColumn; + forStart = mergeRange.startRow; + forEnd = mergeRange.endRow; + break; + case BORDER_LTRB.RIGHT: + col = mergeRange.endColumn; + forStart = mergeRange.startRow; + forEnd = mergeRange.endRow; + break; } for (let i = forStart; i <= forEnd; i++) { - if (type === BORDER_TYPE.TOP) { - column = i; - } else if (type === BORDER_TYPE.BOTTOM) { - column = i; - } else if (type === BORDER_TYPE.LEFT) { - row = i; - } else if (type === BORDER_TYPE.RIGHT) { - row = i; + switch (ltrb) { + case BORDER_LTRB.TOP: + case BORDER_LTRB.BOTTOM: + col = i; + break; + case BORDER_LTRB.LEFT: + case BORDER_LTRB.RIGHT: + row = i; + break; } - - const cell = this.worksheet.getCell(row, column); + const cell = this.getCellForRender(row, col); if (!cell) { isAddBorders = false; break; } - const style = this._styles.getStyleByCell(cell); - if (!style) { isAddBorders = false; break; } - const props: Nullable = style.bd?.[type]; + const props: Nullable = style.bd?.[ltrb]; if (props) { const rgb = getColorStyle(props.cl) || COLOR_BLACK_RGB; borders.push({ r: row, - c: column, + c: col, style: props.s, color: rgb, }); @@ -2071,11 +2092,11 @@ export class SpreadsheetSkeleton extends Skeleton { if (isAddBorders) { borders.forEach((border) => { const { r, c, style, color } = border; - if (!cache.border!.getValue(r, c)) { - cache.border!.setValue(r, c, {}); + if (!cache.borderMatrix!.getValue(r, c)) { + cache.borderMatrix!.setValue(r, c, {}); } - cache.border!.getValue(r, c)![type] = { - type, + cache.borderMatrix!.getValue(r, c)![ltrb] = { + type: ltrb, style, color, }; @@ -2083,14 +2104,15 @@ export class SpreadsheetSkeleton extends Skeleton { } } - private _setBorderProps(r: number, c: number, type: BORDER_TYPE, style: IStyleData, cache: IStylesCache): void { + private _setBorderProps(r: number, c: number, type: BORDER_LTRB, style: IStyleData): void { + const cache = this._stylesCache; const props: Nullable = style.bd?.[type]; - if (!props || !cache.border) { + if (!props || !cache.borderMatrix) { return; } const rgb = getColorStyle(props.cl) || COLOR_BLACK_RGB; - const borderCache = cache.border; + const borderCache = cache.borderMatrix; if (!borderCache.getValue(r, c)) { borderCache.setValue(r, c, { [type]: {} }); @@ -2102,13 +2124,13 @@ export class SpreadsheetSkeleton extends Skeleton { * When the top border of a cell and the bottom border of the cell above it (r-1) overlap, * if the top border of cell r is white, then the rendering is ignored. */ - if (type === BORDER_TYPE.TOP) { - const borderBottom = borderCache.getValue(r - 1, c)?.[BORDER_TYPE.BOTTOM]; + if (type === BORDER_LTRB.TOP) { + const borderBottom = borderCache.getValue(r - 1, c)?.[BORDER_LTRB.BOTTOM]; if (borderBottom && isWhiteColor(rgb)) { return; } - } else if (type === BORDER_TYPE.LEFT) { - const borderRight = borderCache.getValue(r, c - 1)?.[BORDER_TYPE.RIGHT]; + } else if (type === BORDER_LTRB.LEFT) { + const borderRight = borderCache.getValue(r, c - 1)?.[BORDER_LTRB.RIGHT]; if (borderRight && isWhiteColor(rgb)) { return; } diff --git a/packages/engine-render/src/components/skeleton.ts b/packages/engine-render/src/components/skeleton.ts index 4805399db190..e535ff961742 100644 --- a/packages/engine-render/src/components/skeleton.ts +++ b/packages/engine-render/src/components/skeleton.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { Disposable, Inject, LocaleService } from '@univerjs/core'; - import type { IFontLocale } from '../basics/interfaces'; +import { Disposable, Inject, LocaleService } from '@univerjs/core'; + export class Skeleton extends Disposable { private _fontLocale!: IFontLocale; diff --git a/packages/sheets-data-validation/src/controllers/dv-render.controller.ts b/packages/sheets-data-validation/src/controllers/dv-render.controller.ts index 25c9f3d95c7d..07e9db934a1c 100644 --- a/packages/sheets-data-validation/src/controllers/dv-render.controller.ts +++ b/packages/sheets-data-validation/src/controllers/dv-render.controller.ts @@ -392,4 +392,3 @@ export class SheetsDataValidationMobileRenderController extends RxDisposable { }); } } - diff --git a/packages/sheets/src/commands/commands/set-border-command.ts b/packages/sheets/src/commands/commands/set-border-command.ts index e414e695671d..93c80975c033 100644 --- a/packages/sheets/src/commands/commands/set-border-command.ts +++ b/packages/sheets/src/commands/commands/set-border-command.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -/* eslint-disable max-lines-per-function */ -/* eslint-disable complexity */ import { BorderType, CommandType,