Skip to content

Commit

Permalink
feat(engine-render): add render unit system (#2025)
Browse files Browse the repository at this point in the history
  • Loading branch information
wzhudev committed Apr 23, 2024
1 parent bced914 commit a5243da
Show file tree
Hide file tree
Showing 16 changed files with 339 additions and 258 deletions.
6 changes: 6 additions & 0 deletions packages/core/src/shared/lifecycle.ts
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions packages/docs-ui/src/views/doc-canvas-view.ts
Expand Up @@ -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';
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion packages/engine-render/src/index.ts
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion packages/engine-render/src/render-engine.ts
Expand Up @@ -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.
Expand Down
Expand Up @@ -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<Nullable<string>>;
createRender$: Observable<Nullable<string>>;
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<IRender>;
Expand All @@ -44,24 +47,16 @@ export interface IRenderManagerService {
getCurrent(): Nullable<IRender>;
getFirst(): Nullable<IRender>;
has(unitId: string): boolean;
}

export type RenderComponentType = SheetComponent | DocComponent | Slide | BaseObject;

export interface IRender {
unitId: string;
engine: Engine;
scene: Scene;
mainComponent: Nullable<RenderComponentType>;
components: Map<string, RenderComponentType>;
isMainScene: boolean;
registerRenderControllers<T extends UnitModel>(type: UnitType, ctor: IRenderControllerCtor<T>): 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 = '';
Expand All @@ -81,31 +76,62 @@ export class RenderManagerService implements IRenderManagerService {
return this._defaultEngine;
}

dispose() {
private readonly _renderControllers = new Map<UnitType, Set<IRenderControllerCtor>>();

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<IRenderControllerCtor> {
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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -214,4 +241,4 @@ export class RenderManagerService implements IRenderManagerService {
}
}

export const IRenderManagerService = createIdentifier<RenderManagerService>('univer.render-manager-service');
export const IRenderManagerService = createIdentifier<RenderManagerService>('engine-render.render-manager.service');
102 changes: 102 additions & 0 deletions 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<RenderComponentType>;
components: Map<string, RenderComponentType>;
isMainScene: boolean;
}

// eslint-disable-next-line ts/no-explicit-any
export interface IRenderControllerCtor<T extends UnitModel = UnitModel> { new(unit: IRenderContext<T>, ...args: any[]): IRenderController }
export interface IRenderController extends IDisposable {}

/**
* This object encapsulates methods or properties to render each element.
*/
export interface IRenderContext<T extends UnitModel = UnitModel> 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<UnitModel>;
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<RenderComponentType>) { this._renderContext.mainComponent = component; }
get mainComponent(): Nullable<RenderComponentType> { 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<IRenderContext, 'engine' | 'scene' | 'isMainScene' | 'unit' >
) {
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));
}
}
Expand Up @@ -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 }],
Expand Down

0 comments on commit a5243da

Please sign in to comment.