Skip to content

Commit

Permalink
animating remote search to indicate activity
Browse files Browse the repository at this point in the history
  • Loading branch information
h0lg committed May 16, 2024
1 parent 9dd1228 commit 7585d0d
Showing 1 changed file with 34 additions and 10 deletions.
44 changes: 34 additions & 10 deletions samples/Gallery/Pages/AutoCompleteBoxPage.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -240,26 +244,34 @@ module AutoCompleteBoxPage =

member this.SearchAsync (term: string) (cancellation: CancellationToken) : Task<obj seq> =
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
|> Seq.filter(fun state -> contains state.Name term || contains state.Capital term)
|> Seq.cast<obj>
}

/// helps animating an active remote search AutoCompleteBox
module RemoteSearch =
let input = ViewRef<AutoCompleteBox>()
let heartBeat = ViewRef<Animation>()

/// 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
Expand All @@ -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
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit 7585d0d

Please sign in to comment.