diff --git a/packages/core/src/shared/lifecycle.ts b/packages/core/src/shared/lifecycle.ts index 7b5edcc930..f365ccd017 100644 --- a/packages/core/src/shared/lifecycle.ts +++ b/packages/core/src/shared/lifecycle.ts @@ -113,6 +113,12 @@ export class Disposable implements IDisposable { return this._collection.add(d); } + protected ensureNotDisposed(): void { + if (this._disposed) { + throw new Error('[Disposable]: object is disposed!'); + } + } + dispose(): void { if (this._disposed) { return; diff --git a/packages/docs-ui/src/views/doc-canvas-view.ts b/packages/docs-ui/src/views/doc-canvas-view.ts index d968cd492a..a6bfda115b 100644 --- a/packages/docs-ui/src/views/doc-canvas-view.ts +++ b/packages/docs-ui/src/views/doc-canvas-view.ts @@ -23,7 +23,7 @@ import { IConfigService, UniverInstanceType, } from '@univerjs/core'; import { DOCS_COMPONENT_DEFAULT_Z_INDEX, DOCS_COMPONENT_HEADER_LAYER_INDEX, DOCS_COMPONENT_MAIN_LAYER_INDEX, DOCS_VIEW_KEY, VIEWPORT_KEY } from '@univerjs/docs'; -import type { IRender, IWheelEvent, RenderManagerService, Scene } from '@univerjs/engine-render'; +import type { IRender, IWheelEvent, Scene } from '@univerjs/engine-render'; import { Documents, EVENT_TYPE, IRenderManagerService, Layer, ScrollBar, Viewport } from '@univerjs/engine-render'; import { IEditorService } from '@univerjs/ui'; import { BehaviorSubject, takeUntil } from 'rxjs'; @@ -39,7 +39,7 @@ export class DocCanvasView extends RxDisposable { readonly fps$ = this._fps$.asObservable(); constructor( - @IRenderManagerService private readonly _renderManagerService: RenderManagerService, + @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, @IConfigService private readonly _configService: IConfigService, @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, @IEditorService private readonly _editorService: IEditorService diff --git a/packages/engine-render/src/index.ts b/packages/engine-render/src/index.ts index 0a32c821fe..27df8f420d 100644 --- a/packages/engine-render/src/index.ts +++ b/packages/engine-render/src/index.ts @@ -24,7 +24,8 @@ export * from './engine'; export * from './group'; export * from './layer'; export { IRenderingEngine, UniverRenderEnginePlugin } from './render-engine'; -export * from './render-manager.service'; +export { type RenderComponentType, IRenderManagerService, RenderManagerService } from './render-manager/render-manager.service'; +export { RenderUnit, type IRender, type IRenderControllerCtor, type IRenderController, type IRenderContext } from './render-manager/render-unit'; export * from './scene'; export * from './scene-viewer'; export * from './scroll-timer'; diff --git a/packages/engine-render/src/render-engine.ts b/packages/engine-render/src/render-engine.ts index 94d128b0ec..9c70d130d9 100644 --- a/packages/engine-render/src/render-engine.ts +++ b/packages/engine-render/src/render-engine.ts @@ -18,7 +18,7 @@ import { Plugin, UniverInstanceType } from '@univerjs/core'; import { createIdentifier, Inject, Injector } from '@wendellhu/redi'; import { Engine } from './engine'; -import { IRenderManagerService, RenderManagerService } from './render-manager.service'; +import { IRenderManagerService, RenderManagerService } from './render-manager/render-manager.service'; /** * The global rendering engine. diff --git a/packages/engine-render/src/render-manager.service.ts b/packages/engine-render/src/render-manager/render-manager.service.ts similarity index 65% rename from packages/engine-render/src/render-manager.service.ts rename to packages/engine-render/src/render-manager/render-manager.service.ts index e020d8b335..810a49be5b 100644 --- a/packages/engine-render/src/render-manager.service.ts +++ b/packages/engine-render/src/render-manager/render-manager.service.ts @@ -14,27 +14,30 @@ * limitations under the License. */ -import type { Nullable } from '@univerjs/core'; -import { createIdentifier } from '@wendellhu/redi'; +import type { Nullable, UnitModel, UnitType } from '@univerjs/core'; +import { Disposable, IUniverInstanceService, toDisposable } from '@univerjs/core'; +import type { IDisposable } from '@wendellhu/redi'; +import { createIdentifier, Inject, Injector } from '@wendellhu/redi'; import type { Observable } from 'rxjs'; import { BehaviorSubject } from 'rxjs'; -import type { BaseObject } from './base-object'; -import type { DocComponent } from './components/docs/doc-component'; -import type { SheetComponent } from './components/sheets/sheet-component'; -import type { Slide } from './components/slides/slide'; -import { Engine } from './engine'; -import { Scene } from './scene'; -import { SceneViewer } from './scene-viewer'; +import type { BaseObject } from '../base-object'; +import type { DocComponent } from '../components/docs/doc-component'; +import type { SheetComponent } from '../components/sheets/sheet-component'; +import type { Slide } from '../components/slides/slide'; +import { Engine } from '../engine'; +import { Scene } from '../scene'; +import { SceneViewer } from '../scene-viewer'; +import { type IRender, type IRenderControllerCtor, RenderUnit } from './render-unit'; -export interface IRenderManagerService { +export type RenderComponentType = SheetComponent | DocComponent | Slide | BaseObject; + +export interface IRenderManagerService extends IDisposable { currentRender$: Observable>; createRender$: Observable>; - dispose(): void; - // createRenderWithNewEngine(unitId: string): IRenderManagerService; createRenderWithParent(unitId: string, parentUnitId: string): IRender; createRender(unitId: string): IRender; - addItem(unitId: string, item: IRender): void; + addRenderItem(unitId: string, item: IRender): void; removeRender(unitId: string): void; setCurrent(unitId: string): void; getRenderById(unitId: string): Nullable; @@ -44,24 +47,16 @@ export interface IRenderManagerService { getCurrent(): Nullable; getFirst(): Nullable; has(unitId: string): boolean; -} -export type RenderComponentType = SheetComponent | DocComponent | Slide | BaseObject; - -export interface IRender { - unitId: string; - engine: Engine; - scene: Scene; - mainComponent: Nullable; - components: Map; - isMainScene: boolean; + registerRenderControllers(type: UnitType, ctor: IRenderControllerCtor): IDisposable; } const DEFAULT_SCENE_SIZE = { width: 1500, height: 1000 }; const SCENE_NAMESPACE = '_UNIVER_SCENE_'; -export class RenderManagerService implements IRenderManagerService { + +export class RenderManagerService extends Disposable implements IRenderManagerService { private _defaultEngine!: Engine; private _currentUnitId: string = ''; @@ -81,31 +76,62 @@ export class RenderManagerService implements IRenderManagerService { return this._defaultEngine; } - dispose() { + private readonly _renderControllers = new Map>(); + + constructor( + @Inject(Injector) private readonly _injector: Injector, + @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService + ) { + super(); + } + + override dispose() { + super.dispose(); + this._renderMap.forEach((item) => { this._disposeItem(item); }); + this._renderControllers.clear(); this._renderMap.clear(); this._currentRender$.complete(); } + registerRenderControllers(type: UnitType, ctor: IRenderControllerCtor): IDisposable { + if (!this._renderControllers.has(type)) { + this._renderControllers.set(type, new Set()); + } + + const set = this._renderControllers.get(type)!; + set.add(ctor); + + for (const [renderUnitId, render] of this._renderMap) { + const renderType = this._univerInstanceService.getUnitType(renderUnitId); + if (renderType === type) { + (render as RenderUnit).addRenderControllers([ctor]); + } + } + + return toDisposable(() => set.delete(ctor)); + } + + private _getRenderControllersForType(type: UnitType): Array { + return Array.from(this._renderControllers.get(type) ?? []); + } + createRenderWithParent(unitId: string, parentUnitId: string): IRender { const parent = this.getRenderById(parentUnitId); if (parent == null) { throw new Error('parent render is null'); } - const { scene, engine } = parent; + const { scene, engine } = parent; const current = this._createRender(unitId, engine, false); - const currentScene = current.scene; - const sv = new SceneViewer(unitId); sv.addSubScene(currentScene); - scene.addObject(sv); return current; @@ -145,21 +171,22 @@ export class RenderManagerService implements IRenderManagerService { height, }); - const item: IRender = { - unitId, + const unit = this._univerInstanceService.getUnit(unitId)!; + const type = this._univerInstanceService.getUnitType(unitId); + const ctors = this._getRenderControllersForType(type); + const renderUnit = new RenderUnit(this._injector, { + unit, engine, scene, - mainComponent: null, - components: new Map(), isMainScene, - }; - - this.addItem(unitId, item); + }); + renderUnit.addRenderControllers(ctors); - return item; + this.addRenderItem(unitId, renderUnit); + return renderUnit; } - addItem(unitId: string, item: IRender) { + addRenderItem(unitId: string, item: IRender) { this._renderMap.set(unitId, item); } @@ -214,4 +241,4 @@ export class RenderManagerService implements IRenderManagerService { } } -export const IRenderManagerService = createIdentifier('univer.render-manager-service'); +export const IRenderManagerService = createIdentifier('engine-render.render-manager.service'); diff --git a/packages/engine-render/src/render-manager/render-unit.ts b/packages/engine-render/src/render-manager/render-unit.ts new file mode 100644 index 0000000000..26f6d11028 --- /dev/null +++ b/packages/engine-render/src/render-manager/render-unit.ts @@ -0,0 +1,102 @@ +/** + * 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, UnitModel, UnitType } from '@univerjs/core'; +import { Disposable } from '@univerjs/core'; +import type { IDisposable, Injector } from '@wendellhu/redi'; +import type { Engine } from '../engine'; +import type { Scene } from '../scene'; +import type { RenderComponentType } from './render-manager.service'; + +export interface IRender { + unitId: string; + engine: Engine; + scene: Scene; + mainComponent: Nullable; + components: Map; + isMainScene: boolean; +} + +// eslint-disable-next-line ts/no-explicit-any +export interface IRenderControllerCtor { new(unit: IRenderContext, ...args: any[]): IRenderController } +export interface IRenderController extends IDisposable {} + +/** + * This object encapsulates methods or properties to render each element. + */ +export interface IRenderContext extends IRender { + unit: T; + type: UnitType; +} + +/** + * This class is responsible + */ +export class RenderUnit extends Disposable implements IRender { + readonly isRenderUnit: true; + + readonly unitId: string; + readonly type: UnitType; + + private readonly _injector: Injector; + private readonly _renderControllers: IRenderController[] = []; + + private _renderContext: IRenderContext; + set isMainScene(is: boolean) { this._renderContext.isMainScene = is; } + get isMainScene(): boolean { return this._renderContext.isMainScene; } + set engine(engine: Engine) { this._renderContext.engine = engine; } + get engine(): Engine { return this._renderContext.engine; } + set mainComponent(component: Nullable) { this._renderContext.mainComponent = component; } + get mainComponent(): Nullable { return this._renderContext.mainComponent; } + set scene(scene: Scene) { this._renderContext.scene = scene; } + get scene(): Scene { return this._renderContext.scene; } + get components() { return this._renderContext.components; } + + constructor( + parentInjector: Injector, + init: Pick + ) { + super(); + + this._injector = parentInjector.createChild(); + + this._renderContext = { + unit: init.unit, + unitId: this.unitId, + type: init.unit.type, + components: new Map(), + mainComponent: null, + isMainScene: init.isMainScene, + engine: init.engine, + scene: init.scene, + }; + } + + override dispose() { + this._renderControllers.forEach((controller) => controller.dispose()); + this._renderControllers.length = 0; + } + + addRenderControllers(ctors: IRenderControllerCtor[]) { + this._initControllers(ctors); + } + + private _initControllers(ctors: IRenderControllerCtor[]): void { + ctors + .map((ctor) => this._injector.createInstance(ctor, this._renderContext)) + .forEach((controller) => this._renderControllers.push(controller)); + } +} diff --git a/packages/sheets-ui/src/commands/commands/__tests__/set-format-painter.command.spec.ts b/packages/sheets-ui/src/commands/commands/__tests__/set-format-painter.command.spec.ts index 2c1bb5ccd1..074aefe791 100644 --- a/packages/sheets-ui/src/commands/commands/__tests__/set-format-painter.command.spec.ts +++ b/packages/sheets-ui/src/commands/commands/__tests__/set-format-painter.command.spec.ts @@ -212,6 +212,7 @@ describe('Test format painter rules in controller', () => { let commandService: ICommandService; let themeService: ThemeService; let formatPainterController: FormatPainterController; + beforeEach(() => { const testBed = createCommandTestBed(TEST_WORKBOOK_DATA, [ [IMarkSelectionService, { useClass: MarkSelectionService }], diff --git a/packages/sheets-ui/src/controllers/freeze.controller.ts b/packages/sheets-ui/src/controllers/freeze.render-controller.ts similarity index 96% rename from packages/sheets-ui/src/controllers/freeze.controller.ts rename to packages/sheets-ui/src/controllers/freeze.render-controller.ts index 06a4745cce..26341879a0 100644 --- a/packages/sheets-ui/src/controllers/freeze.controller.ts +++ b/packages/sheets-ui/src/controllers/freeze.render-controller.ts @@ -19,16 +19,12 @@ import { ColorKit, Disposable, ICommandService, - IUniverInstanceService, - LifecycleStages, - OnLifecycle, RANGE_TYPE, ThemeService, toDisposable, - UniverInstanceType, } from '@univerjs/core'; -import type { IMouseEvent, IPointerEvent, IScrollObserverParam, Viewport } from '@univerjs/engine-render'; -import { CURSOR_TYPE, IRenderManagerService, Rect, TRANSFORM_CHANGE_OBSERVABLE_TYPE, Vector2 } from '@univerjs/engine-render'; +import type { IMouseEvent, IPointerEvent, IRenderContext, IRenderController, IScrollObserverParam, Viewport } from '@univerjs/engine-render'; +import { CURSOR_TYPE, Rect, TRANSFORM_CHANGE_OBSERVABLE_TYPE, Vector2 } from '@univerjs/engine-render'; import type { IInsertColCommandParams, IInsertRowCommandParams, @@ -71,7 +67,6 @@ import { ScrollCommand } from '../commands/commands/set-scroll.command'; import { SetZoomRatioOperation } from '../commands/operations/set-zoom-ratio.operation'; import { SHEET_COMPONENT_HEADER_LAYER_INDEX, VIEWPORT_KEY } from '../common/keys'; import { ScrollManagerService } from '../services/scroll-manager.service'; -import { ISelectionRenderService } from '../services/selection/selection-render.service'; import { SheetSkeletonManagerService } from '../services/sheet-skeleton-manager.service'; import { getCoordByOffset, getSheetObject } from './utils/component-tools'; @@ -99,8 +94,7 @@ const FREEZE_SIZE_NORMAL = 4; const AUXILIARY_CLICK_HIDDEN_OBJECT_TRANSPARENCY = 0.01; -@OnLifecycle(LifecycleStages.Rendered, FreezeController) -export class FreezeController extends Disposable { +export class HeaderFreezeRenderController extends Disposable implements IRenderController { private _rowFreezeHeaderRect: Nullable; private _rowFreezeMainRect: Nullable; @@ -140,11 +134,11 @@ export class FreezeController extends Disposable { private _lastFreeze: IFreeze | undefined = undefined; constructor( + private readonly _context: IRenderContext, @Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService, - @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, + // @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, @ICommandService private readonly _commandService: ICommandService, - @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, - @ISelectionRenderService private readonly _selectionRenderService: ISelectionRenderService, + // @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, @Inject(SelectionManagerService) private readonly _selectionManagerService: SelectionManagerService, @Inject(ScrollManagerService) private readonly _scrollManagerService: ScrollManagerService, @Inject(ThemeService) private readonly _themeService: ThemeService, @@ -189,57 +183,33 @@ export class FreezeController extends Disposable { freezeConfig?: IFreeze ) { const config = freezeConfig ?? this._getFreeze(); + if (config == null) return null; - if (config == null) { - return; - } - - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; - - if (skeleton == null) { - return; - } + const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton!; const { startRow: freezeRow, startColumn: freezeColumn } = config; - const position = this._getPositionByIndex(freezeRow, freezeColumn); + if (position == null) return null; - if (position == null) { - return; - } - - const sheetObject = this._getSheetObject(); - if (sheetObject == null) { - return; - } - + const sheetObject = this._getSheetObject()!; const engine = sheetObject.engine; const canvasMaxWidth = engine?.width || 0; const canvasMaxHeight = engine?.height || 0; - const scene = sheetObject.scene; - const { startX, startY } = position; + const { rowTotalHeight, columnTotalWidth, rowHeaderWidthAndMarginLeft, columnHeaderHeightAndMarginTop } = skeleton; - const { rowTotalHeight, columnTotalWidth, rowHeaderWidthAndMarginLeft, columnHeaderHeightAndMarginTop } = - skeleton; - - const shapeWidth = - canvasMaxWidth > columnTotalWidth + rowHeaderWidthAndMarginLeft - ? canvasMaxWidth - : columnTotalWidth + columnHeaderHeightAndMarginTop; + const shapeWidth = canvasMaxWidth > columnTotalWidth + rowHeaderWidthAndMarginLeft + ? canvasMaxWidth + : columnTotalWidth + columnHeaderHeightAndMarginTop; - const shapeHeight = - canvasMaxHeight > rowTotalHeight + columnHeaderHeightAndMarginTop - ? canvasMaxHeight - : rowTotalHeight + columnHeaderHeightAndMarginTop; + const shapeHeight = canvasMaxHeight > rowTotalHeight + columnHeaderHeightAndMarginTop + ? canvasMaxHeight + : rowTotalHeight + columnHeaderHeightAndMarginTop; this._changeToRow = freezeRow; - this._changeToColumn = freezeColumn; - this._changeToOffsetX = startX; - this._changeToOffsetY = startY; const scale = Math.max(scene.scaleX, scene.scaleY); @@ -587,7 +557,7 @@ export class FreezeController extends Disposable { return; } - const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.SHEET)!; + const workbook = this._context.unit; const worksheet = workbook.getActiveSheet(); const oldFreeze = worksheet.getConfig()?.freeze; let xSplit = oldFreeze?.xSplit || 0; @@ -1075,7 +1045,7 @@ export class FreezeController extends Disposable { } private _refreshCurrent() { - const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.SHEET)!; + const workbook = this._context.unit; const worksheet = workbook.getActiveSheet(); const freeze = worksheet.getConfig().freeze; @@ -1126,7 +1096,7 @@ export class FreezeController extends Disposable { } const createFreezeMutationAndRefresh = (newFreeze: IFreeze) => { - const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.SHEET)!; + const workbook = this._context.unit; const unitId = workbook.getUnitId(); const worksheet = workbook.getActiveSheet(); const subUnitId = worksheet.getSheetId(); @@ -1354,7 +1324,7 @@ export class FreezeController extends Disposable { this._commandService.onCommandExecuted((command: ICommandInfo) => { if (updateCommandList.includes(command.id)) { const lastFreeze = this._lastFreeze; - const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.SHEET)!; + const workbook = this._context.unit; const worksheet = workbook.getActiveSheet(); const params = command.params as ISetFrozenMutationParams; @@ -1537,7 +1507,7 @@ export class FreezeController extends Disposable { } private _getSheetObject() { - return getSheetObject(this._univerInstanceService, this._renderManagerService); + return getSheetObject(this._context.unit, this._context); } private _refreshFreeze( diff --git a/packages/sheets-ui/src/controllers/header-resize.controller.ts b/packages/sheets-ui/src/controllers/header-resize.render-controller.ts similarity index 91% rename from packages/sheets-ui/src/controllers/header-resize.controller.ts rename to packages/sheets-ui/src/controllers/header-resize.render-controller.ts index f6e2fe9009..0b8389f265 100644 --- a/packages/sheets-ui/src/controllers/header-resize.controller.ts +++ b/packages/sheets-ui/src/controllers/header-resize.render-controller.ts @@ -14,17 +14,16 @@ * limitations under the License. */ -import type { Nullable, Observer } from '@univerjs/core'; +/* eslint-disable max-lines-per-function */ + +import type { Nullable, Observer, Workbook } from '@univerjs/core'; import { Disposable, ICommandService, - IUniverInstanceService, - LifecycleStages, - OnLifecycle, toDisposable, } from '@univerjs/core'; -import type { IMouseEvent, IPointerEvent } from '@univerjs/engine-render'; -import { CURSOR_TYPE, DeviceInputEventType, IRenderManagerService, Rect, Vector2 } from '@univerjs/engine-render'; +import type { IMouseEvent, IPointerEvent, IRenderContext, IRenderController } from '@univerjs/engine-render'; +import { CURSOR_TYPE, DeviceInputEventType, Rect, Vector2 } from '@univerjs/engine-render'; import type { IDeltaColumnWidthCommandParams, IDeltaRowHeightCommand, @@ -60,8 +59,8 @@ enum HEADER_RESIZE_TYPE { COLUMN, } -@OnLifecycle(LifecycleStages.Rendered, HeaderResizeController) -export class HeaderResizeController extends Disposable { + +export class HeaderResizeController extends Disposable implements IRenderController { private _currentRow: number = 0; private _currentColumn: number = 0; @@ -83,15 +82,14 @@ export class HeaderResizeController extends Disposable { private _startOffsetY: number = Number.POSITIVE_INFINITY; constructor( + private readonly _context: IRenderContext, @Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService, - @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, @ICommandService private readonly _commandService: ICommandService, - @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, @IEditorBridgeService private readonly _editorBridgeService: IEditorBridgeService ) { super(); - this._initialize(); + this._init(); } override dispose(): void { @@ -118,7 +116,7 @@ export class HeaderResizeController extends Disposable { this._observers = []; } - private _initialize() { + private _init() { const sheetObject = this._getSheetObject(); if (sheetObject == null) { return; @@ -265,12 +263,7 @@ export class HeaderResizeController extends Disposable { } private _initialHoverResize(initialType: HEADER_RESIZE_TYPE = HEADER_RESIZE_TYPE.ROW) { - const sheetObject = this._getSheetObject(); - - if (sheetObject == null) { - return; - } - + const sheetObject = this._getSheetObject()!; const { scene } = sheetObject; const eventBindingObject = @@ -280,9 +273,6 @@ export class HeaderResizeController extends Disposable { return; } - const engine = scene.getEngine(); - const canvasMaxWidth = engine?.width || 0; - const canvasMaxHeight = engine?.height || 0; this.disposeWithMe( toDisposable( @@ -318,27 +308,18 @@ export class HeaderResizeController extends Disposable { toDisposable( eventBindingObject.onPointerDownObserver.add((evt: IPointerEvent | IMouseEvent, state) => { const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; - - if (skeleton == null) { - return; - } + if (skeleton == null) return; const sheetObject = this._getSheetObject(); - - if (sheetObject == null) { - return; - } - const { scene } = sheetObject; - + const engine = scene.getEngine(); + const canvasMaxHeight = engine?.height || 0; + const canvasMaxWidth = engine?.width || 0; const viewPort = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); const scrollBarHorizontalHeight = (viewPort?.getScrollBar()?.horizonBarRect?.height || 0) + 10; - const scrollBarVerticalWidth = (viewPort?.getScrollBar()?.verticalBarRect?.width || 0) + 10; - const transformCoord = getTransformCoord(evt.offsetX, evt.offsetY, scene, skeleton); - const { scaleX, scaleY } = scene.getAncestorScale(); this._startOffsetX = transformCoord.x; @@ -346,28 +327,22 @@ export class HeaderResizeController extends Disposable { this._startOffsetY = transformCoord.y; const currentOffsetX = skeleton.getOffsetByPositionX(this._currentColumn); - const currentOffsetY = skeleton.getOffsetByPositionY(this._currentRow); - const cell = skeleton.getNoMergeCellPositionByIndex(this._currentRow, this._currentColumn); let isStartMove = false; - let moveChangeX = 0; - let moveChangeY = 0; const { columnTotalWidth, rowHeaderWidth, rowTotalHeight, columnHeaderHeight } = skeleton; - const shapeWidth = - canvasMaxWidth > columnTotalWidth + rowHeaderWidth - ? canvasMaxWidth - : columnTotalWidth + rowHeaderWidth; + const shapeWidth = canvasMaxWidth > columnTotalWidth + rowHeaderWidth + ? canvasMaxWidth + : columnTotalWidth + rowHeaderWidth; - const shapeHeight = - canvasMaxHeight > rowTotalHeight + columnHeaderHeight - ? canvasMaxHeight - : rowTotalHeight + columnHeaderHeight; + const shapeHeight = canvasMaxHeight > rowTotalHeight + columnHeaderHeight + ? canvasMaxHeight + : rowTotalHeight + columnHeaderHeight; const scale = Math.max(scaleX, scaleY); @@ -480,7 +455,7 @@ export class HeaderResizeController extends Disposable { }); this._upObserver = scene.onPointerUpObserver.add((upEvt: IPointerEvent | IMouseEvent) => { - const sheetObject = getSheetObject(this._univerInstanceService, this._renderManagerService); + const sheetObject = this._getSheetObject(); if (sheetObject == null) { return; @@ -524,12 +499,7 @@ export class HeaderResizeController extends Disposable { eventBindingObject.onDblclickObserver.add(() => { if (initialType === HEADER_RESIZE_TYPE.ROW) { const sheetObject = this._getSheetObject(); - if (sheetObject == null) { - return; - } - const { scene } = sheetObject; - scene.resetCursor(); this._commandService.executeCommand( @@ -548,20 +518,14 @@ export class HeaderResizeController extends Disposable { private _clearObserverEvent() { const sheetObject = this._getSheetObject(); - - if (sheetObject == null) { - return; - } - const { scene } = sheetObject; scene.onPointerMoveObserver.remove(this._moveObserver); scene.onPointerUpObserver.remove(this._upObserver); - this._moveObserver = null; this._upObserver = null; } private _getSheetObject() { - return getSheetObject(this._univerInstanceService, this._renderManagerService); + return getSheetObject(this._context.unit, this._context)!; } } diff --git a/packages/sheets-ui/src/controllers/header-unhide.controller.ts b/packages/sheets-ui/src/controllers/header-unhide.render-controller.ts similarity index 55% rename from packages/sheets-ui/src/controllers/header-unhide.controller.ts rename to packages/sheets-ui/src/controllers/header-unhide.render-controller.ts index 69296e47d5..0bb4986e99 100644 --- a/packages/sheets-ui/src/controllers/header-unhide.controller.ts +++ b/packages/sheets-ui/src/controllers/header-unhide.render-controller.ts @@ -14,17 +14,13 @@ * limitations under the License. */ -import type { IKeyValue, Workbook, Worksheet } from '@univerjs/core'; +import type { Workbook, Worksheet } from '@univerjs/core'; import { - getWorksheetUID, ICommandService, - IUniverInstanceService, - LifecycleStages, - OnLifecycle, RxDisposable, } from '@univerjs/core'; -import { IRenderManagerService } from '@univerjs/engine-render'; -import type { ISetSpecificColsVisibleCommandParams, ISetSpecificRowsVisibleCommandParams } from '@univerjs/sheets'; +import type { IRenderContext } from '@univerjs/engine-render'; +import type { ISetSpecificColsVisibleCommandParams, ISetSpecificRowsVisibleCommandParams, ISheetCommandSharedParams } from '@univerjs/sheets'; import { SetColHiddenMutation, SetColVisibleMutation, @@ -34,7 +30,7 @@ import { SetSpecificRowsVisibleCommand, } from '@univerjs/sheets'; import { Inject } from '@wendellhu/redi'; -import { pairwise, startWith, takeUntil } from 'rxjs'; +import { takeUntil } from 'rxjs'; import { SHEET_COMPONENT_UNHIDE_LAYER_INDEX } from '../common/keys'; import { SheetSkeletonManagerService } from '../services/sheet-skeleton-manager.service'; @@ -53,16 +49,14 @@ const RENDER_COMMANDS: string[] = [ /** * This controller controls rendering of the buttons to unhide hidden rows and columns. */ -@OnLifecycle(LifecycleStages.Rendered, HeaderUnhideController) -export class HeaderUnhideController extends RxDisposable { - private _shapes = new Map(); +export class HeaderUnhideRenderController extends RxDisposable { + private _shapes: { cols: HeaderUnhideShape[]; rows: HeaderUnhideShape[] } = { cols: [], rows: [] }; + private get _workbook(): Workbook { return this._context.unit; } - // It should support several workbooks & worksheet. constructor( + private readonly _context: IRenderContext, @Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService, - @ICommandService private readonly _cmdSrv: ICommandService, - @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, - @IRenderManagerService private readonly _rendererManagerService: IRenderManagerService + @ICommandService private readonly _commandService: ICommandService ) { super(); @@ -71,54 +65,50 @@ export class HeaderUnhideController extends RxDisposable { override dispose(): void { super.dispose(); + + this._clearShapes(); } private _init(): void { - const sheetObject = this._getSheetObject(); - if (!sheetObject) { - return; - } - - // Re-render when sheet skeleton changes. - this._sheetSkeletonManagerService.currentSkeleton$ - .pipe(takeUntil(this.dispose$), startWith(undefined), pairwise()) - .subscribe(([lastSkeleton, skeleton]) => { - if (skeleton) { - const workbook = this._univerInstanceService.getUniverSheetInstance(skeleton.unitId)!; - const worksheet = workbook.getSheetBySheetId(skeleton.sheetId)!; - this._updateWorksheet(workbook!, worksheet, lastSkeleton?.sheetId); - } + let activeSheetId: string = ''; + + this._context.unit.activeSheet$.pipe(takeUntil(this.dispose$)) + .subscribe((worksheet) => { + this._clearShapes(); + + if (!worksheet) { + activeSheetId = ''; + return; + }; + + activeSheetId = worksheet.getSheetId(); + this._update(this._workbook, worksheet); }); // Re-render hidden rows / cols when specific commands are executed. this.disposeWithMe( - this._cmdSrv.onCommandExecuted((command) => { + this._commandService.onCommandExecuted((command) => { if ( !RENDER_COMMANDS.includes(command.id) || !command.params || - !(command.params as IKeyValue).unitId || - !(command.params as IKeyValue).subUnitId + !(command.params as ISheetCommandSharedParams).unitId || + (command.params as ISheetCommandSharedParams).subUnitId !== activeSheetId ) { return; } - const workbook = this._univerInstanceService.getUniverSheetInstance( - (command.params as IKeyValue).unitId - ); - const worksheet = workbook?.getSheetBySheetId((command.params as IKeyValue).subUnitId); + const workbook = this._workbook; + const worksheet = workbook.getSheetBySheetId((command.params as ISheetCommandSharedParams).subUnitId); if (worksheet) { - this._updateWorksheet(workbook!, worksheet, worksheet.getSheetId()); + this._update(workbook, worksheet); } }) ); } - /** Initialize header unhide render shapes for a specific worksheet. */ - private _initForWorksheet(workbook: Workbook, worksheet: Worksheet): void { - const skeleton = this._sheetSkeletonManagerService.getCurrent()?.skeleton; - if (!skeleton) { - return; - } + private _update(workbook: Workbook, worksheet: Worksheet): void { + const skeleton = this._sheetSkeletonManagerService.getUnitSkeleton(workbook.getUnitId(), worksheet.getSheetId())?.skeleton; + if (!skeleton) return; // steps to render the unhide button for the current worksheet // 1. and get hidden rows and columns @@ -127,9 +117,6 @@ export class HeaderUnhideController extends RxDisposable { // First just let me render a unhide button of the column header!. const sheetObject = this._getSheetObject(); - if (!sheetObject) { - return; - } // NOTE: for performance consideration we should only get hidden rows & cols in the viewport // 2. create shapes and add them to the scene, the position should be calculated from SheetSkeleton @@ -150,15 +137,14 @@ export class HeaderUnhideController extends RxDisposable { top: position.startY - (hasPrevious ? UNHIDE_ICON_SIZE : 0), left: position.startX - UNHIDE_ICON_SIZE, }, - () => - this._cmdSrv.executeCommand( - SetSpecificRowsVisibleCommand.id, - { - unitId: workbook.getUnitId(), - subUnitId: worksheet.getSheetId(), - ranges: [range], - } - ) + () => this._commandService.executeCommand( + SetSpecificRowsVisibleCommand.id, + { + unitId: workbook.getUnitId(), + subUnitId: worksheet.getSheetId(), + ranges: [range], + } + ) ); }); const colCount = worksheet.getColumnCount(); @@ -178,34 +164,29 @@ export class HeaderUnhideController extends RxDisposable { top: 20 - UNHIDE_ICON_SIZE, }, - () => - this._cmdSrv.executeCommand( - SetSpecificColsVisibleCommand.id, - { - unitId: workbook.getUnitId(), - subUnitId: worksheet.getSheetId(), - ranges: [range], - } - ) + () => this._commandService.executeCommand( + SetSpecificColsVisibleCommand.id, + { + unitId: workbook.getUnitId(), + subUnitId: worksheet.getSheetId(), + ranges: [range], + } + ) ); }); scene.addObjects(colShapes, SHEET_COMPONENT_UNHIDE_LAYER_INDEX); scene.addObjects(rowShapes, SHEET_COMPONENT_UNHIDE_LAYER_INDEX); - this._shapes.set(getWorksheetUID(workbook, worksheet), { cols: colShapes, rows: rowShapes }); + this._shapes = { cols: colShapes, rows: rowShapes }; } - private _updateWorksheet(workbook: Workbook, worksheet: Worksheet, lastWorksheetId?: string): void { - if (lastWorksheetId) { - const shapes = this._shapes.get(`${workbook.getUnitId()}|${lastWorksheetId}`); - shapes?.cols.forEach((shape) => shape.dispose()); - shapes?.rows.forEach((shape) => shape.dispose()); - } - - this._initForWorksheet(workbook, worksheet); + private _clearShapes(): void { + this._shapes.cols.forEach((shape) => shape.dispose()); + this._shapes.rows.forEach((shape) => shape.dispose()); + this._shapes = { cols: [], rows: [] }; } private _getSheetObject() { - return getSheetObject(this._univerInstanceService, this._rendererManagerService); + return getSheetObject(this._workbook, this._context)!; } } diff --git a/packages/sheets-ui/src/controllers/sheet-render.controller.ts b/packages/sheets-ui/src/controllers/sheet-render.controller.ts index 16ede520f6..a4d8839e72 100644 --- a/packages/sheets-ui/src/controllers/sheet-render.controller.ts +++ b/packages/sheets-ui/src/controllers/sheet-render.controller.ts @@ -43,9 +43,6 @@ interface ISetWorksheetMutationParams { subUnitId: string; } -/** - * @todo RenderUnit. This should be merged with `SheetCanvasView`. - */ @OnLifecycle(LifecycleStages.Ready, SheetRenderController) export class SheetRenderController extends RxDisposable { constructor( diff --git a/packages/sheets-ui/src/controllers/utils/component-tools.ts b/packages/sheets-ui/src/controllers/utils/component-tools.ts index 46202527b7..0d6ce4e79c 100644 --- a/packages/sheets-ui/src/controllers/utils/component-tools.ts +++ b/packages/sheets-ui/src/controllers/utils/component-tools.ts @@ -14,10 +14,11 @@ * limitations under the License. */ -import type { IUniverInstanceService, Nullable, Workbook } from '@univerjs/core'; -import { UniverInstanceType } from '@univerjs/core'; +import type { IUniverInstanceService, Nullable } from '@univerjs/core'; +import { UniverInstanceType, Workbook } from '@univerjs/core'; import type { Engine, + IRenderContext, IRenderManagerService, Rect, Scene, @@ -40,18 +41,38 @@ export interface ISheetObjectParam { engine: Engine; } +function isRenderManagerService(renderManagerService: IRenderManagerService | IRenderContext): renderManagerService is IRenderManagerService { + return typeof (renderManagerService as IRenderContext).isMainScene === 'undefined'; +} + export function getSheetObject( - univerInstanceService: IUniverInstanceService, - renderManagerService: IRenderManagerService + univerInstanceService: IUniverInstanceService | Workbook, + renderManagerService: IRenderManagerService | IRenderContext ): Nullable { - const workbook = univerInstanceService.getCurrentUnitForType(UniverInstanceType.SHEET); + const workbook = univerInstanceService instanceof Workbook + ? univerInstanceService + : univerInstanceService.getCurrentUnitForType(UniverInstanceType.SHEET); if (!workbook) return null; const unitId = workbook.getUnitId(); - const currentRender = renderManagerService.getRenderById(unitId); - if (currentRender == null) return null; - const { components, mainComponent, scene, engine } = currentRender; + let components, mainComponent, scene, engine; + if (isRenderManagerService(renderManagerService)) { + const currentRender = renderManagerService.getRenderById(unitId); + if (currentRender == null) return null; + components = currentRender.components; + components = currentRender.components; + mainComponent = currentRender.mainComponent; + scene = currentRender.scene; + engine = currentRender.engine; + } else { + components = renderManagerService.components; + mainComponent = renderManagerService.mainComponent; + scene = renderManagerService.scene; + engine = renderManagerService.engine; + } + + if (!components) return null; const spreadsheet = mainComponent as Spreadsheet; const spreadsheetRowHeader = components.get(SHEET_VIEW_KEY.ROW) as SpreadsheetHeader; 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 4766238936..923dab714c 100644 --- a/packages/sheets-ui/src/services/sheet-skeleton-manager.service.ts +++ b/packages/sheets-ui/src/services/sheet-skeleton-manager.service.ts @@ -71,8 +71,13 @@ export class SheetSkeletonManagerService implements IDisposable { this._sheetSkeletonParam = []; } + /** @deprecated */ getCurrent(): Nullable { - return this._getCurrentBySearch(this._currentSkeleton); + return this._getSkeleton(this._currentSkeleton); + } + + getUnitSkeleton(unitId: string, sheetId: string): Nullable { + return this._getSkeleton({ unitId, sheetId }); } setCurrent(searchParam: ISheetSkeletonManagerSearch): Nullable { @@ -100,7 +105,7 @@ export class SheetSkeletonManagerService implements IDisposable { } private _setCurrent(searchParam: ISheetSkeletonManagerSearch): Nullable { - const param = this._getCurrentBySearch(searchParam); + const param = this._getSkeleton(searchParam); if (param != null) { this._reCalculate(param); } else { @@ -109,7 +114,6 @@ export class SheetSkeletonManagerService implements IDisposable { const workbook = this._univerInstanceService.getUniverSheetInstance(searchParam.unitId); const worksheet = workbook?.getSheetBySheetId(searchParam.sheetId); - if (worksheet == null || workbook == null) { return; } @@ -152,7 +156,7 @@ export class SheetSkeletonManagerService implements IDisposable { } makeDirty(searchParm: ISheetSkeletonManagerSearch, state: boolean = true) { - const param = this._getCurrentBySearch(searchParm); + const param = this._getSkeleton(searchParm); if (param == null) { return; } @@ -160,7 +164,7 @@ export class SheetSkeletonManagerService implements IDisposable { } getOrCreateSkeleton(searchParam: ISheetSkeletonManagerSearch) { - const skeleton = this._getCurrentBySearch(searchParam); + const skeleton = this._getSkeleton(searchParam); if (skeleton) { return skeleton.skeleton; } @@ -182,7 +186,7 @@ export class SheetSkeletonManagerService implements IDisposable { return newSkeleton; } - private _getCurrentBySearch(searchParm: ISheetSkeletonManagerSearch): Nullable { + private _getSkeleton(searchParm: ISheetSkeletonManagerSearch): Nullable { const item = this._sheetSkeletonParam.find( (param) => param.unitId === searchParm.unitId && param.sheetId === searchParm.sheetId ); diff --git a/packages/sheets-ui/src/sheets-ui-plugin.ts b/packages/sheets-ui/src/sheets-ui-plugin.ts index 957a7fea97..6dccc39feb 100644 --- a/packages/sheets-ui/src/sheets-ui-plugin.ts +++ b/packages/sheets-ui/src/sheets-ui-plugin.ts @@ -20,6 +20,7 @@ import type { Dependency } from '@wendellhu/redi'; import { Inject, Injector } from '@wendellhu/redi'; import { filter } from 'rxjs/operators'; +import { IRenderManagerService } from '@univerjs/engine-render'; import { ActiveWorksheetController } from './controllers/active-worksheet/active-worksheet.controller'; import { AutoFillController } from './controllers/auto-fill.controller'; import { AutoHeightController } from './controllers/auto-height.controller'; @@ -31,11 +32,11 @@ import { FormulaEditorController } from './controllers/editor/formula-editor.con import { StartEditController } from './controllers/editor/start-edit.controller'; import { EditorBridgeController } from './controllers/editor-bridge.controller'; import { FormatPainterController } from './controllers/format-painter/format-painter.controller'; -import { FreezeController } from './controllers/freeze.controller'; +import { HeaderFreezeRenderController } from './controllers/freeze.render-controller'; import { HeaderMenuController } from './controllers/header-menu.controller'; import { HeaderMoveController } from './controllers/header-move.controller'; -import { HeaderResizeController } from './controllers/header-resize.controller'; -import { HeaderUnhideController } from './controllers/header-unhide.controller'; +import { HeaderResizeController } from './controllers/header-resize.render-controller'; +import { HeaderUnhideRenderController } from './controllers/header-unhide.render-controller'; import { MarkSelectionController } from './controllers/mark-selection.controller'; import { MoveRangeController } from './controllers/move-range.controller'; import { ScrollController } from './controllers/scroll.controller'; @@ -76,9 +77,10 @@ export class UniverSheetsUIPlugin extends Plugin { static override type = UniverInstanceType.SHEET; constructor( - config: undefined, + _config: undefined, @Inject(Injector) override readonly _injector: Injector, @Inject(LocaleService) private readonly _localeService: LocaleService, + @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService ) { super(); @@ -124,12 +126,10 @@ export class UniverSheetsUIPlugin extends Plugin { [EndEditController], [FormulaEditorController], [FormatPainterController], - [FreezeController], + [HeaderFreezeRenderController], [HeaderMenuController], [HeaderMoveController], [HeaderResizeController], - [HeaderUnhideController], - // [InitializeEditorController], [MoveRangeController], [ScrollController], [SelectionController], @@ -154,6 +154,13 @@ export class UniverSheetsUIPlugin extends Plugin { override onReady(): void { this._markSheetAsFocused(); + this._registerRenderControllers(); + } + + private _registerRenderControllers(): void { + ([HeaderFreezeRenderController, HeaderUnhideRenderController, HeaderResizeController]).forEach((controller) => { + this.disposeWithMe(this._renderManagerService.registerRenderControllers(UniverInstanceType.SHEET, controller)); + }); } private _markSheetAsFocused() { diff --git a/packages/sheets-ui/src/views/sheet-canvas-view.ts b/packages/sheets-ui/src/views/sheet-canvas-view.ts index c4e230092f..610a565fe1 100644 --- a/packages/sheets-ui/src/views/sheet-canvas-view.ts +++ b/packages/sheets-ui/src/views/sheet-canvas-view.ts @@ -139,7 +139,7 @@ export class SheetCanvasView extends RxDisposable { this._renderManagerService.setCurrent(unitId); } - private _addComponent(currentRender: IRender, workbook: Workbook) { + private _addComponent(renderUnit: IRender, workbook: Workbook) { const scene = this._scene; const worksheet = workbook.getActiveSheet(); @@ -162,11 +162,11 @@ export class SheetCanvasView extends RxDisposable { strokeWidth: 1, }); - currentRender.mainComponent = spreadsheet; - currentRender.components.set(SHEET_VIEW_KEY.MAIN, spreadsheet); - currentRender.components.set(SHEET_VIEW_KEY.ROW, spreadsheetRowHeader); - currentRender.components.set(SHEET_VIEW_KEY.COLUMN, spreadsheetColumnHeader); - currentRender.components.set(SHEET_VIEW_KEY.LEFT_TOP, SpreadsheetLeftTopPlaceholder); + renderUnit.mainComponent = spreadsheet; + renderUnit.components.set(SHEET_VIEW_KEY.MAIN, spreadsheet); + renderUnit.components.set(SHEET_VIEW_KEY.ROW, spreadsheetRowHeader); + renderUnit.components.set(SHEET_VIEW_KEY.COLUMN, spreadsheetColumnHeader); + renderUnit.components.set(SHEET_VIEW_KEY.LEFT_TOP, SpreadsheetLeftTopPlaceholder); scene.addObjects([spreadsheet], SHEET_COMPONENT_MAIN_LAYER_INDEX); scene.addObjects( diff --git a/packages/ui/src/render-unit/.gitkeep b/packages/ui/src/render-unit/.gitkeep new file mode 100644 index 0000000000..e69de29bb2