Dialogs await their Task through a generic #:async import

Bjolang now resolves a generic method imported #:async, so
Dispatcher.InvokeAsync(Func<Task<T>>) is imported as it is: it runs the
dialog on the UI thread and is awaited for what the dialog answers. The
detour through the non-generic Task.WhenAll and the Result read by
reflection are gone.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
This commit is contained in:
Linus Björnstam 2026-10-25 18:36:53 +01:00
parent 8941425f05
commit f53877e7a1

View file

@ -29,9 +29,6 @@
(import/class (import/class
(Object (: System.Object)) (Object (: System.Object))
(DispatcherOpBase (: Avalonia.Threading.DispatcherOperation))
(ClrType (: System.Type))
(PropertyInfo (: System.Reflection.PropertyInfo))
(ClrString (: System.String)) (ClrString (: System.String))
((ReadOnlyList %a) (: System.Collections.Generic.IReadOnlyList)) ((ReadOnlyList %a) (: System.Collections.Generic.IReadOnlyList))
((EventHandler %a) (: System.EventHandler)) ((EventHandler %a) (: System.EventHandler))
@ -77,20 +74,12 @@
;; The UI thread, and the window a dialog belongs to. ;; The UI thread, and the window a dialog belongs to.
(ui-dispatcher (: Avalonia.Threading.Dispatcher.UIThread #:get)) (ui-dispatcher (: Avalonia.Threading.Dispatcher.UIThread #:get))
(ui-post (: Avalonia.Threading.Dispatcher.InvokeAsync (-> Dispatcher (-> Unit) (DispatcherOp Unit)))) (ui-post (: Avalonia.Threading.Dispatcher.InvokeAsync (-> Dispatcher (-> Unit) (DispatcherOp Unit))))
;; Runs a function on the UI thread. ;; Runs a function that starts a Task on the UI thread, and waits for the
;; Task. Avalonia's dialogs take no CancellationToken, so this cannot be
;; cancelled: a cancelled command stops waiting, and the dialog stays until
;; it is answered.
(on-ui (: Avalonia.Threading.Dispatcher.InvokeAsync (on-ui (: Avalonia.Threading.Dispatcher.InvokeAsync
(-> Dispatcher (-> Unit) (DispatcherOp Unit)))) (-> Dispatcher (-> (Task %a)) %a) #:async #:uncancellable))
(op-task (: Avalonia.Threading.DispatcherOperation.GetTask (-> DispatcherOpBase Task)))
;; Waits for Tasks. Avalonia's dialogs take no CancellationToken, so this
;; cannot be cancelled: a cancelled command stops at its next sync, and the
;; dialog stays until it is answered.
(await-tasks (: System.Threading.Tasks.Task.WhenAll (-> (Array Task) void)
#:async #:uncancellable))
;; What a finished Task answers. By reflection, because Bjolang reads no
;; Task's Result: on a Task that is not finished, that would block.
(type-of (: System.Object.GetType (-> Object ClrType)))
(property-of (: System.Type.GetProperty (-> ClrType string PropertyInfo)))
(value-of (: System.Reflection.PropertyInfo.GetValue (-> PropertyInfo Object Object)))
(answered (: System.Threading.Tasks.Task.FromResult (-> %a (Task %a)))) (answered (: System.Threading.Tasks.Task.FromResult (-> %a (Task %a))))
(current-app (: Avalonia.Application.Current #:get)) (current-app (: Avalonia.Application.Current #:get))
(app-lifetime (: Avalonia.Application.ApplicationLifetime (-> Application ILifetime) #:get)) (app-lifetime (: Avalonia.Application.ApplicationLifetime (-> Application ILifetime) #:get))
@ -171,16 +160,10 @@
;; Task it makes. `none` is the answer when there is no window. ;; Task it makes. `none` is the answer when there is no window.
(: (-bjo-> (-> Window (Task %a)) %a %a)) (: (-bjo-> (-> Window (Task %a)) %a %a))
(defbjo (run-on-ui start none) (defbjo (run-on-ui start none)
(def/mutable task (: (clr-default) Task)) (on-ui ui-dispatcher
(def op (on-ui ui-dispatcher (fun () (match (owner)
(fun () ((Some w) (start w))
(set! task (cast Task (cast Object (match (owner) (None (answered none))))))
((Some w) (start w))
(None (answered none))))))
unit)))
(await-tasks #[(op-task (cast DispatcherOpBase (cast Object op)))])
(await-tasks #[task])
(cast %a (value-of (property-of (type-of (cast Object task)) "Result") (cast Object task))))
;; The event of what the Task that `start` makes answers, at each sync. ;; The event of what the Task that `start` makes answers, at each sync.
;; `none` is the answer when there is no window, or the Task fails. ;; `none` is the answer when there is no window, or the Task fails.