diff --git a/samples/Gallery/Pages/AutoCompleteBoxPage.fs b/samples/Gallery/Pages/AutoCompleteBoxPage.fs index 066f3fb0..64ac6a5e 100644 --- a/samples/Gallery/Pages/AutoCompleteBoxPage.fs +++ b/samples/Gallery/Pages/AutoCompleteBoxPage.fs @@ -4,6 +4,7 @@ open System open System.Diagnostics open System.Threading open System.Threading.Tasks +open Avalonia.Animation open Avalonia.Controls open Avalonia.Interactivity open Avalonia.Media @@ -215,14 +216,17 @@ module AutoCompleteBoxPage = /// allows searching US federal states asynchronously while cancelling running searches /// to ensure we only populate with the response of the latest request - type UsFederalStateSearch() = + type UsFederalStateSearch(animateActiveInput) = let mutable running: CancellationTokenSource = null // enables cancelling any running search + let isRunning () = running <> null + /// cancels any running search let cancel () = - if running <> null then + if isRunning() then running.Cancel() running.Dispose() + running <- null let simulateWork () = task { @@ -240,19 +244,17 @@ module AutoCompleteBoxPage = member this.SearchAsync (term: string) (cancellation: CancellationToken) : Task = task { - // register cancellation of running search when outer cancellation is requested - cancellation.Register(fun () -> - cancel() - running <- null) - |> ignore - + cancellation.Register(cancel) |> ignore // register cancellation of running search when outer cancellation is requested cancel() // cancel any older running search running <- new CancellationTokenSource() // and create a new source for this one + animateActiveInput(running.Token) // pass running search token to stop it when the search completes or is canceled do! simulateWork() // simulate a really sporadic remote search - if running.IsCancellationRequested then + if isRunning() |> not || running.IsCancellationRequested then + cancel() // to stop animation return Seq.empty else + cancel() // to stop animation return usFederalStates @@ -260,6 +262,16 @@ module AutoCompleteBoxPage = |> Seq.cast } + /// helps animating an active remote search AutoCompleteBox + module RemoteSearch = + let input = ViewRef() + let heartBeat = ViewRef() + + /// animates the input with the heartBeat until searchToken is cancelled + let animate searchToken = + heartBeat.Value.IterationCount <- IterationCount.Infinite + heartBeat.Value.RunAsync(input.Value, searchToken) |> ignore + type Model = { IsOpen: bool SelectedItem: string @@ -281,7 +293,7 @@ module AutoCompleteBoxPage = { IsOpen = false Text = "Arkan" AsyncSearchTerm = "" - UsStateSearch = UsFederalStateSearch() + UsStateSearch = UsFederalStateSearch(RemoteSearch.animate) SelectedItem = "Item 2" Items = [ "Item 1"; "Item 2"; "Item 3"; "Product 1"; "Product 2"; "Product 3" ] UsFederalStates = usFederalStates @@ -477,6 +489,18 @@ module AutoCompleteBoxPage = .onTextChanged(model.AsyncSearchTerm, AsyncSearchTermChanged) .filterMode(AutoCompleteFilterMode.None) // remote filtered .multiBindValue("{2}, {1} ({0})", nameof stateData.Name, nameof stateData.Abbreviation, nameof stateData.Capital) + .reference(RemoteSearch.input) + .animation( + (Animation(TimeSpan.FromSeconds(2.)) { + KeyFrame(ScaleTransform.ScaleXProperty, 1.05).cue(0.1) + KeyFrame(ScaleTransform.ScaleYProperty, 1.05).cue(0.1) + KeyFrame(ScaleTransform.ScaleXProperty, 0.95).cue(0.15) + KeyFrame(ScaleTransform.ScaleYProperty, 0.95).cue(0.15) + KeyFrame(ScaleTransform.ScaleXProperty, 1).cue(0.2) + KeyFrame(ScaleTransform.ScaleYProperty, 1).cue(0.2) + }) + .reference(RemoteSearch.heartBeat) + ) } VStack() {