Skip to content

Commit

Permalink
Add AutomationPeer.IsOffscreen.
Browse files Browse the repository at this point in the history
This is needed in order for controls to be scrolled into view using WinAppDriver. The default is the same as WPF and the default value is overridden in the same controls as WPF (where present).
  • Loading branch information
grokys committed Jul 31, 2024
1 parent 8487a03 commit 0c4f6b5
Show file tree
Hide file tree
Showing 11 changed files with 46 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Avalonia.Controls.DataGrid/DataGridCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Automation.Peers;
using Avalonia.Controls.Metadata;
Expand Down Expand Up @@ -37,6 +38,7 @@ static DataGridCell()
(x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true);
FocusableProperty.OverrideDefaultValue<DataGridCell>(true);
IsTabStopProperty.OverrideDefaultValue<DataGridCell>(false);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridCell>(IsOffscreenBehavior.FromClip);
}
public DataGridCell()
{ }
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Collections;
using Avalonia.Controls.Automation.Peers;
Expand Down Expand Up @@ -74,6 +75,7 @@ static DataGridColumnHeader()
AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>((x, e) => x.OnAreSeparatorsVisibleChanged(e));
PressedMixin.Attach<DataGridColumnHeader>();
IsTabStopProperty.OverrideDefaultValue<DataGridColumnHeader>(false);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridColumnHeader>(IsOffscreenBehavior.FromClip);
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Controls.DataGrid/DataGridRow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Diagnostics;
using Avalonia.Automation.Peers;
using Avalonia.Reactive;
using Avalonia.Automation;

namespace Avalonia.Controls
{
Expand Down Expand Up @@ -130,6 +131,7 @@ static DataGridRow()
AreDetailsVisibleProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnAreDetailsVisibleChanged(e));
PointerPressedEvent.AddClassHandler<DataGridRow>((x, e) => x.DataGridRow_PointerPressed(e), handledEventsToo: true);
IsTabStopProperty.OverrideDefaultValue<DataGridRow>(false);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridRow>(IsOffscreenBehavior.FromClip);
}

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using Avalonia.Automation;
using Avalonia.Controls.Metadata;
using Avalonia.Input;
using Avalonia.Media;
Expand Down Expand Up @@ -50,6 +51,11 @@ public DataGridRowHeader()
AddHandler(PointerPressedEvent, DataGridRowHeader_PointerPressed, handledEventsToo: true);
}

static DataGridRowHeader()
{
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridRowHeader>(IsOffscreenBehavior.FromClip);
}

internal Control Owner
{
get;
Expand Down
12 changes: 12 additions & 0 deletions src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ public abstract class AutomationPeer
/// <returns></returns>
public bool IsKeyboardFocusable() => IsKeyboardFocusableCore();

/// <summary>
/// Gets a value that indicates whether an element is off the screen.
/// </summary>
/// <remarks>
/// This property does not indicate whether the element is visible. In some circumstances,
/// an element is on the screen but is still not visible. For example, if the element is
/// on the screen but obscured by other elements, it might not be visible. In this case,
/// the method returns false.
/// </remarks>
public bool IsOffscreen() => IsOffscreenCore();

/// <summary>
/// Sets the keyboard focus on the element that is associated with this automation peer.
/// </summary>
Expand Down Expand Up @@ -245,6 +256,7 @@ protected virtual string GetLocalizedControlTypeCore()
protected abstract bool IsControlElementCore();
protected abstract bool IsEnabledCore();
protected abstract bool IsKeyboardFocusableCore();
protected virtual bool IsOffscreenCore() => false;
protected abstract void SetFocusCore();
protected abstract bool ShowContextMenuCore();

Expand Down
14 changes: 14 additions & 0 deletions src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Utilities;
using Avalonia.VisualTree;

namespace Avalonia.Automation.Peers
Expand Down Expand Up @@ -201,6 +202,19 @@ protected override bool IsControlElementOverrideCore()
return view == AccessibilityView.Default ? IsControlElementCore() : view >= AccessibilityView.Control;
}

protected override bool IsOffscreenCore()
{
return AutomationProperties.GetIsOffscreenBehavior(Owner) switch
{
IsOffscreenBehavior.Onscreen => false,
IsOffscreenBehavior.Offscreen => true,
IsOffscreenBehavior.FromClip => Owner.GetTransformedBounds() is not { } bounds ||
MathUtilities.IsZero(bounds.Clip.Width) ||
MathUtilities.IsZero(bounds.Clip.Height),
_ => !Owner.IsVisible,
};
}

private static Rect GetBounds(Control control)
{
var root = control.GetVisualRoot();
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Controls/ListBoxItem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
Expand Down Expand Up @@ -30,6 +31,7 @@ static ListBoxItem()
SelectableMixin.Attach<ListBoxItem>(IsSelectedProperty);
PressedMixin.Attach<ListBoxItem>();
FocusableProperty.OverrideDefaultValue<ListBoxItem>(true);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<ListBoxItem>(IsOffscreenBehavior.FromClip);
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Controls/MenuItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
Expand Down Expand Up @@ -141,6 +142,7 @@ static MenuItem()
ItemsPanelProperty.OverrideDefaultValue<MenuItem>(DefaultPanel);
ClickEvent.AddClassHandler<MenuItem>((x, e) => x.OnClick(e));
SubmenuOpenedEvent.AddClassHandler<MenuItem>((x, e) => x.OnSubmenuOpened(e));
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<MenuItem>(IsOffscreenBehavior.FromClip);
}

public MenuItem()
Expand Down
1 change: 1 addition & 0 deletions src/Avalonia.Controls/TabItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ static TabItem()
FocusableProperty.OverrideDefaultValue(typeof(TabItem), true);
DataContextProperty.Changed.AddClassHandler<TabItem>((x, e) => x.UpdateHeader(e));
AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<TabItem>(AutomationControlType.TabItem);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<TabItem>(IsOffscreenBehavior.FromClip);
AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler<TabItem>((tabItem, args) => tabItem.TabItemActivated(args));
}

Expand Down
2 changes: 2 additions & 0 deletions src/Avalonia.Controls/TreeViewItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
Expand Down Expand Up @@ -62,6 +63,7 @@ static TreeViewItem()
PressedMixin.Attach<TreeViewItem>();
FocusableProperty.OverrideDefaultValue<TreeViewItem>(true);
ItemsPanelProperty.OverrideDefaultValue<TreeViewItem>(DefaultPanel);
AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<ListBoxItem>(IsOffscreenBehavior.FromClip);
RequestBringIntoViewEvent.AddClassHandler<TreeViewItem>((x, e) => x.OnRequestBringIntoView(e));
}

Expand Down
1 change: 1 addition & 0 deletions src/Windows/Avalonia.Win32/Automation/AutomationNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public virtual IRawElementProviderFragmentRoot? FragmentRoot
UiaPropertyId.IsControlElement => InvokeSync(() => Peer.IsControlElement()),
UiaPropertyId.IsEnabled => InvokeSync(() => Peer.IsEnabled()),
UiaPropertyId.IsKeyboardFocusable => InvokeSync(() => Peer.IsKeyboardFocusable()),
UiaPropertyId.IsOffscreen => InvokeSync(() => Peer.IsOffscreen()),
UiaPropertyId.LocalizedControlType => InvokeSync(() => Peer.GetLocalizedControlType()),
UiaPropertyId.Name => InvokeSync(() => Peer.GetName()),
UiaPropertyId.ProcessId => Process.GetCurrentProcess().Id,
Expand Down

0 comments on commit 0c4f6b5

Please sign in to comment.