From 52abcff58d45c3b87952538c515ba5f63025705c Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Wed, 25 Sep 2024 15:02:16 -0700 Subject: [PATCH 1/4] feat: Add support for inflating flyout separators. --- core/flyout_separator.ts | 17 +++++++++++++++++ core/separator_flyout_inflater.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 core/flyout_separator.ts create mode 100644 core/separator_flyout_inflater.ts diff --git a/core/flyout_separator.ts b/core/flyout_separator.ts new file mode 100644 index 0000000000..27245d19f2 --- /dev/null +++ b/core/flyout_separator.ts @@ -0,0 +1,17 @@ +import type {IBoundedElement} from './interfaces/i_bounded_element.js'; +import {Rect} from './utils/rect.js'; + +export class FlyoutSeparator implements IBoundedElement { + private x = 0; + private y = 0; + constructor(private gap: number) {} + + getBoundingRectangle(): Rect { + return new Rect(this.y, this.y + this.gap, this.x, this.x + this.gap); + } + + moveBy(dx: number, dy: number, _reason?: string[]) { + this.x += dx; + this.y += dy; + } +} diff --git a/core/separator_flyout_inflater.ts b/core/separator_flyout_inflater.ts new file mode 100644 index 0000000000..e47f7ad80b --- /dev/null +++ b/core/separator_flyout_inflater.ts @@ -0,0 +1,28 @@ +import type {IFlyoutInflater} from './interfaces/i_flyout_inflater.js'; +import type {IBoundedElement} from './interfaces/i_bounded_element.js'; +import type {WorkspaceSvg} from './workspace_svg.js'; +import {FlyoutSeparator} from './flyout_separator.js'; +import type {SeparatorInfo} from './utils/toolbox.js'; +import * as registry from './registry.js'; + +export class SeparatorFlyoutInflater implements IFlyoutInflater { + load(_state: Object, _flyoutWorkspace: WorkspaceSvg): IBoundedElement { + // This empty separator will be substituted by the one created by the + // flyout based on the value returned from gapForElement(). + return new FlyoutSeparator(0); + } + + gapForElement(state: Object, defaultGap: number): number { + const separatorState = state as SeparatorInfo; + const newGap = parseInt(String(separatorState['gap'])); + return newGap ?? defaultGap; + } + + disposeElement(_element: IBoundedElement): void {} +} + +registry.register( + registry.Type.FLYOUT_INFLATER, + 'sep', + SeparatorFlyoutInflater, +); From 5aa35d89b2a24c4c7b2ea56b66ec0e9836fb2897 Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Fri, 27 Sep 2024 12:34:46 -0700 Subject: [PATCH 2/4] chore: Add license. --- core/flyout_separator.ts | 6 ++++++ core/separator_flyout_inflater.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/core/flyout_separator.ts b/core/flyout_separator.ts index 27245d19f2..64ec0b6f5d 100644 --- a/core/flyout_separator.ts +++ b/core/flyout_separator.ts @@ -1,3 +1,9 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + import type {IBoundedElement} from './interfaces/i_bounded_element.js'; import {Rect} from './utils/rect.js'; diff --git a/core/separator_flyout_inflater.ts b/core/separator_flyout_inflater.ts index e47f7ad80b..a5c41b53ec 100644 --- a/core/separator_flyout_inflater.ts +++ b/core/separator_flyout_inflater.ts @@ -1,3 +1,9 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + import type {IFlyoutInflater} from './interfaces/i_flyout_inflater.js'; import type {IBoundedElement} from './interfaces/i_bounded_element.js'; import type {WorkspaceSvg} from './workspace_svg.js'; From 85bfc1541f4dd3ee48097acf552a805cd44f271d Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Fri, 27 Sep 2024 12:56:31 -0700 Subject: [PATCH 3/4] chore: Add TSDoc. --- core/flyout_separator.ts | 21 +++++++++++++++++++ core/separator_flyout_inflater.ts | 35 +++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/core/flyout_separator.ts b/core/flyout_separator.ts index 64ec0b6f5d..a2d392b3e0 100644 --- a/core/flyout_separator.ts +++ b/core/flyout_separator.ts @@ -7,15 +7,36 @@ import type {IBoundedElement} from './interfaces/i_bounded_element.js'; import {Rect} from './utils/rect.js'; +/** + * Representation of a gap between elements in a flyout. + */ export class FlyoutSeparator implements IBoundedElement { private x = 0; private y = 0; + + /** + * Creates a new separator. + * + * @param gap The amount of space this separator should occupy. + */ constructor(private gap: number) {} + /** + * Returns the bounding box of this separator. + * + * @returns The bounding box of this separator. + */ getBoundingRectangle(): Rect { return new Rect(this.y, this.y + this.gap, this.x, this.x + this.gap); } + /** + * Repositions this separator. + * + * @param dx The distance to move this separator on the X axis. + * @param dy The distance to move this separator on the Y axis. + * @param _reason The reason this move was initiated. + */ moveBy(dx: number, dy: number, _reason?: string[]) { this.x += dx; this.y += dy; diff --git a/core/separator_flyout_inflater.ts b/core/separator_flyout_inflater.ts index a5c41b53ec..55ec913516 100644 --- a/core/separator_flyout_inflater.ts +++ b/core/separator_flyout_inflater.ts @@ -11,19 +11,50 @@ import {FlyoutSeparator} from './flyout_separator.js'; import type {SeparatorInfo} from './utils/toolbox.js'; import * as registry from './registry.js'; +/** + * Class responsible for creating separators for flyouts. + */ export class SeparatorFlyoutInflater implements IFlyoutInflater { + /** + * Inflates a dummy flyout separator. + * + * The flyout automatically creates separators between every element with a + * size determined by calling gapForElement on the relevant inflater. + * Additionally, users can explicitly add separators in the flyout definition. + * When separators (implicitly or explicitly created) follow one another, the + * gap of the last one propagates backwards and flattens to one separator. + * This flattening is not additive; if there are initially separators of 2, 3, + * and 4 pixels, after normalization there will be one separator of 4 pixels. + * Therefore, this method returns a zero-width separator, which will be + * replaced by the one implicitly created by the flyout based on the value + * returned by gapForElement, which knows the default gap, unlike this method. + * + * @param _state A JSON representation of a flyout separator. + * @param _flyoutWorkspace The workspace the separator belongs to. + * @returns A newly created FlyoutSeparator. + */ load(_state: Object, _flyoutWorkspace: WorkspaceSvg): IBoundedElement { - // This empty separator will be substituted by the one created by the - // flyout based on the value returned from gapForElement(). return new FlyoutSeparator(0); } + /** + * Returns the size of the separator. See `load` for more details. + * + * @param state A JSON representation of a flyout separator. + * @param defaultGap The default spacing for flyout items. + * @returns The desired size of the separator. + */ gapForElement(state: Object, defaultGap: number): number { const separatorState = state as SeparatorInfo; const newGap = parseInt(String(separatorState['gap'])); return newGap ?? defaultGap; } + /** + * Disposes of the given separator. Intentional no-op. + * + * @param _element The flyout separator to dispose of. + */ disposeElement(_element: IBoundedElement): void {} } From 4a5baabeab180213746d144157b8fb6c4b91726e Mon Sep 17 00:00:00 2001 From: Aaron Dodson Date: Fri, 27 Sep 2024 13:17:35 -0700 Subject: [PATCH 4/4] refactor: Allow specifying an axis for flyout separators. --- core/flyout_separator.ts | 21 +++++++++++++++++++-- core/separator_flyout_inflater.ts | 12 ++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/core/flyout_separator.ts b/core/flyout_separator.ts index a2d392b3e0..733371007a 100644 --- a/core/flyout_separator.ts +++ b/core/flyout_separator.ts @@ -18,8 +18,12 @@ export class FlyoutSeparator implements IBoundedElement { * Creates a new separator. * * @param gap The amount of space this separator should occupy. + * @param axis The axis along which this separator occupies space. */ - constructor(private gap: number) {} + constructor( + private gap: number, + private axis: SeparatorAxis, + ) {} /** * Returns the bounding box of this separator. @@ -27,7 +31,12 @@ export class FlyoutSeparator implements IBoundedElement { * @returns The bounding box of this separator. */ getBoundingRectangle(): Rect { - return new Rect(this.y, this.y + this.gap, this.x, this.x + this.gap); + switch (this.axis) { + case SeparatorAxis.X: + return new Rect(this.y, this.y, this.x, this.x + this.gap); + case SeparatorAxis.Y: + return new Rect(this.y, this.y + this.gap, this.x, this.x); + } } /** @@ -42,3 +51,11 @@ export class FlyoutSeparator implements IBoundedElement { this.y += dy; } } + +/** + * Representation of an axis along which a separator occupies space. + */ +export const enum SeparatorAxis { + X = 'x', + Y = 'y', +} diff --git a/core/separator_flyout_inflater.ts b/core/separator_flyout_inflater.ts index 55ec913516..5ed02aeb97 100644 --- a/core/separator_flyout_inflater.ts +++ b/core/separator_flyout_inflater.ts @@ -7,7 +7,7 @@ import type {IFlyoutInflater} from './interfaces/i_flyout_inflater.js'; import type {IBoundedElement} from './interfaces/i_bounded_element.js'; import type {WorkspaceSvg} from './workspace_svg.js'; -import {FlyoutSeparator} from './flyout_separator.js'; +import {FlyoutSeparator, SeparatorAxis} from './flyout_separator.js'; import type {SeparatorInfo} from './utils/toolbox.js'; import * as registry from './registry.js'; @@ -30,11 +30,15 @@ export class SeparatorFlyoutInflater implements IFlyoutInflater { * returned by gapForElement, which knows the default gap, unlike this method. * * @param _state A JSON representation of a flyout separator. - * @param _flyoutWorkspace The workspace the separator belongs to. + * @param flyoutWorkspace The workspace the separator belongs to. * @returns A newly created FlyoutSeparator. */ - load(_state: Object, _flyoutWorkspace: WorkspaceSvg): IBoundedElement { - return new FlyoutSeparator(0); + load(_state: Object, flyoutWorkspace: WorkspaceSvg): IBoundedElement { + const flyoutAxis = flyoutWorkspace.targetWorkspace?.getFlyout() + ?.horizontalLayout + ? SeparatorAxis.X + : SeparatorAxis.Y; + return new FlyoutSeparator(0, flyoutAxis); } /**