diff --git a/core/comments/rendered_workspace_comment.ts b/core/comments/rendered_workspace_comment.ts index 87a52f9e7..f69662a3e 100644 --- a/core/comments/rendered_workspace_comment.ts +++ b/core/comments/rendered_workspace_comment.ts @@ -12,18 +12,27 @@ import {Rect} from '../utils/rect.js'; import {Size} from '../utils/size.js'; import {IBoundedElement} from '../interfaces/i_bounded_element.js'; import {IRenderedElement} from '../interfaces/i_rendered_element.js'; +import * as dom from '../utils/dom.js'; +import {IDraggable} from '../interfaces/i_draggable.js'; +import {CommentDragStrategy} from '../dragging/comment_drag_strategy.js'; export class RenderedWorkspaceComment extends WorkspaceComment - implements IBoundedElement, IRenderedElement + implements IBoundedElement, IRenderedElement, IDraggable { /** The class encompassing the svg elements making up the workspace comment. */ private view: CommentView; + public readonly workspace: WorkspaceSvg; + + private dragStrategy = new CommentDragStrategy(this); + /** Constructs the workspace comment, including the view. */ constructor(workspace: WorkspaceSvg, id?: string) { super(workspace, id); + this.workspace = workspace; + this.view = new CommentView(workspace); // Set the size to the default size as defined in the superclass. this.view.setSize(this.getSize()); @@ -105,10 +114,58 @@ export class RenderedWorkspaceComment this.view.moveTo(location); } + /** + * Moves the comment during a drag. Doesn't fire move events. + * + * @internal + */ + moveDuringDrag(location: Coordinate): void { + this.location = location; + this.view.moveTo(location); + } + + /** + * Adds the dragging CSS class to this comment. + * + * @internal + */ + setDragging(dragging: boolean): void { + if (dragging) { + dom.addClass(this.getSvgRoot(), 'blocklyDragging'); + } else { + dom.removeClass(this.getSvgRoot(), 'blocklyDragging'); + } + } + /** Disposes of the view. */ override dispose() { this.disposing = true; if (!this.view.isDeadOrDying()) this.view.dispose(); super.dispose(); } + + /** Returns whether this comment is movable or not. */ + isMovable(): boolean { + return this.dragStrategy.isMovable(); + } + + /** Starts a drag on the comment. */ + startDrag(): void { + this.dragStrategy.startDrag(); + } + + /** Drags the comment to the given location. */ + drag(newLoc: Coordinate): void { + this.dragStrategy.drag(newLoc); + } + + /** Ends the drag on the comment. */ + endDrag(): void { + this.dragStrategy.endDrag(); + } + + /** Moves the comment back to where it was at the start of a drag. */ + revertDrag(): void { + this.dragStrategy.revertDrag(); + } } diff --git a/core/comments/workspace_comment.ts b/core/comments/workspace_comment.ts index 91b5cda38..b09d519cd 100644 --- a/core/comments/workspace_comment.ts +++ b/core/comments/workspace_comment.ts @@ -32,7 +32,7 @@ export class WorkspaceComment { private deletable = true; /** The location of the comment in workspace coordinates. */ - private location = new Coordinate(0, 0); + protected location = new Coordinate(0, 0); /** Whether this comment has been disposed or not. */ protected disposed = false; diff --git a/core/dragging/comment_drag_strategy.ts b/core/dragging/comment_drag_strategy.ts new file mode 100644 index 000000000..f030e1488 --- /dev/null +++ b/core/dragging/comment_drag_strategy.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import {IDragStrategy} from '../interfaces/i_draggable.js'; +import {Coordinate} from '../utils.js'; +import * as eventUtils from '../events/utils.js'; +import * as layers from '../layers.js'; +import {RenderedWorkspaceComment} from '../comments.js'; +import {WorkspaceSvg} from '../workspace_svg.js'; + +export class CommentDragStrategy implements IDragStrategy { + private startLoc: Coordinate | null = null; + + private workspace: WorkspaceSvg; + + constructor(private comment: RenderedWorkspaceComment) { + this.workspace = comment.workspace; + } + + isMovable(): boolean { + return this.comment.isOwnMovable() && !this.workspace.options.readOnly; + } + + startDrag(): void { + if (!eventUtils.getGroup()) { + eventUtils.setGroup(true); + } + this.startLoc = this.comment.getRelativeToSurfaceXY(); + this.workspace.setResizesEnabled(false); + this.workspace.getLayerManager()?.moveToDragLayer(this.comment); + this.comment.setDragging(true); + } + + drag(newLoc: Coordinate): void { + this.comment.moveDuringDrag(newLoc); + } + + endDrag(): void { + this.workspace.setResizesEnabled(true); + eventUtils.setGroup(false); + + this.workspace + .getLayerManager() + ?.moveOffDragLayer(this.comment, layers.BLOCK); + this.comment.setDragging(false); + } + + revertDrag(): void { + if (this.startLoc) this.comment.moveDuringDrag(this.startLoc); + } +}