Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Fabulous.Avalonia/Fabulous.Avalonia.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<Compile Include="Views\_RenderOptions.fs" />
<Compile Include="Views\_Animatable.fs" />
<Compile Include="Views\_StyledElement.fs" />
<Compile Include="Views\_AutomationProperties.fs" />
<Compile Include="Views\_ITransform.fs" />
<Compile Include="Views\_Brush.fs" />
<Compile Include="Views\SolidColorBrush.fs" />
Expand Down
17 changes: 14 additions & 3 deletions src/Fabulous.Avalonia/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ module ViewHelpers =
| None -> ValueNone
| Some attr -> ValueSome attr.Value

/// Extend the canReuseView function to check Xamarin.Forms specific constraints
/// Extend the canReuseView function to check AvaloniaUI specific constraints
let rec canReuseView (prev: Widget) (curr: Widget) =
if ViewHelpers.canReuseView prev curr then
if ViewHelpers.canReuseView prev curr && canReuseAutomationId prev curr then
let def = WidgetDefinitionStore.get curr.Key

// TargetType can be null for MemoWidget
Expand All @@ -43,9 +43,20 @@ module ViewHelpers =
else
false

/// Check whether widgets have compatible automation ids.
/// Avalonia only allows setting the automation id once so we can't reuse a control if the id is not the same.
and private canReuseAutomationId (prev: Widget) (curr: Widget) =
let prevIdOpt = tryGetScalarValue prev AutomationProperties.AutomationId

let currIdOpt = tryGetScalarValue curr AutomationProperties.AutomationId

match prevIdOpt with
| ValueSome _ when prevIdOpt <> currIdOpt -> false
| _ -> true

/// TextBlock's text can be defined by both the Text and Inlines property
/// Except when switching between the two, Avalonia will automatically clear out the other property
/// Depending on the order of execution, this can lead to a desync between Avalonia and Fabulous
/// Depending on the order of execution, this can lead to a de-sync between Avalonia and Fabulous
/// So, it's better to not reuse a TextBlock when we are about to switch between Text and Inlines
and canReuseTextBlock (prev: Widget) (curr: Widget) =
let switchingFromTextToInlines =
Expand Down
161 changes: 161 additions & 0 deletions src/Fabulous.Avalonia/Views/_AutomationProperties.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
namespace Fabulous.Avalonia

open System.Runtime.CompilerServices
open Avalonia.Automation
open Avalonia.Automation.Peers
open Fabulous

module AutomationProperties =
let AcceleratorKey =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.AcceleratorKeyProperty

let AccessibilityView =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.AccessibilityViewProperty

let AccessKey =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.AccessKeyProperty

let AutomationId =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.AutomationIdProperty

let ControlTypeOverride =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.ControlTypeOverrideProperty

let HelpText =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.HelpTextProperty

let IsIsColumnHeader =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.IsColumnHeaderProperty

let IsRequiredForForm =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.IsRequiredForFormProperty

let IsRowHeader =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.IsRowHeaderProperty

let IsOffscreenBehavior =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.IsOffscreenBehaviorProperty

let ItemStatus =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.ItemStatusProperty

let ItemType =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.ItemTypeProperty

let LiveSetting =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.LiveSettingProperty

let Name =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.NameProperty

let PositionInSet =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.PositionInSetProperty

let SizeOfSet =
Attributes.defineAvaloniaPropertyWithEquality AutomationProperties.SizeOfSetProperty

type AutomationPropertiesModifiers =
/// <summary>Sets the AcceleratorKey property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The AcceleratorKey value.</param>
[<Extension>]
static member inline acceleratorKey(this: WidgetBuilder<'msg, #IFabStyledElement>, value: string) =
this.AddScalar(AutomationProperties.AcceleratorKey.WithValue(value))

/// <summary>Sets the AccessibilityView property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The AccessibilityView value.</param>
[<Extension>]
static member inline accessibilityView(this: WidgetBuilder<'msg, #IFabStyledElement>, value: AccessibilityView) =
this.AddScalar(AutomationProperties.AccessibilityView.WithValue(value))

/// <summary>Sets the AccessKey property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The AccessKey value.</param>
[<Extension>]
static member inline accessKey(this: WidgetBuilder<'msg, #IFabStyledElement>, value: string) =
this.AddScalar(AutomationProperties.AccessKey.WithValue(value))

/// <summary>Sets the AutomationId property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The AutomationId value.</param>
[<Extension>]
static member inline automationId(this: WidgetBuilder<'msg, #IFabStyledElement>, value: string) =
this.AddScalar(AutomationProperties.AutomationId.WithValue(value))

/// <summary>Sets the ControlTypeOverride property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The ControlTypeOverride value.</param>
[<Extension>]
static member inline controlTypeOverride(this: WidgetBuilder<'msg, #IFabStyledElement>, value: AutomationControlType) =
this.AddScalar(AutomationProperties.ControlTypeOverride.WithValue(value))

/// <summary>Sets the HelpText property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The HelpText value.</param>
[<Extension>]
static member inline helpText(this: WidgetBuilder<'msg, #IFabStyledElement>, value: string) =
this.AddScalar(AutomationProperties.HelpText.WithValue(value))

/// <summary>Sets the IsIsColumnHeader property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The IsIsColumnHeader value.</param>
[<Extension>]
static member inline isColumnHeader(this: WidgetBuilder<'msg, #IFabStyledElement>, value: bool) =
this.AddScalar(AutomationProperties.IsIsColumnHeader.WithValue(value))

/// <summary>Sets the IsRequiredForForm property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The IsRequiredForForm value.</param>
[<Extension>]
static member inline isRequiredForForm(this: WidgetBuilder<'msg, #IFabStyledElement>, value: bool) =
this.AddScalar(AutomationProperties.IsRequiredForForm.WithValue(value))

/// <summary>Sets the IsRowHeader property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The IsRowHeader value.</param>
[<Extension>]
static member inline isRowHeader(this: WidgetBuilder<'msg, #IFabStyledElement>, value: bool) =
this.AddScalar(AutomationProperties.IsRowHeader.WithValue(value))

/// <summary>Sets the IsOffscreenBehavior property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The IsOffscreenBehavior value.</param>
[<Extension>]
static member inline isOffscreenBehavior(this: WidgetBuilder<'msg, #IFabStyledElement>, value: IsOffscreenBehavior) =
this.AddScalar(AutomationProperties.IsOffscreenBehavior.WithValue(value))

/// <summary>Sets the ItemStatus property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The ItemStatus value.</param>
[<Extension>]
static member inline itemStatus(this: WidgetBuilder<'msg, #IFabStyledElement>, value: string) =
this.AddScalar(AutomationProperties.ItemStatus.WithValue(value))

/// <summary>Sets the ItemType property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The ItemType value.</param>
[<Extension>]
static member inline itemType(this: WidgetBuilder<'msg, #IFabStyledElement>, value: string) =
this.AddScalar(AutomationProperties.ItemType.WithValue(value))

/// <summary>Sets the LiveSetting property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The LiveSetting value.</param>
[<Extension>]
static member inline liveSetting(this: WidgetBuilder<'msg, #IFabStyledElement>, value: AutomationLiveSetting) =
this.AddScalar(AutomationProperties.LiveSetting.WithValue(value))

/// <summary>Sets the PositionInSet property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The PositionInSet value.</param>
[<Extension>]
static member inline positionInSet(this: WidgetBuilder<'msg, #IFabStyledElement>, value: int) =
this.AddScalar(AutomationProperties.PositionInSet.WithValue(value))

/// <summary>Sets the SizeOfSet property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The SizeOfSet value.</param>
[<Extension>]
static member inline sizeOfSet(this: WidgetBuilder<'msg, #IFabStyledElement>, value: int) =
this.AddScalar(AutomationProperties.SizeOfSet.WithValue(value))
6 changes: 3 additions & 3 deletions templates/content/blank/App.fs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ module App =
DesktopApplication(Window(content model))
#endif

// #if DEBUG
// .attachDevTools()
// #endif
// #if DEBUG
// .attachDevTools()
// #endif

let create () =
let program = Program.statefulWithCmd init update |> Program.withView view
Expand Down
6 changes: 3 additions & 3 deletions templates/content/multi/NewApp/App.fs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ module App =
DesktopApplication(Window(content model))

//-:cnd:noEmit
// #if DEBUG
// .attachDevTools()
// #endif
// #if DEBUG
// .attachDevTools()
// #endif

let create () =
let program = Program.statefulWithCmd init update |> Program.withView view
Expand Down