Files
blockly/core/comments/workspace_comment.ts
2025-06-10 11:24:42 -07:00

248 lines
6.2 KiB
TypeScript

/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {CommentMove} from '../events/events_comment_move.js';
import {CommentResize} from '../events/events_comment_resize.js';
import {EventType} from '../events/type.js';
import * as eventUtils from '../events/utils.js';
import {Coordinate} from '../utils/coordinate.js';
import * as idGenerator from '../utils/idgenerator.js';
import {Size} from '../utils/size.js';
import {Workspace} from '../workspace.js';
import {CommentView} from './comment_view.js';
export class WorkspaceComment {
/** The unique identifier for this comment. */
public readonly id: string;
/** The text of the comment. */
private text = '';
/** The size of the comment in workspace units. */
private size: Size;
/** Whether the comment is collapsed or not. */
private collapsed = false;
/** Whether the comment is editable or not. */
private editable = true;
/** Whether the comment is movable or not. */
private movable = true;
/** Whether the comment is deletable or not. */
private deletable = true;
/** The location of the comment in workspace coordinates. */
protected location = new Coordinate(0, 0);
/** Whether this comment has been disposed or not. */
protected disposed = false;
/** Whether this comment is being disposed or not. */
protected disposing = false;
/**
* Constructs the comment.
*
* @param workspace The workspace to construct the comment in.
* @param id An optional ID to give to the comment. If not provided, one will
* be generated.
*/
constructor(
public readonly workspace: Workspace,
id?: string,
) {
this.id = id && !workspace.getCommentById(id) ? id : idGenerator.genUid();
this.size = CommentView.defaultCommentSize;
workspace.addTopComment(this);
this.fireCreateEvent();
}
private fireCreateEvent() {
if (eventUtils.isEnabled()) {
eventUtils.fire(new (eventUtils.get(EventType.COMMENT_CREATE))(this));
}
}
private fireDeleteEvent() {
if (eventUtils.isEnabled()) {
eventUtils.fire(new (eventUtils.get(EventType.COMMENT_DELETE))(this));
}
}
/** Fires a comment change event. */
private fireChangeEvent(oldText: string, newText: string) {
if (eventUtils.isEnabled()) {
eventUtils.fire(
new (eventUtils.get(EventType.COMMENT_CHANGE))(this, oldText, newText),
);
}
}
/** Fires a comment collapse event. */
private fireCollapseEvent(newCollapsed: boolean) {
if (eventUtils.isEnabled()) {
eventUtils.fire(
new (eventUtils.get(EventType.COMMENT_COLLAPSE))(this, newCollapsed),
);
}
}
/** Sets the text of the comment. */
setText(text: string) {
const oldText = this.text;
this.text = text;
this.fireChangeEvent(oldText, text);
}
/** Returns the text of the comment. */
getText(): string {
return this.text;
}
/** Sets the comment's size in workspace units. */
setSize(size: Size) {
const event = new (eventUtils.get(EventType.COMMENT_RESIZE))(
this,
) as CommentResize;
this.size = size;
event.recordCurrentSizeAsNewSize();
eventUtils.fire(event);
}
/** Returns the comment's size in workspace units. */
getSize(): Size {
return this.size;
}
/** Sets whether the comment is collapsed or not. */
setCollapsed(collapsed: boolean) {
this.collapsed = collapsed;
this.fireCollapseEvent(collapsed);
}
/** Returns whether the comment is collapsed or not. */
isCollapsed(): boolean {
return this.collapsed;
}
/** Sets whether the comment is editable or not. */
setEditable(editable: boolean) {
this.editable = editable;
}
/**
* Returns whether the comment is editable or not, respecting whether the
* workspace is read-only.
*/
isEditable(): boolean {
return this.isOwnEditable() && !this.workspace.isReadOnly();
}
/**
* Returns whether the comment is editable or not, only examining its own
* state and ignoring the state of the workspace.
*/
isOwnEditable(): boolean {
return this.editable;
}
/** Sets whether the comment is movable or not. */
setMovable(movable: boolean) {
this.movable = movable;
}
/**
* Returns whether the comment is movable or not, respecting whether the
* workspace is read-only.
*/
isMovable() {
return (
this.isOwnMovable() &&
!this.workspace.isReadOnly() &&
!this.workspace.isFlyout
);
}
/**
* Returns whether the comment is movable or not, only examining its own
* state and ignoring the state of the workspace.
*/
isOwnMovable() {
return this.movable;
}
/** Sets whether the comment is deletable or not. */
setDeletable(deletable: boolean) {
this.deletable = deletable;
}
/**
* Returns whether the comment is deletable or not, respecting whether the
* workspace is read-only.
*/
isDeletable(): boolean {
return (
this.isOwnDeletable() &&
!this.isDeadOrDying() &&
!this.workspace.isReadOnly() &&
!this.workspace.isFlyout
);
}
/**
* Returns whether the comment is deletable or not, only examining its own
* state and ignoring the state of the workspace.
*/
isOwnDeletable(): boolean {
return this.deletable;
}
/** Moves the comment to the given location in workspace coordinates. */
moveTo(location: Coordinate, reason?: string[] | undefined) {
const event = new (eventUtils.get(EventType.COMMENT_MOVE))(
this,
) as CommentMove;
if (reason) event.setReason(reason);
this.location = location;
event.recordNew();
eventUtils.fire(event);
}
/** Returns the position of the comment in workspace coordinates. */
getRelativeToSurfaceXY(): Coordinate {
return this.location;
}
/** Disposes of this comment. */
dispose() {
this.disposing = true;
this.fireDeleteEvent();
this.workspace.removeTopComment(this);
this.disposed = true;
}
/** Returns whether the comment has been disposed or not. */
isDisposed() {
return this.disposed;
}
/**
* Returns true if this comment view is currently being disposed or has
* already been disposed.
*/
isDeadOrDying(): boolean {
return this.disposing || this.disposed;
}
}