diff --git a/src/Fabulous.Avalonia/Attributes.fs b/src/Fabulous.Avalonia/Attributes.fs index 555ea3da2..2efefb238 100644 --- a/src/Fabulous.Avalonia/Attributes.fs +++ b/src/Fabulous.Avalonia/Attributes.fs @@ -2,9 +2,11 @@ namespace Fabulous.Avalonia open System open System.Collections +open System.Collections.Generic open System.ComponentModel open Avalonia open Avalonia.Collections +open Avalonia.Input.GestureRecognizers open Avalonia.Interactivity open Fabulous open Fabulous.ScalarAttributeDefinitions @@ -238,6 +240,73 @@ module Attributes = Attributes.defineWidgetCollection name applyDiff updateNode + let defineAvaloniaGestureRecognizerCollection<'itemType> name (getCollection: obj -> GestureRecognizerCollection) = + let applyDiff _ (diffs: WidgetCollectionItemChanges) (node: IViewNode) = + let targetColl = getCollection node.Target + let targetColl = List(targetColl) + + for diff in diffs do + match diff with + | WidgetCollectionItemChange.Remove(index, widget) -> + let itemNode = node.TreeContext.GetViewNode(box targetColl[index]) + + // Trigger the unmounted event + Dispatcher.dispatchEventForAllChildren itemNode widget Lifecycle.Unmounted + itemNode.Disconnect() + + // Remove the child from the UI tree + targetColl.RemoveAt(index) + + | _ -> () + + for diff in diffs do + match diff with + | WidgetCollectionItemChange.Insert(index, widget) -> + let struct (itemNode, view) = Helpers.createViewForWidget node widget + + // Insert the new child into the UI tree + targetColl.Insert(index, unbox view) + + // Trigger the mounted event + Dispatcher.dispatchEventForAllChildren itemNode widget Lifecycle.Mounted + + | WidgetCollectionItemChange.Update(index, widgetDiff) -> + let childNode = node.TreeContext.GetViewNode(box targetColl[index]) + + childNode.ApplyDiff(&widgetDiff) + + | WidgetCollectionItemChange.Replace(index, oldWidget, newWidget) -> + let prevItemNode = node.TreeContext.GetViewNode(box targetColl[index]) + + let struct (nextItemNode, view) = Helpers.createViewForWidget node newWidget + + // Trigger the unmounted event for the old child + Dispatcher.dispatchEventForAllChildren prevItemNode oldWidget Lifecycle.Unmounted + prevItemNode.Disconnect() + + // Replace the existing child in the UI tree at the index with the new one + targetColl[index] <- unbox view + + // Trigger the mounted event for the new child + Dispatcher.dispatchEventForAllChildren nextItemNode newWidget Lifecycle.Mounted + + | _ -> () + + let updateNode _ (newValueOpt: ArraySlice voption) (node: IViewNode) = + let targetColl = getCollection node.Target + // let targetColl = List(targetColl) + // targetColl.Clear() + + match newValueOpt with + | ValueNone -> () + | ValueSome widgets -> + for widget in ArraySlice.toSpan widgets do + let struct (_, view) = Helpers.createViewForWidget node widget + + targetColl.Add(unbox view) + + Attributes.defineWidgetCollection name applyDiff updateNode + /// Define an attribute storing a Widget for an AvaloniaProperty let inline defineAvaloniaPropertyWidget (property: AvaloniaProperty<'T>) = Attributes.definePropertyWidget property.Name (fun target -> (target :?> AvaloniaObject).GetValue(property)) (fun target value -> diff --git a/src/Fabulous.Avalonia/Fabulous.Avalonia.fsproj b/src/Fabulous.Avalonia/Fabulous.Avalonia.fsproj index e44d0cf42..03e5ffeb5 100644 --- a/src/Fabulous.Avalonia/Fabulous.Avalonia.fsproj +++ b/src/Fabulous.Avalonia/Fabulous.Avalonia.fsproj @@ -50,6 +50,7 @@ + @@ -213,6 +214,9 @@ + + + diff --git a/src/Fabulous.Avalonia/Views/_InputElement.fs b/src/Fabulous.Avalonia/Views/_InputElement.fs index 6cc50e8a5..4114a9244 100644 --- a/src/Fabulous.Avalonia/Views/_InputElement.fs +++ b/src/Fabulous.Avalonia/Views/_InputElement.fs @@ -3,6 +3,7 @@ namespace Fabulous.Avalonia open System.Runtime.CompilerServices open Avalonia.Collections open Avalonia.Input +open Avalonia.Input.GestureRecognizers open Avalonia.Input.TextInput open Avalonia.Interactivity open Fabulous @@ -31,9 +32,7 @@ module InputElement = Attributes.defineAvaloniaPropertyWithEquality InputElement.TabIndexProperty let GestureRecognizers = - Attributes.defineAvaloniaListWidgetCollection "InputElement_GestureRecognizers" (fun target -> - let target = (target :?> InputElement) - AvaloniaList(target.GestureRecognizers)) + Attributes.defineAvaloniaGestureRecognizerCollection "InputElement_GestureRecognizers" (fun target -> (target :?> InputElement).GestureRecognizers) let GotFocus = Attributes.defineEvent "InputElement_GotFocus" (fun target -> (target :?> InputElement).GotFocus) @@ -239,3 +238,16 @@ type InputElementModifiers = [] static member inline onDoubleTapped(this: WidgetBuilder<'msg, #IFabInputElement>, fn: RoutedEventArgs -> 'msg) = this.AddScalar(InputElement.DoubleTapped.WithValue(fn)) + + /// Sets the GestureRecognizers property. + /// Current widget. + [] + static member inline gestureRecognizers<'msg, 'marker when 'marker :> IFabInputElement>(this: WidgetBuilder<'msg, 'marker>) = + AttributeCollectionBuilder<'msg, 'marker, IFabGestureRecognizer>(this, InputElement.GestureRecognizers) + + /// Sets the GestureRecognizers property. + /// Current widget. + /// Gesture recognizer. + [] + static member inline gestureRecognizer(this: WidgetBuilder<'msg, #IFabInputElement>, value: WidgetBuilder<'msg, #IFabGestureRecognizer>) = + AttributeCollectionBuilder<'msg, 'marker, IFabGestureRecognizer>(this, InputElement.GestureRecognizers) { value }