diff --git a/packages/client/src/features/accessibility/keyboard-pointer/keyboard-pointer-position.ts b/packages/client/src/features/accessibility/keyboard-pointer/keyboard-pointer-position.ts index 315be5ff..01c296bc 100644 --- a/packages/client/src/features/accessibility/keyboard-pointer/keyboard-pointer-position.ts +++ b/packages/client/src/features/accessibility/keyboard-pointer/keyboard-pointer-position.ts @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2023 Business Informatics Group (TU Wien) and others. + * Copyright (c) 2023-2024 Business Informatics Group (TU Wien) and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -14,11 +14,11 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ import { findChildrenAtPosition, findParentByFeature, GModelElement, Point } from '@eclipse-glsp/sprotty'; -import { Containable, isContainable } from '../../hints/model'; +import { CursorCSS } from '../../../base/feedback/css-feedback'; import { getAbsolutePositionByPoint } from '../../../utils/viewpoint-util'; +import { ContainerElement, isContainable } from '../../hints/model'; import { KeyboardPointerMetadata } from './constants'; import { KeyboardPointer } from './keyboard-pointer'; -import { CursorCSS } from '../../../base/feedback/css-feedback'; export class KeyboardPointerPosition { public renderPosition: Point = { x: 20, y: 20 }; @@ -46,7 +46,7 @@ export class KeyboardPointerPosition { } containableParentAtDiagramPosition(elementTypeId: string): { - container: (GModelElement & Containable) | undefined; + container: ContainerElement | undefined; status: CursorCSS; } { const children = this.childrenAtDiagramPosition(); @@ -64,15 +64,13 @@ export class KeyboardPointerPosition { private containableParentOf( target: GModelElement, elementTypeId: string - ): { container: (GModelElement & Containable) | undefined; status: CursorCSS } { + ): { container: ContainerElement | undefined; status: CursorCSS } { const container = findParentByFeature(target, isContainable); return { container, - status: this.isCreationAllowed(container, elementTypeId) ? CursorCSS.NODE_CREATION : CursorCSS.OPERATION_NOT_ALLOWED + status: this.keyboardPointer.containerManager.isCreationAllowed(container, elementTypeId) + ? CursorCSS.NODE_CREATION + : CursorCSS.OPERATION_NOT_ALLOWED }; } - - private isCreationAllowed(container: (GModelElement & Containable) | undefined, elementTypeId: string): boolean | undefined { - return container && container.isContainableElement(elementTypeId); - } } diff --git a/packages/client/src/features/accessibility/keyboard-pointer/keyboard-pointer.ts b/packages/client/src/features/accessibility/keyboard-pointer/keyboard-pointer.ts index 5ed728df..2f011a16 100644 --- a/packages/client/src/features/accessibility/keyboard-pointer/keyboard-pointer.ts +++ b/packages/client/src/features/accessibility/keyboard-pointer/keyboard-pointer.ts @@ -18,6 +18,7 @@ import { inject, injectable } from 'inversify'; import { EditorContextService } from '../../../base/editor-context-service'; import { CursorCSS } from '../../../base/feedback/css-feedback'; import { GLSPAbstractUIExtension } from '../../../base/ui-extension/ui-extension'; +import { ContainerManager } from '../../tools/node-creation/container-manager'; import { KeyboardGridCellSelectedAction } from '../keyboard-grid/action'; import { SetKeyboardPointerRenderPositionAction } from './actions'; import { KeyboardPointerMetadata } from './constants'; @@ -31,10 +32,13 @@ export class KeyboardPointer extends GLSPAbstractUIExtension implements IActionH kind: 'triggerNodeCreation' }; - protected position: KeyboardPointerPosition = new KeyboardPointerPosition(this); - protected keyListener: KeyboardPointerKeyboardListener; @inject(EditorContextService) public editorContextService: EditorContextService; + @inject(TYPES.IContainerManager) + public containerManager: ContainerManager; + + protected position: KeyboardPointerPosition = new KeyboardPointerPosition(this); + protected keyListener: KeyboardPointerKeyboardListener; constructor(@inject(TYPES.IActionDispatcher) protected readonly actionDispatcher: IActionDispatcher) { super(); diff --git a/packages/client/src/features/tools/node-creation/container-manager.ts b/packages/client/src/features/tools/node-creation/container-manager.ts index 397126c2..56961335 100644 --- a/packages/client/src/features/tools/node-creation/container-manager.ts +++ b/packages/client/src/features/tools/node-creation/container-manager.ts @@ -44,14 +44,26 @@ export interface TrackedInsert { options: InsertOptions; } +export interface IContainerManager { + insert(proxy: GModelElement, location: Point, elementTypeId: string, opts?: Partial): TrackedInsert; + isCreationAllowed(container: ContainerElement | undefined, elementTypeId: string, opts?: Partial): boolean; + findContainer(location: Point, ctx: GModelElement, evt?: MouseEvent): ContainerElement | undefined; + addInsertFeedback(feedback: FeedbackEmitter, trackedInsert: TrackedInsert, ctx?: GModelElement, event?: MouseEvent): FeedbackEmitter; +} + +/** + * The default {@link IContainerManager} implementation. + * This class class manages the insertion of elements into containers by validating their positions and types, + * providing feedback on the insertion process, and determining the appropriate container based on the location and context. + */ @injectable() -export class ContainerManager { +export class ContainerManager implements IContainerManager { @inject(ChangeBoundsManager) protected readonly changeBoundsManager: ChangeBoundsManager; insert(proxy: GModelElement, location: Point, elementTypeId: string, opts?: Partial): TrackedInsert { const options = { ...DEFAULT_INSERT_OPTIONS, ...opts }; const container = this.findContainer(location, proxy, opts?.evt); - let valid = !container || container.isContainableElement(elementTypeId); + let valid = this.isCreationAllowed(container, elementTypeId, opts); if (valid && (!container || options.validateLocationInContainer)) { // we need to check whether the location is valid either because we do not have a container or the option is set valid = opts?.validLocationOverwrite ?? this.changeBoundsManager.hasValidPosition(proxy, location); @@ -59,6 +71,10 @@ export class ContainerManager { return { elementTypeId, container, location, valid, options }; } + isCreationAllowed(container: ContainerElement | undefined, elementTypeId: string, opts?: Partial): boolean { + return !container || container.isContainableElement(elementTypeId); + } + findContainer(location: Point, ctx: GModelElement, evt?: MouseEvent): ContainerElement | undefined { // reverse order of children to find the innermost, top-rendered containers first return findChildrenAtPosition(ctx.root, location) diff --git a/packages/client/src/features/tools/node-creation/node-creation-module.ts b/packages/client/src/features/tools/node-creation/node-creation-module.ts index d26e9454..f5915607 100644 --- a/packages/client/src/features/tools/node-creation/node-creation-module.ts +++ b/packages/client/src/features/tools/node-creation/node-creation-module.ts @@ -30,7 +30,7 @@ import { InsertIndicatorView } from './node-creation-views'; export const nodeCreationToolModule = new FeatureModule( (bind, unbind, isBound, rebind) => { const context = { bind, unbind, isBound, rebind }; - bind(ContainerManager).toSelf().inSingletonScope(); + bindAsService(context, TYPES.IContainerManager, ContainerManager); bindAsService(context, TYPES.ITool, NodeCreationTool); configureActionHandler(context, TriggerNodeCreationAction.KIND, NodeCreationTool); configureModelElement(context, InsertIndicator.TYPE, InsertIndicator, InsertIndicatorView); diff --git a/packages/client/src/features/tools/node-creation/node-creation-tool.ts b/packages/client/src/features/tools/node-creation/node-creation-tool.ts index e66e93d8..a3c24c1b 100644 --- a/packages/client/src/features/tools/node-creation/node-creation-tool.ts +++ b/packages/client/src/features/tools/node-creation/node-creation-tool.ts @@ -45,7 +45,7 @@ import { RemoveTemplateElementsAction } from '../../element-template/remove-temp import { BaseCreationTool } from '../base-tools'; import { ChangeBoundsManager } from '../change-bounds/change-bounds-manager'; import { TrackedMove } from '../change-bounds/change-bounds-tracker'; -import { ContainerManager, TrackedInsert } from './container-manager'; +import { IContainerManager, TrackedInsert } from './container-manager'; import { InsertIndicator } from './insert-indicator'; @injectable() @@ -55,7 +55,7 @@ export class NodeCreationTool extends BaseCreationTool