import { inject, injectable, optional } from 'inversify'; import { PipeEventName, PipelineDimension, PipelineEventHandler, PipelineEventRegsiter, PipeSupportEvent } from './pipeline'; import { Disposable, DisposableCollection, Emitter } from '@gedit/utils'; import { PipelineRenderer } from './pipeline-renderer'; import { Layer, LayerRegistry, PlaygroundConfigEntity } from '../layer'; import { AbleManager, AbleRegistry, ConfigEntity, Entity, EntityManager, EntityRegistry, getAbleMetadata, getEntityMetadata, PlaygroundContext } from '../../common'; import { PipelineEntitiesSelector } from './pipeline-entities-selector'; import { PipelineDispatcher } from './pipeline-dispatcher'; import { PipelineEntitiesImpl } from './pipeline-entities'; import { ConflatableMessage, IMessageHandler, Message, MessageLoop } from '@phosphor/messaging'; import { PlaygroundCommandRegistry, PlaygroundKeybindingRegistry, PlaygroundMenuRegistry } from '../playground-registries'; import { SelectionService } from '@gedit/application-common'; import { FrontendApplicationStateService } from '@gedit/application-common/lib/browser'; export enum PipelineMessage { ZOOM = 'PIPELINE_ZOOM', SCROLL = 'PIPELINE_SCROLL' } const zoomMessage = new ConflatableMessage(PipelineMessage.ZOOM); const scrollMessage = new ConflatableMessage(PipelineMessage.SCROLL); /** * pipeline 注册器, 用于注册一些事件 */ @injectable() export class PipelineRegistry implements Disposable, IMessageHandler { private _isFocused = false; protected toDispose = new DisposableCollection(); readonly onResizeEmitter = new Emitter(); readonly onFocusEmitter = new Emitter(); readonly onBlurEmitter = new Emitter(); readonly onZoomEmitter = new Emitter(); readonly onScrollEmitter = new Emitter<{ scrollX: number, scrollY: number }>(); readonly onFocus = this.onFocusEmitter.event; readonly onBlur = this.onBlurEmitter.event; readonly onZoom = this.onZoomEmitter.event; readonly onScroll = this.onScrollEmitter.event; constructor( ) { this.toDispose.pushAll([ this.onResizeEmitter, this.onFocusEmitter, this.onZoomEmitter, this.onBlurEmitter, this.onScrollEmitter ]); this.onFocusEmitter.event(() => { this._isFocused = true; }); this.onBlurEmitter.event(() => { this._isFocused = false; }); } @inject(PipelineDispatcher) dispatcher: PipelineDispatcher; @inject(PipelineRenderer) renderer: PipelineRenderer; @inject(PlaygroundCommandRegistry) readonly commands: PlaygroundCommandRegistry; @inject(PlaygroundMenuRegistry) readonly menus: PlaygroundMenuRegistry; @inject(PlaygroundKeybindingRegistry) readonly keybindings: PlaygroundKeybindingRegistry; @inject(PipelineEntitiesSelector) selector: PipelineEntitiesSelector; @inject(EntityManager) entityManager: EntityManager; @inject(AbleManager) ableManager: AbleManager; @inject(PlaygroundContext) context: PlaygroundContext; @inject(SelectionService) @optional() selectionService?: SelectionService; @inject(FrontendApplicationStateService) readonly stateService?: FrontendApplicationStateService; protected playgroundEvents: { [key: string]: { handlers: PipelineEventRegsiter[] } & Disposable } = {}; protected globalEvents: { [key: string]: { handlers: PipelineEventRegsiter[] } & Disposable } = {}; _listenEvent(name: PipeEventName, handle: PipelineEventHandler, isGlobal: boolean, priority: number = 0): Disposable { const eventsCache = isGlobal ? this.globalEvents : this.playgroundEvents; const domNode = isGlobal ? document : this.renderer.node.parentNode!; let eventRegister = eventsCache[name]; if (!eventRegister) { const realHandler = { handleEvent: (e: PipeSupportEvent) => { const list = eventRegister.handlers; for (let i = 0, len = list.length; i < len; i++) { const prevent = list[i].handle(e); if (prevent) return; } } }; domNode.addEventListener(name, realHandler, false); eventRegister = eventsCache[name] = { handlers: [], dispose: () => { domNode.removeEventListener(name, realHandler); delete eventsCache[name]; } }; } const handlers = eventRegister.handlers; const item = { handle, priority }; /** * handlers排序: * 1. 后注册先执行(符合冒泡规则) * 2. 按priority排序 */ handlers.unshift(item); handlers.sort((a, b) => b.priority - a.priority); const dispose = Disposable.create(() => { const index = eventRegister.handlers.indexOf(item); if (index !== -1) eventRegister.handlers.splice(index, 1); if (eventRegister.handlers.length === 0) { eventRegister.dispose(); } }); this.toDispose.push(dispose); return dispose; } /** * 监听画布上的浏览器事件 */ listenPlaygroundEvent(name: PipeEventName, handle: (event: PipeSupportEvent) => boolean | undefined, priority?: number): Disposable { return this._listenEvent(name, handle, false, priority); } /** * 监听全局的事件 * @param name * @param handle */ listenGlobalEvent(name: PipeEventName, handle: (event: PipeSupportEvent) => boolean | undefined, priority?: number): Disposable { return this._listenEvent(name, handle, true, priority); } /** * 订阅关联的entity,会影响draw */ protected subscribeEntities(layer: Layer, entities: EntityRegistry[]): void { this.selector.subscribeEntities(layer, entities); } /** * 订阅关联的able, 会影响draw */ protected subscribeAbles(layer: Layer, ables: AbleRegistry[]): void { this.selector.subscribeAbles(layer, ables); } /** * 注册layer * @param Registry */ registerLayer(Registry: LayerRegistry): void { const layer = new Registry(); const ableRegistries = getAbleMetadata(Registry); const entityRegistries = getEntityMetadata(Registry); // 注册类 ableRegistries.forEach(r => this.ableManager.registerAble(r)); entityRegistries.forEach(r => { this.entityManager.registerEntity(r); if (Entity.isRegistryOf(r, ConfigEntity)) { // 自动创建注册的entity this.entityManager.createEntity(r); } }); this.subscribeAbles(layer, ableRegistries); this.subscribeEntities(layer, entityRegistries); layer.entityManager = this.entityManager; layer.entityBindedList = new PipelineEntitiesImpl(this.entityManager); layer.context = this.context; layer.commands = this.commands; layer.menus = this.menus; layer.keybindings = this.keybindings; layer.selectionService = this.selectionService; layer.stateService = this.stateService; layer.reloadEntities = () => { const result = this.selector.getEntities(layer); if (result.changed) { (layer.entityBindedList as PipelineEntitiesImpl).load(result.entities); } return result.changed; }; // @ts-ignore layer.dispatch = (payloadKey: string | Symbol, payloadValue: object, cb?: () => void) => { const changedEntities = this.dispatcher.dispatch(payloadKey, payloadValue); if (cb) { if (changedEntities.length > 0) { const changed = layer.reloadEntities(); if (changed && layer.draw) layer.draw(); } cb(); } }; layer.listenPlaygroundEvent = this.listenPlaygroundEvent.bind(this); layer.listenGlobalEvent = this.listenGlobalEvent.bind(this); layer.config = this.configEntity; Object.defineProperty(layer, 'isFocused', { get: () => this._isFocused }); if (layer.onResize) { this.onResize(layer.onResize.bind(layer)); } if (layer.onBlur) { this.onBlurEmitter.event(layer.onBlur.bind(layer)); } if (layer.onFocus) { this.onFocusEmitter.event(layer.onFocus.bind(layer)); } if (layer.onZoom) { this.onZoomEmitter.event(layer.onZoom.bind(layer)); } if (layer.onScroll) { this.onScrollEmitter.event(layer.onScroll.bind(layer)); } this.renderer.addLayer(layer); } get configEntity(): PlaygroundConfigEntity { return this.entityManager.getEntity(PlaygroundConfigEntity, true)!; } ready(): void { const config = this.configEntity; let lastScale = config.finalScale; let lastScroll = config.scrollData; // 监听zoom config.onConfigChanged(() => { const newScale = config.finalScale; const newScroll = config.scrollData; if (newScale !== lastScale) { lastScale = newScale; MessageLoop.postMessage(this, zoomMessage); } if (lastScroll.scrollX !== newScroll.scrollX || lastScroll.scrollY !== newScroll.scrollY) { lastScroll = newScroll; MessageLoop.postMessage(this, scrollMessage); } }); } processMessage(msg: Message): void { const config = this.configEntity; switch (msg.type) { case PipelineMessage.SCROLL: this.onScrollEmitter.fire(config.scrollData); break; case PipelineMessage.ZOOM: this.onZoomEmitter.fire(config.finalScale); break; } } /** * pipline大小变化时候会触发 */ readonly onResize = this.onResizeEmitter.event; dispose(): void { this.toDispose.dispose(); } }