@effindomv2/fui-as 0.2.0 → 0.2.2-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,7 +33,6 @@ export {
33
33
  export {
34
34
  BuildMode,
35
35
  DevToolsDomMirrorMode,
36
- PageZoomMode,
37
36
  } from '@effindomv2/runtime';
38
37
 
39
38
  export type {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effindomv2/fui-as",
3
- "version": "0.2.0",
3
+ "version": "0.2.2-alpha1",
4
4
  "private": false,
5
5
  "license": "AGPL-3.0-only OR LicenseRef-EffinDom-Commercial",
6
6
  "description": "EffinDom v2 AssemblyScript frontend framework SDK and browser harness",
@@ -89,6 +89,7 @@
89
89
  "LICENSES"
90
90
  ],
91
91
  "scripts": {
92
+ "update:upstream": "node scripts/update-upstream-versions.mjs",
92
93
  "guard:runtime-dependency": "bash scripts/check-runtime-dependency.sh",
93
94
  "generate:abi": "mkdir -p build && esbuild ../abi/generate.ts --bundle --format=esm --platform=node --target=node20 --packages=external --outfile=build/generate-abi.mjs && node build/generate-abi.mjs fui-as-ui && node build/generate-abi.mjs fui-as-host && node build/generate-abi.mjs fui-as-enums",
94
95
  "check:abi": "mkdir -p build && esbuild ../abi/generate.ts --bundle --format=esm --platform=node --target=node20 --packages=external --outfile=build/generate-abi.mjs && node build/generate-abi.mjs fui-as-ui --check && node build/generate-abi.mjs fui-as-host --check && node build/generate-abi.mjs fui-as-enums --check",
@@ -105,7 +106,7 @@
105
106
  "typecheck": "npm run guard:runtime-dependency && npm run check:abi && tsc -p tsconfig.json --noEmit"
106
107
  },
107
108
  "dependencies": {
108
- "@effindomv2/runtime": "0.2.2"
109
+ "@effindomv2/runtime": "0.2.10"
109
110
  },
110
111
  "devDependencies": {
111
112
  "@as-pect/assembly": "9.0.0",
@@ -0,0 +1,27 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const packageDirectory = dirname(dirname(fileURLToPath(import.meta.url)));
7
+ const packagePath = join(packageDirectory, 'package.json');
8
+ const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
9
+ const versionPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
10
+
11
+ const runtimeVersion = execFileSync(
12
+ npm,
13
+ ['view', '@effindomv2/runtime', 'dist-tags.latest'],
14
+ { encoding: 'utf8' },
15
+ ).trim();
16
+ if (!versionPattern.test(runtimeVersion)) {
17
+ throw new Error(`npm returned an invalid EffinDOM runtime version: ${JSON.stringify(runtimeVersion)}`);
18
+ }
19
+
20
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
21
+ packageJson.dependencies['@effindomv2/runtime'] = runtimeVersion;
22
+ writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
23
+ execFileSync(npm, ['install', '--package-lock-only', '--ignore-scripts'], {
24
+ cwd: packageDirectory,
25
+ stdio: 'inherit',
26
+ });
27
+ console.log(`Pinned @effindomv2/runtime@${runtimeVersion}.`);
package/src/Fui.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export {
2
2
  Application,
3
3
  ApplicationRegistration,
4
+ PageZoomMode,
4
5
  ManagedApplicationController,
5
6
  createApplication,
6
7
  createManagedApplication,
@@ -44,6 +45,7 @@ export {
44
45
  export { Fetch, FetchErrorEventArgs, FetchRequest, FetchResponse } from "./core/Fetch";
45
46
  export { log } from "./core/Logger";
46
47
  export { disposeAll, Disposable } from "./core/Disposable";
48
+ export { retainedView, RetainedView, RetainedViewLifecycleHandler } from "./core/RetainedView";
47
49
  export { cancelAllTimers, cancelTimer, hasTimer, scheduleTimer } from "./core/Timers";
48
50
  export { LoadedEventArgs, LoadedHandler, onLoaded, onLoadedWith } from "./core/FrameScheduler";
49
51
  export { SetBackgroundAction, SetTextAction } from "./core/Actions";
@@ -217,12 +219,10 @@ export {
217
219
  CheckboxIndicatorTemplate,
218
220
  CheckboxIndicatorVisualState,
219
221
  Checkbox,
220
- clearControlTemplates,
221
222
  ComboBox,
222
223
  ComboBoxCommitMode,
223
224
  ComboBoxFilterMode,
224
225
  ComboBoxItem,
225
- ControlTemplateSet,
226
226
  ContextMenu,
227
227
  Dialog,
228
228
  DropdownSizing,
@@ -241,7 +241,6 @@ export {
241
241
  DropdownOptionRowTemplate,
242
242
  DropdownOptionRowVisualState,
243
243
  Form,
244
- getControlTemplates,
245
244
  MenuItem,
246
245
  NavLink,
247
246
  LabeledControlSizing,
@@ -265,13 +264,16 @@ export {
265
264
  SwitchIndicatorTemplate,
266
265
  SwitchIndicatorVisualState,
267
266
  Switch,
267
+ TabContentFactory,
268
+ TabItem,
269
+ TabSelectionChangedEventArgs,
270
+ TabView,
268
271
  TextArea,
269
272
  TextInputPresenter,
270
273
  TextInput,
271
274
  TextInputTemplate,
272
275
  TextInputColors,
273
276
  TextInputVisualState,
274
- useControlTemplates,
275
277
  } from "./controls";
276
278
  export {
277
279
  Border,
package/src/FuiExports.ts CHANGED
@@ -26,6 +26,7 @@ export {
26
26
  __fui_on_cross_selection_changed,
27
27
  __fui_on_focus_changed,
28
28
  __fui_on_frame,
29
+ __fui_needs_animation_frame,
29
30
  __fui_get_gesture_intent,
30
31
  __fui_get_long_press_minimum_duration_ms,
31
32
  __fui_get_long_press_movement_tolerance,
@@ -22,7 +22,6 @@ import { FontFamily, FontStyle, FontWeight } from "../core/Typography";
22
22
  import { Border, FlexBox, TextCore } from "../nodes";
23
23
  import { ClickEventArgs, HoverChangedEventArgs, PointerEventArgs } from "../core/Node";
24
24
  import { ButtonColors } from "./ButtonColors";
25
- import { getControlTemplates } from "./ControlTemplateSet";
26
25
  import {
27
26
  ButtonPresenter,
28
27
  ButtonTemplate,
@@ -642,9 +641,7 @@ export class Button extends FlexBox {
642
641
  if (template !== null) {
643
642
  return template.create();
644
643
  }
645
- const templateSet = getControlTemplates();
646
- const appTemplate = templateSet !== null ? templateSet.button : null;
647
- return (appTemplate === null ? defaultButtonTemplate : appTemplate).create();
644
+ return defaultButtonTemplate.create();
648
645
  }
649
646
 
650
647
  private replacePresenter(nextPresenter: ButtonPresenter): void {
@@ -4,7 +4,6 @@ import { CheckboxChangedEventArgs, ClickEventArgs } from "../core/Node";
4
4
  import { PersistedInt32Codec, PersistedValueState } from "../core/PersistedState";
5
5
  import { bind1 } from "../core/bind";
6
6
  import { LabeledControlSizing } from "./ControlSizing";
7
- import { getControlTemplates } from "./ControlTemplateSet";
8
7
  import { activeTheme } from "../core/Theme";
9
8
  import { PressableLabeledControl } from "./internal/PressableLabeledControl";
10
9
  import {
@@ -45,11 +44,7 @@ function createIndicatorPresenter(template: CheckboxIndicatorTemplate | null, si
45
44
  if (template !== null) {
46
45
  return template.create(sizing);
47
46
  }
48
- const templateSet = getControlTemplates();
49
- const appTemplate = templateSet !== null ? templateSet.checkboxIndicator : null;
50
- return appTemplate === null
51
- ? createDefaultCheckboxIndicatorPresenter(sizing)
52
- : appTemplate.create(sizing);
47
+ return createDefaultCheckboxIndicatorPresenter(sizing);
53
48
  }
54
49
 
55
50
  export class Checkbox extends PressableLabeledControl {
@@ -214,11 +209,7 @@ export class Checkbox extends PressableLabeledControl {
214
209
  }
215
210
 
216
211
  private usesDefaultIndicatorPresenter(): bool {
217
- if (this.templateOverride !== null) {
218
- return false;
219
- }
220
- const templateSet = getControlTemplates();
221
- return templateSet === null || templateSet.checkboxIndicator === null;
212
+ return this.templateOverride === null;
222
213
  }
223
214
 
224
215
  }
@@ -23,7 +23,6 @@ import { FlexBox, ScrollView } from "../nodes";
23
23
  import { bind1 } from "../core/bind";
24
24
  import { DropdownSizing } from "./ControlSizing";
25
25
  import { DropdownColors } from "./DropdownColors";
26
- import { getControlTemplates } from "./ControlTemplateSet";
27
26
  import {
28
27
  createDefaultDropdownChevronPresenter,
29
28
  DropdownChevronPresenter,
@@ -74,22 +73,14 @@ function createFieldPresenter(template: DropdownFieldTemplate | null, sizing: Dr
74
73
  if (template !== null) {
75
74
  return template.create(sizing);
76
75
  }
77
- const templateSet = getControlTemplates();
78
- const appTemplate = templateSet !== null ? templateSet.dropdownField : null;
79
- return appTemplate === null
80
- ? createDefaultDropdownFieldPresenter(sizing)
81
- : appTemplate.create(sizing);
76
+ return createDefaultDropdownFieldPresenter(sizing);
82
77
  }
83
78
 
84
79
  function createChevronPresenter(template: DropdownChevronTemplate | null, sizing: DropdownSizing | null = null): DropdownChevronPresenter {
85
80
  if (template !== null) {
86
81
  return template.create(sizing);
87
82
  }
88
- const templateSet = getControlTemplates();
89
- const appTemplate = templateSet !== null ? templateSet.dropdownChevron : null;
90
- return appTemplate === null
91
- ? createDefaultDropdownChevronPresenter(sizing)
92
- : appTemplate.create(sizing);
83
+ return createDefaultDropdownChevronPresenter(sizing);
93
84
  }
94
85
 
95
86
  export class DropdownItem {
@@ -722,19 +713,11 @@ export class Dropdown extends FlexBox implements GlobalKeyHandler, SelectablePop
722
713
  }
723
714
 
724
715
  private usesDefaultFieldPresenter(): bool {
725
- if (this.fieldTemplateValue !== null) {
726
- return false;
727
- }
728
- const templateSet = getControlTemplates();
729
- return templateSet === null || templateSet.dropdownField === null;
716
+ return this.fieldTemplateValue === null;
730
717
  }
731
718
 
732
719
  private usesDefaultChevronPresenter(): bool {
733
- if (this.chevronTemplateValue !== null) {
734
- return false;
735
- }
736
- const templateSet = getControlTemplates();
737
- return templateSet === null || templateSet.dropdownChevron === null;
720
+ return this.chevronTemplateValue === null;
738
721
  }
739
722
 
740
723
  getPopupListItemCount(): i32 {
@@ -5,7 +5,6 @@ import { activeTheme } from "../core/Theme";
5
5
  import { bind1 } from "../core/bind";
6
6
  import { LabeledControlSizing } from "./ControlSizing";
7
7
  import { PressableLabeledControl } from "./internal/PressableLabeledControl";
8
- import { getControlTemplates } from "./ControlTemplateSet";
9
8
  import { RadioGroup } from "./RadioGroup";
10
9
  import {
11
10
  createDefaultRadioIndicatorPresenter,
@@ -18,11 +17,7 @@ function createIndicatorPresenter(template: RadioIndicatorTemplate | null, sizin
18
17
  if (template !== null) {
19
18
  return template.create(sizing);
20
19
  }
21
- const templateSet = getControlTemplates();
22
- const appTemplate = templateSet !== null ? templateSet.radioIndicator : null;
23
- return appTemplate === null
24
- ? createDefaultRadioIndicatorPresenter(sizing)
25
- : appTemplate.create(sizing);
20
+ return createDefaultRadioIndicatorPresenter(sizing);
26
21
  }
27
22
 
28
23
  export class RadioButton extends PressableLabeledControl {
@@ -188,10 +183,6 @@ export class RadioButton extends PressableLabeledControl {
188
183
  }
189
184
 
190
185
  private usesDefaultIndicatorPresenter(): bool {
191
- if (this.templateOverride !== null) {
192
- return false;
193
- }
194
- const templateSet = getControlTemplates();
195
- return templateSet === null || templateSet.radioIndicator === null;
186
+ return this.templateOverride === null;
196
187
  }
197
188
  }
@@ -21,7 +21,6 @@ import { FlexBox } from "../nodes";
21
21
  import { bind1 } from "../core/bind";
22
22
  import { SliderSizing } from "./ControlSizing";
23
23
  import { SliderColors } from "./SliderColors";
24
- import { getControlTemplates } from "./ControlTemplateSet";
25
24
  import {
26
25
  createDefaultSliderPresenter,
27
26
  SliderPresenter,
@@ -68,11 +67,7 @@ function createSliderPresenter(template: SliderTemplate | null, sizing: SliderSi
68
67
  if (template !== null) {
69
68
  return template.create(sizing);
70
69
  }
71
- const templateSet = getControlTemplates();
72
- const appTemplate = templateSet !== null ? templateSet.slider : null;
73
- return appTemplate === null
74
- ? createDefaultSliderPresenter(sizing)
75
- : appTemplate.create(sizing);
70
+ return createDefaultSliderPresenter(sizing);
76
71
  }
77
72
 
78
73
  export class Slider extends FlexBox implements DragGestureHost {
@@ -472,11 +467,7 @@ export class Slider extends FlexBox implements DragGestureHost {
472
467
  }
473
468
 
474
469
  private usesDefaultPresenter(): bool {
475
- if (this.templateOverride !== null) {
476
- return false;
477
- }
478
- const templateSet = getControlTemplates();
479
- return templateSet === null || templateSet.slider === null;
470
+ return this.templateOverride === null;
480
471
  }
481
472
 
482
473
  private track(disposable: Disposable): void {
@@ -5,7 +5,6 @@ import { PersistedBoolCodec, PersistedValueState } from "../core/PersistedState"
5
5
  import { bind1 } from "../core/bind";
6
6
  import { activeTheme } from "../core/Theme";
7
7
  import { LabeledControlSizing } from "./ControlSizing";
8
- import { getControlTemplates } from "./ControlTemplateSet";
9
8
  import { PressableLabeledControl } from "./internal/PressableLabeledControl";
10
9
  import {
11
10
  defaultSwitchIndicatorTemplate,
@@ -36,9 +35,7 @@ function createIndicatorPresenter(template: SwitchIndicatorTemplate | null, sizi
36
35
  if (template !== null) {
37
36
  return template.create(sizing);
38
37
  }
39
- const templateSet = getControlTemplates();
40
- const appTemplate = templateSet !== null ? templateSet.switchIndicator : null;
41
- return (appTemplate === null ? defaultSwitchIndicatorTemplate : appTemplate).create(sizing);
38
+ return defaultSwitchIndicatorTemplate.create(sizing);
42
39
  }
43
40
 
44
41
  export class Switch extends PressableLabeledControl {
@@ -0,0 +1,345 @@
1
+ import { Callback1, Handler1 } from "../core/BoundCallback";
2
+ import { throwNullArgument } from "../core/Errors";
3
+ import { FlexDirection, SemanticRole } from "../core/ffi";
4
+ import { warn } from "../core/Logger";
5
+ import { retainedView, RetainedView } from "../core/RetainedView";
6
+ import { bind1 } from "../core/bind";
7
+ import { FlexBox } from "../nodes";
8
+
9
+ const NO_PENDING_SELECTION: i32 = -2;
10
+
11
+ export type TabContentFactory = () => RetainedView;
12
+
13
+ export class TabSelectionChangedEventArgs {
14
+ constructor(
15
+ readonly previousIndex: i32,
16
+ readonly selectedIndex: i32,
17
+ readonly previousItem: TabItem | null,
18
+ readonly selectedItem: TabItem | null,
19
+ ) {}
20
+ }
21
+
22
+ export class TabItem {
23
+ private labelValue: string;
24
+ private descriptionValue: string = "";
25
+ private enabledValue: bool = true;
26
+ private factoryValue: TabContentFactory | null;
27
+ private preparedViewValue: RetainedView | null = null;
28
+ private retainedViewValue: RetainedView | null = null;
29
+ private ownerValue: TabView | null = null;
30
+
31
+ constructor(label: string, content: TabContentFactory | null = null) {
32
+ this.labelValue = label;
33
+ this.factoryValue = content;
34
+ }
35
+
36
+ get labelText(): string { return this.labelValue; }
37
+ get descriptionText(): string { return this.descriptionValue; }
38
+ get isEnabled(): bool { return this.enabledValue; }
39
+ get isMaterialized(): bool { return this.retainedViewValue !== null; }
40
+
41
+ label(value: string): this {
42
+ this.labelValue = value;
43
+ const owner = this.ownerValue;
44
+ if (owner !== null) changetype<TabView>(owner)._itemMetadataChanged(this);
45
+ return this;
46
+ }
47
+
48
+ description(value: string): this {
49
+ this.descriptionValue = value;
50
+ const owner = this.ownerValue;
51
+ if (owner !== null) changetype<TabView>(owner)._itemMetadataChanged(this);
52
+ return this;
53
+ }
54
+
55
+ enabled(flag: bool): this {
56
+ if (this.enabledValue == flag) return this;
57
+ this.enabledValue = flag;
58
+ const owner = this.ownerValue;
59
+ if (owner !== null) changetype<TabView>(owner)._itemEnabledChanged(this);
60
+ return this;
61
+ }
62
+
63
+ content(factory: TabContentFactory): this {
64
+ if (this.isMaterialized) {
65
+ warn("Lifecycle", "TabItem.content() cannot replace content after the tab has been materialized.");
66
+ return this;
67
+ }
68
+ this.preparedViewValue = null;
69
+ this.factoryValue = factory;
70
+ return this;
71
+ }
72
+
73
+ contentView(view: RetainedView): this {
74
+ if (this.isMaterialized) {
75
+ warn("Lifecycle", "TabItem.contentView() cannot replace content after the tab has been materialized.");
76
+ return this;
77
+ }
78
+ this.preparedViewValue = view;
79
+ this.factoryValue = null;
80
+ return this;
81
+ }
82
+
83
+ _bindOwner(owner: TabView | null): void { this.ownerValue = owner; }
84
+
85
+ _resolveView(): RetainedView {
86
+ const retained = this.retainedViewValue;
87
+ if (retained !== null) return changetype<RetainedView>(retained);
88
+ const prepared = this.preparedViewValue;
89
+ let resolved: RetainedView;
90
+ if (prepared !== null) {
91
+ resolved = changetype<RetainedView>(prepared);
92
+ } else {
93
+ const factory = this.factoryValue;
94
+ resolved = factory === null
95
+ ? retainedView(new FlexBox().fillSize())
96
+ : changetype<TabContentFactory>(factory)();
97
+ }
98
+ this.preparedViewValue = null;
99
+ this.retainedViewValue = resolved;
100
+ return resolved;
101
+ }
102
+
103
+ _existingView(): RetainedView | null { return this.retainedViewValue; }
104
+
105
+ _disposeContent(): void {
106
+ const retained = this.retainedViewValue;
107
+ if (retained !== null) changetype<RetainedView>(retained).dispose();
108
+ const prepared = this.preparedViewValue;
109
+ if (prepared !== null && prepared !== retained) changetype<RetainedView>(prepared).dispose();
110
+ this.retainedViewValue = null;
111
+ this.preparedViewValue = null;
112
+ }
113
+ }
114
+
115
+ export class TabView extends FlexBox {
116
+ private readonly itemsValue: Array<TabItem> = new Array<TabItem>();
117
+ private selectedIndexValue: i32 = -1;
118
+ private selectingValue: bool = false;
119
+ private pendingSelectionValue: i32 = NO_PENDING_SELECTION;
120
+ private pendingEmitValue: bool = false;
121
+ private changedCallback: ((event: TabSelectionChangedEventArgs) => void) | null = null;
122
+ private changedBinding: Callback1<TabSelectionChangedEventArgs> | null = null;
123
+ readonly contentPresenter: FlexBox;
124
+
125
+ constructor(items: Array<TabItem> = new Array<TabItem>()) {
126
+ super();
127
+ this.contentPresenter = new FlexBox().fillSize() as FlexBox;
128
+ this.contentPresenter.semanticRole(SemanticRole.TabPanel);
129
+ this.contentPresenter.semanticLabel("Tab panel");
130
+ this.flexDirection(FlexDirection.Column);
131
+ this.addChildNode(this.contentPresenter);
132
+ this.items(items);
133
+ }
134
+
135
+ get selectedIndex(): i32 { return this.selectedIndexValue; }
136
+ get selectedItem(): TabItem | null {
137
+ return this.selectedIndexValue >= 0 && this.selectedIndexValue < this.itemsValue.length
138
+ ? unchecked(this.itemsValue[this.selectedIndexValue])
139
+ : null;
140
+ }
141
+ get itemCount(): i32 { return this.itemsValue.length; }
142
+
143
+ itemAt(index: i32): TabItem | null {
144
+ return index >= 0 && index < this.itemsValue.length ? unchecked(this.itemsValue[index]) : null;
145
+ }
146
+
147
+ items(items: Array<TabItem>): this {
148
+ this.clearItems();
149
+ for (let index = 0; index < items.length; ++index) this.addItem(unchecked(items[index]));
150
+ return this;
151
+ }
152
+
153
+ addItem(item: TabItem): this {
154
+ if (item == null) throwNullArgument("TabView.addItem", "item");
155
+ if (this.indexOfItem(item) >= 0) return this;
156
+ item._bindOwner(this);
157
+ const index = this.itemsValue.length;
158
+ this.itemsValue.push(item);
159
+ if (this.selectedIndexValue < 0 && item.isEnabled) this.selectIndexInternal(index, false, null);
160
+ return this;
161
+ }
162
+
163
+ removeItem(item: TabItem): this {
164
+ const index = this.indexOfItem(item);
165
+ if (index >= 0) this.removeItemAt(index);
166
+ return this;
167
+ }
168
+
169
+ removeItemAt(index: i32): this {
170
+ if (index < 0 || index >= this.itemsValue.length) return this;
171
+ const removed = unchecked(this.itemsValue[index]);
172
+ const wasSelected = index == this.selectedIndexValue;
173
+ if (wasSelected) this.detachActiveView();
174
+ removed._bindOwner(null);
175
+ for (let cursor = index; cursor < this.itemsValue.length - 1; ++cursor) {
176
+ unchecked(this.itemsValue[cursor] = unchecked(this.itemsValue[cursor + 1]));
177
+ }
178
+ this.itemsValue.length -= 1;
179
+ if (wasSelected) {
180
+ const previousIndex = this.selectedIndexValue;
181
+ this.selectedIndexValue = -1;
182
+ const replacement = this.findReplacementIndex(index);
183
+ if (replacement >= 0) this.selectIndexInternal(replacement, true, removed, previousIndex);
184
+ else this.emitChanged(previousIndex, -1, removed, null);
185
+ } else if (this.selectedIndexValue > index) {
186
+ this.selectedIndexValue -= 1;
187
+ }
188
+ removed._disposeContent();
189
+ return this;
190
+ }
191
+
192
+ clearItems(): this {
193
+ const previousIndex = this.selectedIndexValue;
194
+ const previousItem = this.selectedItem;
195
+ this.detachActiveView();
196
+ for (let index = 0; index < this.itemsValue.length; ++index) {
197
+ const item = unchecked(this.itemsValue[index]);
198
+ item._bindOwner(null);
199
+ item._disposeContent();
200
+ }
201
+ this.itemsValue.length = 0;
202
+ this.selectedIndexValue = -1;
203
+ if (previousItem !== null) this.emitChanged(previousIndex, -1, previousItem, null);
204
+ return this;
205
+ }
206
+
207
+ selectIndex(index: i32): this {
208
+ this.selectIndexInternal(index, true, null);
209
+ return this;
210
+ }
211
+
212
+ onSelectionChanged(callback: ((event: TabSelectionChangedEventArgs) => void) | null): this {
213
+ this.changedCallback = callback;
214
+ this.changedBinding = null;
215
+ return this;
216
+ }
217
+
218
+ bindSelectionChanged<Owner>(owner: Owner, handler: Handler1<Owner, TabSelectionChangedEventArgs>): this {
219
+ this.changedCallback = null;
220
+ this.changedBinding = bind1<Owner, TabSelectionChangedEventArgs>(owner, handler);
221
+ return this;
222
+ }
223
+
224
+ onSelectionChangedWith<Owner>(owner: Owner, handler: Handler1<Owner, TabSelectionChangedEventArgs>): this {
225
+ return this.bindSelectionChanged(owner, handler);
226
+ }
227
+
228
+ _itemEnabledChanged(item: TabItem): void {
229
+ const index = this.indexOfItem(item);
230
+ if (index < 0) return;
231
+ if (!item.isEnabled && index == this.selectedIndexValue) {
232
+ const previousIndex = this.selectedIndexValue;
233
+ this.detachActiveView();
234
+ this.selectedIndexValue = -1;
235
+ const replacement = this.findReplacementIndex(index);
236
+ if (replacement >= 0) this.selectIndexInternal(replacement, true, item, previousIndex);
237
+ else this.emitChanged(previousIndex, -1, item, null);
238
+ } else if (item.isEnabled && this.selectedIndexValue < 0) {
239
+ this.selectIndexInternal(index, true, null);
240
+ }
241
+ }
242
+
243
+ _itemMetadataChanged(item: TabItem): void {
244
+ const index = this.indexOfItem(item);
245
+ if (index < 0) return;
246
+ if (index == this.selectedIndexValue) this.contentPresenter.semanticLabel(item.labelText + " tab panel");
247
+ }
248
+
249
+ dispose(): void {
250
+ this.detachActiveView();
251
+ for (let index = 0; index < this.itemsValue.length; ++index) {
252
+ const item = unchecked(this.itemsValue[index]);
253
+ item._bindOwner(null);
254
+ item._disposeContent();
255
+ }
256
+ super.dispose();
257
+ }
258
+
259
+ private selectIndexInternal(
260
+ index: i32,
261
+ emit: bool,
262
+ previousItemOverride: TabItem | null,
263
+ previousIndexOverride: i32 = -1,
264
+ ): void {
265
+ if (!this.isEnabledIndex(index)) return;
266
+ if (this.selectingValue) {
267
+ this.pendingSelectionValue = index;
268
+ this.pendingEmitValue = emit;
269
+ return;
270
+ }
271
+ this.selectingValue = true;
272
+ let requestedIndex = index;
273
+ let requestedEmit = emit;
274
+ let overrideItem = previousItemOverride;
275
+ let overrideIndex = previousIndexOverride;
276
+ let iterations = 0;
277
+ while (requestedIndex >= 0 && this.isEnabledIndex(requestedIndex)) {
278
+ const previousIndex = overrideItem !== null ? overrideIndex : this.selectedIndexValue;
279
+ const previousItem = overrideItem !== null ? overrideItem : this.selectedItem;
280
+ overrideItem = null;
281
+ overrideIndex = -1;
282
+ if (this.selectedIndexValue != requestedIndex) {
283
+ this.detachActiveView();
284
+ this.selectedIndexValue = requestedIndex;
285
+ const item = unchecked(this.itemsValue[requestedIndex]);
286
+ const view = item._resolveView();
287
+ this.contentPresenter.addChildNode(view.root);
288
+ this.contentPresenter.semanticLabel(item.labelText + " tab panel");
289
+ view.activate();
290
+ if (requestedEmit) this.emitChanged(previousIndex, requestedIndex, previousItem, item);
291
+ }
292
+ if (this.pendingSelectionValue == NO_PENDING_SELECTION) break;
293
+ requestedIndex = this.pendingSelectionValue;
294
+ requestedEmit = this.pendingEmitValue;
295
+ this.pendingSelectionValue = NO_PENDING_SELECTION;
296
+ iterations += 1;
297
+ if (iterations > this.itemsValue.length + 8) {
298
+ warn("Lifecycle", "TabView stopped a re-entrant selection loop.");
299
+ break;
300
+ }
301
+ }
302
+ this.pendingSelectionValue = NO_PENDING_SELECTION;
303
+ this.selectingValue = false;
304
+ }
305
+
306
+ private detachActiveView(): void {
307
+ const item = this.selectedItem;
308
+ if (item === null) return;
309
+ const view = changetype<TabItem>(item)._existingView();
310
+ if (view === null) return;
311
+ const retained = changetype<RetainedView>(view);
312
+ retained.deactivate();
313
+ this.contentPresenter.removeChildNode(retained.root);
314
+ }
315
+
316
+ private emitChanged(
317
+ previousIndex: i32,
318
+ selectedIndex: i32,
319
+ previousItem: TabItem | null,
320
+ selectedItem: TabItem | null,
321
+ ): void {
322
+ const event = new TabSelectionChangedEventArgs(previousIndex, selectedIndex, previousItem, selectedItem);
323
+ const callback = this.changedCallback;
324
+ if (callback !== null) callback(event);
325
+ const binding = this.changedBinding;
326
+ if (binding !== null) binding.invoke(event);
327
+ }
328
+
329
+ private indexOfItem(item: TabItem): i32 {
330
+ for (let index = 0; index < this.itemsValue.length; ++index) {
331
+ if (unchecked(this.itemsValue[index]) === item) return index;
332
+ }
333
+ return -1;
334
+ }
335
+
336
+ private isEnabledIndex(index: i32): bool {
337
+ return index >= 0 && index < this.itemsValue.length && unchecked(this.itemsValue[index]).isEnabled;
338
+ }
339
+
340
+ private findReplacementIndex(index: i32): i32 {
341
+ for (let cursor = index; cursor < this.itemsValue.length; ++cursor) if (this.isEnabledIndex(cursor)) return cursor;
342
+ for (let cursor = index - 1; cursor >= 0; --cursor) if (this.isEnabledIndex(cursor)) return cursor;
343
+ return -1;
344
+ }
345
+ }
@@ -20,6 +20,12 @@ export { AntiSelectionArea } from "./AntiSelectionArea";
20
20
  export { SelectionArea } from "./SelectionArea";
21
21
  export { Slider } from "./Slider";
22
22
  export { Switch } from "./Switch";
23
+ export {
24
+ TabContentFactory,
25
+ TabItem,
26
+ TabSelectionChangedEventArgs,
27
+ TabView,
28
+ } from "./TabView";
23
29
  export { TextArea } from "./TextArea";
24
30
  export { TextInput } from "./TextInput";
25
31
  export * from "./templating";
@@ -6,7 +6,6 @@ import { Theme, activeTheme } from "../../core/Theme";
6
6
  import { FlexBox, Portal, ScrollBarVisibility, ScrollBox } from "../../nodes";
7
7
  import { DropdownSizing } from "../ControlSizing";
8
8
  import { DropdownColors } from "../DropdownColors";
9
- import { getControlTemplates } from "../ControlTemplateSet";
10
9
  import {
11
10
  createDefaultDropdownOptionRowPresenter,
12
11
  DropdownOptionRowPresenter,
@@ -35,11 +34,7 @@ function createOptionRowPresenter(template: DropdownOptionRowTemplate | null, si
35
34
  if (template !== null) {
36
35
  return template.create(sizing);
37
36
  }
38
- const templateSet = getControlTemplates();
39
- const appTemplate = templateSet !== null ? templateSet.dropdownOptionRow : null;
40
- return appTemplate === null
41
- ? createDefaultDropdownOptionRowPresenter(sizing)
42
- : appTemplate.create(sizing);
37
+ return createDefaultDropdownOptionRowPresenter(sizing);
43
38
  }
44
39
 
45
40
  class SelectablePopupListOptionNode extends FlexBox {
@@ -22,7 +22,6 @@ import { FlexBox, ScrollBarVisibility, ScrollBox, TextCore } from "../../nodes";
22
22
  import { bind1 } from "../../core/bind";
23
23
  import { ScrollState } from "../../nodes/ScrollState";
24
24
  import { ScrollView } from "../../nodes/ScrollView";
25
- import { getControlTemplates } from "../ControlTemplateSet";
26
25
  import { TextInputColors } from "../TextInputColors";
27
26
  import {
28
27
  defaultTextInputTemplate,
@@ -973,11 +972,7 @@ export class TextInputCore extends FlexBox {
973
972
  if (template !== null) {
974
973
  return template.create();
975
974
  }
976
- const templateSet = getControlTemplates();
977
- const appTemplate = templateSet !== null
978
- ? (this.profile.multiline ? templateSet.textArea : templateSet.textInput)
979
- : null;
980
- return (appTemplate === null ? defaultTextInputTemplate : appTemplate).create();
975
+ return defaultTextInputTemplate.create();
981
976
  }
982
977
 
983
978
  private editorHostNode(): Node {
@@ -5,12 +5,6 @@ export {
5
5
  createDefaultButtonPresenter,
6
6
  defaultButtonTemplate,
7
7
  } from "./internal/ButtonPresenter";
8
- export {
9
- ControlTemplateSet,
10
- clearControlTemplates,
11
- getControlTemplates,
12
- useControlTemplates,
13
- } from "./ControlTemplateSet";
14
8
  export { PressableIndicatorMetrics } from "./internal/PressableIndicatorPresenter";
15
9
  export {
16
10
  CheckboxIndicatorPresenter,
@@ -10,7 +10,7 @@ import { SelectionHandleAdornerManager } from "./SelectionHandleAdornerManager";
10
10
  import { ToolTipManager } from "./ToolTipManager";
11
11
  import * as ui from "../bindings/ui";
12
12
  import { flushCommit, fireLoadedCallbacks, markNeedsCommit, resetCommitState } from "./FrameScheduler";
13
- import { fui_set_application_caption, HandleValue, Unit } from "./ffi";
13
+ import { fui_set_application_caption, fui_set_page_zoom_enabled, HandleValue, Unit } from "./ffi";
14
14
  import { Node } from "./Node";
15
15
  import { cancelAllTimers } from "./Timers";
16
16
  import {
@@ -23,12 +23,16 @@ import {
23
23
  useSystemTheme,
24
24
  } from "./Theme";
25
25
  import { disposeAllWorkers } from "./Worker";
26
- import { ControlTemplateSet, clearControlTemplates, getControlTemplates, useControlTemplates } from "../controls/ControlTemplateSet";
27
26
 
28
27
  let mountedRoot: Node | null = null;
29
28
  let mountedShell: FlexBox | null = null;
30
29
  let exportedApplication: Application<Node> | null = null;
31
30
 
31
+ export enum PageZoomMode {
32
+ Disabled = 0,
33
+ Enabled = 1,
34
+ }
35
+
32
36
  function createEmptyPage(): Node {
33
37
  return new FlexBox().width(100.0, Unit.Percent).height(100.0, Unit.Percent);
34
38
  }
@@ -78,8 +82,6 @@ export class ApplicationRegistration {
78
82
  private customThemeOnRegister: Theme | null = null;
79
83
  private accentColorOnRegister: u32 = 0;
80
84
  private hasAccentColorOnRegister: bool = false;
81
- private controlTemplatesOnRegister: ControlTemplateSet | null = null;
82
- private hasControlTemplatesOnRegister: bool = false;
83
85
 
84
86
  page(buildPage: () => Node): this {
85
87
  this.buildPageFn = buildPage;
@@ -108,15 +110,8 @@ export class ApplicationRegistration {
108
110
  return this;
109
111
  }
110
112
 
111
- controlTemplates(templates: ControlTemplateSet | null): this {
112
- this.controlTemplatesOnRegister = templates;
113
- this.hasControlTemplatesOnRegister = true;
114
- return this;
115
- }
116
-
117
113
  register(): Application<Node> {
118
114
  const app = createApplication(this.buildPageFn);
119
- app.useControlTemplates(this.hasControlTemplatesOnRegister ? this.controlTemplatesOnRegister : null);
120
115
  if (this.useSystemThemeOnRegister) {
121
116
  app.useSystemTheme();
122
117
  return app;
@@ -208,18 +203,6 @@ export class Application<TPage> {
208
203
  return activeTheme.value;
209
204
  }
210
205
 
211
- useControlTemplates(templates: ControlTemplateSet | null): ControlTemplateSet | null {
212
- return useControlTemplates(templates);
213
- }
214
-
215
- clearControlTemplates(): void {
216
- clearControlTemplates();
217
- }
218
-
219
- getControlTemplates(): ControlTemplateSet | null {
220
- return getControlTemplates();
221
- }
222
-
223
206
  static mount(root: Node): Node {
224
207
  const previousShell = mountedShell;
225
208
  if (previousShell !== null) {
@@ -252,6 +235,10 @@ export class Application<TPage> {
252
235
  fui_set_application_caption(bytes.length > 0 ? bytes.dataStart : 0, <u32>bytes.length);
253
236
  }
254
237
 
238
+ static pageZoom(value: PageZoomMode): void {
239
+ fui_set_page_zoom_enabled(value === PageZoomMode.Enabled);
240
+ }
241
+
255
242
  static unmount(): void {
256
243
  const previous = mountedShell;
257
244
  if (previous === null) {
@@ -147,20 +147,20 @@ export class Bitmap implements Disposable {
147
147
  *
148
148
  * @param node A built Text or RichText node sized to match this bitmap's dimensions.
149
149
  */
150
- render(node: Node, x: f32 = 0.0, y: f32 = 0.0, scale: f32 = 1.0): void {
150
+ render(node: Node, x: f32 = 0.0, y: f32 = 0.0, scale: f32 = 1.0): bool {
151
151
  this.ensureNotDisposed("Bitmap.render");
152
152
  const handle = node.builtHandle;
153
- if (handle == <u64>0) return;
154
- ui.renderNodeToRgba(handle, this.width, this.height, this.pixelPtr(), <u32>this.pixelBytes.length, scale, x, y);
153
+ if (handle == <u64>0) return false;
154
+ return ui.renderNodeToRgba(handle, this.width, this.height, this.pixelPtr(), <u32>this.pixelBytes.length, scale, x, y) != 0;
155
155
  }
156
156
 
157
- renderTextLayout(layout: TextLayout, x: f32 = 0.0, y: f32 = 0.0, scale: f32 = 1.0): void {
157
+ renderTextLayout(layout: TextLayout, x: f32 = 0.0, y: f32 = 0.0, scale: f32 = 1.0): bool {
158
158
  this.ensureNotDisposed("Bitmap.renderTextLayout");
159
159
  if (!layout.isReady) {
160
160
  error("TextLayout", "Bitmap.renderTextLayout() called before the TextLayout was ready; register onReady/onReadyWith and render after the callback.");
161
- return;
161
+ return false;
162
162
  }
163
- this.render(layout._drawNode(), x, y, scale);
163
+ return this.render(layout._drawNode(), x, y, scale);
164
164
  }
165
165
 
166
166
  /** Queue text shaping for a built text node before the next CommitFrame. */
@@ -21,6 +21,11 @@ export function __fui_debug_pointer_event(eventType: u32, handle: u64, x: f32, y
21
21
  0.0,
22
22
  0.0,
23
23
  0,
24
+ true,
25
+ 0.0,
26
+ 0.0,
27
+ 0.0,
28
+ 0.0,
24
29
  );
25
30
  }
26
31
 
@@ -95,6 +95,11 @@ export class EventRouter {
95
95
  width: f32 = 0.0,
96
96
  height: f32 = 0.0,
97
97
  clickCount: i32 = 0,
98
+ isPrimary: bool = true,
99
+ tangentialPressure: f32 = 0.0,
100
+ tiltX: f32 = 0.0,
101
+ tiltY: f32 = 0.0,
102
+ twist: f32 = 0.0,
98
103
  ): bool {
99
104
  showKeyboardFocusForPointerEvent(eventType);
100
105
  SelectionHandleAdornerManager.recordPointerEvent(eventType, pointerType);
@@ -145,6 +150,11 @@ export class EventRouter {
145
150
  width,
146
151
  height,
147
152
  clickCount,
153
+ isPrimary,
154
+ tangentialPressure,
155
+ tiltX,
156
+ tiltY,
157
+ twist,
148
158
  );
149
159
  Node._dispatchPointerEventWithArgs(capturedNode, event);
150
160
  this.bubblePointerEvent(changetype<Node>(capturedNode).parentNode, event);
@@ -174,6 +184,11 @@ export class EventRouter {
174
184
  width,
175
185
  height,
176
186
  clickCount,
187
+ isPrimary,
188
+ tangentialPressure,
189
+ tiltX,
190
+ tiltY,
191
+ twist,
177
192
  );
178
193
  if (SelectionHandleAdornerManager.routeActiveHandleDragEvent(handleDragEvent)) {
179
194
  DragDropManager.handlePointerEvent(pointedNode, eventType, x, y, modifiers);
@@ -218,6 +233,11 @@ export class EventRouter {
218
233
  width,
219
234
  height,
220
235
  clickCount,
236
+ isPrimary,
237
+ tangentialPressure,
238
+ tiltX,
239
+ tiltY,
240
+ twist,
221
241
  );
222
242
  Node._dispatchPointerEventWithArgs(node, event);
223
243
  this.bubblePointerEvent(changetype<Node>(node).parentNode, event);
@@ -571,6 +591,37 @@ export class EventRouter {
571
591
  }
572
592
  }
573
593
 
594
+ static deactivateSubtree(root: Node): void {
595
+ const captured = this.resolveNode(this.capturedPointerHandle);
596
+ if (captured !== null && this.belongsToSubtree(changetype<Node>(captured), root)) {
597
+ this.capturedPointerHandle = <u64>HandleValue.Invalid;
598
+ }
599
+ if (this.focusedNode !== null && this.belongsToSubtree(changetype<Node>(this.focusedNode), root)) {
600
+ this.focusedNode = null;
601
+ ui.requestFocus(<u64>HandleValue.Invalid);
602
+ }
603
+ for (let index = this.hoverStack.length - 1; index >= 0; --index) {
604
+ if (this.belongsToSubtree(unchecked(this.hoverStack[index]), root)) this.removeHoverAt(index);
605
+ }
606
+ this.selectionCursorHandle = <u64>HandleValue.Invalid;
607
+ DragDropManager.reset();
608
+ ExternalDropManager.reset();
609
+ ContextMenuManager.hideActiveMenu();
610
+ ToolTipManager.clear();
611
+ SelectionHandleAdornerManager.clear();
612
+ MobileTextSelectionToolbarManager.clear();
613
+ this.applyCurrentCursor();
614
+ }
615
+
616
+ private static belongsToSubtree(node: Node, root: Node): bool {
617
+ let current: Node | null = node;
618
+ while (current !== null) {
619
+ if (current === root) return true;
620
+ current = changetype<Node>(current).parentNode;
621
+ }
622
+ return false;
623
+ }
624
+
574
625
  static reset(): void {
575
626
  this.nodes.clear();
576
627
  this.generations.clear();
package/src/core/Node.ts CHANGED
@@ -239,6 +239,11 @@ export class PointerEventArgs {
239
239
  readonly width: f32;
240
240
  readonly height: f32;
241
241
  readonly clickCount: i32;
242
+ readonly isPrimary: bool;
243
+ readonly tangentialPressure: f32;
244
+ readonly tiltX: f32;
245
+ readonly tiltY: f32;
246
+ readonly twist: f32;
242
247
  x: f32;
243
248
  y: f32;
244
249
  handled: bool = false;
@@ -256,6 +261,11 @@ export class PointerEventArgs {
256
261
  width: f32 = 0.0,
257
262
  height: f32 = 0.0,
258
263
  clickCount: i32 = 0,
264
+ isPrimary: bool = true,
265
+ tangentialPressure: f32 = 0.0,
266
+ tiltX: f32 = 0.0,
267
+ tiltY: f32 = 0.0,
268
+ twist: f32 = 0.0,
259
269
  ) {
260
270
  this.eventType = eventType;
261
271
  this.sceneX = sceneX;
@@ -271,6 +281,11 @@ export class PointerEventArgs {
271
281
  this.width = width;
272
282
  this.height = height;
273
283
  this.clickCount = clickCount;
284
+ this.isPrimary = isPrimary;
285
+ this.tangentialPressure = tangentialPressure;
286
+ this.tiltX = tiltX;
287
+ this.tiltY = tiltY;
288
+ this.twist = twist;
274
289
  }
275
290
  }
276
291
 
@@ -0,0 +1,72 @@
1
+ import { Disposable, disposeAll } from "./Disposable";
2
+ import { EventRouter } from "./EventRouter";
3
+ import { Node } from "./Node";
4
+
5
+ export type RetainedViewLifecycleHandler = (view: RetainedView) => void;
6
+
7
+ export class RetainedView implements Disposable {
8
+ private readonly retainedResources: Array<Disposable> = new Array<Disposable>();
9
+ private readonly activationHandlers: Array<RetainedViewLifecycleHandler> = new Array<RetainedViewLifecycleHandler>();
10
+ private readonly deactivationHandlers: Array<RetainedViewLifecycleHandler> = new Array<RetainedViewLifecycleHandler>();
11
+ private readonly disposalHandlers: Array<RetainedViewLifecycleHandler> = new Array<RetainedViewLifecycleHandler>();
12
+ private activeValue: bool = false;
13
+ private disposedValue: bool = false;
14
+
15
+ constructor(readonly root: Node) {}
16
+
17
+ retain(resource: Disposable): this {
18
+ if (this.disposedValue) {
19
+ resource.dispose();
20
+ return this;
21
+ }
22
+ this.retainedResources.push(resource);
23
+ return this;
24
+ }
25
+
26
+ onActivate(handler: RetainedViewLifecycleHandler): this {
27
+ this.activationHandlers.push(handler);
28
+ return this;
29
+ }
30
+
31
+ onDeactivate(handler: RetainedViewLifecycleHandler): this {
32
+ this.deactivationHandlers.push(handler);
33
+ return this;
34
+ }
35
+
36
+ onDispose(handler: RetainedViewLifecycleHandler): this {
37
+ this.disposalHandlers.push(handler);
38
+ return this;
39
+ }
40
+
41
+ get isActive(): bool { return this.activeValue; }
42
+ get isDisposed(): bool { return this.disposedValue; }
43
+
44
+ activate(): void {
45
+ if (this.disposedValue || this.activeValue) return;
46
+ this.activeValue = true;
47
+ for (let i = 0; i < this.activationHandlers.length; ++i) unchecked(this.activationHandlers[i])(this);
48
+ }
49
+
50
+ deactivate(): void {
51
+ if (this.disposedValue || !this.activeValue) return;
52
+ EventRouter.deactivateSubtree(this.root);
53
+ this.activeValue = false;
54
+ for (let i = 0; i < this.deactivationHandlers.length; ++i) unchecked(this.deactivationHandlers[i])(this);
55
+ }
56
+
57
+ dispose(): void {
58
+ if (this.disposedValue) return;
59
+ this.deactivate();
60
+ this.disposedValue = true;
61
+ for (let i = 0; i < this.disposalHandlers.length; ++i) unchecked(this.disposalHandlers[i])(this);
62
+ disposeAll(this.retainedResources);
63
+ this.root.dispose();
64
+ this.activationHandlers.length = 0;
65
+ this.deactivationHandlers.length = 0;
66
+ this.disposalHandlers.length = 0;
67
+ }
68
+ }
69
+
70
+ export function retainedView(root: Node): RetainedView {
71
+ return new RetainedView(root);
72
+ }
@@ -28,7 +28,7 @@ import { handleRouteChanged } from "./Navigation";
28
28
  import { handleSystemAccentColorChanged, handleSystemDarkModeChanged } from "./Theme";
29
29
  import { handleTimer } from "./Timers";
30
30
  import { handleWorkerComplete, handleWorkerError, handleWorkerProgress } from "./Worker";
31
- import { tickAnimations } from "./Animation";
31
+ import { getAnimationManager, tickAnimations } from "./Animation";
32
32
  import { ExternalDragEventType } from "./ExternalDropManager";
33
33
  import {
34
34
  describeHandle,
@@ -74,6 +74,11 @@ export function __fui_on_pointer_event_with_metadata(
74
74
  width: f32,
75
75
  height: f32,
76
76
  clickCount: i32,
77
+ isPrimary: bool,
78
+ tangentialPressure: f32,
79
+ tiltX: f32,
80
+ tiltY: f32,
81
+ twist: f32,
77
82
  ): bool {
78
83
  log(
79
84
  "Event",
@@ -106,6 +111,11 @@ export function __fui_on_pointer_event_with_metadata(
106
111
  width,
107
112
  height,
108
113
  clickCount,
114
+ isPrimary,
115
+ tangentialPressure,
116
+ tiltX,
117
+ tiltY,
118
+ twist,
109
119
  );
110
120
  }
111
121
 
@@ -191,6 +201,10 @@ export function __fui_on_frame(timestampMs: f64): void {
191
201
  tickAnimations(timestampMs);
192
202
  }
193
203
 
204
+ export function __fui_needs_animation_frame(): bool {
205
+ return getAnimationManager().hasActiveAnimations();
206
+ }
207
+
194
208
  export function __fui_on_timer(timerId: u32): void {
195
209
  log("Event", "timer fired id=" + timerId.toString());
196
210
  handleTimer(timerId);
@@ -18,6 +18,9 @@ export declare function get_device_pixel_ratio(): f32;
18
18
  @external("fui_host", "fui_set_application_caption")
19
19
  export declare function fui_set_application_caption(captionPtr: usize, captionLen: u32): void;
20
20
 
21
+ @external("fui_host", "fui_set_page_zoom_enabled")
22
+ export declare function fui_set_page_zoom_enabled(enabled: bool): void;
23
+
21
24
  @external("fui_host", "fui_set_pointer_capture")
22
25
  export declare function fui_set_pointer_capture(handle: u64): void;
23
26
 
@@ -164,6 +164,9 @@ export enum SemanticRole {
164
164
  Switch = 14,
165
165
  Slider = 15,
166
166
  ComboBox = 16,
167
+ TabList = 17,
168
+ Tab = 18,
169
+ TabPanel = 19,
167
170
  }
168
171
 
169
172
  export enum PlatformFamily {
@@ -1,37 +0,0 @@
1
- import { ButtonTemplate } from "./internal/ButtonPresenter";
2
- import { CheckboxIndicatorTemplate } from "./internal/CheckboxIndicatorPresenter";
3
- import { RadioIndicatorTemplate } from "./internal/RadioIndicatorPresenter";
4
- import { SwitchIndicatorTemplate } from "./internal/SwitchIndicatorPresenter";
5
- import { SliderTemplate } from "./internal/SliderPresenter";
6
- import { DropdownFieldTemplate } from "./internal/DropdownFieldPresenter";
7
- import { DropdownChevronTemplate } from "./internal/DropdownChevronPresenter";
8
- import { DropdownOptionRowTemplate } from "./internal/DropdownOptionRowPresenter";
9
- import { TextInputTemplate } from "./internal/TextInputPresenter";
10
-
11
- export class ControlTemplateSet {
12
- button: ButtonTemplate | null = null;
13
- checkboxIndicator: CheckboxIndicatorTemplate | null = null;
14
- radioIndicator: RadioIndicatorTemplate | null = null;
15
- switchIndicator: SwitchIndicatorTemplate | null = null;
16
- slider: SliderTemplate | null = null;
17
- dropdownField: DropdownFieldTemplate | null = null;
18
- dropdownChevron: DropdownChevronTemplate | null = null;
19
- dropdownOptionRow: DropdownOptionRowTemplate | null = null;
20
- textInput: TextInputTemplate | null = null;
21
- textArea: TextInputTemplate | null = null;
22
- }
23
-
24
- let activeControlTemplatesValue: ControlTemplateSet | null = null;
25
-
26
- export function getControlTemplates(): ControlTemplateSet | null {
27
- return activeControlTemplatesValue;
28
- }
29
-
30
- export function useControlTemplates(templates: ControlTemplateSet | null): ControlTemplateSet | null {
31
- activeControlTemplatesValue = templates;
32
- return activeControlTemplatesValue;
33
- }
34
-
35
- export function clearControlTemplates(): void {
36
- activeControlTemplatesValue = null;
37
- }