Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GLSP-1388 Enhance extensibility of ContainerManager #385

Merged
merged 2 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 };
Expand Down Expand Up @@ -46,7 +46,7 @@ export class KeyboardPointerPosition {
}

containableParentAtDiagramPosition(elementTypeId: string): {
container: (GModelElement & Containable) | undefined;
container: ContainerElement | undefined;
status: CursorCSS;
} {
const children = this.childrenAtDiagramPosition();
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,37 @@ export interface TrackedInsert {
options: InsertOptions;
}

export interface IContainerManager {
martin-fleck-at marked this conversation as resolved.
Show resolved Hide resolved
insert(proxy: GModelElement, location: Point, elementTypeId: string, opts?: Partial<InsertOptions>): TrackedInsert;
isCreationAllowed(container: ContainerElement | undefined, elementTypeId: string, opts?: Partial<InsertOptions>): 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<InsertOptions>): 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);
}
return { elementTypeId, container, location, valid, options };
}

isCreationAllowed(container: ContainerElement | undefined, elementTypeId: string, opts?: Partial<InsertOptions>): 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -55,7 +55,7 @@ export class NodeCreationTool extends BaseCreationTool<TriggerNodeCreationAction
protected isTriggerAction = TriggerNodeCreationAction.is;

@inject(ChangeBoundsManager) readonly changeBoundsManager: ChangeBoundsManager;
@inject(ContainerManager) readonly containerManager: ContainerManager;
@inject(TYPES.IContainerManager) readonly containerManager: IContainerManager;
@inject(TYPES.IModelFactory) modelFactory: IModelFactory;

get id(): string {
Expand Down Expand Up @@ -95,7 +95,7 @@ export class NodeCreationTool extends BaseCreationTool<TriggerNodeCreationAction
}

export interface ContainerPositioningTool extends PositioningTool {
readonly containerManager: ContainerManager;
readonly containerManager: IContainerManager;
}

export class NodeInsertTrackingListener extends MouseTrackingElementPositionListener {
Expand Down
1 change: 1 addition & 0 deletions packages/glsp-sprotty/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ export const TYPES = {
IDiagramOptions: Symbol('IDiagramOptions'),
IDiagramStartup: Symbol('IDiagramStartup'),
IToolManager: Symbol('IToolManager'),
IContainerManager: Symbol('IContainerManager'),
Grid: Symbol('Grid')
};
Loading