Files
blockly/core/comments/workspace_comment.ts
Christopher Allen 7ccdcc5cef refactor(events): introduce EventType enum in separate module (#8530)
* refactor(events): Use "export ... from" where applicable

* refactor(events): Introduce EventType enum

  Introduce an enum for the event .type values.  We can't actually
  use it as the type of the .type property on Abstract events,
  because we want to allow developers to add their own custom
  event types inheriting from this type, but at least this way we
  can be reasonably sure that all of our own event subclasses have
  distinct .type values—plus consistent use of enum syntax
  (EventType.TYPE_NAME) is probably good for readability overall.

  Put it in a separate module from the rest of events/utils.ts
  because it would be helpful if event utils could use

      event instanceof SomeEventType

  for type narrowing but but at the moment most events are in
  modules that depend on events/utils.ts for their .type
  constant, and although circular ESM dependencies should work
  in principle there are various restrictions and this
  particular circularity causes issues at the moment.

  A few of the event classes also depend on utils.ts for fire()
  or other functions, which will be harder to deal with, but at
  least this commit is win in terms of reducing the complexity
  of our dependencies, making most of the Abstract event subclass
  module dependent on type.ts, which has no imports, rather than
  on utils.ts which has multiple imports.
2024-08-20 08:36:33 +01:00

237 lines
6.0 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';
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 = new Size(120, 100);
/** 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();
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.options.readOnly;
}
/**
* 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.options.readOnly;
}
/**
* 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.workspace.options.readOnly;
}
/**
* 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;
}
}