diff --git a/core/field.ts b/core/field.ts index 43558ce4090..391ab5ac84a 100644 --- a/core/field.ts +++ b/core/field.ts @@ -42,6 +42,7 @@ import * as userAgent from './utils/useragent.js'; import * as utilsXml from './utils/xml.js'; import * as WidgetDiv from './widgetdiv.js'; import type {WorkspaceSvg} from './workspace_svg.js'; +import {ISerializable} from './interfaces/i_serializable.js'; /** * A function that is called to validate changes to the field's value before @@ -68,7 +69,7 @@ export type FieldValidator = (newValue: T) => T|null|undefined; export abstract class Field implements IASTNodeLocationSvg, IASTNodeLocationWithBlock, IKeyboardAccessible, - IRegistrable { + IRegistrable, ISerializable { /** * To overwrite the default value which is set in **Field**, directly update * the prototype. diff --git a/core/interfaces/i_has_bubble.ts b/core/interfaces/i_has_bubble.ts new file mode 100644 index 00000000000..f82f1a9dccb --- /dev/null +++ b/core/interfaces/i_has_bubble.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + + +export interface IHasBubble { + /** @return True if the bubble is currently open, false otherwise. */ + isBubbleVisible(): boolean; + + /** Sets whether the bubble is open or not. */ + setBubbleVisible(visible: boolean): void; +} + +/** Type guard that checks whether the given object is a IHasBubble. */ +export function hasBubble(obj: any): obj is IHasBubble { + return obj.isBubbleVisible !== undefined && + obj.setBubbleVisible !== undefined; +} diff --git a/core/interfaces/i_icon.ts b/core/interfaces/i_icon.ts new file mode 100644 index 00000000000..fa7dc345d20 --- /dev/null +++ b/core/interfaces/i_icon.ts @@ -0,0 +1,92 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type {Coordinate} from '../utils/coordinate.js'; +import type {Size} from '../utils/size.js'; + + +export interface IIcon { + /** + * @return the string representing the type of the icon. + * E.g. 'comment', 'warning', etc. This string should also be used when + * registering the icon class. + */ + getType(): string; + + /** + * Creates the SVG elements for the icon that will live on the block. + * + * @param pointerdownListener An event listener that must be attached to the + * root SVG element by the implementation of `initView`. Used by Blockly's + * gesture system to properly handle clicks and drags. + */ + initView(pointerdownListener: (e: PointerEvent) => void): void; + + /** + * Disposes of any elements of the icon. + * + * @remarks + * + * In particular, if this icon is currently showing a bubble, this should be + * used to hide it. + */ + dispose(): void; + + /** + * @return the "weight" of the icon, which determines the static order which + * icons should be rendered in. More positive numbers are rendered farther + * toward the end of the block. + */ + getWeight(): number; + + /** @return The dimensions of the icon for use in rendering. */ + getSize(): Size; + + /** Notifies the icon that the block's colour has changed. */ + applyColour(): void; + + /** Notifies the icon that the block's editability has changed. */ + updateEditable(): void; + + /** Notifies the icon that the block's collapsed-ness has changed. */ + updateCollapsed(): void; + + /** + * @return Whether this icon is shown when the block is collapsed. Used + * to allow renderers to account for padding. + */ + isShownWhenCollapsed(): boolean; + + /** + * Notifies the icon where it is relative to its block's top-start, in + * workspace units. + */ + setOffsetInBlock(offset: Coordinate): void; + + /** + * Notifies the icon that it has changed locations. + * + * @param blockOrigin The location of this icon's block's top-start corner + * in workspace coordinates. + */ + onLocationChange(blockOrigin: Coordinate): void; + + /** + * Notifies the icon that it has been clicked. + */ + onClick(): void; +} + +/** Type guard that checks whether the given object is an IIcon. */ +export function isIcon(obj: any): obj is IIcon { + return obj.getType !== undefined && obj.initView !== undefined && + obj.dispose !== undefined && obj.getWeight !== undefined && + obj.getSize !== undefined && obj.applyColour !== undefined && + obj.updateEditable !== undefined && obj.updateCollapsed !== undefined && + obj.isShownWhenCollapsed !== undefined && + obj.setOffsetInBlock !== undefined && + obj.onLocationChange !== undefined && obj.onClick !== undefined; +} diff --git a/core/interfaces/i_serializable.ts b/core/interfaces/i_serializable.ts new file mode 100644 index 00000000000..ad65cb3f026 --- /dev/null +++ b/core/interfaces/i_serializable.ts @@ -0,0 +1,29 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + + +export interface ISerializable { + /** + * @param doFullSerialization If true, this signals that any backing data + * structures used by this ISerializable should also be serialized. This + * is used for copy-paste. + * @return a JSON serializable value that records the icon's state. + */ + saveState(doFullSerialization: boolean): any; + + /** + * Takes in a JSON serializable value and sets the ISerializable's state + * based on that. + * + * @param state The state to apply to the ISerializable. + */ + loadState(state: any): void; +} + +/** Type guard that checks whether the given object is a ISerializable. */ +export function isSerializable(obj: any): obj is ISerializable { + return obj.saveState !== undefined && obj.loadState !== undefined; +}