@effindomv2/fui-as 0.1.37 → 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 (118) 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 +23 -13
  7. package/scripts/build.sh +47 -46
  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 +65 -5
  16. package/src/FuiExports.ts +12 -1
  17. package/src/bindings/ui.ts +157 -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 +17 -8
  46. package/src/core/Bitmap.ts +192 -3
  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 +104 -5
  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 -865
  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 +17 -1
  75. package/src/drawing/DrawContext.ts +140 -32
  76. package/src/nodes/CustomDrawable.ts +8 -1
  77. package/src/nodes/FlexBox.ts +40 -11
  78. package/src/nodes/Grid.ts +5 -5
  79. package/src/nodes/Image.ts +20 -1
  80. package/src/nodes/RichText.ts +36 -25
  81. package/src/nodes/ScrollBar.ts +2 -1
  82. package/src/nodes/ScrollBox.ts +7 -2
  83. package/src/nodes/ScrollView.ts +17 -6
  84. package/src/nodes/Svg.ts +18 -1
  85. package/src/nodes/Text.ts +1 -1
  86. package/src/nodes/TextCore.ts +115 -20
  87. package/src/nodes/index.ts +1 -1
  88. package/src/text/DynamicTextLayout.ts +275 -0
  89. package/src/text/TextLayout.ts +308 -0
  90. package/src/worker/Worker.ts +3 -36
  91. package/src/worker/WorkerJob.ts +5 -9
  92. package/src/worker/ffi.ts +0 -6
  93. package/browser/src/common-harness/host-imports.ts +0 -447
  94. package/browser/src/common-harness/interop.ts +0 -39
  95. package/browser/src/common-harness/managed-harness-bitmap-host.ts +0 -92
  96. package/browser/src/common-harness/managed-harness-canvas-host.ts +0 -72
  97. package/browser/src/common-harness/managed-harness-fetch-host.ts +0 -201
  98. package/browser/src/common-harness/managed-harness-file-host.ts +0 -1066
  99. package/browser/src/common-harness/managed-harness-file-payloads.ts +0 -143
  100. package/browser/src/common-harness/managed-harness-file-types.ts +0 -71
  101. package/browser/src/common-harness/managed-harness-session.ts +0 -15
  102. package/browser/src/common-harness/managed-harness.ts +0 -1472
  103. package/browser/src/common-harness/managed-history.ts +0 -168
  104. package/browser/src/common-harness/persisted-restore-policy.ts +0 -50
  105. package/browser/src/common-harness/persisted-ui-state-controller.ts +0 -309
  106. package/browser/src/common-harness/text-session-bridge.ts +0 -452
  107. package/browser/src/common-harness/types.ts +0 -206
  108. package/browser/src/common-harness/ui-chrome.ts +0 -206
  109. package/browser/src/common-harness/ui-imports.ts +0 -559
  110. package/browser/src/common-harness/wasm-module-cache.ts +0 -47
  111. package/browser/src/file-processing-worker.ts +0 -89
  112. package/browser/src/host-events.ts +0 -118
  113. package/browser/src/host-services.ts +0 -483
  114. package/browser/src/persisted-ui-state.ts +0 -206
  115. package/browser/src/routed-app-conventions.ts +0 -292
  116. package/browser/src/worker-bootstrap.ts +0 -916
  117. package/browser/src/worker-manager.ts +0 -234
  118. package/browser/src/worker-types.ts +0 -68
package/src/core/File.ts CHANGED
@@ -23,6 +23,7 @@ const FUNCTION_SAVE_TEXT_WITH = "FileSaveRequest.saveTextWith";
23
23
  const FUNCTION_SAVE_BYTES_WITH = "FileSaveRequest.saveBytesWith";
24
24
  const FUNCTION_CREATE_WRITER_WITH = "FileSaveRequest.createWriterWith";
25
25
  const FUNCTION_FILE_WORKER_PROCESS_START = "FileWorkerProcessRequest.start";
26
+ const FUNCTION_FILE_WORKER_PROCESS_WORKER = "FileWorkerProcessRequest.worker";
26
27
  const FUNCTION_WRITE_TEXT_CHUNK_WITH = "BrowserFileWriter.writeTextChunkWith";
27
28
  const FUNCTION_WRITE_BYTES_CHUNK_WITH = "BrowserFileWriter.writeBytesChunkWith";
28
29
  const FUNCTION_FINISH_WITH = "BrowserFileWriter.finishWith";
@@ -70,9 +71,9 @@ function describeFileFailure(status: u32, fallback: string): string {
70
71
  return fallback;
71
72
  }
72
73
 
73
- function dispatchFileError(binding: Callback1<string> | null, message: string): void {
74
+ function dispatchFileError(binding: Callback1<FileErrorEventArgs> | null, message: string): void {
74
75
  if (binding !== null) {
75
- binding.invoke(message);
76
+ binding.invoke(new FileErrorEventArgs(message));
76
77
  return;
77
78
  }
78
79
  warn("File", message);
@@ -163,43 +164,43 @@ class FileRequestDisposable implements Disposable {
163
164
 
164
165
  class PendingOpenRequest {
165
166
  constructor(
166
- readonly completeBinding: Callback1<Array<BrowserFile>>,
167
- readonly errorBinding: Callback1<string> | null,
167
+ readonly completeBinding: Callback1<FileOpenEventArgs>,
168
+ readonly errorBinding: Callback1<FileErrorEventArgs> | null,
168
169
  ) {}
169
170
  }
170
171
 
171
172
  class PendingReadRequest {
172
173
  constructor(
173
174
  readonly completeBinding: Callback1<FileReadChunk>,
174
- readonly errorBinding: Callback1<string> | null,
175
+ readonly errorBinding: Callback1<FileErrorEventArgs> | null,
175
176
  ) {}
176
177
  }
177
178
 
178
179
  class PendingSaveRequest {
179
180
  constructor(
180
181
  readonly completeBinding: Callback1<FileSaveResult>,
181
- readonly errorBinding: Callback1<string> | null,
182
+ readonly errorBinding: Callback1<FileErrorEventArgs> | null,
182
183
  ) {}
183
184
  }
184
185
 
185
186
  class PendingWriterCreateRequest {
186
187
  constructor(
187
188
  readonly completeBinding: Callback1<BrowserFileWriter>,
188
- readonly errorBinding: Callback1<string> | null,
189
+ readonly errorBinding: Callback1<FileErrorEventArgs> | null,
189
190
  ) {}
190
191
  }
191
192
 
192
193
  class PendingWriterWriteRequest {
193
194
  constructor(
194
195
  readonly completeBinding: Callback1<FileWriteProgress>,
195
- readonly errorBinding: Callback1<string> | null,
196
+ readonly errorBinding: Callback1<FileErrorEventArgs> | null,
196
197
  ) {}
197
198
  }
198
199
 
199
200
  class PendingWriterFinishRequest {
200
201
  constructor(
201
202
  readonly completeBinding: Callback1<FileSaveResult>,
202
- readonly errorBinding: Callback1<string> | null,
203
+ readonly errorBinding: Callback1<FileErrorEventArgs> | null,
203
204
  ) {}
204
205
  }
205
206
 
@@ -228,6 +229,22 @@ export class FileCapabilities {
228
229
  }
229
230
  }
230
231
 
232
+ export class FileOpenEventArgs {
233
+ readonly files: Array<BrowserFile>;
234
+
235
+ constructor(files: Array<BrowserFile>) {
236
+ this.files = files;
237
+ }
238
+ }
239
+
240
+ export class FileErrorEventArgs {
241
+ readonly message: string;
242
+
243
+ constructor(message: string) {
244
+ this.message = message;
245
+ }
246
+ }
247
+
231
248
  export class FileReadChunk {
232
249
  readonly offsetBytes: u64;
233
250
  readonly fileSizeBytes: u64;
@@ -314,14 +331,14 @@ export class BrowserFile {
314
331
  offsetBytes: u64,
315
332
  maxBytes: u32,
316
333
  handler: Handler1<Owner, FileReadChunk>,
317
- errorHandler: Handler1<Owner, string> | null = null,
334
+ errorHandler: Handler1<Owner, FileErrorEventArgs> | null = null,
318
335
  ): Disposable {
319
336
  if (changetype<usize>(handler) == 0) {
320
337
  throwNullArgument(FUNCTION_READ_BYTES_CHUNK_WITH, "handler");
321
338
  }
322
339
  if (maxBytes == 0) {
323
340
  dispatchFileError(
324
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
341
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
325
342
  "BrowserFile.readBytesChunkWith: maxBytes must be greater than zero.",
326
343
  );
327
344
  return new FileRequestDisposable(REQUEST_KIND_READ, 0);
@@ -329,7 +346,7 @@ export class BrowserFile {
329
346
  const fileIdBytes = encodeUtf8(this.id);
330
347
  const requestId = registerPendingReadRequest(new PendingReadRequest(
331
348
  bind1<Owner, FileReadChunk>(owner, handler),
332
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
349
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
333
350
  ));
334
351
  fui_file_read_chunk(
335
352
  requestId,
@@ -344,13 +361,15 @@ export class BrowserFile {
344
361
 
345
362
  export class FileWorkerProcessRequest implements Disposable {
346
363
  private readonly file: BrowserFile;
364
+ private workerWasmPathValue: string = "";
365
+ private workerEntryNameValue: string = "";
347
366
  private suggestedNameValue: string;
348
367
  private saveToPickedFileEnabled: bool = false;
349
368
  private chunkBytesValue: u32 = 64 * 1024;
350
369
  private chunkBinding: Callback1<FileReadChunk> | null = null;
351
370
  private progressBinding: Callback1<FileWorkerProcessProgress> | null = null;
352
371
  private completeBinding: Callback1<FileWorkerProcessResult> | null = null;
353
- private errorBinding: Callback1<string> | null = null;
372
+ private errorBinding: Callback1<FileErrorEventArgs> | null = null;
354
373
  private requestId: u32 = 0;
355
374
  private started: bool = false;
356
375
  private finished: bool = false;
@@ -368,6 +387,18 @@ export class FileWorkerProcessRequest implements Disposable {
368
387
  return this;
369
388
  }
370
389
 
390
+ worker(wasmPath: string, entryName: string): this {
391
+ if (changetype<usize>(wasmPath) == 0) {
392
+ throwNullArgument(FUNCTION_FILE_WORKER_PROCESS_WORKER, "wasmPath");
393
+ }
394
+ if (changetype<usize>(entryName) == 0) {
395
+ throwNullArgument(FUNCTION_FILE_WORKER_PROCESS_WORKER, "entryName");
396
+ }
397
+ this.workerWasmPathValue = wasmPath;
398
+ this.workerEntryNameValue = entryName;
399
+ return this;
400
+ }
401
+
371
402
  saveToPickedFile(value: string): this {
372
403
  this.saveToPickedFileEnabled = true;
373
404
  return this.suggestedName(value);
@@ -414,15 +445,15 @@ export class FileWorkerProcessRequest implements Disposable {
414
445
  return this.onComplete<Owner>(owner, handler);
415
446
  }
416
447
 
417
- onError<Owner>(owner: Owner, handler: Handler1<Owner, string>): this {
448
+ onError<Owner>(owner: Owner, handler: Handler1<Owner, FileErrorEventArgs>): this {
418
449
  if (changetype<usize>(handler) == 0) {
419
450
  throwNullArgument("FileWorkerProcessRequest.onError", "handler");
420
451
  }
421
- this.errorBinding = bind1<Owner, string>(owner, handler);
452
+ this.errorBinding = bind1<Owner, FileErrorEventArgs>(owner, handler);
422
453
  return this;
423
454
  }
424
455
 
425
- onErrorWith<Owner>(owner: Owner, handler: Handler1<Owner, string>): this {
456
+ onErrorWith<Owner>(owner: Owner, handler: Handler1<Owner, FileErrorEventArgs>): this {
426
457
  return this.onError<Owner>(owner, handler);
427
458
  }
428
459
 
@@ -439,16 +470,26 @@ export class FileWorkerProcessRequest implements Disposable {
439
470
  dispatchFileError(this.errorBinding, "FileWorkerProcessRequest.start: chunkBytes must be greater than zero.");
440
471
  return this;
441
472
  }
473
+ if (this.workerWasmPathValue.length == 0 || this.workerEntryNameValue.length == 0) {
474
+ dispatchFileError(this.errorBinding, "FileWorkerProcessRequest.start: worker(wasmPath, entryName) is required.");
475
+ return this;
476
+ }
442
477
  if (!this.saveToPickedFileEnabled && this.chunkBinding === null) {
443
478
  dispatchFileError(this.errorBinding, "FileWorkerProcessRequest.start: either saveToPickedFile(...) or onChunk(...) is required.");
444
479
  return this;
445
480
  }
481
+ const workerWasmPathBytes = encodeUtf8(this.workerWasmPathValue);
482
+ const workerEntryNameBytes = encodeUtf8(this.workerEntryNameValue);
446
483
  const fileIdBytes = encodeUtf8(this.file.id);
447
484
  const suggestedNameBytes = encodeUtf8(this.suggestedNameValue);
448
485
  this.requestId = registerPendingWorkerProcessRequest(this);
449
486
  this.started = true;
450
487
  fui_file_process_worker_start(
451
488
  this.requestId,
489
+ workerWasmPathBytes.length > 0 ? workerWasmPathBytes.dataStart : 0,
490
+ <u32>workerWasmPathBytes.length,
491
+ workerEntryNameBytes.length > 0 ? workerEntryNameBytes.dataStart : 0,
492
+ <u32>workerEntryNameBytes.length,
452
493
  fileIdBytes.length > 0 ? fileIdBytes.dataStart : 0,
453
494
  <u32>fileIdBytes.length,
454
495
  suggestedNameBytes.length > 0 ? suggestedNameBytes.dataStart : 0,
@@ -550,7 +591,7 @@ export class BrowserFileWriter {
550
591
  owner: Owner,
551
592
  text: string,
552
593
  handler: Handler1<Owner, FileWriteProgress>,
553
- errorHandler: Handler1<Owner, string> | null = null,
594
+ errorHandler: Handler1<Owner, FileErrorEventArgs> | null = null,
554
595
  ): Disposable {
555
596
  if (changetype<usize>(text) == 0) {
556
597
  throwNullArgument(FUNCTION_WRITE_TEXT_CHUNK_WITH, "text");
@@ -562,7 +603,7 @@ export class BrowserFileWriter {
562
603
  const textBytes = encodeUtf8(text);
563
604
  const requestId = registerPendingWriterWriteRequest(new PendingWriterWriteRequest(
564
605
  bind1<Owner, FileWriteProgress>(owner, handler),
565
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
606
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
566
607
  ));
567
608
  fui_file_writer_write_text(
568
609
  requestId,
@@ -578,7 +619,7 @@ export class BrowserFileWriter {
578
619
  owner: Owner,
579
620
  bytes: Uint8Array,
580
621
  handler: Handler1<Owner, FileWriteProgress>,
581
- errorHandler: Handler1<Owner, string> | null = null,
622
+ errorHandler: Handler1<Owner, FileErrorEventArgs> | null = null,
582
623
  ): Disposable {
583
624
  if (changetype<usize>(bytes) == 0) {
584
625
  throwNullArgument(FUNCTION_WRITE_BYTES_CHUNK_WITH, "bytes");
@@ -589,7 +630,7 @@ export class BrowserFileWriter {
589
630
  const writerIdBytes = encodeUtf8(this.writerId);
590
631
  const requestId = registerPendingWriterWriteRequest(new PendingWriterWriteRequest(
591
632
  bind1<Owner, FileWriteProgress>(owner, handler),
592
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
633
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
593
634
  ));
594
635
  fui_file_writer_write_bytes(
595
636
  requestId,
@@ -604,7 +645,7 @@ export class BrowserFileWriter {
604
645
  finishWith<Owner>(
605
646
  owner: Owner,
606
647
  handler: Handler1<Owner, FileSaveResult>,
607
- errorHandler: Handler1<Owner, string> | null = null,
648
+ errorHandler: Handler1<Owner, FileErrorEventArgs> | null = null,
608
649
  ): Disposable {
609
650
  if (changetype<usize>(handler) == 0) {
610
651
  throwNullArgument(FUNCTION_FINISH_WITH, "handler");
@@ -612,7 +653,7 @@ export class BrowserFileWriter {
612
653
  const writerIdBytes = encodeUtf8(this.writerId);
613
654
  const requestId = registerPendingWriterFinishRequest(new PendingWriterFinishRequest(
614
655
  bind1<Owner, FileSaveResult>(owner, handler),
615
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
656
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
616
657
  ));
617
658
  fui_file_writer_finish(
618
659
  requestId,
@@ -642,16 +683,16 @@ export class FileOpenRequest {
642
683
 
643
684
  pickWith<Owner>(
644
685
  owner: Owner,
645
- handler: Handler1<Owner, Array<BrowserFile>>,
646
- errorHandler: Handler1<Owner, string> | null = null,
686
+ handler: Handler1<Owner, FileOpenEventArgs>,
687
+ errorHandler: Handler1<Owner, FileErrorEventArgs> | null = null,
647
688
  ): Disposable {
648
689
  if (changetype<usize>(handler) == 0) {
649
690
  throwNullArgument(FUNCTION_PICK_WITH, "handler");
650
691
  }
651
692
  const acceptBytes = encodeUtf8(this.acceptValue);
652
693
  const requestId = registerPendingOpenRequest(new PendingOpenRequest(
653
- bind1<Owner, Array<BrowserFile>>(owner, handler),
654
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
694
+ bind1<Owner, FileOpenEventArgs>(owner, handler),
695
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
655
696
  ));
656
697
  fui_file_pick(
657
698
  requestId,
@@ -696,7 +737,7 @@ export class FileSaveRequest {
696
737
  owner: Owner,
697
738
  text: string,
698
739
  handler: Handler1<Owner, FileSaveResult>,
699
- errorHandler: Handler1<Owner, string> | null = null,
740
+ errorHandler: Handler1<Owner, FileErrorEventArgs> | null = null,
700
741
  ): Disposable {
701
742
  if (changetype<usize>(text) == 0) {
702
743
  throwNullArgument(FUNCTION_SAVE_TEXT_WITH, "text");
@@ -710,7 +751,7 @@ export class FileSaveRequest {
710
751
  const textBytes = encodeUtf8(text);
711
752
  const requestId = registerPendingSaveRequest(new PendingSaveRequest(
712
753
  bind1<Owner, FileSaveResult>(owner, handler),
713
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
754
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
714
755
  ));
715
756
  fui_file_save_text(
716
757
  requestId,
@@ -730,7 +771,7 @@ export class FileSaveRequest {
730
771
  owner: Owner,
731
772
  bytes: Uint8Array,
732
773
  handler: Handler1<Owner, FileSaveResult>,
733
- errorHandler: Handler1<Owner, string> | null = null,
774
+ errorHandler: Handler1<Owner, FileErrorEventArgs> | null = null,
734
775
  ): Disposable {
735
776
  if (changetype<usize>(bytes) == 0) {
736
777
  throwNullArgument(FUNCTION_SAVE_BYTES_WITH, "bytes");
@@ -743,7 +784,7 @@ export class FileSaveRequest {
743
784
  const extensionBytes = encodeUtf8(this.fileExtensionValue);
744
785
  const requestId = registerPendingSaveRequest(new PendingSaveRequest(
745
786
  bind1<Owner, FileSaveResult>(owner, handler),
746
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
787
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
747
788
  ));
748
789
  fui_file_save_bytes(
749
790
  requestId,
@@ -762,7 +803,7 @@ export class FileSaveRequest {
762
803
  createWriterWith<Owner>(
763
804
  owner: Owner,
764
805
  handler: Handler1<Owner, BrowserFileWriter>,
765
- errorHandler: Handler1<Owner, string> | null = null,
806
+ errorHandler: Handler1<Owner, FileErrorEventArgs> | null = null,
766
807
  ): Disposable {
767
808
  if (changetype<usize>(handler) == 0) {
768
809
  throwNullArgument(FUNCTION_CREATE_WRITER_WITH, "handler");
@@ -772,7 +813,7 @@ export class FileSaveRequest {
772
813
  const extensionBytes = encodeUtf8(this.fileExtensionValue);
773
814
  const requestId = registerPendingWriterCreateRequest(new PendingWriterCreateRequest(
774
815
  bind1<Owner, BrowserFileWriter>(owner, handler),
775
- errorHandler === null ? null : bind1<Owner, string>(owner, errorHandler),
816
+ errorHandler === null ? null : bind1<Owner, FileErrorEventArgs>(owner, errorHandler),
776
817
  ));
777
818
  fui_file_create_writer(
778
819
  requestId,
@@ -842,7 +883,7 @@ export function handleFilePickResult(requestId: u32, status: u32, files: Array<B
842
883
  return;
843
884
  }
844
885
  if (status == FILE_STATUS_SUCCESS) {
845
- request.completeBinding.invoke(files);
886
+ request.completeBinding.invoke(new FileOpenEventArgs(files));
846
887
  return;
847
888
  }
848
889
  dispatchFileError(request.errorBinding, message === null ? describeFileFailure(status, "File picker failed.") : message);
@@ -998,7 +1039,7 @@ export function disposeAllFileRequests(): void {
998
1039
  pendingWorkerProcessRequests.clear();
999
1040
  }
1000
1041
 
1001
- export function __resetFileForTests(): void {
1042
+ export function resetFileRuntime(): void {
1002
1043
  disposeAllFileRequests();
1003
1044
  browserFiles.clear();
1004
1045
  nextFileRequestId = 1;
@@ -1,5 +1,5 @@
1
1
  import * as ui from "../bindings/ui";
2
- import { BorderStyle, Unit } from "./ffi";
2
+ import { Unit } from "./ffi";
3
3
  import { Node } from "./Node";
4
4
  import { activeTheme } from "./Theme";
5
5
  import { Portal, FlexBox } from "../nodes";
@@ -53,7 +53,7 @@ export class FocusAdornerManager {
53
53
  const ringNode = new FlexBox()
54
54
  .positionAbsolute()
55
55
  .bgColor(0x00000000)
56
- .border(STANDARD_FOCUS_RING_WIDTH, 0x00000000, BorderStyle.Solid);
56
+ .border(STANDARD_FOCUS_RING_WIDTH, 0x00000000);
57
57
  const hostRoot = new Portal()
58
58
  .positionAbsolute()
59
59
  .position(0.0, 0.0)
@@ -168,7 +168,7 @@ export class FocusAdornerManager {
168
168
  style.bottomLeftRadius != this.lastBottomLeftRadius
169
169
  ) {
170
170
  ringNode.corners(style.topLeftRadius, style.topRightRadius, style.bottomRightRadius, style.bottomLeftRadius);
171
- ringNode.border(STANDARD_FOCUS_RING_WIDTH, color, BorderStyle.Solid);
171
+ ringNode.border(STANDARD_FOCUS_RING_WIDTH, color);
172
172
  this.lastColor = color;
173
173
  this.lastTopLeftRadius = style.topLeftRadius;
174
174
  this.lastTopRightRadius = style.topRightRadius;
@@ -17,6 +17,10 @@ function hasNonShiftModifier(modifiers: u32): bool {
17
17
  return (modifiers & (KeyModifier.Ctrl | KeyModifier.Alt | KeyModifier.Meta)) != 0;
18
18
  }
19
19
 
20
+ function isCaretNavigationKey(key: string): bool {
21
+ return key == "ArrowLeft" || key == "ArrowRight" || key == "ArrowUp" || key == "ArrowDown" || key == "Home" || key == "End" || key == "PageUp" || key == "PageDown";
22
+ }
23
+
20
24
  export function showKeyboardFocusForKeyEvent(eventType: KeyEventType, key: string, modifiers: u32 = 0): void {
21
25
  if (eventType != KeyEventType.Down || isModifierKey(key)) {
22
26
  return;
@@ -24,6 +28,9 @@ export function showKeyboardFocusForKeyEvent(eventType: KeyEventType, key: strin
24
28
  if (hasNonShiftModifier(modifiers)) {
25
29
  return;
26
30
  }
31
+ if (isCaretNavigationKey(key)) {
32
+ return;
33
+ }
27
34
  if (key == "Escape") {
28
35
  keyboardFocusVisible.value = false;
29
36
  return;
@@ -3,9 +3,70 @@ import * as ui from "../bindings/ui";
3
3
  let needsCommit: bool = false;
4
4
  let flushScheduled: bool = false;
5
5
  let didFirstCommit: bool = false;
6
+ const nextCommitCallbacks = new Array<() => void>();
7
+ export type AfterCommitHandler<Owner> = (owner: Owner) => void;
8
+ abstract class NextCommitRegistration {
9
+ abstract invoke(): void;
10
+ }
11
+
12
+ class NextCommitCallbackRegistration extends NextCommitRegistration {
13
+ constructor(private readonly callback: () => void) {
14
+ super();
15
+ }
16
+
17
+ invoke(): void {
18
+ this.callback();
19
+ }
20
+ }
21
+
22
+ class NextCommitHandlerRegistration<Owner> extends NextCommitRegistration {
23
+ constructor(
24
+ private readonly owner: Owner,
25
+ private readonly handler: AfterCommitHandler<Owner>,
26
+ ) {
27
+ super();
28
+ }
29
+
30
+ invoke(): void {
31
+ this.handler(this.owner);
32
+ }
33
+ }
34
+
35
+ export class LoadedEventArgs {
36
+ static readonly Empty: LoadedEventArgs = new LoadedEventArgs();
37
+ }
6
38
 
7
39
  // WPF-style Loaded event — fires once after the tree is built and mounted.
8
- const loadedCallbacks = new Array<() => void>();
40
+ export type LoadedHandler<Owner> = (owner: Owner, event: LoadedEventArgs) => void;
41
+
42
+ abstract class LoadedRegistration {
43
+ abstract invoke(): void;
44
+ }
45
+
46
+ class LoadedCallbackRegistration extends LoadedRegistration {
47
+ constructor(private readonly callback: (event: LoadedEventArgs) => void) {
48
+ super();
49
+ }
50
+
51
+ invoke(): void {
52
+ this.callback(LoadedEventArgs.Empty);
53
+ }
54
+ }
55
+
56
+ class LoadedHandlerRegistration<Owner> extends LoadedRegistration {
57
+ constructor(
58
+ private readonly owner: Owner,
59
+ private readonly handler: LoadedHandler<Owner>,
60
+ ) {
61
+ super();
62
+ }
63
+
64
+ invoke(): void {
65
+ this.handler(this.owner, LoadedEventArgs.Empty);
66
+ }
67
+ }
68
+
69
+ const loadedCallbacks = new Array<LoadedRegistration>();
9
70
 
10
71
  export function markNeedsCommit(): void {
11
72
  needsCommit = true;
@@ -21,23 +82,45 @@ export function markNeedsCommit(): void {
21
82
  * the first frame has been committed. Safe to call during `buildPage()` —
22
83
  * timers started here survive `cancelAllTimers()` in `Application.mount()`.
23
84
  */
24
- export function onLoaded(cb: () => void): void {
85
+ export function onLoaded(cb: (event: LoadedEventArgs) => void): void {
86
+ if (didFirstCommit) {
87
+ cb(LoadedEventArgs.Empty);
88
+ return;
89
+ }
90
+ loadedCallbacks.push(new LoadedCallbackRegistration(cb));
91
+ }
92
+
93
+ export function onLoadedWith<Owner>(owner: Owner, handler: LoadedHandler<Owner>): void {
25
94
  if (didFirstCommit) {
26
- cb();
95
+ handler(owner, LoadedEventArgs.Empty);
27
96
  return;
28
97
  }
29
- loadedCallbacks.push(cb);
98
+ loadedCallbacks.push(new LoadedHandlerRegistration<Owner>(owner, handler));
99
+ }
100
+
101
+ export function onLoadedFor<Owner>(owner: Owner, handler: LoadedHandler<Owner>): void {
102
+ onLoadedWith<Owner>(owner, handler);
30
103
  }
31
104
 
32
105
  export function fireLoadedCallbacks(): void {
33
106
  if (didFirstCommit) return;
34
107
  didFirstCommit = true;
35
108
  for (let i: i32 = 0; i < loadedCallbacks.length; i++) {
36
- loadedCallbacks[i]();
109
+ unchecked(loadedCallbacks[i]).invoke();
37
110
  }
38
111
  loadedCallbacks.length = 0;
39
112
  }
40
113
 
114
+ export function afterNextCommit(callback: () => void): void {
115
+ nextCommitCallbacks.push(callback);
116
+ }
117
+
118
+ const nextCommitRegistrations = new Array<NextCommitRegistration>();
119
+
120
+ export function afterNextCommitWith<Owner>(owner: Owner, handler: AfterCommitHandler<Owner>): void {
121
+ nextCommitRegistrations.push(new NextCommitHandlerRegistration<Owner>(owner, handler));
122
+ }
123
+
41
124
  export function flushCommit(): bool {
42
125
  flushScheduled = false;
43
126
  if (!needsCommit) {
@@ -45,6 +128,20 @@ export function flushCommit(): bool {
45
128
  }
46
129
  needsCommit = false;
47
130
  ui.commitFrame();
131
+ if (nextCommitCallbacks.length > 0) {
132
+ const callbacks = nextCommitCallbacks.slice();
133
+ nextCommitCallbacks.length = 0;
134
+ for (let i: i32 = 0; i < callbacks.length; i++) {
135
+ unchecked(callbacks[i])();
136
+ }
137
+ }
138
+ if (nextCommitRegistrations.length > 0) {
139
+ const registrations = nextCommitRegistrations.slice();
140
+ nextCommitRegistrations.length = 0;
141
+ for (let i: i32 = 0; i < registrations.length; i++) {
142
+ unchecked(registrations[i]).invoke();
143
+ }
144
+ }
48
145
  return true;
49
146
  }
50
147
 
@@ -52,4 +149,6 @@ export function resetCommitState(): void {
52
149
  needsCommit = false;
53
150
  flushScheduled = false;
54
151
  didFirstCommit = false;
152
+ nextCommitCallbacks.length = 0;
153
+ nextCommitRegistrations.length = 0;
55
154
  }
@@ -0,0 +1,43 @@
1
+ import { ImageSamplingKind } from "./ffi";
2
+
3
+ export class ImageSampling {
4
+ private static LINEAR: ImageSampling = new ImageSampling(ImageSamplingKind.Linear, 0);
5
+ private static NEAREST: ImageSampling = new ImageSampling(ImageSamplingKind.Nearest, 0);
6
+ private static LINEAR_MIPMAP_NEAREST: ImageSampling = new ImageSampling(ImageSamplingKind.LinearMipmapNearest, 0);
7
+ private static LINEAR_MIPMAP_LINEAR: ImageSampling = new ImageSampling(ImageSamplingKind.LinearMipmapLinear, 0);
8
+ private static CUBIC_MITCHELL: ImageSampling = new ImageSampling(ImageSamplingKind.CubicMitchell, 0);
9
+ private static CUBIC_CATMULL_ROM: ImageSampling = new ImageSampling(ImageSamplingKind.CubicCatmullRom, 0);
10
+
11
+ static linear(): ImageSampling {
12
+ return ImageSampling.LINEAR;
13
+ }
14
+
15
+ static nearest(): ImageSampling {
16
+ return ImageSampling.NEAREST;
17
+ }
18
+
19
+ static linearMipmapNearest(): ImageSampling {
20
+ return ImageSampling.LINEAR_MIPMAP_NEAREST;
21
+ }
22
+
23
+ static linearMipmapLinear(): ImageSampling {
24
+ return ImageSampling.LINEAR_MIPMAP_LINEAR;
25
+ }
26
+
27
+ static cubicMitchell(): ImageSampling {
28
+ return ImageSampling.CUBIC_MITCHELL;
29
+ }
30
+
31
+ static cubicCatmullRom(): ImageSampling {
32
+ return ImageSampling.CUBIC_CATMULL_ROM;
33
+ }
34
+
35
+ static anisotropic(maxAniso: u32 = 8): ImageSampling {
36
+ return new ImageSampling(ImageSamplingKind.Anisotropic, maxAniso);
37
+ }
38
+
39
+ private constructor(
40
+ public readonly kind: ImageSamplingKind,
41
+ public readonly maxAniso: u32,
42
+ ) {}
43
+ }
@@ -63,6 +63,8 @@ export function describePointerEventType(eventType: PointerEventType): string {
63
63
  return "enter";
64
64
  case PointerEventType.Leave:
65
65
  return "leave";
66
+ case PointerEventType.Cancel:
67
+ return "cancel";
66
68
  default:
67
69
  return "pointer(" + (<u32>eventType).toString() + ")";
68
70
  }