From 5127019c279834c20af53feba6f0f5f524b194a5 Mon Sep 17 00:00:00 2001 From: Wenzhao Hu <12122021+wzhudev@users.noreply.github.com> Date: Tue, 18 Jun 2024 09:30:50 +0800 Subject: [PATCH] refactor: refactor doc drawing --- examples/src/docs/main.ts | 4 +- .../operations/insert-image.operation.ts | 7 +- .../doc-drawing-update.controller.ts | 212 ++++-------------- packages/docs-drawing-ui/src/plugin.ts | 33 +-- .../src/controllers/back-scroll.controller.ts | 122 ++++++++++ .../back-scroll.render-controller.ts | 4 +- .../src/controllers/ime-input.controller.ts | 2 +- .../services/doc-skeleton-manager.service.ts | 2 +- 8 files changed, 193 insertions(+), 193 deletions(-) create mode 100644 packages/docs-ui/src/controllers/back-scroll.controller.ts diff --git a/examples/src/docs/main.ts b/examples/src/docs/main.ts index 5ae6f0a975..9bd7211c3b 100644 --- a/examples/src/docs/main.ts +++ b/examples/src/docs/main.ts @@ -24,6 +24,8 @@ import { UniverRenderEnginePlugin } from '@univerjs/engine-render'; import { UniverUIPlugin } from '@univerjs/ui'; import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'; import { UniverDebuggerPlugin } from '@univerjs/debugger'; +import { UniverDocsDrawingUIPlugin } from '@univerjs/docs-drawing-ui'; + import { DEFAULT_DOCUMENT_DATA_CN } from '../data'; import { enUS, ruRU, zhCN } from '../locales'; @@ -66,7 +68,7 @@ univer.registerPlugin(UniverDocsUIPlugin, { }, }); -// univer.registerPlugin(UniverDocsDrawingUIPlugin); +univer.registerPlugin(UniverDocsDrawingUIPlugin); univer.createUnit(UniverInstanceType.UNIVER_DOC, DEFAULT_DOCUMENT_DATA_CN); diff --git a/packages/docs-drawing-ui/src/commands/operations/insert-image.operation.ts b/packages/docs-drawing-ui/src/commands/operations/insert-image.operation.ts index b6e01bc977..151e010c0e 100644 --- a/packages/docs-drawing-ui/src/commands/operations/insert-image.operation.ts +++ b/packages/docs-drawing-ui/src/commands/operations/insert-image.operation.ts @@ -21,10 +21,11 @@ export interface IInsertImageOperationParams { files: Nullable; }; +/** + * @deprecated Do not use command as event! + */ export const InsertDocImageOperation: IOperation = { id: 'doc.operation.insert-float-image', type: CommandType.OPERATION, - handler: (accessor, params) => { - return true; - }, + handler: () => true, }; diff --git a/packages/docs-drawing-ui/src/controllers/doc-drawing-update.controller.ts b/packages/docs-drawing-ui/src/controllers/doc-drawing-update.controller.ts index 014fc87952..cadc0526b4 100644 --- a/packages/docs-drawing-ui/src/controllers/doc-drawing-update.controller.ts +++ b/packages/docs-drawing-ui/src/controllers/doc-drawing-update.controller.ts @@ -15,7 +15,7 @@ */ import type { DocumentDataModel, ICommandInfo, IDocDrawingPosition, Nullable } from '@univerjs/core'; -import { Disposable, FOCUSING_COMMON_DRAWINGS, ICommandService, IContextService, IUniverInstanceService, LifecycleStages, LocaleService, ObjectRelativeFromH, ObjectRelativeFromV, OnLifecycle, PositionedObjectLayoutType, UniverInstanceType } from '@univerjs/core'; +import { Disposable, FOCUSING_COMMON_DRAWINGS, ICommandService, IContextService, LocaleService, ObjectRelativeFromH, ObjectRelativeFromV, PositionedObjectLayoutType } from '@univerjs/core'; import { Inject } from '@wendellhu/redi'; import type { IImageIoServiceParam } from '@univerjs/drawing'; import { DRAWING_IMAGE_ALLOW_SIZE, DRAWING_IMAGE_COUNT_LIMIT, DRAWING_IMAGE_HEIGHT_LIMIT, DRAWING_IMAGE_WIDTH_LIMIT, DrawingTypeEnum, getImageSize, IDrawingManagerService, IImageIoService, ImageUploadStatusType } from '@univerjs/drawing'; @@ -25,8 +25,8 @@ import type { IDocDrawing } from '@univerjs/docs-drawing'; import { IDocDrawingService } from '@univerjs/docs-drawing'; import { DocSkeletonManagerService, TextSelectionManagerService } from '@univerjs/docs'; import { docDrawingPositionToTransform, transformToDocDrawingPosition } from '@univerjs/docs-ui'; -import type { Documents } from '@univerjs/engine-render'; -import { IRenderManagerService, Liquid } from '@univerjs/engine-render'; +import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; + import type { IInsertImageOperationParams } from '../commands/operations/insert-image.operation'; import { InsertDocImageOperation } from '../commands/operations/insert-image.operation'; import type { IInsertDrawingCommandParams, ISetDrawingCommandParams } from '../commands/commands/interfaces'; @@ -36,35 +36,25 @@ import { GroupDocDrawingCommand } from '../commands/commands/group-doc-drawing.c import { UngroupDocDrawingCommand } from '../commands/commands/ungroup-doc-drawing.command'; import { SetDocDrawingCommand } from '../commands/commands/set-doc-drawing.command'; -@OnLifecycle(LifecycleStages.Rendered, DocDrawingUpdateController) -export class DocDrawingUpdateController extends Disposable { +export class DocDrawingUpdateRenderController extends Disposable implements IRenderModule { constructor( + private readonly _context: IRenderContext, @ICommandService private readonly _commandService: ICommandService, - @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, @Inject(TextSelectionManagerService) private readonly _textSelectionManagerService: TextSelectionManagerService, - @IImageIoService private readonly _imageIoService: IImageIoService, + @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService, @IDocDrawingService private readonly _sheetDrawingService: IDocDrawingService, + @IImageIoService private readonly _imageIoService: IImageIoService, @IDrawingManagerService private readonly _drawingManagerService: IDrawingManagerService, @IContextService private readonly _contextService: IContextService, @IMessageService private readonly _messageService: IMessageService, - @Inject(LocaleService) private readonly _localeService: LocaleService, - @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService, - @IRenderManagerService private readonly _renderManagerService: IRenderManagerService + @Inject(LocaleService) private readonly _localeService: LocaleService ) { super(); - this._init(); - } - - private _init(): void { this._initCommandListeners(); - this._updateDrawingListener(); - this._updateOrderListener(); - this._groupDrawingListener(); - this._focusDrawingListener(); } @@ -72,32 +62,30 @@ export class DocDrawingUpdateController extends Disposable { * Upload image to cell or float image */ private _initCommandListeners() { - this.disposeWithMe( - this._commandService.onCommandExecuted(async (command: ICommandInfo) => { - if (command.id === InsertDocImageOperation.id) { - const params = command.params as IInsertImageOperationParams; - if (params.files == null) { - return; - } - - const fileLength = params.files.length; - - if (fileLength > DRAWING_IMAGE_COUNT_LIMIT) { - this._messageService.show({ - type: MessageType.Error, - content: this._localeService.t('update-status.exceedMaxCount', String(DRAWING_IMAGE_COUNT_LIMIT)), - }); - return; - } - - this._imageIoService.setWaitCount(fileLength); - - params.files.forEach(async (file) => { - await this._insertFloatImage(file); + this.disposeWithMe(this._commandService.onCommandExecuted(async (command: ICommandInfo) => { + if (command.id === InsertDocImageOperation.id) { + const params = command.params as IInsertImageOperationParams; + if (params.files == null) { + return; + } + + const fileLength = params.files.length; + + if (fileLength > DRAWING_IMAGE_COUNT_LIMIT) { + this._messageService.show({ + type: MessageType.Error, + content: this._localeService.t('update-status.exceedMaxCount', String(DRAWING_IMAGE_COUNT_LIMIT)), }); + return; } - }) - ); + + this._imageIoService.setWaitCount(fileLength); + + params.files.forEach(async (file) => { + await this._insertFloatImage(file); + }); + } + })); } private async _insertFloatImage(file: File) { @@ -129,21 +117,11 @@ export class DocDrawingUpdateController extends Disposable { return; } - const info = this._getUnitInfo(); - if (info == null) { - return; - } - const { unitId, subUnitId } = info; + const { unitId } = this._context; const { imageId, imageSourceType, source, base64Cache } = imageParam; const { width, height, image } = await getImageSize(base64Cache || ''); - const renderObject = this._renderManagerService.getRenderById(unitId); - - if (renderObject == null) { - return; - } - - const { width: sceneWidth, height: sceneHeight } = renderObject.scene; + const { width: sceneWidth, height: sceneHeight } = this._context.scene; this._imageIoService.addImageSourceCache(imageId, imageSourceType, image); @@ -154,7 +132,7 @@ export class DocDrawingUpdateController extends Disposable { scale = Math.max(scaleWidth, scaleHeight); } - const docTransform = this._getImagePosition(width * scale, height * scale, sceneWidth, sceneHeight); + const docTransform = this._getImagePosition(width * scale, height * scale); if (docTransform == null) { return; @@ -162,7 +140,7 @@ export class DocDrawingUpdateController extends Disposable { const docDrawingParam: IDocDrawing = { unitId, - subUnitId, + subUnitId: unitId, drawingId: imageId, drawingType: DrawingTypeEnum.DRAWING_IMAGE, imageSourceType, @@ -177,25 +155,10 @@ export class DocDrawingUpdateController extends Disposable { drawings: [docDrawingParam], } as IInsertDrawingCommandParams); - this._docSkeletonManagerService.getCurrent()?.skeleton.calculate(); + this._docSkeletonManagerService.getSkeleton()?.calculate(); } - private _getUnitInfo() { - const documentDataModel = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC); - if (documentDataModel == null) { - return; - } - - const unitId = documentDataModel.getUnitId(); - const subUnitId = unitId; - - return { - unitId, - subUnitId, - }; - } - - private _getImagePosition(imageWidth: number, imageHeight: number, sceneWidth: number, sceneHeight: number): Nullable { + private _getImagePosition(imageWidth: number, imageHeight: number): Nullable { const activeTextRange = this._textSelectionManagerService.getActiveTextRange(); const position = activeTextRange?.getAbsolutePosition() || { left: 0, @@ -235,18 +198,13 @@ export class DocDrawingUpdateController extends Disposable { private _updateDrawingListener() { this._drawingManagerService.featurePluginUpdate$.subscribe((params) => { - const drawings: Partial[] = []; - if (params.length === 0) { return; } - // const offsetInfo = this._getDocsOffsetInfo(); - - // const { pageMarginCache, docsLeft, docsTop } = offsetInfo; - + const drawings: Partial[] = []; (params as IDocDrawing[]).forEach((param) => { - const { unitId, subUnitId, drawingId, drawingType, transform } = param; + const { unitId, subUnitId, drawingId, transform } = param; if (transform == null) { return; } @@ -275,8 +233,9 @@ export class DocDrawingUpdateController extends Disposable { }); if (drawings.length > 0) { + const unitId = params[0].unitId; this._commandService.syncExecuteCommand(SetDocDrawingCommand.id, { - unitId: params[0].unitId, + unitId, drawings, } as ISetDrawingCommandParams); @@ -285,97 +244,10 @@ export class DocDrawingUpdateController extends Disposable { }); } - private _getDocsOffsetInfo() { - const docsSkeletonObject = this._docSkeletonManagerService.getCurrent(); - if (docsSkeletonObject == null) { - return { - pageMarginCache: new Map(), - docsLeft: 0, - docsTop: 0, - }; - } - - const { unitId, skeleton } = docsSkeletonObject; - - const currentRender = this._renderManagerService.getRenderById(unitId); - - const skeletonData = skeleton?.getSkeletonData(); - - if (currentRender == null || !skeletonData) { - return { - pageMarginCache: new Map(), - docsLeft: 0, - docsTop: 0, - }; - } - - const { mainComponent } = currentRender; - - const documentComponent = mainComponent as Documents; - - const { left: docsLeft, top: docsTop, pageLayoutType, pageMarginLeft, pageMarginTop } = documentComponent; - - const { pages } = skeletonData; - - const liquid = new Liquid(); - - const pageMarginCache = new Map(); - - for (let i = 0, len = pages.length; i < len; i++) { - const page = pages[i]; - const { skeDrawings, marginLeft, marginTop } = page; - // cumPageLeft + = pageWidth + documents.pageMarginLeft; - - liquid.translatePagePadding(page); - - skeDrawings.forEach((drawing) => { - const { aLeft, aTop, height, width, drawingId, drawingOrigin } = drawing; - // const behindText = drawingOrigin.layoutType === PositionedObjectLayoutType.WRAP_NONE && drawingOrigin.behindDoc === BooleanNumber.TRUE; - // floatObjects.push({ - // unitId, - // subUnitId: DEFAULT_DOCUMENT_SUB_COMPONENT_ID, - // floatingObjectId: drawingId, - // behindText, - // floatingObject: { - // left: aLeft + docsLeft + liquid.x, - // top: aTop + docsTop + liquid.y, - // width, - // height, - // }, - // }); - - pageMarginCache.set(drawingId, { - marginLeft: liquid.x, - marginTop: liquid.y, - }); - }); - - liquid.restorePagePadding(page); - - liquid.translatePage(page, pageLayoutType, pageMarginLeft, pageMarginTop); - } - - return { pageMarginCache, docsLeft, docsTop }; - } - private _refreshDocSkeleton() { - const docsSkeletonObject = this._docSkeletonManagerService.getCurrent(); - if (docsSkeletonObject == null) { - return; - } - - const { unitId, skeleton } = docsSkeletonObject; - - const currentRender = this._renderManagerService.getRenderById(unitId); - - if (currentRender == null) { - return; - } - - const { mainComponent } = currentRender; - + const skeleton = this._docSkeletonManagerService.getSkeleton(); + const { mainComponent } = this._context; skeleton?.calculate(); - mainComponent?.makeDirty(); } diff --git a/packages/docs-drawing-ui/src/plugin.ts b/packages/docs-drawing-ui/src/plugin.ts index 024795686c..fd76d92e20 100644 --- a/packages/docs-drawing-ui/src/plugin.ts +++ b/packages/docs-drawing-ui/src/plugin.ts @@ -14,41 +14,44 @@ * limitations under the License. */ -import { LocaleService, Plugin, UniverInstanceType } from '@univerjs/core'; +import { DependentOn, Plugin, UniverInstanceType } from '@univerjs/core'; import type { Dependency } from '@wendellhu/redi'; import { Inject, Injector } from '@wendellhu/redi'; +import { UniverDrawingUIPlugin } from '@univerjs/drawing-ui'; +import { UniverDrawingPlugin } from '@univerjs/drawing'; +import { UniverDocsDrawingPlugin } from '@univerjs/docs-drawing'; +import { IRenderManagerService } from '@univerjs/engine-render'; import { DocDrawingPopupMenuController } from './controllers/drawing-popup-menu.controller'; import { DocDrawingUIController } from './controllers/doc-drawing.controller'; -import { DocDrawingUpdateController } from './controllers/doc-drawing-update.controller'; +import { DocDrawingUpdateRenderController } from './controllers/doc-drawing-update.controller'; -const PLUGIN_NAME = 'Docs_Drawing_UI_PLUGIN'; +const PLUGIN_NAME = 'DOCS_DRAWING_UI_PLUGIN'; +@DependentOn(UniverDrawingUIPlugin, UniverDrawingPlugin, UniverDocsDrawingPlugin) export class UniverDocsDrawingUIPlugin extends Plugin { static override type = UniverInstanceType.UNIVER_DOC; static override pluginName = PLUGIN_NAME; + constructor( - config: undefined, + _config: undefined, @Inject(Injector) protected _injector: Injector, - @Inject(LocaleService) private readonly _localeService: LocaleService + @IRenderManagerService private readonly _renderManagerSrv: IRenderManagerService ) { super(); } - override onStarting(_injector: Injector): void { - this._initDependencies(_injector); - } - - private _initDependencies(injector: Injector): void { + override onStarting(injector: Injector): void { const dependencies: Dependency[] = [ - - // services - - // controllers [DocDrawingUIController], - [DocDrawingUpdateController], [DocDrawingPopupMenuController], ]; dependencies.forEach((dependency) => injector.add(dependency)); } + + override onReady(): void { + ([ + DocDrawingUpdateRenderController, + ]).forEach((m) => this._renderManagerSrv.registerRenderController(UniverInstanceType.UNIVER_DOC, m)); + } } diff --git a/packages/docs-ui/src/controllers/back-scroll.controller.ts b/packages/docs-ui/src/controllers/back-scroll.controller.ts new file mode 100644 index 0000000000..c76be88826 --- /dev/null +++ b/packages/docs-ui/src/controllers/back-scroll.controller.ts @@ -0,0 +1,122 @@ +/** + * 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 { IUniverInstanceService, LifecycleStages, OnLifecycle, RxDisposable } from '@univerjs/core'; +import { DocSkeletonManagerService, getDocObject, TextSelectionManagerService, VIEWPORT_KEY } from '@univerjs/docs'; +import { getAnchorBounding, IRenderManagerService, NodePositionConvertToCursor } from '@univerjs/engine-render'; +import { IEditorService } from '@univerjs/ui'; +import { Inject } from '@wendellhu/redi'; +import { takeUntil } from 'rxjs'; + +const ANCHOR_WIDTH = 1.5; + +@OnLifecycle(LifecycleStages.Rendered, BackScrollController) +export class BackScrollController extends RxDisposable { + constructor( + @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService, + @Inject(TextSelectionManagerService) private readonly _textSelectionManagerService: TextSelectionManagerService, + @IEditorService private readonly _editorService: IEditorService, + @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, + @IRenderManagerService private readonly _renderManagerService: IRenderManagerService + ) { + super(); + + this._init(); + } + + private _init() { + this._textSelectionManagerService.textSelection$.pipe(takeUntil(this.dispose$)).subscribe((params) => { + if (params == null) { + return; + } + + const { isEditing, unitId } = params; + + if (isEditing) { + this._scrollToSelection(unitId); + } + }); + } + + // Let the selection show on the current screen. + private _scrollToSelection(unitId: string) { + const activeTextRange = this._textSelectionManagerService.getActiveRange(); + const docObject = this._getDocObject(); + const skeleton = this._docSkeletonManagerService.getSkeleton(); + + if (activeTextRange == null || docObject == null || skeleton == null) { + return; + } + + const { collapsed, startNodePosition } = activeTextRange; + + if (!collapsed) { + return; + } + + const documentOffsetConfig = docObject.document.getOffsetConfig(); + const { docsLeft, docsTop } = documentOffsetConfig; + + const convertor = new NodePositionConvertToCursor(documentOffsetConfig, skeleton); + + const { contentBoxPointGroup } = convertor.getRangePointData(startNodePosition, startNodePosition); + + const { left: aLeft, top: aTop, height } = getAnchorBounding(contentBoxPointGroup); + + const left = aLeft + docsLeft; + + const top = aTop + docsTop; + + const viewportMain = docObject.scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); + + const isEditor = !!this._editorService.getEditor(unitId); + + if (viewportMain == null) { + return; + } + + const { + left: boundLeft, + top: boundTop, + right: boundRight, + bottom: boundBottom, + } = viewportMain.getBounding().viewBound; + + let offsetY = 0; + let offsetX = 0; + + const delta = isEditor ? 0 : 100; + + if (top < boundTop) { + offsetY = top - boundTop; + } else if (top > boundBottom - height) { + offsetY = top - boundBottom + height + delta; + } + + if (left < boundLeft) { + offsetX = left - boundLeft; + } else if (left > boundRight - ANCHOR_WIDTH) { + offsetX = left - boundRight + ANCHOR_WIDTH; + } + + const config = viewportMain.transViewportScroll2ScrollValue(offsetX, offsetY); + viewportMain.scrollBy(config); + } + + private _getDocObject() { + return getDocObject(this._univerInstanceService, this._renderManagerService); + } +} diff --git a/packages/docs-ui/src/controllers/render-controllers/back-scroll.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/back-scroll.render-controller.ts index 79d386efee..d634f08cc6 100644 --- a/packages/docs-ui/src/controllers/render-controllers/back-scroll.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/back-scroll.render-controller.ts @@ -55,9 +55,9 @@ export class DocBackScrollRenderController extends RxDisposable implements IRend private _scrollToSelection(unitId: string) { const activeTextRange = this._textSelectionManagerService.getActiveRange(); const docObject = neoGetDocObject(this._context); - const skeleton = this._docSkeletonManagerService.getSkeleton()?.skeleton; + const skeleton = this._docSkeletonManagerService.getSkeleton(); - if (activeTextRange == null || docObject == null || skeleton == null) { + if (activeTextRange == null || docObject == null) { return; } diff --git a/packages/docs/src/controllers/ime-input.controller.ts b/packages/docs/src/controllers/ime-input.controller.ts index 8b8b2f6833..06ab2c8c67 100644 --- a/packages/docs/src/controllers/ime-input.controller.ts +++ b/packages/docs/src/controllers/ime-input.controller.ts @@ -111,7 +111,7 @@ export class IMEInputController extends Disposable { } const skeleton = this._renderManagerSrv.getRenderById(documentModel.getUnitId()) - ?.with(DocSkeletonManagerService).getSkeleton()?.skeleton; + ?.with(DocSkeletonManagerService).getSkeleton(); const { event, activeRange } = config; diff --git a/packages/docs/src/services/doc-skeleton-manager.service.ts b/packages/docs/src/services/doc-skeleton-manager.service.ts index 508ee578f1..4e41c47080 100644 --- a/packages/docs/src/services/doc-skeleton-manager.service.ts +++ b/packages/docs/src/services/doc-skeleton-manager.service.ts @@ -70,7 +70,7 @@ export class DocSkeletonManagerService extends RxDisposable implements IRenderMo }); } - getSkeleton(): Nullable { + getSkeleton(): DocumentSkeleton { return this._skeleton; }