@effindomv2/fui-as 0.1.36 → 0.1.39

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.
Files changed (119) hide show
  1. package/browser/routed-app-conventions.ts +1 -1
  2. package/browser/src/common-harness.ts +1 -27
  3. package/browser/src/index.ts +19 -26
  4. package/browser/src/routed-harness.ts +1 -194
  5. package/browser/src/shared-browser.ts +6 -0
  6. package/package.json +26 -16
  7. package/scripts/build.sh +51 -47
  8. package/scripts/framework-host-services.ts +1 -1
  9. package/scripts/generate-host-events.ts +15 -241
  10. package/scripts/generate-host-services.ts +13 -261
  11. package/scripts/hostgen/assemblyscript-host-events.ts +165 -0
  12. package/scripts/hostgen/assemblyscript-host-services.ts +207 -0
  13. package/scripts/hostgen/common.ts +73 -0
  14. package/scripts/hostgen/registry.ts +159 -0
  15. package/src/Fui.ts +70 -4
  16. package/src/FuiExports.ts +13 -1
  17. package/src/bindings/ui.ts +165 -8
  18. package/src/controls/Button.ts +98 -71
  19. package/src/controls/Checkbox.ts +23 -21
  20. package/src/controls/ComboBox.ts +1201 -0
  21. package/src/controls/ContextMenu.ts +279 -165
  22. package/src/controls/Dialog.ts +91 -17
  23. package/src/controls/Dropdown.ts +101 -333
  24. package/src/controls/NavLink.ts +29 -6
  25. package/src/controls/Popup.ts +10 -5
  26. package/src/controls/ProgressBar.ts +1 -2
  27. package/src/controls/RadioButton.ts +34 -13
  28. package/src/controls/RadioGroup.ts +32 -19
  29. package/src/controls/Slider.ts +21 -23
  30. package/src/controls/Switch.ts +34 -17
  31. package/src/controls/index.ts +2 -1
  32. package/src/controls/internal/ButtonPresenter.ts +5 -2
  33. package/src/controls/internal/CheckboxIndicatorPresenter.ts +10 -10
  34. package/src/controls/internal/DropdownChevronPresenter.ts +3 -3
  35. package/src/controls/internal/DropdownFieldPresenter.ts +6 -6
  36. package/src/controls/internal/DropdownOptionRowPresenter.ts +5 -4
  37. package/src/controls/internal/PressableLabeledControl.ts +4 -7
  38. package/src/controls/internal/RadioIndicatorPresenter.ts +7 -7
  39. package/src/controls/internal/SelectablePopupList.ts +463 -0
  40. package/src/controls/internal/SliderPresenter.ts +5 -5
  41. package/src/controls/internal/SwitchIndicatorPresenter.ts +63 -17
  42. package/src/controls/internal/TextInputCore.ts +307 -41
  43. package/src/controls/internal/TextInputPresenter.ts +2 -2
  44. package/src/controls/templating.ts +10 -0
  45. package/src/core/Application.ts +19 -9
  46. package/src/core/Bitmap.ts +219 -1
  47. package/src/core/ContextMenuManager.ts +128 -44
  48. package/src/core/DebugApi.ts +17 -2
  49. package/src/core/DragGesture.ts +8 -7
  50. package/src/core/EventRouter.ts +332 -12
  51. package/src/core/Fetch.ts +15 -7
  52. package/src/core/FetchFfi.ts +1 -15
  53. package/src/core/File.ts +75 -34
  54. package/src/core/FocusAdornerManager.ts +3 -3
  55. package/src/core/FocusVisibility.ts +7 -0
  56. package/src/core/FrameScheduler.ts +126 -0
  57. package/src/core/ImageSampling.ts +43 -0
  58. package/src/core/Logger.ts +2 -0
  59. package/src/core/MobileTextSelectionToolbarManager.ts +901 -0
  60. package/src/core/Node.ts +1039 -97
  61. package/src/core/Platform.ts +16 -16
  62. package/src/core/SelectionHandleAdornerManager.ts +697 -0
  63. package/src/core/Theme.ts +34 -20
  64. package/src/core/ToolTipManager.ts +3 -3
  65. package/src/core/Typography.ts +411 -19
  66. package/src/core/Worker.ts +69 -33
  67. package/src/core/event_exports.ts +123 -5
  68. package/src/core/ffi.ts +5 -746
  69. package/src/core/generated/FetchHostAbi.ts +10 -0
  70. package/src/core/generated/FrameworkHostServices.ts +1 -1
  71. package/src/core/generated/HostAbi.ts +220 -0
  72. package/src/core/generated/UiAbi.ts +356 -0
  73. package/src/core/generated/UiEnums.ts +186 -0
  74. package/src/drawing/DrawCallback.ts +34 -0
  75. package/src/drawing/DrawContext.ts +189 -0
  76. package/src/drawing/Paint.ts +34 -0
  77. package/src/drawing/Path.ts +40 -0
  78. package/src/nodes/CustomDrawable.ts +61 -0
  79. package/src/nodes/FlexBox.ts +57 -11
  80. package/src/nodes/Grid.ts +5 -5
  81. package/src/nodes/Image.ts +20 -1
  82. package/src/nodes/RichText.ts +36 -25
  83. package/src/nodes/ScrollBar.ts +2 -1
  84. package/src/nodes/ScrollBox.ts +7 -2
  85. package/src/nodes/ScrollView.ts +17 -6
  86. package/src/nodes/Svg.ts +18 -1
  87. package/src/nodes/Text.ts +1 -1
  88. package/src/nodes/TextCore.ts +115 -20
  89. package/src/nodes/index.ts +3 -1
  90. package/src/text/DynamicTextLayout.ts +275 -0
  91. package/src/text/TextLayout.ts +308 -0
  92. package/src/worker/Worker.ts +3 -36
  93. package/src/worker/WorkerJob.ts +5 -9
  94. package/src/worker/ffi.ts +0 -6
  95. package/browser/src/common-harness/host-imports.ts +0 -447
  96. package/browser/src/common-harness/interop.ts +0 -39
  97. package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
  98. package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
  99. package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
  100. package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
  101. package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
  102. package/browser/src/common-harness/managed-harness-session.ts +0 -15
  103. package/browser/src/common-harness/managed-harness.ts +0 -1451
  104. package/browser/src/common-harness/managed-history.ts +0 -168
  105. package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
  106. package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
  107. package/browser/src/common-harness/text-session-bridge.ts +0 -452
  108. package/browser/src/common-harness/types.ts +0 -205
  109. package/browser/src/common-harness/ui-chrome.ts +0 -206
  110. package/browser/src/common-harness/ui-imports.ts +0 -553
  111. package/browser/src/common-harness/wasm-module-cache.ts +0 -47
  112. package/browser/src/file-processing-worker.ts +0 -89
  113. package/browser/src/host-events.ts +0 -118
  114. package/browser/src/host-services.ts +0 -483
  115. package/browser/src/persisted-ui-state.ts +0 -206
  116. package/browser/src/routed-app-conventions.ts +0 -292
  117. package/browser/src/worker-bootstrap.ts +0 -916
  118. package/browser/src/worker-manager.ts +0 -234
  119. package/browser/src/worker-types.ts +0 -68
package/src/core/Theme.ts CHANGED
@@ -55,25 +55,35 @@ export class Spacing {
55
55
  }
56
56
 
57
57
  export class Fonts {
58
+ readonly bodyStack: FontStack;
59
+ readonly headingStack: FontStack;
60
+ readonly monoStack: FontStack;
61
+ readonly monoBoldStack: FontStack;
58
62
  readonly bodyFamily: FontFamily;
59
63
  readonly headingFamily: FontFamily;
60
64
  readonly monoFamily: FontFamily;
61
65
 
62
66
  constructor(
63
- readonly body: u32,
64
- readonly heading: u32,
67
+ bodyStack: FontStack,
68
+ headingStack: FontStack,
65
69
  readonly sizeBody: f32,
66
70
  readonly sizeHeading: f32,
67
- readonly mono: u32 = 5,
68
- readonly monoBold: u32 = 6,
71
+ monoStack: FontStack | null = null,
72
+ monoBoldStack: FontStack | null = null,
69
73
  readonly sizeMono: f32 = sizeBody,
70
74
  bodyFamily: FontFamily | null = null,
71
75
  headingFamily: FontFamily | null = null,
72
76
  monoFamily: FontFamily | null = null,
73
77
  ) {
74
- this.bodyFamily = bodyFamily !== null ? changetype<FontFamily>(bodyFamily) : FontFamily.regularBold(body, heading);
75
- this.headingFamily = headingFamily !== null ? changetype<FontFamily>(headingFamily) : FontFamily.regularBold(heading, heading);
76
- this.monoFamily = monoFamily !== null ? changetype<FontFamily>(monoFamily) : FontFamily.regularBold(mono, monoBold);
78
+ const resolvedMonoStack = monoStack !== null ? changetype<FontStack>(monoStack) : bodyStack;
79
+ const resolvedMonoBoldStack = monoBoldStack !== null ? changetype<FontStack>(monoBoldStack) : resolvedMonoStack;
80
+ this.bodyStack = bodyStack;
81
+ this.headingStack = headingStack;
82
+ this.monoStack = resolvedMonoStack;
83
+ this.monoBoldStack = resolvedMonoBoldStack;
84
+ this.bodyFamily = bodyFamily !== null ? changetype<FontFamily>(bodyFamily) : FontFamily.regularBold(bodyStack, headingStack);
85
+ this.headingFamily = headingFamily !== null ? changetype<FontFamily>(headingFamily) : FontFamily.regularBold(headingStack, headingStack);
86
+ this.monoFamily = monoFamily !== null ? changetype<FontFamily>(monoFamily) : FontFamily.regularBold(resolvedMonoStack, resolvedMonoBoldStack);
77
87
  }
78
88
  }
79
89
 
@@ -83,7 +93,6 @@ export class ContextMenuItemTheme {
83
93
  readonly hoverBackground: u32,
84
94
  readonly textColor: u32,
85
95
  readonly cornerRadius: f32,
86
- readonly fontId: u32,
87
96
  readonly fontFamily: FontFamily,
88
97
  readonly fontSize: f32,
89
98
  readonly height: f32,
@@ -115,7 +124,6 @@ export class ToolTipTheme {
115
124
  readonly panelShadowColor: u32,
116
125
  readonly panelCornerRadius: f32,
117
126
  readonly textColor: u32,
118
- readonly fontId: u32,
119
127
  readonly fontFamily: FontFamily,
120
128
  readonly fontSize: f32,
121
129
  readonly maxWidth: f32,
@@ -203,20 +211,20 @@ function estimateThemeDark(theme: Theme): bool {
203
211
  }
204
212
 
205
213
  const DEFAULT_SPACING = new Spacing(4.0, 8.0, 16.0, 24.0, 32.0);
206
- const DEFAULT_BODY_STACK = new FontStack(1).fallback(3);
207
- const DEFAULT_HEADING_STACK = new FontStack(2).fallback(3);
208
- const DEFAULT_MONO_STACK = new FontStack(5).fallback(4).fallback(3);
209
- const DEFAULT_MONO_BOLD_STACK = new FontStack(6).fallback(4).fallback(3);
214
+ const DEFAULT_BODY_STACK = FontStack._fromId(1)._fallbackId(3);
215
+ const DEFAULT_HEADING_STACK = FontStack._fromId(2)._fallbackId(3);
216
+ const DEFAULT_MONO_STACK = FontStack._fromId(7)._fallbackId(4)._fallbackId(3);
217
+ const DEFAULT_MONO_BOLD_STACK = FontStack._fromId(8)._fallbackId(4)._fallbackId(3);
210
218
 
211
219
  const DEFAULT_FONTS = new Fonts(
212
- 1,
213
- 2,
220
+ DEFAULT_BODY_STACK,
221
+ DEFAULT_HEADING_STACK,
214
222
  16.0,
215
223
  24.0,
216
- 5,
217
- 6,
224
+ DEFAULT_MONO_STACK,
225
+ DEFAULT_MONO_BOLD_STACK,
218
226
  15.0,
219
- new FontFamily(1, 2, 9, 10),
227
+ FontFamily._fromIds(1, 2, 5, 6),
220
228
  FontFamily.regularBoldStacks(DEFAULT_HEADING_STACK, DEFAULT_HEADING_STACK),
221
229
  FontFamily.regularBoldStacks(DEFAULT_MONO_STACK, DEFAULT_MONO_BOLD_STACK),
222
230
  );
@@ -289,7 +297,6 @@ export function generateTheme(isDark: bool, accentColor: u32 = DEFAULT_ACCENT_CO
289
297
  contextMenuItemHover,
290
298
  textPrimary,
291
299
  isDark ? 10.0 : 9.0,
292
- DEFAULT_FONTS.body,
293
300
  DEFAULT_FONTS.bodyFamily,
294
301
  13.0,
295
302
  30.0,
@@ -305,7 +312,6 @@ export function generateTheme(isDark: bool, accentColor: u32 = DEFAULT_ACCENT_CO
305
312
  toolTipPanelShadowColor,
306
313
  isDark ? 12.0 : 10.0,
307
314
  textPrimary,
308
- DEFAULT_FONTS.body,
309
315
  DEFAULT_FONTS.bodyFamily,
310
316
  13.0,
311
317
  280.0,
@@ -377,3 +383,11 @@ export function handleSystemDarkModeChanged(isDark: bool): Theme {
377
383
  systemAccentColor = normalizeAccentColor(ffi.fui_get_accent_color());
378
384
  return applySystemTheme();
379
385
  }
386
+
387
+ export function handleSystemAccentColorChanged(color: u32): Theme {
388
+ systemAccentColor = normalizeAccentColor(color);
389
+ if (themeSource != ThemeSource.System) {
390
+ return activeTheme.value;
391
+ }
392
+ return applySystemTheme();
393
+ }
@@ -1,7 +1,7 @@
1
1
  import * as ui from "../bindings/ui";
2
2
  import { PopupPresenter } from "../controls/internal/PopupPresenter";
3
3
  import { FlexBox, Portal, TextCore } from "../nodes";
4
- import { BorderStyle, FlexDirection, HandleValue, Unit } from "./ffi";
4
+ import { FlexDirection, HandleValue, Unit } from "./ffi";
5
5
  import { flushCommit } from "./FrameScheduler";
6
6
  import { keyboardFocusVisible } from "./FocusVisibility";
7
7
  import { cancelTimer, scheduleTimer } from "./Timers";
@@ -314,14 +314,14 @@ export class ToolTipManager {
314
314
  const panelBackground = toolTip.hasPanelColorOverride ? toolTip.panelBackgroundColor : theme.panelBackground;
315
315
  const textColor = toolTip.hasTextColorOverride ? toolTip.tooltipTextColor : theme.textColor;
316
316
  labelNode.text(toolTip.contentText)
317
- .font(theme.fontId, theme.fontSize)
318
317
  .fontFamily(theme.fontFamily)
318
+ .fontSize(theme.fontSize)
319
319
  .textColor(textColor)
320
320
  .width(0.0, Unit.Auto)
321
321
  .height(0.0, Unit.Auto);
322
322
  panelNode.padding(theme.paddingLeft, theme.paddingTop, theme.paddingRight, theme.paddingBottom)
323
323
  .cornerRadius(theme.panelCornerRadius)
324
- .border(1.0, theme.panelBorderColor, BorderStyle.Solid)
324
+ .border(1.0, theme.panelBorderColor)
325
325
  .dropShadow(theme.panelShadowColor, 0.0, theme.shadowOffsetY, theme.shadowBlur, theme.shadowSpread)
326
326
  .bgColor(panelBackground)
327
327
  .width(0.0, Unit.Auto)
@@ -18,6 +18,7 @@ const MAX_FONT_SCORE: i32 = 0x7fffffff;
18
18
  const FIRST_DYNAMIC_FONT_ID: u32 = 1024;
19
19
 
20
20
  let nextDynamicFontId: u32 = FIRST_DYNAMIC_FONT_ID;
21
+ const loadedFontIds: Array<u32> = new Array<u32>();
21
22
 
22
23
  function absI32(value: i32): i32 {
23
24
  return value < 0 ? -value : value;
@@ -29,9 +30,206 @@ function allocateDynamicFontId(): u32 {
29
30
  return allocated;
30
31
  }
31
32
 
33
+ export class FontFaceLoadedEventArgs {
34
+ readonly font: FontFace;
35
+ readonly fontId: u32;
36
+
37
+ constructor(fontId: u32) {
38
+ this.fontId = fontId;
39
+ this.font = FontFace._fromId(fontId);
40
+ }
41
+ }
42
+
43
+ export class FontsLoadedEventArgs {
44
+ readonly fontIds: Array<u32>;
45
+
46
+ constructor(fontIds: Array<u32>) {
47
+ this.fontIds = fontIds;
48
+ }
49
+ }
50
+
51
+ export class FontStackLoadedEventArgs {
52
+ readonly stack: FontStack;
53
+ readonly fontIds: Array<u32>;
54
+
55
+ constructor(stack: FontStack, fontIds: Array<u32>) {
56
+ this.stack = stack;
57
+ this.fontIds = fontIds;
58
+ }
59
+ }
60
+
61
+ export type FontLoadedHandler<Owner> = (owner: Owner, event: FontFaceLoadedEventArgs) => void;
62
+ export type FontReadyHandler<Owner> = (owner: Owner, event: FontsLoadedEventArgs) => void;
63
+ export type FontStackLoadedHandler<Owner> = (owner: Owner, event: FontStackLoadedEventArgs) => void;
64
+
65
+ abstract class FontLoadedRegistration {
66
+ abstract invoke(fontId: u32): void;
67
+ }
68
+
69
+ abstract class FontReadyRegistration {
70
+ abstract invoke(): void;
71
+ }
72
+
73
+ // Callback wrappers — AS can't nest function types in generics
74
+ class FontLoadedCallback extends FontLoadedRegistration {
75
+ constructor(
76
+ readonly fontId: u32,
77
+ readonly cb: (event: FontFaceLoadedEventArgs) => void,
78
+ ) {
79
+ super();
80
+ }
81
+
82
+ invoke(_fontId: u32): void {
83
+ this.cb(new FontFaceLoadedEventArgs(this.fontId));
84
+ }
85
+ }
86
+
87
+ class FontReadyCallback extends FontReadyRegistration {
88
+ constructor(
89
+ private readonly fontIds: Array<u32>,
90
+ readonly cb: (event: FontsLoadedEventArgs) => void,
91
+ ) {
92
+ super();
93
+ }
94
+
95
+ invoke(): void {
96
+ this.cb(new FontsLoadedEventArgs(this.fontIds));
97
+ }
98
+ }
99
+
100
+ class FontReadyHandlerRegistration<Owner> extends FontReadyRegistration {
101
+ constructor(
102
+ private readonly fontIds: Array<u32>,
103
+ private readonly owner: Owner,
104
+ private readonly handler: FontReadyHandler<Owner>,
105
+ ) {
106
+ super();
107
+ }
108
+
109
+ invoke(): void {
110
+ this.handler(this.owner, new FontsLoadedEventArgs(this.fontIds));
111
+ }
112
+ }
113
+
114
+ class FontLoadedHandlerRegistration<Owner> extends FontLoadedRegistration {
115
+ constructor(
116
+ private readonly fontId: u32,
117
+ private readonly owner: Owner,
118
+ private readonly handler: FontLoadedHandler<Owner>,
119
+ ) {
120
+ super();
121
+ }
122
+
123
+ invoke(_fontId: u32): void {
124
+ this.handler(this.owner, new FontFaceLoadedEventArgs(this.fontId));
125
+ }
126
+ }
127
+
128
+ class FontStackLoadedCallback extends FontReadyRegistration {
129
+ constructor(
130
+ private readonly stack: FontStack,
131
+ private readonly cb: (event: FontStackLoadedEventArgs) => void,
132
+ ) {
133
+ super();
134
+ }
135
+
136
+ invoke(): void {
137
+ this.cb(new FontStackLoadedEventArgs(this.stack, this.stack.requiredFontIds()));
138
+ }
139
+ }
140
+
141
+ class FontStackLoadedHandlerRegistration<Owner> extends FontReadyRegistration {
142
+ constructor(
143
+ private readonly stack: FontStack,
144
+ private readonly owner: Owner,
145
+ private readonly handler: FontStackLoadedHandler<Owner>,
146
+ ) {
147
+ super();
148
+ }
149
+
150
+ invoke(): void {
151
+ this.handler(this.owner, new FontStackLoadedEventArgs(this.stack, this.stack.requiredFontIds()));
152
+ }
153
+ }
154
+
155
+ class FontLoadBarrierCallback extends FontLoadedRegistration {
156
+ constructor(private readonly barrier: FontLoadBarrier) {
157
+ super();
158
+ }
159
+
160
+ invoke(_fontId: u32): void {
161
+ this.barrier.notify();
162
+ }
163
+ }
164
+
165
+ class FontLoadBarrier {
166
+ private done: bool = false;
167
+
168
+ constructor(
169
+ private readonly fontIds: Array<u32>,
170
+ private readonly callback: FontReadyRegistration,
171
+ ) {}
172
+
173
+ notify(): void {
174
+ if (this.done) {
175
+ return;
176
+ }
177
+ if (!areFontIdsLoaded(this.fontIds)) {
178
+ return;
179
+ }
180
+ this.done = true;
181
+ this.callback.invoke();
182
+ }
183
+ }
184
+
185
+ function isPreloadedBuiltInFont(fontId: u32): bool {
186
+ return fontId >= 1 && fontId <= 6;
187
+ }
188
+
189
+ function pushUniqueFontId(fontIds: Array<u32>, fontId: u32): void {
190
+ if (fontId == 0) {
191
+ return;
192
+ }
193
+ for (let index = 0; index < fontIds.length; ++index) {
194
+ if (unchecked(fontIds[index]) == fontId) {
195
+ return;
196
+ }
197
+ }
198
+ fontIds.push(fontId);
199
+ }
200
+
201
+ function markFontLoaded(fontId: u32): void {
202
+ pushUniqueFontId(loadedFontIds, fontId);
203
+ }
204
+
205
+ function normalizeFontIds(fontIds: Array<u32>): Array<u32> {
206
+ const unique = new Array<u32>();
207
+ for (let index = 0; index < fontIds.length; ++index) {
208
+ pushUniqueFontId(unique, unchecked(fontIds[index]));
209
+ }
210
+ return unique;
211
+ }
212
+
213
+ function areFontIdsLoaded(fontIds: Array<u32>): bool {
214
+ for (let index = 0; index < fontIds.length; ++index) {
215
+ if (!FontFace.isLoaded(unchecked(fontIds[index]))) {
216
+ return false;
217
+ }
218
+ }
219
+ return true;
220
+ }
221
+
32
222
  export class FontFace {
223
+ private static readonly registrations: Array<FontLoadedRegistration> = new Array();
224
+ private static readonly regFontIds: Array<u32> = new Array();
225
+
33
226
  constructor(readonly id: u32) {}
34
227
 
228
+ /** @internal */
229
+ static _fromId(id: u32): FontFace {
230
+ return new FontFace(id);
231
+ }
232
+
35
233
  static load(url: string, id: u32 = allocateDynamicFontId()): FontFace {
36
234
  return new FontFace(id).load(url);
37
235
  }
@@ -43,15 +241,113 @@ export class FontFace {
43
241
  ui.loadFont(this.id, url);
44
242
  return this;
45
243
  }
244
+
245
+ onLoaded(callback: (event: FontFaceLoadedEventArgs) => void): this {
246
+ if (FontFace.isLoaded(this.id)) {
247
+ callback(new FontFaceLoadedEventArgs(this.id));
248
+ return this;
249
+ }
250
+ FontFace.registrations.push(new FontLoadedCallback(this.id, callback));
251
+ FontFace.regFontIds.push(this.id);
252
+ return this;
253
+ }
254
+
255
+ onLoadedWith<Owner>(owner: Owner, handler: FontLoadedHandler<Owner>): this {
256
+ FontFace.whenLoadedWith<Owner>(this.id, owner, handler);
257
+ return this;
258
+ }
259
+
260
+ isLoaded(): bool {
261
+ return FontFace.isLoaded(this.id);
262
+ }
263
+
264
+ static isLoaded(fontId: u32): bool {
265
+ if (fontId == 0 || isPreloadedBuiltInFont(fontId)) {
266
+ return true;
267
+ }
268
+ for (let index = 0; index < loadedFontIds.length; ++index) {
269
+ if (unchecked(loadedFontIds[index]) == fontId) {
270
+ return true;
271
+ }
272
+ }
273
+ return false;
274
+ }
275
+
276
+ static whenLoaded(fontId: u32, callback: (event: FontFaceLoadedEventArgs) => void): void {
277
+ if (FontFace.isLoaded(fontId)) {
278
+ callback(new FontFaceLoadedEventArgs(fontId));
279
+ return;
280
+ }
281
+ FontFace.registrations.push(new FontLoadedCallback(fontId, callback));
282
+ FontFace.regFontIds.push(fontId);
283
+ }
284
+
285
+ static whenLoadedWith<Owner>(fontId: u32, owner: Owner, handler: FontLoadedHandler<Owner>): void {
286
+ if (FontFace.isLoaded(fontId)) {
287
+ handler(owner, new FontFaceLoadedEventArgs(fontId));
288
+ return;
289
+ }
290
+ FontFace.registrations.push(new FontLoadedHandlerRegistration<Owner>(fontId, owner, handler));
291
+ FontFace.regFontIds.push(fontId);
292
+ }
293
+
294
+ static whenFontsLoaded(fontIds: Array<u32>, callback: (event: FontsLoadedEventArgs) => void): void {
295
+ const uniqueFontIds = normalizeFontIds(fontIds);
296
+ FontFace.whenFontsLoadedWithRegistration(uniqueFontIds, new FontReadyCallback(uniqueFontIds, callback));
297
+ }
298
+
299
+ static whenFontsLoadedWith<Owner>(fontIds: Array<u32>, owner: Owner, callback: FontReadyHandler<Owner>): void {
300
+ const uniqueFontIds = normalizeFontIds(fontIds);
301
+ FontFace.whenFontsLoadedWithRegistration(uniqueFontIds, new FontReadyHandlerRegistration<Owner>(uniqueFontIds, owner, callback));
302
+ }
303
+
304
+ static whenFontsLoadedFor<Owner>(fontIds: Array<u32>, owner: Owner, callback: FontReadyHandler<Owner>): void {
305
+ FontFace.whenFontsLoadedWith<Owner>(fontIds, owner, callback);
306
+ }
307
+
308
+ private static whenFontsLoadedWithRegistration(fontIds: Array<u32>, callback: FontReadyRegistration): void {
309
+ const uniqueFontIds = fontIds;
310
+ if (areFontIdsLoaded(uniqueFontIds)) {
311
+ callback.invoke();
312
+ return;
313
+ }
314
+ const barrier = new FontLoadBarrier(uniqueFontIds, callback);
315
+ for (let index = 0; index < uniqueFontIds.length; ++index) {
316
+ const fontId = unchecked(uniqueFontIds[index]);
317
+ if (FontFace.isLoaded(fontId)) {
318
+ continue;
319
+ }
320
+ FontFace.registrations.push(new FontLoadBarrierCallback(barrier));
321
+ FontFace.regFontIds.push(fontId);
322
+ }
323
+ }
324
+
325
+ /** @internal */
326
+ static _dispatchFontLoaded(fontId: u32): void {
327
+ markFontLoaded(fontId);
328
+ for (let i = 0; i < FontFace.regFontIds.length; i++) {
329
+ if (unchecked(FontFace.regFontIds[i]) == fontId) {
330
+ unchecked(FontFace.registrations[i]).invoke(fontId);
331
+ }
332
+ }
333
+ }
46
334
  }
47
335
 
48
336
  export class FontStack {
337
+ readonly id: u32;
49
338
  private readonly fallbackIds: Array<u32> = new Array<u32>();
50
339
 
51
- constructor(readonly id: u32) {}
340
+ constructor(readonly face: FontFace) {
341
+ this.id = face.id;
342
+ }
343
+
344
+ /** @internal */
345
+ static _fromId(id: u32): FontStack {
346
+ return new FontStack(FontFace._fromId(id));
347
+ }
52
348
 
53
349
  static load(url: string, id: u32 = allocateDynamicFontId()): FontStack {
54
- return new FontStack(id).load(url);
350
+ return new FontStack(FontFace.load(url, id));
55
351
  }
56
352
 
57
353
  load(url: string): this {
@@ -62,7 +358,8 @@ export class FontStack {
62
358
  return this;
63
359
  }
64
360
 
65
- fallback(fontId: u32): this {
361
+ /** @internal */
362
+ _fallbackId(fontId: u32): this {
66
363
  if (fontId == 0 || fontId == this.id) {
67
364
  warn(
68
365
  "Typography",
@@ -80,12 +377,16 @@ export class FontStack {
80
377
  return this;
81
378
  }
82
379
 
380
+ fallback(face: FontFace): this {
381
+ return this._fallbackId(face.id);
382
+ }
383
+
83
384
  fallbackFace(face: FontFace): this {
84
- return this.fallback(face.id);
385
+ return this.fallback(face);
85
386
  }
86
387
 
87
388
  fallbackStack(stack: FontStack): this {
88
- return this.fallback(stack.id);
389
+ return this._fallbackId(stack.id);
89
390
  }
90
391
 
91
392
  fallbackLoaded(url: string, fontId: u32 = allocateDynamicFontId()): this {
@@ -93,40 +394,131 @@ export class FontStack {
93
394
  warn("Typography", "FontStack.fallbackLoaded() received an empty font URL.");
94
395
  }
95
396
  ui.loadFont(fontId, url);
96
- return this.fallback(fontId);
397
+ return this._fallbackId(fontId);
398
+ }
399
+
400
+ requiredFontIds(): Array<u32> {
401
+ const fontIds = new Array<u32>();
402
+ pushUniqueFontId(fontIds, this.id);
403
+ for (let index = 0; index < this.fallbackIds.length; ++index) {
404
+ pushUniqueFontId(fontIds, unchecked(this.fallbackIds[index]));
405
+ }
406
+ return fontIds;
407
+ }
408
+
409
+ isLoaded(): bool {
410
+ return areFontIdsLoaded(this.requiredFontIds());
411
+ }
412
+
413
+ onLoaded(callback: (event: FontStackLoadedEventArgs) => void): this {
414
+ FontFace.whenFontsLoadedWithRegistration(this.requiredFontIds(), new FontStackLoadedCallback(this, callback));
415
+ return this;
416
+ }
417
+
418
+ onLoadedWith<Owner>(owner: Owner, handler: FontStackLoadedHandler<Owner>): this {
419
+ FontFace.whenFontsLoadedWithRegistration(this.requiredFontIds(), new FontStackLoadedHandlerRegistration<Owner>(this, owner, handler));
420
+ return this;
97
421
  }
98
422
  }
99
423
 
100
424
  export class FontFamily {
101
425
  constructor(
102
- readonly regular: u32,
103
- readonly bold: u32 = 0,
104
- readonly italic: u32 = 0,
105
- readonly boldItalic: u32 = 0,
106
- readonly medium: u32 = 0,
107
- readonly mediumItalic: u32 = 0,
108
- readonly semibold: u32 = 0,
109
- readonly semiboldItalic: u32 = 0,
426
+ readonly regularStack: FontStack,
427
+ readonly boldStack: FontStack | null = null,
428
+ readonly italicStack: FontStack | null = null,
429
+ readonly boldItalicStack: FontStack | null = null,
430
+ readonly mediumStack: FontStack | null = null,
431
+ readonly mediumItalicStack: FontStack | null = null,
432
+ readonly semiboldStack: FontStack | null = null,
433
+ readonly semiboldItalicStack: FontStack | null = null,
110
434
  ) {}
111
435
 
112
- static withRegular(regular: u32): FontFamily {
436
+ get regular(): u32 {
437
+ return this.regularStack.id;
438
+ }
439
+
440
+ get bold(): u32 {
441
+ return this.boldStack !== null ? changetype<FontStack>(this.boldStack).id : 0;
442
+ }
443
+
444
+ get italic(): u32 {
445
+ return this.italicStack !== null ? changetype<FontStack>(this.italicStack).id : 0;
446
+ }
447
+
448
+ get boldItalic(): u32 {
449
+ return this.boldItalicStack !== null ? changetype<FontStack>(this.boldItalicStack).id : 0;
450
+ }
451
+
452
+ get medium(): u32 {
453
+ return this.mediumStack !== null ? changetype<FontStack>(this.mediumStack).id : 0;
454
+ }
455
+
456
+ get mediumItalic(): u32 {
457
+ return this.mediumItalicStack !== null ? changetype<FontStack>(this.mediumItalicStack).id : 0;
458
+ }
459
+
460
+ get semibold(): u32 {
461
+ return this.semiboldStack !== null ? changetype<FontStack>(this.semiboldStack).id : 0;
462
+ }
463
+
464
+ get semiboldItalic(): u32 {
465
+ return this.semiboldItalicStack !== null ? changetype<FontStack>(this.semiboldItalicStack).id : 0;
466
+ }
467
+
468
+ /** @internal */
469
+ static _fromIds(
470
+ regular: u32,
471
+ bold: u32 = 0,
472
+ italic: u32 = 0,
473
+ boldItalic: u32 = 0,
474
+ medium: u32 = 0,
475
+ mediumItalic: u32 = 0,
476
+ semibold: u32 = 0,
477
+ semiboldItalic: u32 = 0,
478
+ ): FontFamily {
479
+ return new FontFamily(
480
+ FontStack._fromId(regular),
481
+ bold != 0 ? FontStack._fromId(bold) : null,
482
+ italic != 0 ? FontStack._fromId(italic) : null,
483
+ boldItalic != 0 ? FontStack._fromId(boldItalic) : null,
484
+ medium != 0 ? FontStack._fromId(medium) : null,
485
+ mediumItalic != 0 ? FontStack._fromId(mediumItalic) : null,
486
+ semibold != 0 ? FontStack._fromId(semibold) : null,
487
+ semiboldItalic != 0 ? FontStack._fromId(semiboldItalic) : null,
488
+ );
489
+ }
490
+
491
+ static withRegular(regular: FontStack): FontFamily {
113
492
  return new FontFamily(regular);
114
493
  }
115
494
 
116
495
  static withRegularStack(regular: FontStack): FontFamily {
117
- return new FontFamily(regular.id);
496
+ return new FontFamily(regular);
118
497
  }
119
498
 
120
499
  static withRegularFace(regular: FontFace): FontFamily {
121
- return new FontFamily(regular.id);
500
+ return new FontFamily(new FontStack(regular));
122
501
  }
123
502
 
124
- static regularBold(regular: u32, bold: u32): FontFamily {
503
+ static regularBold(regular: FontStack, bold: FontStack): FontFamily {
125
504
  return new FontFamily(regular, bold);
126
505
  }
127
506
 
128
507
  static regularBoldStacks(regular: FontStack, bold: FontStack): FontFamily {
129
- return new FontFamily(regular.id, bold.id);
508
+ return new FontFamily(regular, bold);
509
+ }
510
+
511
+ requiredFontIds(): Array<u32> {
512
+ const fontIds = new Array<u32>();
513
+ pushUniqueFontId(fontIds, this.regular);
514
+ pushUniqueFontId(fontIds, this.bold);
515
+ pushUniqueFontId(fontIds, this.italic);
516
+ pushUniqueFontId(fontIds, this.boldItalic);
517
+ pushUniqueFontId(fontIds, this.medium);
518
+ pushUniqueFontId(fontIds, this.mediumItalic);
519
+ pushUniqueFontId(fontIds, this.semibold);
520
+ pushUniqueFontId(fontIds, this.semiboldItalic);
521
+ return fontIds;
130
522
  }
131
523
 
132
524
  resolve(weight: FontWeight = FontWeight.Regular, style: FontStyle = FontStyle.Normal): u32 {