From 4e439660c8890826008b2119768531d0867b864c Mon Sep 17 00:00:00 2001 From: Louis Poirier Date: Thu, 31 Oct 2024 01:54:58 +0100 Subject: [PATCH] refactor: added fields to widgets. Define properties of widgets as fields. WIP. Closes #47 --- src/app/formatters/lua-sol2.formatter.ts | 4 +- src/app/models/fields/array.field.ts | 11 ++ src/app/models/fields/bool.field.ts | 11 ++ src/app/models/fields/color.field.ts | 12 ++ src/app/models/fields/enum.field.ts | 22 +++ src/app/models/fields/field.ts | 65 +++++++ src/app/models/fields/flags.field.ts | 11 ++ src/app/models/fields/float.field.ts | 11 ++ src/app/models/fields/integer.field.ts | 11 ++ src/app/models/fields/number.field.ts | 11 ++ src/app/models/fields/size.field.ts | 12 ++ src/app/models/fields/string.field.ts | 11 ++ src/app/models/object.ts | 8 + src/app/models/widgets/bloc-for.widget.ts | 4 +- src/app/models/widgets/button.widget.ts | 27 ++- src/app/models/widgets/checkbox.widget.ts | 10 +- src/app/models/widgets/child-window.widget.ts | 16 +- .../widgets/collapsing-header.widget.ts | 8 +- src/app/models/widgets/combo.widget.ts | 14 +- src/app/models/widgets/dummy.widget.ts | 10 +- .../models/widgets/input-color-edit.widget.ts | 18 +- src/app/models/widgets/input-number.widget.ts | 58 +++--- src/app/models/widgets/input-text.widget.ts | 20 +- .../models/widgets/input-textarea.widget.ts | 22 +-- src/app/models/widgets/label.widget.ts | 10 +- src/app/models/widgets/listbox.widget.ts | 18 +- src/app/models/widgets/menu-item.widget.ts | 18 +- src/app/models/widgets/menu.widget.ts | 8 +- src/app/models/widgets/modal.widget.ts | 12 +- src/app/models/widgets/plot.widget.ts | 51 +++--- src/app/models/widgets/popup.widget.ts | 14 +- src/app/models/widgets/progress-bar.widget.ts | 12 +- src/app/models/widgets/text.widget.ts | 24 +-- src/app/models/widgets/widget.ts | 171 +++++++++++++++++- src/app/models/widgets/window.widget.ts | 24 ++- .../editor/fields/abstract-field.component.ts | 73 ++++++++ .../bool-field/bool-field.component.css | 0 .../bool-field/bool-field.component.html | 5 + .../bool-field/bool-field.component.spec.ts | 23 +++ .../fields/bool-field/bool-field.component.ts | 25 +++ .../enum-field/enum-field.component.css | 0 .../enum-field/enum-field.component.html | 8 + .../enum-field/enum-field.component.spec.ts | 23 +++ .../fields/enum-field/enum-field.component.ts | 32 ++++ .../string-field/string-field.component.css | 0 .../string-field/string-field.component.html | 6 + .../string-field.component.spec.ts | 23 +++ .../string-field/string-field.component.ts | 33 ++++ .../abstract-properties.component.ts | 47 ++++- .../button-properties.component.html | 38 +--- .../button-properties.component.ts | 65 +------ .../checkbox-properties.component.html | 22 +-- .../checkbox-properties.component.ts | 48 +---- .../input-number-properties.component.html | 26 +-- .../input-number-properties.component.ts | 45 +++-- .../menu-item-properties.component.html | 12 +- .../plot-properties.component.html | 46 ++--- .../popup-properties.component.html | 10 +- .../progress-bar-properties.component.html | 14 +- 59 files changed, 978 insertions(+), 415 deletions(-) create mode 100644 src/app/models/fields/array.field.ts create mode 100644 src/app/models/fields/bool.field.ts create mode 100644 src/app/models/fields/color.field.ts create mode 100644 src/app/models/fields/enum.field.ts create mode 100644 src/app/models/fields/field.ts create mode 100644 src/app/models/fields/flags.field.ts create mode 100644 src/app/models/fields/float.field.ts create mode 100644 src/app/models/fields/integer.field.ts create mode 100644 src/app/models/fields/number.field.ts create mode 100644 src/app/models/fields/size.field.ts create mode 100644 src/app/models/fields/string.field.ts create mode 100644 src/app/models/object.ts create mode 100644 src/app/pages/editor/fields/abstract-field.component.ts create mode 100644 src/app/pages/editor/fields/bool-field/bool-field.component.css create mode 100644 src/app/pages/editor/fields/bool-field/bool-field.component.html create mode 100644 src/app/pages/editor/fields/bool-field/bool-field.component.spec.ts create mode 100644 src/app/pages/editor/fields/bool-field/bool-field.component.ts create mode 100644 src/app/pages/editor/fields/enum-field/enum-field.component.css create mode 100644 src/app/pages/editor/fields/enum-field/enum-field.component.html create mode 100644 src/app/pages/editor/fields/enum-field/enum-field.component.spec.ts create mode 100644 src/app/pages/editor/fields/enum-field/enum-field.component.ts create mode 100644 src/app/pages/editor/fields/string-field/string-field.component.css create mode 100644 src/app/pages/editor/fields/string-field/string-field.component.html create mode 100644 src/app/pages/editor/fields/string-field/string-field.component.spec.ts create mode 100644 src/app/pages/editor/fields/string-field/string-field.component.ts diff --git a/src/app/formatters/lua-sol2.formatter.ts b/src/app/formatters/lua-sol2.formatter.ts index 86ed33b..e6663e8 100644 --- a/src/app/formatters/lua-sol2.formatter.ts +++ b/src/app/formatters/lua-sol2.formatter.ts @@ -594,8 +594,8 @@ export class FIGLuaSol2Formatter extends FIGFormatter { const isInteger: boolean = FIGInputNumberWidget.isInteger(widget.dataType); let value: string; - if (size === 0) { - const number: number = widget.value as number; + if (size === 1) { + const number: number = widget.value[0]; value = isInteger ? number.toString() : number.toFixed(precision); } else { diff --git a/src/app/models/fields/array.field.ts b/src/app/models/fields/array.field.ts new file mode 100644 index 0000000..7f418e6 --- /dev/null +++ b/src/app/models/fields/array.field.ts @@ -0,0 +1,11 @@ +import {Field, FieldType} from "./field"; + +export class ArrayField extends Field { + constructor(name: string, + label: string, + value?: unknown[], + isOptional: boolean = false, + defaultValue?: unknown[]) { + super(FieldType.array, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/fields/bool.field.ts b/src/app/models/fields/bool.field.ts new file mode 100644 index 0000000..506ef92 --- /dev/null +++ b/src/app/models/fields/bool.field.ts @@ -0,0 +1,11 @@ +import {Field, FieldType} from "./field"; + +export class BoolField extends Field { + constructor(name: string, + label: string, + value?: boolean, + isOptional: boolean = false, + defaultValue?: boolean) { + super(FieldType.bool, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/fields/color.field.ts b/src/app/models/fields/color.field.ts new file mode 100644 index 0000000..ed54d0f --- /dev/null +++ b/src/app/models/fields/color.field.ts @@ -0,0 +1,12 @@ +import {Field, FieldType} from "./field"; +import {Color} from "../math"; + +export class ColorField extends Field { + constructor(name: string, + label: string, + value?: Color, + isOptional: boolean = false, + defaultValue?: Color) { + super(FieldType.color, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/fields/enum.field.ts b/src/app/models/fields/enum.field.ts new file mode 100644 index 0000000..5fd559f --- /dev/null +++ b/src/app/models/fields/enum.field.ts @@ -0,0 +1,22 @@ +import {Field, FieldType} from "./field"; + +export type EnumFieldType = string | number; + +export interface EnumOption { + readonly value: number; + readonly label: string; +} + +export class EnumField extends Field { + public readonly options: EnumOption[]; + + constructor(name: string, + label: string, + options: EnumOption[], + value?: T, + isOptional: boolean = false, + defaultValue?: T) { + super(FieldType.enum, name, label, value, isOptional, defaultValue); + this.options = options; + } +} diff --git a/src/app/models/fields/field.ts b/src/app/models/fields/field.ts new file mode 100644 index 0000000..1401e81 --- /dev/null +++ b/src/app/models/fields/field.ts @@ -0,0 +1,65 @@ +export enum FieldType { + bool, + integer, + float, + number, + string, + array, + size, + flags, + color, + enum, +} + +export type FieldCallback = (value: unknown) => void; + +export class Field { + readonly type: FieldType; + readonly name: string; + readonly label: string; + readonly isOptional: boolean; + + value?: T; + defaultValue?: T; + + private readonly listeners: FieldCallback[]; + + protected constructor(type: FieldType, + name: string, + label: string, + value?: T, + isOptional: boolean = false, + defaultValue?: T) { + this.type = type; + this.name = name; + this.label = label; + this.value = value; + this.isOptional = isOptional; + this.defaultValue = defaultValue; + + this.listeners = []; + } + + get isRequired(): boolean { + return !this.isOptional; + } + + public addListener(fn: FieldCallback): void { + this.listeners.push(fn); + } + + public removeListener(fn: FieldCallback): void { + const index: number = this.listeners.findIndex((listener) => listener === fn); + + if (index !== -1) { + this.listeners.splice(index, 1); + } + } + + public emit(): void { + for (const listener of this.listeners) { + listener(this.value); + } + } + +} diff --git a/src/app/models/fields/flags.field.ts b/src/app/models/fields/flags.field.ts new file mode 100644 index 0000000..6286e88 --- /dev/null +++ b/src/app/models/fields/flags.field.ts @@ -0,0 +1,11 @@ +import {Field, FieldType} from "./field"; + +export class FlagsField extends Field { + constructor(name: string, + label: string, + value?: number, + isOptional: boolean = false, + defaultValue?: number) { + super(FieldType.flags, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/fields/float.field.ts b/src/app/models/fields/float.field.ts new file mode 100644 index 0000000..b6e0830 --- /dev/null +++ b/src/app/models/fields/float.field.ts @@ -0,0 +1,11 @@ +import {Field, FieldType} from "./field"; + +export class FloatField extends Field { + constructor(name: string, + label: string, + value?: number, + isOptional: boolean = false, + defaultValue?: number) { + super(FieldType.float, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/fields/integer.field.ts b/src/app/models/fields/integer.field.ts new file mode 100644 index 0000000..bd8fb87 --- /dev/null +++ b/src/app/models/fields/integer.field.ts @@ -0,0 +1,11 @@ +import {Field, FieldType} from "./field"; + +export class IntegerField extends Field { + constructor(name: string, + label: string, + value?: number, + isOptional: boolean = false, + defaultValue?: number) { + super(FieldType.integer, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/fields/number.field.ts b/src/app/models/fields/number.field.ts new file mode 100644 index 0000000..31386a6 --- /dev/null +++ b/src/app/models/fields/number.field.ts @@ -0,0 +1,11 @@ +import {Field, FieldType} from "./field"; + +export class NumberField extends Field { + constructor(name: string, + label: string, + value?: number, + isOptional: boolean = false, + defaultValue?: number) { + super(FieldType.number, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/fields/size.field.ts b/src/app/models/fields/size.field.ts new file mode 100644 index 0000000..43d66e8 --- /dev/null +++ b/src/app/models/fields/size.field.ts @@ -0,0 +1,12 @@ +import {Field, FieldType} from "./field"; +import {Size} from "../math"; + +export class SizeField extends Field { + constructor(name: string, + label: string, + value?: Size, + isOptional: boolean = false, + defaultValue?: Size) { + super(FieldType.size, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/fields/string.field.ts b/src/app/models/fields/string.field.ts new file mode 100644 index 0000000..3b13d86 --- /dev/null +++ b/src/app/models/fields/string.field.ts @@ -0,0 +1,11 @@ +import {Field, FieldType} from "./field"; + +export class StringField extends Field { + constructor(name: string, + label: string, + value?: string, + isOptional: boolean = false, + defaultValue?: string) { + super(FieldType.string, name, label, value, isOptional, defaultValue); + } +} diff --git a/src/app/models/object.ts b/src/app/models/object.ts new file mode 100644 index 0000000..01ce4a0 --- /dev/null +++ b/src/app/models/object.ts @@ -0,0 +1,8 @@ +export function hasFunction(obj: object | null, fnName: string): boolean { + while ((obj = Reflect.getPrototypeOf(obj as object)) !== null) { + if (Reflect.ownKeys(obj).find((key) => key === fnName)) { + return true; + } + } + return false; +} diff --git a/src/app/models/widgets/bloc-for.widget.ts b/src/app/models/widgets/bloc-for.widget.ts index 7854904..5a5b9ea 100644 --- a/src/app/models/widgets/bloc-for.widget.ts +++ b/src/app/models/widgets/bloc-for.widget.ts @@ -11,11 +11,11 @@ export class FIGBlocForWidget extends FIGContainer { {name: 'size', optional: true, default: 10} ]; - size: number; + size: number = 10; constructor(options?: FIGBlocForOptions) { super(FIGWidgetType.blocFor, true); - this.size = options?.size ?? 10; + this.registerInteger('size', 'Size', options?.size, true, 10); this._focusOffset.y = 0; } diff --git a/src/app/models/widgets/button.widget.ts b/src/app/models/widgets/button.widget.ts index ac0f680..9ab1a46 100644 --- a/src/app/models/widgets/button.widget.ts +++ b/src/app/models/widgets/button.widget.ts @@ -2,6 +2,7 @@ import {FIGWidgetType} from "./widget"; import {FIGTooltipOption, FIGWithTooltip} from "./with-tooltip.widget"; import {Vector2} from "../math"; import {FIGSerializeProperty} from "../../parsers/document.parser"; +import {EnumOption} from "../fields/enum.field"; export enum FIGDir { left, @@ -11,6 +12,14 @@ export enum FIGDir { none = -1 } +export const FIGDirOptions: EnumOption[] = [ + {value: FIGDir.none, label: 'None'}, + {value: FIGDir.left, label: 'Left'}, + {value: FIGDir.right, label: 'Right'}, + {value: FIGDir.up, label: 'Up'}, + {value: FIGDir.down, label: 'Down'}, +]; + export interface FIGButtonOptions extends FIGTooltipOption { readonly label?: string; readonly isFill?: boolean; @@ -27,18 +36,18 @@ export class FIGButtonWidget extends FIGWithTooltip { {name: 'tooltip', optional: true, default: undefined}, ]; - label: string; - isFill: boolean; - isSmall: boolean; - arrow: FIGDir; + label: string = 'Button'; + isFill: boolean = false; + isSmall: boolean = false; + arrow: FIGDir = FIGDir.none; constructor(options?: FIGButtonOptions) { super(FIGWidgetType.button, true); - this.label = options?.label ?? 'Button'; - this.isFill = options?.isFill ?? false; - this.isSmall = options?.isSmall ?? false; - this.arrow = options?.arrow ?? FIGDir.none; - this.tooltip = options?.tooltip; + this.registerString('label', 'Label', options?.label ?? 'Button'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerBool('isFill', 'Fill', options?.isFill, true, false); + this.registerBool('isSmall', 'Small', options?.isSmall, true, false); + this.registerEnum('arrow', 'Arrow', FIGDirOptions, options?.arrow, true, FIGDir.none); } public get name(): string { diff --git a/src/app/models/widgets/checkbox.widget.ts b/src/app/models/widgets/checkbox.widget.ts index 2431f39..5967a5e 100644 --- a/src/app/models/widgets/checkbox.widget.ts +++ b/src/app/models/widgets/checkbox.widget.ts @@ -14,14 +14,14 @@ export class FIGCheckboxWidget extends FIGWithTooltip { {name: 'tooltip', optional: true, default: undefined}, ]; - label: string; - isChecked: boolean; + label: string = 'Checkbox'; + isChecked: boolean = false; constructor(options?: FIGCheckboxOptions) { super(FIGWidgetType.checkbox, true); - this.label = options?.label ?? 'Checkbox'; - this.isChecked = options?.isChecked ?? false; - this.tooltip = options?.tooltip; + this.registerString('label', 'Label', options?.label ?? 'Checkbox'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerBool('isChecked', 'Checked', options?.isChecked, true, false); } public get name(): string { diff --git a/src/app/models/widgets/child-window.widget.ts b/src/app/models/widgets/child-window.widget.ts index 698f4b9..9bcfbc2 100644 --- a/src/app/models/widgets/child-window.widget.ts +++ b/src/app/models/widgets/child-window.widget.ts @@ -21,17 +21,17 @@ export class FIGChildWindowWidget extends FIGContainer { {name: 'flags', optional: true, default: 0}, ]; - label: string; - size: Size; - frameBorder: boolean; - flags: number; + label: string = 'Child Window'; + size: Size = {width: 0, height: 0}; + frameBorder: boolean = true; + flags: number = 0; constructor(options?: FIGChildWindowOptions) { super(FIGWidgetType.childWindow, true); - this.label = options?.label ?? 'Child Window'; - this.size = options?.size ?? {width: 0, height: 0}; - this.frameBorder = options?.frameBorder ?? true; - this.flags = options?.flags ?? 0; + this.registerString('label', 'Label', options?.label ?? 'Child Window'); + this.registerSize('size', 'Size', options?.size ?? {width: 0, height: 0}); + this.registerBool('frameBorder', 'Show frame border', options?.frameBorder, true, true); + this.registerFlags('flags', 'Flags', options?.flags, true, 0); } public get name(): string { diff --git a/src/app/models/widgets/collapsing-header.widget.ts b/src/app/models/widgets/collapsing-header.widget.ts index d4f2c22..1040a0a 100644 --- a/src/app/models/widgets/collapsing-header.widget.ts +++ b/src/app/models/widgets/collapsing-header.widget.ts @@ -13,13 +13,13 @@ export class FIGCollapsingHeaderWidget extends FIGContainer { {name: 'flags', optional: true, default: 0}, ]; - label: string; - flags: number; + label: string = 'Header'; + flags: number = 0; constructor(options?: FIGCollapsingHeaderOptions) { super(FIGWidgetType.collapsingHeader, true); - this.label = options?.label ?? 'Header'; - this.flags = options?.flags ?? 0; + this.registerString('label', 'Label', options?.label ?? 'Header'); + this.registerFlags('flags', 'Flags', options?.flags, true, 0); this._focusOffset.x = 0; } diff --git a/src/app/models/widgets/combo.widget.ts b/src/app/models/widgets/combo.widget.ts index 754b256..da11bea 100644 --- a/src/app/models/widgets/combo.widget.ts +++ b/src/app/models/widgets/combo.widget.ts @@ -14,17 +14,17 @@ export class FIGComboWidget extends FIGWithTooltip { {name: 'tooltip', optional: true, default: undefined} ]; - label: string; - readonly items: string[]; + label: string = 'Combo'; + items: string[] = []; - selectedItem: number; + selectedItem: number = 0; constructor(options?: FIGComboOptions) { super(FIGWidgetType.combo, true); - this.label = options?.label ?? 'Combo'; - this.items = options?.items ?? []; - this.selectedItem = 0; - this.tooltip = options?.tooltip; + this.registerString('label', 'Label', options?.label ?? 'Combo'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerArray('items', 'List of items', options?.items, true, []); + this.registerInteger('selectedItem', 'Selected item', 0, true, 0); } public get name(): string { diff --git a/src/app/models/widgets/dummy.widget.ts b/src/app/models/widgets/dummy.widget.ts index 91c6783..65f1d93 100644 --- a/src/app/models/widgets/dummy.widget.ts +++ b/src/app/models/widgets/dummy.widget.ts @@ -14,14 +14,14 @@ export class FIGDummyWidget extends FIGWithTooltip { {name: 'tooltip', optional: true, default: undefined} ]; - width: number; - height: number; + width: number = 100; + height: number = 100; constructor(options?: FIGDummyOptions) { super(FIGWidgetType.dummy, true); - this.width = options?.width ?? 100; - this.height = options?.height ?? 100; - this.tooltip = options?.tooltip; + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerInteger('width', 'Width', options?.width, true, 100); + this.registerInteger('height', 'Height', options?.height, true, 100); } public readonly name = 'Dummy'; diff --git a/src/app/models/widgets/input-color-edit.widget.ts b/src/app/models/widgets/input-color-edit.widget.ts index d32f95f..aff5655 100644 --- a/src/app/models/widgets/input-color-edit.widget.ts +++ b/src/app/models/widgets/input-color-edit.widget.ts @@ -53,18 +53,18 @@ export class FIGInputColorEditWidget extends FIGWithTooltip { {name: 'flags', optional: true, default: 0} ]; - label: string; - color: Color; - withAlpha: boolean; - flags: number; + label: string = 'Input Color Edit'; + color: Color = {r: 0.5, g: 0.5, b: 0.5, a: 0.5}; + withAlpha: boolean = false; + flags: number = 0; constructor(options?: FIGInputColorEditOptions) { super(FIGWidgetType.inputColorEdit, true); - this.label = options?.label ?? 'Input Color Edit'; - this.color = options?.color ?? {r: 0.5, g: 0.5, b: 0.5, a: 0.5}; - this.withAlpha = options?.withAlpha ?? false; - this.tooltip = options?.tooltip; - this.flags = options?.flags ?? 0; + this.registerString('label', 'Label', options?.label ?? 'Input Color Edit'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerColor('color', 'Color', options?.color, true, {r: 0.5, g: 0.5, b: 0.5, a: 0.5}); + this.registerBool('withAlpha', 'Alpha channel', true, false); + this.registerFlags('flags', 'Flags', options?.flags, true, 0); } public get name(): string { diff --git a/src/app/models/widgets/input-number.widget.ts b/src/app/models/widgets/input-number.widget.ts index 36278ee..c325200 100644 --- a/src/app/models/widgets/input-number.widget.ts +++ b/src/app/models/widgets/input-number.widget.ts @@ -2,6 +2,7 @@ import {FIGWidgetType} from "./widget"; import {FIGTooltipOption, FIGWithTooltip} from "./with-tooltip.widget"; import {getPrecision} from "../string"; import {FIGSerializeProperty} from "../../parsers/document.parser"; +import {EnumOption} from "../fields/enum.field"; export enum FIGInputNumberType { int, @@ -17,9 +18,22 @@ export enum FIGInputNumberType { double } +export const FIGInputNumberTypeOptions: EnumOption[] = [ + {value: FIGInputNumberType.int, label: 'Int'}, + {value: FIGInputNumberType.int2, label: 'Int2'}, + {value: FIGInputNumberType.int3, label: 'Int3'}, + {value: FIGInputNumberType.int4, label: 'Int4'}, + {value: FIGInputNumberType.float, label: 'Float'}, + {value: FIGInputNumberType.float2, label: 'Float2'}, + {value: FIGInputNumberType.float3, label: 'Float3'}, + {value: FIGInputNumberType.float4, label: 'Float4'}, + {value: FIGInputNumberType.double, label: 'Double'}, +]; + + export interface FIGInputNumberOptions extends FIGTooltipOption { readonly label?: string; - readonly value?: number | number[]; + readonly value?: number[]; readonly step?: number; readonly stepFast?: number; readonly format?: string; @@ -27,8 +41,8 @@ export interface FIGInputNumberOptions extends FIGTooltipOption { } interface DrawItem { - readonly fn: (...args: any[]) => any; - readonly args: (self: FIGInputNumberWidget) => any[]; + readonly fn: (...args: unknown[]) => unknown; + readonly args: (self: FIGInputNumberWidget) => unknown[]; } export class FIGInputNumberWidget extends FIGWithTooltip { @@ -45,7 +59,7 @@ export class FIGInputNumberWidget extends FIGWithTooltip { private static readonly drawers: DrawItem[] = [ { fn: ImGui.InputInt, - args: (self: FIGInputNumberWidget) => [(_ = self.value) => self.value = _, self.step, self.stepFast] + args: (self: FIGInputNumberWidget) => [(_ = self.value[0]) => self.value[0] = _, self.step, self.stepFast] }, {fn: ImGui.InputInt2, args: (self: FIGInputNumberWidget) => [self.value]}, {fn: ImGui.InputInt3, args: (self: FIGInputNumberWidget) => [self.value]}, @@ -53,7 +67,7 @@ export class FIGInputNumberWidget extends FIGWithTooltip { { fn: ImGui.InputFloat, - args: (self: FIGInputNumberWidget) => [(_ = self.value) => self.value = _, self.step, self.stepFast, self.format] + args: (self: FIGInputNumberWidget) => [(_ = self.value[0]) => self.value[0] = _, self.step, self.stepFast, self.format] }, {fn: ImGui.InputFloat2, args: (self: FIGInputNumberWidget) => [self.value, self.format]}, {fn: ImGui.InputFloat3, args: (self: FIGInputNumberWidget) => [self.value, self.format]}, @@ -61,26 +75,26 @@ export class FIGInputNumberWidget extends FIGWithTooltip { { fn: ImGui.InputDouble, - args: (self: FIGInputNumberWidget) => [(_ = self.value) => self.value = _, self.step, self.stepFast, self.format] + args: (self: FIGInputNumberWidget) => [(_ = self.value[0]) => self.value[0] = _, self.step, self.stepFast, self.format] } ]; - label: string; - dataType: FIGInputNumberType; - value: number | number[]; - step: number; - stepFast: number; - format: string; + label: string = 'Input Number'; + dataType: FIGInputNumberType = FIGInputNumberType.int; + value: number[] = [0]; + step: number = 1; + stepFast: number = 10; + format: string = '%.3f'; constructor(options?: FIGInputNumberOptions) { super(FIGWidgetType.inputNumber, true); - this.label = options?.label ?? 'Input Number'; - this.dataType = options?.dataType ?? FIGInputNumberType.int; - this.value = options?.value ?? 0; - this.step = options?.step ?? (FIGInputNumberWidget.isInteger(this.dataType) ? 1 : 0.01); - this.stepFast = options?.stepFast ?? (FIGInputNumberWidget.isInteger(this.dataType) ? 10 : 1); - this.format = options?.format ?? (this.dataType === FIGInputNumberType.double ? '%.8f' : '%.3f'); - this.tooltip = options?.tooltip; + this.registerString('label', 'Label', options?.label ?? 'Input Number'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerEnum('dataType', 'Data type', FIGInputNumberTypeOptions, options?.dataType, true, FIGInputNumberType.int); + this.registerArray('value', 'Value', options?.value, true, [0]); + this.registerNumber('step', 'Step', options?.step, true, (FIGInputNumberWidget.isInteger(this.dataType) ? 1 : 0.01)); + this.registerNumber('stepFast', 'Step fast', options?.stepFast, true, (FIGInputNumberWidget.isInteger(this.dataType) ? 10 : 1)); + this.registerString('format', 'Format', options?.format, true, (this.dataType === FIGInputNumberType.double ? '%.8f' : '%.3f')); } public get name(): string { @@ -104,7 +118,7 @@ export class FIGInputNumberWidget extends FIGWithTooltip { } else if (dataType === FIGInputNumberType.int4 || dataType === FIGInputNumberType.float4) { return 4; } - return 0; + return 1; } public static getPrecision(widget: FIGInputNumberWidget): number | undefined { @@ -115,9 +129,9 @@ export class FIGInputNumberWidget extends FIGWithTooltip { } public override draw(): void { - const prevValue: number | number[] = (this.value instanceof Array) ? [...this.value] : this.value; + const prevValue: number | number[] = [...this.value]; const drawer: DrawItem = FIGInputNumberWidget.drawers[this.dataType]; - const args: any[] = [this.label]; + const args: unknown[] = [this.label]; args.push(...drawer.args(this)); drawer.fn(...args); diff --git a/src/app/models/widgets/input-text.widget.ts b/src/app/models/widgets/input-text.widget.ts index 944cfe4..33c5707 100644 --- a/src/app/models/widgets/input-text.widget.ts +++ b/src/app/models/widgets/input-text.widget.ts @@ -46,20 +46,20 @@ export class FIGInputTextWidget extends FIGWithTooltip { {name: 'flags', optional: true, default: 0} ]; - label: string; - value: string; + label: string = 'Text'; + value: string = ''; hint?: string; - bufferSize: number; - flags: number; + bufferSize: number = 256; + flags: number = 0; constructor(options?: FIGInputTextOptions) { super(FIGWidgetType.inputText, true); - this.label = options?.label ?? 'Text'; - this.value = options?.value ?? ''; - this.hint = options?.hint; - this.tooltip = options?.tooltip; - this.bufferSize = options?.bufferSize ?? 256; - this.flags = options?.flags ?? 0; + this.registerString('label', 'Label', options?.label ?? 'Text'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerString('hint', 'Hint', options?.hint, true); + this.registerString('value', 'Value', options?.value, true, ''); + this.registerInteger('bufferSize', 'Buffer size', options?.bufferSize, true, 256); + this.registerFlags('flags', 'Flags', options?.flags, true, 0); } public get name(): string { diff --git a/src/app/models/widgets/input-textarea.widget.ts b/src/app/models/widgets/input-textarea.widget.ts index 36b3bd0..4267c5d 100644 --- a/src/app/models/widgets/input-textarea.widget.ts +++ b/src/app/models/widgets/input-textarea.widget.ts @@ -22,21 +22,21 @@ export class FIGInputTextareaWidget extends FIGWithTooltip { {name: 'flags', optional: true, default: 0} ]; - label: string; - value: string; - linesSize: number; - bufferSize: number; - flags: number; + label: string = '##InputTextMultiline'; + value: string = ''; + linesSize: number = 6; + bufferSize: number = 256; + flags: number = 0; constructor(options?: FIGInputTextareaOptions) { super(FIGWidgetType.inputTextarea, true); - this.label = options?.label ?? '##InputTextMultiline'; - this.value = options?.value ?? ''; - this.tooltip = options?.tooltip; - this.linesSize = options?.linesSize ?? 6; - this.bufferSize = options?.bufferSize ?? 256; + this.registerString('label', 'Label', options?.label ?? '##InputTextMultiline'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerString('value', 'Value', options?.value, true, ''); + this.registerInteger('linesSize', 'Height in lines', options?.linesSize, true, 6); + this.registerInteger('bufferSize', 'Buffer size', options?.bufferSize, true, 256); + this.registerFlags('flags', 'Flags', options?.flags, true, 0); this.bufferSize = Math.max(this.value.length, this.bufferSize); - this.flags = options?.flags ?? 0; } public get name(): string { diff --git a/src/app/models/widgets/label.widget.ts b/src/app/models/widgets/label.widget.ts index 7454ea1..7b6dcb5 100644 --- a/src/app/models/widgets/label.widget.ts +++ b/src/app/models/widgets/label.widget.ts @@ -14,14 +14,14 @@ export class FIGLabelWidget extends FIGWithTooltip { {name: 'tooltip', optional: true, default: undefined} ]; - label: string; - value: string; + label: string = 'Label'; + value: string = 'Value'; constructor(options?: FIGLabelOptions) { super(FIGWidgetType.label, true); - this.label = options?.label ?? 'Label'; - this.value = options?.value ?? 'Value'; - this.tooltip = options?.tooltip; + this.registerString('label', 'Label', options?.label ?? 'Label'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerString('value', 'Value', options?.value ?? 'Value'); } public get name(): string { diff --git a/src/app/models/widgets/listbox.widget.ts b/src/app/models/widgets/listbox.widget.ts index 56bcd17..72137e5 100644 --- a/src/app/models/widgets/listbox.widget.ts +++ b/src/app/models/widgets/listbox.widget.ts @@ -16,19 +16,19 @@ export class FIGListBoxWidget extends FIGWithTooltip { {name: 'tooltip', optional: true, default: undefined} ]; - label: string; - readonly items: string[]; - itemsSize: number; + label: string = 'ListBox'; + items: string[] = ['Item 1', 'Item 2', 'Item 3', 'Item 4']; + itemsSize: number = 4; - selectedItem: number; + selectedItem: number = 0; constructor(options?: FIGListBoxOptions) { super(FIGWidgetType.listbox, true); - this.label = options?.label ?? 'ListBox'; - this.items = options?.items ?? ['Item 1', 'Item 2', 'Item 3', 'Item 4']; - this.itemsSize = options?.itemsSize ?? 4; - this.selectedItem = 0; - this.tooltip = options?.tooltip; + this.registerString('label', 'Label', options?.label ?? 'ListBox'); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerArray('items', 'List of items', options?.items, true, ['Item 1', 'Item 2', 'Item 3', 'Item 4']); + this.registerInteger('itemsSize', 'Height in items', options?.itemsSize, true, 4); + //this.registerInteger('selectedItem', 'Selected item', 0, true, 0); } public get name(): string { diff --git a/src/app/models/widgets/menu-item.widget.ts b/src/app/models/widgets/menu-item.widget.ts index 2481bb9..d7e7825 100644 --- a/src/app/models/widgets/menu-item.widget.ts +++ b/src/app/models/widgets/menu-item.widget.ts @@ -18,19 +18,19 @@ export class FIGMenuItemWidget extends FIGWidget { {name: 'enabled', optional: true, default: true} ]; - label: string; + label: string = 'MenuItem'; shortcut?: string; - isSelectable: boolean; - isSelected: boolean; - enabled: boolean; + isSelectable: boolean = false; + isSelected: boolean = false; + enabled: boolean = true; constructor(options?: FIGMenuItemOptions) { super(FIGWidgetType.menuItem, true); - this.label = options?.label ?? 'MenuItem'; - this.shortcut = options?.shortcut; - this.isSelectable = options?.isSelectable ?? false; - this.isSelected = options?.isSelected ?? false; - this.enabled = options?.enabled ?? true; + this.registerString('label', 'Label', options?.label ?? 'MenuItem'); + this.registerString('shortcut', 'Shortcut', options?.shortcut, true); + this.registerBool('enabled', 'Enabled', options?.enabled, true, true); + this.registerBool('isSelectable', 'Is selectable', options?.isSelectable, true, false); + this.registerBool('isSelected', 'Is selected', options?.isSelected, true, false); this._focusOffset.x = 0; this._focusOffset.y = 0; } diff --git a/src/app/models/widgets/menu.widget.ts b/src/app/models/widgets/menu.widget.ts index 5e3715c..065fbc0 100644 --- a/src/app/models/widgets/menu.widget.ts +++ b/src/app/models/widgets/menu.widget.ts @@ -13,13 +13,13 @@ export class FIGMenuWidget extends FIGContainer { {name: 'enabled', optional: true, default: true} ]; - label: string; - enabled: boolean; + label: string = 'Menu'; + enabled: boolean = true; constructor(options?: FIGMenuOptions) { super(FIGWidgetType.menu, true); - this.label = options?.label ?? 'Menu'; - this.enabled = options?.enabled ?? true; + this.registerString('label', 'Label', options?.label ?? 'Menu'); + this.registerBool('enabled', 'Enabled', options?.enabled, true, true); this._focusOffset.x = 0; this._focusOffset.y = 0; } diff --git a/src/app/models/widgets/modal.widget.ts b/src/app/models/widgets/modal.widget.ts index 75defc0..80fa59c 100644 --- a/src/app/models/widgets/modal.widget.ts +++ b/src/app/models/widgets/modal.widget.ts @@ -6,8 +6,8 @@ import {FIGSerializeProperty} from "../../parsers/document.parser"; export interface FIGModalOptions { readonly label?: string; - readonly isOpen?: boolean; readonly flags?: number; + readonly isOpen?: boolean; } export class FIGModalWidget extends FIGContainer { @@ -17,17 +17,17 @@ export class FIGModalWidget extends FIGContainer { {name: 'flags', optional: true, default: 0} ]; - label: string; - isOpen: boolean; - flags: number; + label: string = 'Modal'; + flags: number = 0; + isOpen: boolean; debug: boolean; constructor(options?: FIGModalOptions) { super(FIGWidgetType.modal, true); - this.label = options?.label ?? 'Modal'; + this.registerString('label', 'Label', options?.label ?? 'Modal'); + this.registerFlags('flags', 'flags', options?.flags, true, 0); this.isOpen = false; - this.flags = options?.flags ?? 0; this.debug = true; } diff --git a/src/app/models/widgets/plot.widget.ts b/src/app/models/widgets/plot.widget.ts index ba47c67..71df516 100644 --- a/src/app/models/widgets/plot.widget.ts +++ b/src/app/models/widgets/plot.widget.ts @@ -1,12 +1,18 @@ import {FIGWidget, FIGWidgetType} from "./widget"; import {plotSin, Size, Vector2} from "../math"; import {FIGSerializeProperty} from "../../parsers/document.parser"; +import {EnumOption} from "../fields/enum.field"; export enum FIGPlotType { lines, histogram } +export const FIGPlotTypeOptions: EnumOption[] = [ + {value: FIGPlotType.lines, label: 'Lines'}, + {value: FIGPlotType.histogram, label: 'Histogram'}, +]; + export interface FIGPlotOptions { readonly plotType?: FIGPlotType; readonly label?: string; @@ -37,9 +43,8 @@ export class FIGPlotWidget extends FIGWidget { {name: 'stride', optional: true, default: undefined} ]; - plotType: FIGPlotType; - label: string; - values: number[]; + label: string = ''; + plotType: FIGPlotType = FIGPlotType.lines; valueOffset?: number; overlayText?: string; scaleMin?: number; @@ -47,17 +52,18 @@ export class FIGPlotWidget extends FIGWidget { size?: Size; stride?: number; + values: number[] = plotSin(31); + constructor(options?: FIGPlotOptions) { super(FIGWidgetType.plot, true); - this.plotType = options?.plotType ?? FIGPlotType.lines; - this.label = options?.label ?? 'Lines'; - this.values = options?.values ?? plotSin(31); - this.valueOffset = options?.valueOffset; - this.overlayText = options?.overlayText; - this.scaleMin = options?.scaleMin; - this.scaleMax = options?.scaleMax; - this.size = options?.size; - this.stride = options?.stride; + this.registerString('label', 'Label', options?.label ?? 'Lines'); + this.registerEnum('plotType', 'Plot Type', FIGPlotTypeOptions, options?.plotType, true, FIGPlotType.lines); + this.registerString('overlayText', 'Overlay text', options?.overlayText, true); + this.registerSize('size', 'Size', options?.size, true, {width: 0, height: 100}); + this.registerInteger('valueOffset', 'Value offset', options?.valueOffset, true); + this.registerFloat('scaleMin', 'Scale min', options?.scaleMin, true); + this.registerFloat('scaleMax', 'Scale max', options?.scaleMax, true); + this.registerInteger('stride', 'Stride', options?.stride, true); } public get name(): string { @@ -66,22 +72,19 @@ export class FIGPlotWidget extends FIGWidget { public override draw(): void { const size: Vector2 | undefined = this.size ? {x: this.size.width, y: this.size.height} : undefined; + let plotFn: (...args: unknown[]) => void = ImGui.PlotLines; if (this.plotType === FIGPlotType.lines) { - ImGui.PlotLines( - this.label, this.values, this.values.length, - this.valueOffset, this.overlayText, - this.scaleMin, this.scaleMax, - size, this.stride - ); + plotFn = ImGui.PlotLines; } else if (this.plotType === FIGPlotType.histogram) { - ImGui.PlotHistogram( - this.label, this.values, this.values.length, - this.valueOffset, this.overlayText, - this.scaleMin, this.scaleMax, - size, this.stride - ); + plotFn = ImGui.PlotHistogram; } + plotFn( + this.label, this.values, this.values.length, + this.valueOffset, this.overlayText, + this.scaleMin, this.scaleMax, + size, this.stride + ); this.drawFocus(); this.scrollTo(); } diff --git a/src/app/models/widgets/popup.widget.ts b/src/app/models/widgets/popup.widget.ts index a9ff500..71bd4fd 100644 --- a/src/app/models/widgets/popup.widget.ts +++ b/src/app/models/widgets/popup.widget.ts @@ -13,19 +13,21 @@ export class FIGPopupWidget extends FIGContainer { {name: 'contextItem', optional: true, default: false} ]; - label: string; - contextItem: boolean; + label: string = '##Popup'; + contextItem: boolean = false; + isOpen: boolean; - debugLabel: string; debug: boolean; + debugLabel: string; constructor(options?: FIGPopupOptions) { super(FIGWidgetType.popup, true); - this.label = options?.label ?? '##Popup'; - this.contextItem = options?.contextItem ?? false; + this.registerString('label', 'Label', options?.label ?? '##Popup'); + this.registerBool('contextItem', 'Context item (right click)', options?.contextItem, true, false); + this.isOpen = false; - this.debugLabel = `Open '${this.label.slice(2)}'`; this.debug = true; + this.debugLabel = `Open '${this.label.slice(2)}'`; } public get name(): string { diff --git a/src/app/models/widgets/progress-bar.widget.ts b/src/app/models/widgets/progress-bar.widget.ts index cb93044..b0f08dd 100644 --- a/src/app/models/widgets/progress-bar.widget.ts +++ b/src/app/models/widgets/progress-bar.widget.ts @@ -17,16 +17,16 @@ export class FIGProgressBarWidget extends FIGWithTooltip { {name: 'tooltip', optional: true, default: undefined} ]; - value: number; label?: string; - isFill: boolean; + isFill: boolean = false; + value: number = 0; constructor(options?: FIGProgressBarOptions) { super(FIGWidgetType.progressBar, true); - this.value = options?.value ?? 0.0; - this.label = options?.label; - this.isFill = options?.isFill ?? false; - this.tooltip = options?.tooltip; + this.registerString('label', 'Label', options?.label, true); + this.registerString('tooltip', 'Tooltip', options?.tooltip, true); + this.registerBool('isFill', 'Fill', options?.isFill, true, false); + this.registerInteger('value', 'Value', options?.value, true, 0); } public get name(): string { diff --git a/src/app/models/widgets/text.widget.ts b/src/app/models/widgets/text.widget.ts index 89b6901..43b02e0 100644 --- a/src/app/models/widgets/text.widget.ts +++ b/src/app/models/widgets/text.widget.ts @@ -29,22 +29,22 @@ export class FIGTextWidget extends FIGWithTooltip { {name: 'tooltip', optional: true, default: undefined} ]; - text: string; + text: string = 'Text'; color?: Color; - isDisabled: boolean; - isWrapped: boolean; - hasBullet: boolean; - align: boolean; + isDisabled: boolean = false; + isWrapped: boolean = false; + hasBullet: boolean = false; + align: boolean = false; constructor(options?: FIGTextOptions) { super(FIGWidgetType.text, true); - this.text = options?.text ?? 'Text'; - this.color = options?.color; - this.isDisabled = options?.isDisabled ?? false; - this.isWrapped = options?.isWrapped ?? false; - this.hasBullet = options?.hasBullet ?? false; - this.align = options?.align ?? false; - this.tooltip = options?.tooltip; + this.registerString('text', 'Text', options?.text ?? 'Text'); + this.registerString('tooltip', 'Tooltip', options?.tooltip); + this.registerColor('color', 'Color', options?.color); + this.registerBool('isDisabled', 'Disabled', options?.isDisabled ?? false); + this.registerBool('isWrapped', 'Wrapped', options?.isWrapped ?? false); + this.registerBool('hasBullet', 'Bullet', options?.hasBullet ?? false); + this.registerBool('align', 'Align to frame padding', options?.align ?? false); } public get name(): string { diff --git a/src/app/models/widgets/widget.ts b/src/app/models/widgets/widget.ts index 40389b1..2f92592 100644 --- a/src/app/models/widgets/widget.ts +++ b/src/app/models/widgets/widget.ts @@ -1,8 +1,19 @@ import {v4 as uuidv4} from "uuid"; import {BehaviorSubject, Observable} from "rxjs"; import {FIGContainer} from "./container"; -import {Vector2} from "../math"; +import {Color, Size, Vector2} from "../math"; import {FIGEvent, FIGEventType} from "../events/event"; +import {Field} from "../fields/field"; +import {StringField} from "../fields/string.field"; +import {SizeField} from "../fields/size.field"; +import {FlagsField} from "../fields/flags.field"; +import {BoolField} from "../fields/bool.field"; +import {ColorField} from "../fields/color.field"; +import {IntegerField} from "../fields/integer.field"; +import {EnumField, EnumFieldType, EnumOption} from "../fields/enum.field"; +import {ArrayField} from "../fields/array.field"; +import {NumberField} from "../fields/number.field"; +import {FloatField} from "../fields/float.field"; export enum FIGWidgetType { // NOTE: order types per category. Manually increment type's value for @@ -55,6 +66,8 @@ export enum FIGWidgetType { blocFor } +type Fields = Record; + export abstract class FIGWidget { public static readonly excludeKeys: string[] = [ 'uuid', 'type', 'needParent', 'parent', 'isFocused', 'children', @@ -73,12 +86,15 @@ export abstract class FIGWidget { protected readonly updateSubject: BehaviorSubject = new BehaviorSubject(undefined); public readonly update$: Observable = this.updateSubject.asObservable(); + protected readonly _focusOffset: Vector2 = {x: 4, y: 4}; private eventSubject?: BehaviorSubject; - protected readonly _focusOffset: Vector2 = {x: 4, y: 4}; private readonly _focusMin: Vector2 = {x: Number.MAX_VALUE, y: Number.MAX_VALUE}; private readonly _focusMax: Vector2 = {x: Number.MIN_VALUE, y: Number.MIN_VALUE}; + private readonly fields: Fields; + private readonly properties: string[]; + private _isSelected: boolean = false; protected constructor(type: FIGWidgetType, @@ -86,8 +102,12 @@ export abstract class FIGWidget { this.uuid = uuidv4(); this.type = type; this.needParent = needParent; + this.fields = {}; + this.properties = []; } + public abstract get name(): string; + public static isContainer(type: FIGWidgetType): boolean { return type === FIGWidgetType.window || type === FIGWidgetType.childWindow || @@ -106,8 +126,6 @@ export abstract class FIGWidget { type === FIGWidgetType.blocFor; } - public abstract get name(): string; - public abstract draw(): void; /** @@ -134,6 +152,10 @@ export abstract class FIGWidget { } + public getField(name: string): Field { + return this.fields[name]; + } + public listen(): void { if (!this.eventSubject) { return; @@ -164,6 +186,10 @@ export abstract class FIGWidget { this._isSelected = true; } + public getFields(): Field[] { + return this.properties.map((name) => this.fields[name]); + } + protected drawFocus(): void { if (this.isFocused) { this.growFocusRect(); @@ -202,4 +228,141 @@ export abstract class FIGWidget { this._focusMax.y = Math.max(this._focusMax.y, max.y); } + protected registerBool(name: string, + label: string, + value?: boolean, + isOptional: boolean = false, + defaultValue?: boolean): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new BoolField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerInteger(name: string, + label: string, + value?: number, + isOptional: boolean = false, + defaultValue?: number): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new IntegerField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerFloat(name: string, + label: string, + value?: number, + isOptional: boolean = false, + defaultValue?: number): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new FloatField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerNumber(name: string, + label: string, + value?: number, + isOptional: boolean = false, + defaultValue?: number): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new NumberField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerString(name: string, + label: string, + value?: string, + isOptional: boolean = false, + defaultValue?: string): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new StringField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerArray(name: string, + label: string, + value?: unknown[], + isOptional: boolean = false, + defaultValue?: unknown[]): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new ArrayField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerSize(name: string, + label: string, + value?: Size, + isOptional: boolean = false, + defaultValue?: Size): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new SizeField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerFlags(name: string, + label: string, + value?: number, + isOptional: boolean = false, + defaultValue: number = 0): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new FlagsField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerColor(name: string, + label: string, + value?: Color, + isOptional: boolean = false, + defaultValue?: Color): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new ColorField(name, label, value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + protected registerEnum(name: string, + label: string, + options: E | EnumOption[], + value?: EnumFieldType, + isOptional: boolean = false, + defaultValue?: EnumFieldType): void { + if (isOptional) { + value ??= defaultValue; + } + this.fields[name] = new EnumField(name, label, options as EnumOption[], value, isOptional, defaultValue) as Field; + this.registerField(name); + } + + private registerField(name: string): void { + this.properties.push(name); + Object.defineProperty(this, name, { + get: () => this.fields[name].value, + set: (value: never) => { + const prevValue: unknown = this.fields[name].value; + + if (prevValue === value) { + return; + } + this.fields[name].value = value; + this.fields[name].emit(); + }, + }); + } + } diff --git a/src/app/models/widgets/window.widget.ts b/src/app/models/widgets/window.widget.ts index f639dd0..efa127e 100644 --- a/src/app/models/widgets/window.widget.ts +++ b/src/app/models/widgets/window.widget.ts @@ -62,22 +62,28 @@ export class FIGWindowWidget extends FIGContainer { {name: 'size', optional: true, default: undefined, type: 'object', innerType: [{name: 'width'}, {name: 'height'}]}, {name: 'flags', optional: true, default: 0}, {name: 'sizeFlags', optional: true, default: 0}, - {name: 'minSize', optional: true, default: undefined, type: 'object', innerType: [{name: 'width'}, {name: 'height'}]} + { + name: 'minSize', + optional: true, + default: undefined, + type: 'object', + innerType: [{name: 'width'}, {name: 'height'}] + } ]; - label: string; + label: string = ''; + flags: number = 0; size?: Size; - flags: number; - sizeFlags: number; minSize?: Size; + sizeFlags: number = 0; constructor(options?: FIGWindowOptions) { super(FIGWidgetType.window, false); - this.label = options?.label ?? 'Window'; - this.size = options?.size; - this.flags = options?.flags ?? 0; - this.sizeFlags = options?.sizeFlags ?? 0; - this.minSize = options?.minSize; + this.registerString( 'label', 'Title', options?.label ?? 'Window'); + this.registerFlags('flags', 'Flags', options?.flags, true); + this.registerSize('size', 'Size', options?.size, true); + this.registerSize('minSize', 'Min size', options?.minSize, true); + this.registerFlags('sizeFlags', 'Size flags', options?.sizeFlags, true); } public get name(): string { diff --git a/src/app/pages/editor/fields/abstract-field.component.ts b/src/app/pages/editor/fields/abstract-field.component.ts new file mode 100644 index 0000000..7bb41d5 --- /dev/null +++ b/src/app/pages/editor/fields/abstract-field.component.ts @@ -0,0 +1,73 @@ +import {Component, DestroyRef, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import {FormControl, ValidatorFn, Validators} from "@angular/forms"; +import {Field} from "../../../models/fields/field"; +import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; + +@Component({ + template: '' +}) +export abstract class AbstractFieldComponent implements OnInit { + + @Output() + update: EventEmitter = new EventEmitter(); + + field!: F; + form: FormControl = new FormControl(); + + protected constructor(protected readonly dr: DestroyRef) { + } + + @Input({ + alias: 'field', + transform: (value: Field) => value as F, + required: true + }) + set _field(field: F) { + this.field?.removeListener(this.onFieldChanged.bind(this)); + this.field = field; + this.field.addListener(this.onFieldChanged.bind(this)); + this.form.setValue(field.value as T, {emitEvent: false}); + this.form.setValidators(this.getValidators()); + this.form.updateValueAndValidity(); + this.onFieldLoaded(); + } + + public ngOnInit(): void { + this.form.valueChanges.pipe(takeUntilDestroyed(this.dr)).subscribe(this.onFormChanged.bind(this)); + } + + protected transform: (value?: T) => T | undefined = (value) => value; + + protected onFieldLoaded(): void { + // NOTE: implemented by ...FieldComponent children + } + + protected getValidators(): ValidatorFn[] { + const validators: ValidatorFn[] = []; + + if (this.field.isRequired) { + validators.push(Validators.required); + } + return validators; + } + + protected onFieldChanged(value: unknown): void { + this.form.setValue(value as T, {emitEvent: false}); + } + + protected onFormChanged(): void { + const prevValue: T = this.field.value as T; + const value: T | undefined = this.transform(this.form.value); + + if (prevValue === value) { + return; + } + if (this.field.isRequired && value === undefined) { + return; + } + this.field.value = value; + this.field.emit(); + this.update.emit(this.field); + } + +} diff --git a/src/app/pages/editor/fields/bool-field/bool-field.component.css b/src/app/pages/editor/fields/bool-field/bool-field.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/editor/fields/bool-field/bool-field.component.html b/src/app/pages/editor/fields/bool-field/bool-field.component.html new file mode 100644 index 0000000..b68eb83 --- /dev/null +++ b/src/app/pages/editor/fields/bool-field/bool-field.component.html @@ -0,0 +1,5 @@ +
+ {{ field.label }} + +
diff --git a/src/app/pages/editor/fields/bool-field/bool-field.component.spec.ts b/src/app/pages/editor/fields/bool-field/bool-field.component.spec.ts new file mode 100644 index 0000000..f405f83 --- /dev/null +++ b/src/app/pages/editor/fields/bool-field/bool-field.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BoolFieldComponent } from './bool-field.component'; + +describe('BoolFieldComponent', () => { + let component: BoolFieldComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BoolFieldComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(BoolFieldComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/editor/fields/bool-field/bool-field.component.ts b/src/app/pages/editor/fields/bool-field/bool-field.component.ts new file mode 100644 index 0000000..61d293d --- /dev/null +++ b/src/app/pages/editor/fields/bool-field/bool-field.component.ts @@ -0,0 +1,25 @@ +import {Component, DestroyRef} from '@angular/core'; +import {AbstractFieldComponent} from "../abstract-field.component"; +import {BoolField} from "../../../../models/fields/bool.field"; +import {ReactiveFormsModule} from "@angular/forms"; +import {MatLabel} from "@angular/material/form-field"; +import {MatSlideToggle} from "@angular/material/slide-toggle"; + +@Component({ + selector: 'fig-bool-field', + standalone: true, + imports: [ + MatLabel, + MatSlideToggle, + ReactiveFormsModule + ], + templateUrl: './bool-field.component.html', + styleUrl: './bool-field.component.css' +}) +export class BoolFieldComponent extends AbstractFieldComponent { + + constructor(dr: DestroyRef) { + super(dr); + } + +} diff --git a/src/app/pages/editor/fields/enum-field/enum-field.component.css b/src/app/pages/editor/fields/enum-field/enum-field.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/editor/fields/enum-field/enum-field.component.html b/src/app/pages/editor/fields/enum-field/enum-field.component.html new file mode 100644 index 0000000..479e653 --- /dev/null +++ b/src/app/pages/editor/fields/enum-field/enum-field.component.html @@ -0,0 +1,8 @@ + + {{ field.label }} + + @for (option of options; track option.value) { + {{ option.label }} + } + + diff --git a/src/app/pages/editor/fields/enum-field/enum-field.component.spec.ts b/src/app/pages/editor/fields/enum-field/enum-field.component.spec.ts new file mode 100644 index 0000000..231dfc4 --- /dev/null +++ b/src/app/pages/editor/fields/enum-field/enum-field.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EnumFieldComponent } from './enum-field.component'; + +describe('EnumFieldComponent', () => { + let component: EnumFieldComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [EnumFieldComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(EnumFieldComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/editor/fields/enum-field/enum-field.component.ts b/src/app/pages/editor/fields/enum-field/enum-field.component.ts new file mode 100644 index 0000000..6018421 --- /dev/null +++ b/src/app/pages/editor/fields/enum-field/enum-field.component.ts @@ -0,0 +1,32 @@ +import {Component, DestroyRef} from '@angular/core'; +import {ReactiveFormsModule} from "@angular/forms"; +import {MatFormField, MatLabel} from "@angular/material/form-field"; +import {MatOption, MatSelect} from "@angular/material/select"; +import {AbstractFieldComponent} from "../abstract-field.component"; +import {EnumField, EnumFieldType, EnumOption} from "../../../../models/fields/enum.field"; + +@Component({ + selector: 'fig-enum-field', + standalone: true, + imports: [ + MatLabel, + MatOption, + MatSelect, + MatFormField, + ReactiveFormsModule + ], + templateUrl: './enum-field.component.html', + styleUrl: './enum-field.component.css' +}) +export class EnumFieldComponent extends AbstractFieldComponent, EnumFieldType> { + + public readonly options: EnumOption[] = []; + + constructor(dr: DestroyRef) { + super(dr); + } + + protected override onFieldLoaded() { + this.options.push(...this.field.options); + } +} diff --git a/src/app/pages/editor/fields/string-field/string-field.component.css b/src/app/pages/editor/fields/string-field/string-field.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/editor/fields/string-field/string-field.component.html b/src/app/pages/editor/fields/string-field/string-field.component.html new file mode 100644 index 0000000..446188c --- /dev/null +++ b/src/app/pages/editor/fields/string-field/string-field.component.html @@ -0,0 +1,6 @@ + + {{field.label}} + + diff --git a/src/app/pages/editor/fields/string-field/string-field.component.spec.ts b/src/app/pages/editor/fields/string-field/string-field.component.spec.ts new file mode 100644 index 0000000..965dab4 --- /dev/null +++ b/src/app/pages/editor/fields/string-field/string-field.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StringFieldComponent } from './string-field.component'; + +describe('StringFieldComponent', () => { + let component: StringFieldComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [StringFieldComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(StringFieldComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/editor/fields/string-field/string-field.component.ts b/src/app/pages/editor/fields/string-field/string-field.component.ts new file mode 100644 index 0000000..1c76850 --- /dev/null +++ b/src/app/pages/editor/fields/string-field/string-field.component.ts @@ -0,0 +1,33 @@ +import {Component, DestroyRef} from '@angular/core'; +import {MatFormField, MatLabel} from "@angular/material/form-field"; +import {ReactiveFormsModule} from "@angular/forms"; +import {MatInput} from "@angular/material/input"; +import {AbstractFieldComponent} from "../abstract-field.component"; +import {StringField} from "../../../../models/fields/string.field"; + +@Component({ + selector: 'fig-string-field', + standalone: true, + imports: [ + MatInput, + MatLabel, + MatFormField, + ReactiveFormsModule + ], + templateUrl: './string-field.component.html', + styleUrl: './string-field.component.css' +}) +export class StringFieldComponent extends AbstractFieldComponent { + + constructor(dr: DestroyRef) { + super(dr); + } + + protected override onFieldLoaded() { + this.transform = (value?: string) => { + value = value?.trim(); + return value; + }; + } + +} diff --git a/src/app/pages/editor/properties/abstract-properties.component.ts b/src/app/pages/editor/properties/abstract-properties.component.ts index a289496..ae01ae3 100644 --- a/src/app/pages/editor/properties/abstract-properties.component.ts +++ b/src/app/pages/editor/properties/abstract-properties.component.ts @@ -3,6 +3,9 @@ import {FIGWidget} from "../../../models/widgets/widget"; import {debounceTime, map, Observable, Subscription} from "rxjs"; import {FormGroup} from "@angular/forms"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; +import {Field} from "../../../models/fields/field"; +import {hasFunction} from "../../../models/object"; +import {capitalize} from "../../../models/string"; export interface FlagItem { readonly label: string; @@ -36,6 +39,14 @@ export abstract class AbstractPropertiesComponent implement this.dispose(); } + protected getField(name: string): Field { + return this.widget.getField(name); + } + + protected updateWidget(): void { + this.update.emit(this.widget); + } + protected listenProperty(property: string, debounce?: number): Observable { return this.form.get(property)!.valueChanges.pipe( (debounce) ? debounceTime(debounce) : map((_) => _), @@ -65,12 +76,16 @@ export abstract class AbstractPropertiesComponent implement protected load(): void { this.updateS = this.widget.update$.subscribe(this.onUpdated.bind(this)); + this.addFieldListeners(); this.updateForm(); } - protected abstract updateForm(): void; + protected updateForm(): void { + // TODO: remove me + } protected dispose(): void { + this.removeFieldListeners(); this.updateS?.unsubscribe(); } @@ -78,4 +93,34 @@ export abstract class AbstractPropertiesComponent implement this.updateForm(); } + private addFieldListeners(): void { + if (!this.widget) { + return; + } + for (const field of this.widget.getFields()) { + const name: string = `on${capitalize(field.name)}Changed`; + + if (hasFunction(this, name)) { + field.addListener(this.getListener(name)); + } + } + } + + private removeFieldListeners(): void { + if (!this.widget) { + return; + } + for (const field of this.widget.getFields()) { + const name: string = `on${capitalize(field.name)}Changed`; + + if (hasFunction(this, name)) { + field.removeListener(this.getListener(name)); + } + } + } + + private getListener(name: string): () => void { + return ((this as never)[name] as () => void).bind(this); + } + } diff --git a/src/app/pages/editor/properties/button-properties/button-properties.component.html b/src/app/pages/editor/properties/button-properties/button-properties.component.html index 98c22fe..afbd0fb 100644 --- a/src/app/pages/editor/properties/button-properties/button-properties.component.html +++ b/src/app/pages/editor/properties/button-properties/button-properties.component.html @@ -1,36 +1,12 @@ -
- - Label - - + + - - Tooltip - - + -
- Fill - -
+ -
- Small - -
+ - - Arrow - - None - Left - Right - Up - Down - - + diff --git a/src/app/pages/editor/properties/button-properties/button-properties.component.ts b/src/app/pages/editor/properties/button-properties/button-properties.component.ts index 10a68be..a7b53f2 100644 --- a/src/app/pages/editor/properties/button-properties/button-properties.component.ts +++ b/src/app/pages/editor/properties/button-properties/button-properties.component.ts @@ -1,101 +1,52 @@ import {Component, DestroyRef} from '@angular/core'; -import {MatFormField, MatLabel} from "@angular/material/form-field"; -import {MatInput} from "@angular/material/input"; -import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; import {FIGButtonWidget, FIGDir} from "../../../../models/widgets/button.widget"; import {AbstractPropertiesComponent} from "../abstract-properties.component"; -import {MatSlideToggle} from "@angular/material/slide-toggle"; -import {MatOption, MatSelect} from "@angular/material/select"; +import {StringFieldComponent} from "../../fields/string-field/string-field.component"; +import {BoolFieldComponent} from "../../fields/bool-field/bool-field.component"; +import {EnumFieldComponent} from "../../fields/enum-field/enum-field.component"; @Component({ selector: 'fig-button-properties', standalone: true, imports: [ - MatInput, - MatLabel, - MatSelect, - MatOption, - MatFormField, - MatSlideToggle, - ReactiveFormsModule + BoolFieldComponent, + EnumFieldComponent, + StringFieldComponent ], templateUrl: './button-properties.component.html', styleUrl: './button-properties.component.css' }) export class ButtonPropertiesComponent extends AbstractPropertiesComponent { - override form: FormGroup = new FormGroup({ - label: new FormControl(''), - tooltip: new FormControl(null), - isFill: new FormControl(false), - isSmall: new FormControl(false), - arrow: new FormControl(FIGDir.none), - }); - - protected readonly FIGArrowDirection = FIGDir; - constructor(dr: DestroyRef) { super(dr); - this.listenProperty('label').subscribe(this.onLabelChanged.bind(this)); - this.listenProperty('tooltip').subscribe(this.onTooltipChanged.bind(this)); - this.listenProperty('isFill').subscribe(this.onIsFillChanged.bind(this)); - this.listenProperty('isSmall').subscribe(this.onIsSmallChanged.bind(this)); - this.listenProperty('arrow').subscribe(this.onArrowChanged.bind(this)); - } - - protected override updateForm() { - this.setProperty('label', this.widget.label); - this.setProperty('tooltip', this.widget.tooltip ?? null); - this.setProperty('isFill', this.widget.isFill); - this.setProperty('isSmall', this.widget.isSmall); - this.setProperty('arrow', this.widget.arrow); - } - - private onLabelChanged(value: string): void { - this.widget.label = value; - this.update.emit(); - } - - private onTooltipChanged(value: string | null): void { - if (value && value.trim().length === 0) { - value = null; - } - this.widget.tooltip = value ?? undefined; - this.update.emit(); } private onIsFillChanged(value: boolean): void { - this.widget.isFill = value; if (value) { this.resetIsSmall(); this.resetArrow(); } - this.update.emit(); } private onIsSmallChanged(value: boolean): void { - this.widget.isSmall = value; if (value) { this.resetIsFill(); this.resetArrow(); } - this.update.emit(); } private onArrowChanged(value: FIGDir): void { - this.widget.arrow = value; - if (value) { + if (value !== FIGDir.none) { this.resetIsFill(); this.resetIsSmall(); } - this.update.emit(); } private resetIsFill(): void { if (!this.widget.isFill) { return; } - this.setProperty('isFill', false); this.widget.isFill = false; } @@ -103,7 +54,6 @@ export class ButtonPropertiesComponent extends AbstractPropertiesComponent - - Label - - +
+ - - Tooltip - - + -
- Checked - -
+ diff --git a/src/app/pages/editor/properties/checkbox-properties/checkbox-properties.component.ts b/src/app/pages/editor/properties/checkbox-properties/checkbox-properties.component.ts index 4881897..38f9577 100644 --- a/src/app/pages/editor/properties/checkbox-properties/checkbox-properties.component.ts +++ b/src/app/pages/editor/properties/checkbox-properties/checkbox-properties.component.ts @@ -1,61 +1,25 @@ import {Component, DestroyRef} from '@angular/core'; -import {MatFormField, MatLabel} from "@angular/material/form-field"; -import {MatInput} from "@angular/material/input"; -import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; import {AbstractPropertiesComponent} from "../abstract-properties.component"; import {FIGCheckboxWidget} from "../../../../models/widgets/checkbox.widget"; -import {MatSlideToggle} from "@angular/material/slide-toggle"; +import {BoolFieldComponent} from "../../fields/bool-field/bool-field.component"; +import {EnumFieldComponent} from "../../fields/enum-field/enum-field.component"; +import {StringFieldComponent} from "../../fields/string-field/string-field.component"; @Component({ selector: 'fig-checkbox-properties', standalone: true, imports: [ - MatInput, - MatLabel, - MatFormField, - MatSlideToggle, - ReactiveFormsModule + BoolFieldComponent, + EnumFieldComponent, + StringFieldComponent ], templateUrl: './checkbox-properties.component.html', styleUrl: './checkbox-properties.component.css' }) export class CheckboxPropertiesComponent extends AbstractPropertiesComponent { - override form: FormGroup = new FormGroup({ - label: new FormControl(''), - isChecked: new FormControl(false), - tooltip: new FormControl(null), - }); - constructor(dr: DestroyRef) { super(dr); - this.listenProperty('label').subscribe(this.onLabelChanged.bind(this)); - this.listenProperty('tooltip').subscribe(this.onTooltipChanged.bind(this)); - this.listenProperty('isChecked').subscribe(this.onIsCheckedChanged.bind(this)); - } - - protected override updateForm(): void { - this.setProperty('label', this.widget.label); - this.setProperty('tooltip', this.widget.tooltip ?? null); - this.setProperty('isChecked', this.widget.isChecked); - } - - private onLabelChanged(value: string): void { - this.widget.label = value; - this.update.emit(); - } - - private onTooltipChanged(value: string | null): void { - if (value && value.trim().length === 0) { - value = null; - } - this.widget.tooltip = value ?? undefined; - this.update.emit(); - } - - private onIsCheckedChanged(value: boolean): void { - this.widget.isChecked = value; - this.update.emit(); } } diff --git a/src/app/pages/editor/properties/input-number-properties/input-number-properties.component.html b/src/app/pages/editor/properties/input-number-properties/input-number-properties.component.html index 83061b6..86d0ef7 100644 --- a/src/app/pages/editor/properties/input-number-properties/input-number-properties.component.html +++ b/src/app/pages/editor/properties/input-number-properties/input-number-properties.component.html @@ -1,4 +1,16 @@
+ + Label + + + + + Tooltip + + + Data Type @@ -18,7 +30,7 @@
@switch (getArraySize(widget.dataType)) { - @case (0) { + @case (1) { Value @@ -105,16 +117,4 @@
} - - - Label - - - - - Tooltip - - diff --git a/src/app/pages/editor/properties/input-number-properties/input-number-properties.component.ts b/src/app/pages/editor/properties/input-number-properties/input-number-properties.component.ts index bbc4f6d..57eecf0 100644 --- a/src/app/pages/editor/properties/input-number-properties/input-number-properties.component.ts +++ b/src/app/pages/editor/properties/input-number-properties/input-number-properties.component.ts @@ -65,20 +65,17 @@ export class InputNumberPropertiesComponent extends AbstractPropertiesComponent< protected override updateForm() { this.setProperty('dataType', this.widget.dataType); const size: number = FIGInputNumberWidget.getArraySize(this.widget.dataType); + const values: number[] = this.widget.value; - if (size > 0) { - const values: number[] = this.widget.value as number[]; - - this.setProperty('value0', this.formatNumber(values[0])); - this.setProperty('value1', this.formatNumber(values[1])); - if (size > 2) { - this.setProperty('value2', this.formatNumber(values[2])); - } - if (size > 3) { - this.setProperty('value3', this.formatNumber(values[3])); - } - } else { - this.setProperty('value0', this.formatNumber(this.widget.value as number)); + this.setProperty('value0', this.formatNumber(values[0])); + if (size > 1) { + this.setProperty('value2', this.formatNumber(values[1])); + } + if (size > 2) { + this.setProperty('value2', this.formatNumber(values[2])); + } + if (size > 3) { + this.setProperty('value3', this.formatNumber(values[3])); } this.setProperty('step', this.widget.step); this.setProperty('stepFast', this.widget.stepFast); @@ -96,16 +93,16 @@ export class InputNumberPropertiesComponent extends AbstractPropertiesComponent< } private onDataTypeChanged(dataType: FIGInputNumberType): void { - const prevValue: number | number[] = this.widget.value; + const prevValue: number[] = this.widget.value; const prevSize: number = this.getArraySize(this.widget.dataType); const size: number = this.getArraySize(dataType); this.widget.dataType = dataType; - if (size === 0 && prevValue instanceof Array) { - this.widget.value = prevValue[0]; - this.setProperty('value0', this.formatNumber(this.widget.value)); - } else if (size > 0 && !(prevValue instanceof Array)) { - const values: number[] = [prevValue]; + if (size === 1) { + this.widget.value[0] = prevValue[0]; + this.setProperty('value0', this.formatNumber(this.widget.value[0])); + } else if (size > 1) { + const values: number[] = prevValue; for (let i = 0; i < size - 1; i++) { values.push(0); @@ -115,14 +112,14 @@ export class InputNumberPropertiesComponent extends AbstractPropertiesComponent< this.setProperty(`value${i}`, this.formatNumber(values[i])); } } else if (prevSize < size) { - const values: number[] = this.widget.value as number[]; + const values: number[] = this.widget.value; for (let i = prevSize; i < size; i++) { values.push(0); this.setProperty(`value${i}`, this.formatNumber(values[i])); } } else if (prevSize > size) { - const values: number[] = this.widget.value as number[]; + const values: number[] = this.widget.value; const delta: number = prevSize - size; const index: number = prevSize - delta; @@ -133,7 +130,7 @@ export class InputNumberPropertiesComponent extends AbstractPropertiesComponent< private onValueChanged(value: string, index: number): void { const isArray: boolean = FIGInputNumberWidget.isArray(this.widget.dataType); - const prevValue: number = (isArray) ? (this.widget.value as number[])[index] : this.widget.value as number; + const prevValue: number = (isArray) ? this.widget.value[index] : this.widget.value[0]; const number: number = +value; if (FIGInputNumberWidget.isInteger(this.widget.dataType) && !Number.isInteger(number)) { @@ -141,9 +138,9 @@ export class InputNumberPropertiesComponent extends AbstractPropertiesComponent< return; } if (isArray) { - (this.widget.value as number[])[index] = number; + this.widget.value[index] = number; } else { - this.widget.value = number; + this.widget.value[0] = number; } this.update.emit(); } diff --git a/src/app/pages/editor/properties/menu-item-properties/menu-item-properties.component.html b/src/app/pages/editor/properties/menu-item-properties/menu-item-properties.component.html index 251865d..f452f6d 100644 --- a/src/app/pages/editor/properties/menu-item-properties/menu-item-properties.component.html +++ b/src/app/pages/editor/properties/menu-item-properties/menu-item-properties.component.html @@ -12,20 +12,20 @@
- Is selectable + Enabled + formControlName="enabled">
- Is selected + Is selectable + formControlName="isSelectable">
- Enabled + Is selected + formControlName="isSelected">
diff --git a/src/app/pages/editor/properties/plot-properties/plot-properties.component.html b/src/app/pages/editor/properties/plot-properties/plot-properties.component.html index 4291061..d05b3cc 100644 --- a/src/app/pages/editor/properties/plot-properties/plot-properties.component.html +++ b/src/app/pages/editor/properties/plot-properties/plot-properties.component.html @@ -1,4 +1,9 @@
+ + Label + + + Plot Type @@ -8,46 +13,48 @@ - Label - - - - - Value offset + Overlay text + type="text" + formControlName="overlayText" />
- Scale min + Width + formControlName="width" /> - Scale max + Height + formControlName="height" />
+ + Value offset + + +
- Width + Scale min + formControlName="scaleMin" /> - Height + Scale max + formControlName="scaleMax" />
@@ -57,11 +64,4 @@ type="number" formControlName="stride" /> - - - Overlay text - -
diff --git a/src/app/pages/editor/properties/popup-properties/popup-properties.component.html b/src/app/pages/editor/properties/popup-properties/popup-properties.component.html index 8e9d01f..3882f0e 100644 --- a/src/app/pages/editor/properties/popup-properties/popup-properties.component.html +++ b/src/app/pages/editor/properties/popup-properties/popup-properties.component.html @@ -11,14 +11,14 @@ formControlName="contextItem"> - - Debug label - - -
Show debug button
+ + + Debug label + + diff --git a/src/app/pages/editor/properties/progress-bar-properties/progress-bar-properties.component.html b/src/app/pages/editor/properties/progress-bar-properties/progress-bar-properties.component.html index 80dbcbb..ce223e9 100644 --- a/src/app/pages/editor/properties/progress-bar-properties/progress-bar-properties.component.html +++ b/src/app/pages/editor/properties/progress-bar-properties/progress-bar-properties.component.html @@ -1,11 +1,4 @@
-
- Value - - - -
- Label +
+ Value + + + +
+
Fill