diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml
index e160bda11e..dd20d0f39e 100644
--- a/api/Avalonia.nupkg.xml
+++ b/api/Avalonia.nupkg.xml
@@ -1117,12 +1117,48 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Input.FocusManager.#ctor(Avalonia.Input.IInputElement)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.FocusManager.ClearFocus
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.FocusManager.ClearFocusOnElementRemoved(Avalonia.Input.IInputElement,Avalonia.Visual)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.FocusManager.FindNextElement(Avalonia.Input.NavigationDirection)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.FocusManager.TryMoveFocus(Avalonia.Input.NavigationDirection)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Input.HoldingRoutedEventArgs.#ctor(Avalonia.Input.HoldingState,Avalonia.Point,Avalonia.Input.PointerType)
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Input.IFocusManager.ClearFocus
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Input.IInputRoot.get_KeyboardNavigationHandler
@@ -2611,12 +2647,48 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Input.FocusManager.#ctor(Avalonia.Input.IInputElement)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.FocusManager.ClearFocus
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.FocusManager.ClearFocusOnElementRemoved(Avalonia.Input.IInputElement,Avalonia.Visual)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.FocusManager.FindNextElement(Avalonia.Input.NavigationDirection)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0002
+ M:Avalonia.Input.FocusManager.TryMoveFocus(Avalonia.Input.NavigationDirection)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Input.HoldingRoutedEventArgs.#ctor(Avalonia.Input.HoldingState,Avalonia.Point,Avalonia.Input.PointerType)
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0002
+ M:Avalonia.Input.IFocusManager.ClearFocus
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0002
M:Avalonia.Input.IInputRoot.get_KeyboardNavigationHandler
@@ -4027,6 +4099,36 @@
baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.FindFirstFocusableElement
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.FindLastFocusableElement
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.FindNextElement(Avalonia.Input.NavigationDirection,Avalonia.Input.FindNextElementOptions)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.Focus(Avalonia.Input.IInputElement,Avalonia.Input.NavigationMethod,Avalonia.Input.KeyModifiers)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.TryMoveFocus(Avalonia.Input.NavigationDirection,Avalonia.Input.FindNextElementOptions)
+ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll
+ current/Avalonia/lib/net10.0/Avalonia.Base.dll
+
CP0006
M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers,System.Nullable{Avalonia.Input.KeyDeviceType})
@@ -4315,6 +4417,36 @@
baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.FindFirstFocusableElement
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.FindLastFocusableElement
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.FindNextElement(Avalonia.Input.NavigationDirection,Avalonia.Input.FindNextElementOptions)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.Focus(Avalonia.Input.IInputElement,Avalonia.Input.NavigationMethod,Avalonia.Input.KeyModifiers)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
+
+ CP0006
+ M:Avalonia.Input.IFocusManager.TryMoveFocus(Avalonia.Input.NavigationDirection,Avalonia.Input.FindNextElementOptions)
+ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll
+ current/Avalonia/lib/net8.0/Avalonia.Base.dll
+
CP0006
M:Avalonia.Input.IKeyboardNavigationHandler.Move(Avalonia.Input.IInputElement,Avalonia.Input.NavigationDirection,Avalonia.Input.KeyModifiers,System.Nullable{Avalonia.Input.KeyDeviceType})
diff --git a/src/Avalonia.Base/Input/FindNextElementOptions.cs b/src/Avalonia.Base/Input/FindNextElementOptions.cs
index e6062daf9b..72d83ec419 100644
--- a/src/Avalonia.Base/Input/FindNextElementOptions.cs
+++ b/src/Avalonia.Base/Input/FindNextElementOptions.cs
@@ -6,12 +6,49 @@ using System.Threading.Tasks;
namespace Avalonia.Input
{
+ ///
+ /// Provides options to customize the behavior when identifying the next element to focus
+ /// during a navigation operation.
+ ///
public sealed class FindNextElementOptions
{
+ ///
+ /// Gets or sets the root within which the search for the next
+ /// focusable element will be conducted.
+ ///
+ ///
+ /// This property defines the boundary for focus navigation operations. It determines the root element
+ /// in the visual tree under which the focusable item search is performed. If not specified, the search
+ /// will default to the current scope.
+ ///
public InputElement? SearchRoot { get; init; }
+
+ ///
+ /// Gets or sets the rectangular region within the visual hierarchy that will be excluded
+ /// from consideration during focus navigation.
+ ///
public Rect ExclusionRect { get; init; }
+
+ ///
+ /// Gets or sets a rectangular region that serves as a hint for focus navigation.
+ /// This property specifies a rectangle, relative to the coordinate system of the search root,
+ /// which can be used as a preferred or prioritized target when navigating focus.
+ /// It can be null if no specific hint region is provided.
+ ///
public Rect? FocusHintRectangle { get; init; }
+
+ ///
+ /// Specifies an optional override for the navigation strategy used in XY focus navigation.
+ /// This property allows customizing the focus movement behavior when navigating between UI elements.
+ ///
public XYFocusNavigationStrategy? NavigationStrategyOverride { get; init; }
+
+ ///
+ /// Specifies whether occlusivity (overlapping of elements or obstructions)
+ /// should be ignored during focus navigation. When set to true,
+ /// the navigation logic disregards obstructions that may block a potential
+ /// focus target, allowing elements behind such obstructions to be considered.
+ ///
public bool IgnoreOcclusivity { get; init; }
}
}
diff --git a/src/Avalonia.Base/Input/FocusManager.cs b/src/Avalonia.Base/Input/FocusManager.cs
index 15b8fea77d..dc62171f48 100644
--- a/src/Avalonia.Base/Input/FocusManager.cs
+++ b/src/Avalonia.Base/Input/FocusManager.cs
@@ -4,7 +4,6 @@ using System.Linq;
using Avalonia.Input.Navigation;
using Avalonia.Interactivity;
using Avalonia.Metadata;
-using Avalonia.Reactive;
using Avalonia.VisualTree;
namespace Avalonia.Input
@@ -12,7 +11,6 @@ namespace Avalonia.Input
///
/// Manages focus for the application.
///
- [PrivateApi]
public class FocusManager : IFocusManager
{
///
@@ -42,58 +40,51 @@ namespace Avalonia.Input
RoutingStrategies.Tunnel);
}
+ [PrivateApi]
public FocusManager()
{
- _contentRoot = null;
}
- public FocusManager(IInputElement contentRoot)
- {
- _contentRoot = contentRoot;
- }
-
- internal void SetContentRoot(IInputElement? contentRoot)
+ ///
+ /// Gets or sets the content root for the focus management system.
+ ///
+ [PrivateApi]
+ public IInputElement? ContentRoot
{
- _contentRoot = contentRoot;
+ get => _contentRoot;
+ set => _contentRoot = value;
}
private IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;
- private XYFocus _xyFocus = new();
- private XYFocusOptions _xYFocusOptions = new XYFocusOptions();
+ private readonly XYFocus _xyFocus = new();
private IInputElement? _contentRoot;
+ private XYFocusOptions? _reusableFocusOptions;
- ///
- /// Gets the currently focused .
- ///
+ ///
public IInputElement? GetFocusedElement() => Current;
- ///
- /// Focuses a control.
- ///
- /// The control to focus.
- /// The method by which focus was changed.
- /// Any key modifiers active at the time of focus.
+ ///
public bool Focus(
- IInputElement? control,
+ IInputElement? element,
NavigationMethod method = NavigationMethod.Unspecified,
KeyModifiers keyModifiers = KeyModifiers.None)
{
if (KeyboardDevice.Instance is not { } keyboardDevice)
return false;
- if (control is not null)
+ if (element is not null)
{
- if (!CanFocus(control))
+ if (!CanFocus(element))
return false;
- if (GetFocusScope(control) is StyledElement scope)
+ if (GetFocusScope(element) is StyledElement scope)
{
- scope.SetValue(FocusedElementProperty, control);
+ scope.SetValue(FocusedElementProperty, element);
_focusRoot = GetFocusRoot(scope);
}
- keyboardDevice.SetFocusedElement(control, method, keyModifiers);
+ keyboardDevice.SetFocusedElement(element, method, keyModifiers);
return true;
}
else if (_focusRoot?.GetValue(FocusedElementProperty) is { } restore &&
@@ -110,12 +101,7 @@ namespace Avalonia.Input
}
}
- public void ClearFocus()
- {
- Focus(null);
- }
-
- public void ClearFocusOnElementRemoved(IInputElement removedElement, Visual oldParent)
+ internal void ClearFocusOnElementRemoved(IInputElement removedElement, Visual oldParent)
{
if (oldParent is IInputElement parentElement &&
GetFocusScope(parentElement) is StyledElement scope &&
@@ -129,6 +115,7 @@ namespace Avalonia.Input
Focus(null);
}
+ [PrivateApi]
public IInputElement? GetFocusedElement(IFocusScope scope)
{
return (scope as StyledElement)?.GetValue(FocusedElementProperty);
@@ -138,6 +125,7 @@ namespace Avalonia.Input
/// Notifies the focus manager of a change in focus scope.
///
/// The new focus scope.
+ [PrivateApi]
public void SetFocusScope(IFocusScope scope)
{
if (GetFocusedElement(scope) is { } focused)
@@ -153,12 +141,14 @@ namespace Avalonia.Input
}
}
+ [PrivateApi]
public void RemoveFocusRoot(IFocusScope scope)
{
if (scope == _focusRoot)
- ClearFocus();
+ Focus(null);
}
+ [PrivateApi]
public static bool GetIsFocusScope(IInputElement e) => e is IFocusScope;
///
@@ -176,25 +166,15 @@ namespace Avalonia.Input
?? (FocusManager?)AvaloniaLocator.Current.GetService();
}
- ///
- /// Attempts to change focus from the element with focus to the next focusable element in the specified direction.
- ///
- /// The direction to traverse (in tab order).
- /// true if focus moved; otherwise, false.
- public bool TryMoveFocus(NavigationDirection direction)
+ ///
+ public bool TryMoveFocus(NavigationDirection direction, FindNextElementOptions? options = null)
{
- return FindAndSetNextFocus(direction, _xYFocusOptions);
- }
+ ValidateDirection(direction);
- ///
- /// Attempts to change focus from the element with focus to the next focusable element in the specified direction, using the specified navigation options.
- ///
- /// The direction to traverse (in tab order).
- /// The options to help identify the next element to receive focus with keyboard/controller/remote navigation.
- /// true if focus moved; otherwise, false.
- public bool TryMoveFocus(NavigationDirection direction, FindNextElementOptions options)
- {
- return FindAndSetNextFocus(direction, ValidateAndCreateFocusOptions(direction, options));
+ var focusOptions = ToFocusOptions(options, true);
+ var result = FindAndSetNextFocus(direction, focusOptions);
+ _reusableFocusOptions = focusOptions;
+ return result;
}
///
@@ -295,10 +275,7 @@ namespace Avalonia.Input
return true;
}
- ///
- /// Retrieves the first element that can receive focus.
- ///
- /// The first focusable element.
+ ///
public IInputElement? FindFirstFocusableElement()
{
var root = (_contentRoot as Visual)?.GetSelfAndVisualDescendants().FirstOrDefault(x => x is IInputElement) as IInputElement;
@@ -317,10 +294,7 @@ namespace Avalonia.Input
return GetFirstFocusableElement(searchScope);
}
- ///
- /// Retrieves the last element that can receive focus.
- ///
- /// The last focusable element.
+ ///
public IInputElement? FindLastFocusableElement()
{
var root = (_contentRoot as Visual)?.GetSelfAndVisualDescendants().FirstOrDefault(x => x is IInputElement) as IInputElement;
@@ -339,52 +313,59 @@ namespace Avalonia.Input
return GetFocusManager(searchScope)?.GetLastFocusableElement(searchScope);
}
- ///
- /// Retrieves the element that should receive focus based on the specified navigation direction.
- ///
- ///
- ///
- public IInputElement? FindNextElement(NavigationDirection direction)
+ ///
+ public IInputElement? FindNextElement(NavigationDirection direction, FindNextElementOptions? options = null)
{
- var xyOption = new XYFocusOptions()
- {
- UpdateManifold = false
- };
+ ValidateDirection(direction);
- return FindNextFocus(direction, xyOption);
+ var focusOptions = ToFocusOptions(options, false);
+ var result = FindNextFocus(direction, focusOptions);
+ _reusableFocusOptions = focusOptions;
+ return result;
}
- ///
- /// Retrieves the element that should receive focus based on the specified navigation direction (cannot be used with tab navigation).
- ///
- /// The direction that focus moves from element to element within the app UI.
- /// The options to help identify the next element to receive focus with the provided navigation.
- /// The next element to receive focus.
- public IInputElement? FindNextElement(NavigationDirection direction, FindNextElementOptions options)
+ private static void ValidateDirection(NavigationDirection direction)
{
- return FindNextFocus(direction, ValidateAndCreateFocusOptions(direction, options));
+ if (direction is not (
+ NavigationDirection.Next or
+ NavigationDirection.Previous or
+ NavigationDirection.Up or
+ NavigationDirection.Down or
+ NavigationDirection.Left or
+ NavigationDirection.Right))
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(direction),
+ direction,
+ $"Only {nameof(NavigationDirection.Next)}, {nameof(NavigationDirection.Previous)}, " +
+ $"{nameof(NavigationDirection.Up)}, {nameof(NavigationDirection.Down)}," +
+ $" {nameof(NavigationDirection.Left)} and {nameof(NavigationDirection.Right)} directions are supported");
+ }
}
- private static XYFocusOptions ValidateAndCreateFocusOptions(NavigationDirection direction, FindNextElementOptions options)
+ private XYFocusOptions ToFocusOptions(FindNextElementOptions? options, bool updateManifold)
{
- if (direction is not NavigationDirection.Up
- and not NavigationDirection.Down
- and not NavigationDirection.Left
- and not NavigationDirection.Right)
+ // XYFocus only uses the options and never modifies them; we can cache and reset them between calls.
+ var focusOptions = _reusableFocusOptions;
+ _reusableFocusOptions = null;
+
+ if (focusOptions is null)
+ focusOptions = new XYFocusOptions();
+ else
+ focusOptions.Reset();
+
+ if (options is not null)
{
- throw new ArgumentOutOfRangeException(nameof(direction),
- $"{direction} is not supported with FindNextElementOptions. Only Up, Down, Left and right are supported");
+ focusOptions.SearchRoot = options.SearchRoot;
+ focusOptions.ExclusionRect = options.ExclusionRect;
+ focusOptions.FocusHintRectangle = options.FocusHintRectangle;
+ focusOptions.NavigationStrategyOverride = options.NavigationStrategyOverride;
+ focusOptions.IgnoreOcclusivity = options.IgnoreOcclusivity;
}
- return new XYFocusOptions
- {
- UpdateManifold = false,
- SearchRoot = options.SearchRoot,
- ExclusionRect = options.ExclusionRect,
- FocusHintRectangle = options.FocusHintRectangle,
- NavigationStrategyOverride = options.NavigationStrategyOverride,
- IgnoreOcclusivity = options.IgnoreOcclusivity
- };
+ focusOptions.UpdateManifold = updateManifold;
+
+ return focusOptions;
}
internal IInputElement? FindNextFocus(NavigationDirection direction, XYFocusOptions focusOptions, bool updateManifolds = true)
diff --git a/src/Avalonia.Base/Input/IFocusManager.cs b/src/Avalonia.Base/Input/IFocusManager.cs
index 5691172f3f..9bd1fb4239 100644
--- a/src/Avalonia.Base/Input/IFocusManager.cs
+++ b/src/Avalonia.Base/Input/IFocusManager.cs
@@ -14,9 +14,66 @@ namespace Avalonia.Input
IInputElement? GetFocusedElement();
///
- /// Clears currently focused element.
+ /// Focuses a control.
///
- [Unstable("This API might be removed in 11.x minor updates. Please consider focusing another element instead of removing focus at all for better UX.")]
- void ClearFocus();
+ /// The control to focus.
+ /// The method by which focus was changed.
+ /// Any key modifiers active at the time of focus.
+ /// true if the focus moved to a control; otherwise, false.
+ ///
+ /// If is null, this method tries to clear the focus. However, it is not advised.
+ /// For a better user experience, focus should be moved to another element when possible.
+ ///
+ /// When this method return true, it is not guaranteed that the focus has been moved
+ /// to . The focus might have been redirected to another element.
+ ///
+ bool Focus(
+ IInputElement? element,
+ NavigationMethod method = NavigationMethod.Unspecified,
+ KeyModifiers keyModifiers = KeyModifiers.None);
+
+ ///
+ /// Attempts to change focus from the element with focus to the next focusable element in the specified direction.
+ ///
+ ///
+ /// The direction that focus moves from element to element.
+ /// Must be one of , ,
+ /// , ,
+ /// and .
+ ///
+ ///
+ /// The options to help identify the next element to receive focus.
+ /// They only apply to directional navigation.
+ ///
+ /// true if focus moved; otherwise, false.
+ bool TryMoveFocus(NavigationDirection direction, FindNextElementOptions? options = null);
+
+ ///
+ /// Retrieves the first element that can receive focus.
+ ///
+ /// The first focusable element.
+ IInputElement? FindFirstFocusableElement();
+
+ ///
+ /// Retrieves the last element that can receive focus.
+ ///
+ /// The last focusable element.
+ IInputElement? FindLastFocusableElement();
+
+ ///
+ /// Retrieves the element that should receive focus based on the specified navigation direction.
+ ///
+ ///
+ /// The direction that focus moves from element to element.
+ /// Must be one of , ,
+ /// , ,
+ /// and .
+ ///
+ ///
+ /// The options to help identify the next element to receive focus.
+ /// They only apply to directional navigation.
+ ///
+ /// The next element to receive focus, if any.
+ IInputElement? FindNextElement(NavigationDirection direction, FindNextElementOptions? options = null);
}
}
diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs
index 1beccf341e..e908e818e8 100644
--- a/src/Avalonia.Base/Input/InputElement.cs
+++ b/src/Avalonia.Base/Input/InputElement.cs
@@ -523,7 +523,7 @@ namespace Avalonia.Input
if (!IsEffectivelyEnabled && FocusManager.GetFocusManager(this) is { } focusManager
&& Equals(focusManager.GetFocusedElement(), this))
{
- focusManager.ClearFocus();
+ focusManager.Focus(null);
}
}
}
@@ -995,7 +995,7 @@ namespace Avalonia.Input
}
else
{
- focusManager.ClearFocus();
+ focusManager.Focus(null);
}
}
}
diff --git a/src/Avalonia.Base/Input/Navigation/XYFocusOptions.cs b/src/Avalonia.Base/Input/Navigation/XYFocusOptions.cs
index 4bfcb22502..8e4c847aa9 100644
--- a/src/Avalonia.Base/Input/Navigation/XYFocusOptions.cs
+++ b/src/Avalonia.Base/Input/Navigation/XYFocusOptions.cs
@@ -1,17 +1,38 @@
namespace Avalonia.Input.Navigation;
-internal class XYFocusOptions
+internal sealed class XYFocusOptions
{
public InputElement? SearchRoot { get; set; }
public Rect ExclusionRect { get; set; }
public Rect? FocusHintRectangle { get; set; }
public Rect? FocusedElementBounds { get; set; }
public XYFocusNavigationStrategy? NavigationStrategyOverride { get; set; }
- public bool IgnoreClipping { get; set; } = true;
+ public bool IgnoreClipping { get; set; }
public bool IgnoreCone { get; set; }
public KeyDeviceType? KeyDeviceType { get; set; }
- public bool ConsiderEngagement { get; set; } = true;
- public bool UpdateManifold { get; set; } = true;
+ public bool ConsiderEngagement { get; set; }
+ public bool UpdateManifold { get; set; }
public bool UpdateManifoldsFromFocusHintRect { get; set; }
public bool IgnoreOcclusivity { get; set; }
+
+ public XYFocusOptions()
+ {
+ Reset();
+ }
+
+ internal void Reset()
+ {
+ SearchRoot = null;
+ ExclusionRect = default;
+ FocusHintRectangle = null;
+ FocusedElementBounds = null;
+ NavigationStrategyOverride = null;
+ IgnoreClipping = true;
+ IgnoreCone = false;
+ KeyDeviceType = null;
+ ConsiderEngagement = true;
+ UpdateManifold = true;
+ UpdateManifoldsFromFocusHintRect = false;
+ IgnoreOcclusivity = false;
+ }
}
diff --git a/src/Avalonia.Controls/PresentationSource/PresentationSource.cs b/src/Avalonia.Controls/PresentationSource/PresentationSource.cs
index c98a380640..9917f82c93 100644
--- a/src/Avalonia.Controls/PresentationSource/PresentationSource.cs
+++ b/src/Avalonia.Controls/PresentationSource/PresentationSource.cs
@@ -61,7 +61,7 @@ internal partial class PresentationSource : IPresentationSource, IInputRoot, IDi
field?.SetPresentationSourceForRootVisual(this);
Renderer.CompositionTarget.Root = field?.CompositionVisual;
- FocusManager.SetContentRoot(value as IInputElement);
+ FocusManager.ContentRoot = value;
}
}
@@ -152,4 +152,4 @@ internal partial class PresentationSource : IPresentationSource, IInputRoot, IDi
}
return null;
}
-}
\ No newline at end of file
+}
diff --git a/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs b/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs
index 7755eb80cf..cdb4588fff 100644
--- a/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs
+++ b/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs
@@ -577,7 +577,7 @@ namespace Avalonia.Base.UnitTests.Input
};
target.Focus();
- root.FocusManager.ClearFocus();
+ root.FocusManager.Focus(null);
Assert.Null(root.FocusManager.GetFocusedElement());
}
diff --git a/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs
index d11872ba6a..b1446d961f 100644
--- a/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardDeviceTests.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Base.UnitTests.Input
using (UnitTestApplication.Start(TestServices.FocusableWindow))
{
var window = new Window();
- window.FocusManager.ClearFocus();
+ window.FocusManager.Focus(null);
int raised = 0;
window.KeyDown += (sender, ev) =>
{
@@ -71,7 +71,7 @@ namespace Avalonia.Base.UnitTests.Input
using (UnitTestApplication.Start(TestServices.FocusableWindow))
{
var window = new Window();
- window.FocusManager.ClearFocus();
+ window.FocusManager.Focus(null);
int raised = 0;
window.TextInput += (sender, ev) =>
{
diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs
index ed91463346..4400d77267 100644
--- a/tests/Avalonia.UnitTests/TestRoot.cs
+++ b/tests/Avalonia.UnitTests/TestRoot.cs
@@ -74,7 +74,7 @@ namespace Avalonia.UnitTests
IRenderer IPresentationSource.Renderer => Renderer;
IHitTester IPresentationSource.HitTester => HitTester;
- public IFocusManager FocusManager => _focusManager ??= new FocusManager(this);
+ public IFocusManager FocusManager => _focusManager ??= new FocusManager { ContentRoot = this };
public IPlatformSettings? PlatformSettings => AvaloniaLocator.Current.GetService();
public IInputElement? PointerOverElement { get; set; }