diff --git a/src/Avalonia.Base/Input/AccessKeyHandler.cs b/src/Avalonia.Base/Input/AccessKeyHandler.cs
index 2bd9fce947..23d1f51730 100644
--- a/src/Avalonia.Base/Input/AccessKeyHandler.cs
+++ b/src/Avalonia.Base/Input/AccessKeyHandler.cs
@@ -141,9 +141,11 @@ namespace Avalonia.Input
if (MainMenu == null || !MainMenu.IsOpen)
{
+ var focusManager = FocusManager.GetFocusManager(e.Source as IInputElement);
+
// TODO: Use FocusScopes to store the current element and restore it when context menu is closed.
// Save currently focused input element.
- _restoreFocusElement = FocusManager.Instance?.Current;
+ _restoreFocusElement = focusManager?.GetFocusedElement();
// When Alt is pressed without a main menu, or with a closed main menu, show
// access key markers in the window (i.e. "_File").
diff --git a/src/Avalonia.Base/Input/FocusManager.cs b/src/Avalonia.Base/Input/FocusManager.cs
index c8de7267ca..c3316eb278 100644
--- a/src/Avalonia.Base/Input/FocusManager.cs
+++ b/src/Avalonia.Base/Input/FocusManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Avalonia.Interactivity;
+using Avalonia.Metadata;
using Avalonia.VisualTree;
namespace Avalonia.Input
@@ -10,6 +11,7 @@ namespace Avalonia.Input
///
/// Manages focus for the application.
///
+ [PrivateApi]
public class FocusManager : IFocusManager
{
///
@@ -29,15 +31,12 @@ namespace Avalonia.Input
RoutingStrategies.Tunnel);
}
- ///
- /// Gets the instance of the .
- ///
- public static IFocusManager? Instance => AvaloniaLocator.Current.GetService();
+ private IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;
///
/// Gets the currently focused .
///
- public IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;
+ public IInputElement? GetFocusedElement() => Current;
///
/// Gets the current focus scope.
@@ -54,7 +53,7 @@ namespace Avalonia.Input
/// The control to focus.
/// The method by which focus was changed.
/// Any key modifiers active at the time of focus.
- public void Focus(
+ public bool Focus(
IInputElement? control,
NavigationMethod method = NavigationMethod.Unspecified,
KeyModifiers keyModifiers = KeyModifiers.None)
@@ -67,7 +66,7 @@ namespace Avalonia.Input
if (scope != null)
{
Scope = scope;
- SetFocusedElement(scope, control, method, keyModifiers);
+ return SetFocusedElement(scope, control, method, keyModifiers);
}
}
else if (Current != null)
@@ -79,28 +78,29 @@ namespace Avalonia.Input
_focusScopes.TryGetValue(scope, out var element) &&
element != null)
{
- Focus(element, method);
- return;
+ return Focus(element, method);
}
}
if (Scope is object)
{
// Couldn't find a focus scope, clear focus.
- SetFocusedElement(Scope, null);
+ return SetFocusedElement(Scope, null);
}
}
+
+ return false;
}
- public IInputElement? GetFocusedElement(IInputElement e)
+ public void ClearFocus()
{
- if (e is IFocusScope scope)
- {
- _focusScopes.TryGetValue(scope, out var result);
- return result;
- }
+ Focus(null);
+ }
- return null;
+ public IInputElement? GetFocusedElement(IFocusScope scope)
+ {
+ _focusScopes.TryGetValue(scope, out var result);
+ return result;
}
///
@@ -114,7 +114,7 @@ namespace Avalonia.Input
/// If the specified scope is the current then the keyboard focus
/// will change.
///
- public void SetFocusedElement(
+ public bool SetFocusedElement(
IFocusScope scope,
IInputElement? element,
NavigationMethod method = NavigationMethod.Unspecified,
@@ -124,7 +124,7 @@ namespace Avalonia.Input
if (element is not null && !CanFocus(element))
{
- return;
+ return false;
}
if (_focusScopes.TryGetValue(scope, out var existingElement))
@@ -144,6 +144,8 @@ namespace Avalonia.Input
{
KeyboardDevice.Instance?.SetFocusedElement(element, method, keyModifiers);
}
+
+ return true;
}
///
@@ -185,6 +187,20 @@ namespace Avalonia.Input
public static bool GetIsFocusScope(IInputElement e) => e is IFocusScope;
+ ///
+ /// Public API customers should use TopLevel.GetTopLevel(control).FocusManager.
+ /// But since we have split projects, we can't access TopLevel from Avalonia.Base.
+ /// That's why we need this helper method instead.
+ ///
+ internal static FocusManager? GetFocusManager(IInputElement? element)
+ {
+ // Element might not be a visual, and not attached to the root.
+ // But IFocusManager is always expected to be a FocusManager.
+ return (FocusManager?)((element as Visual)?.VisualRoot as IInputRoot)?.FocusManager
+ // In our unit tests some elements might not have a root. Remove when we migrate to headless tests.
+ ?? (FocusManager?)AvaloniaLocator.Current.GetService();
+ }
+
///
/// Checks if the specified element can be focused.
///
@@ -237,7 +253,7 @@ namespace Avalonia.Input
{
if (element is IInputElement inputElement && CanFocus(inputElement))
{
- Instance?.Focus(inputElement, NavigationMethod.Pointer, ev.KeyModifiers);
+ inputElement.Focus(NavigationMethod.Pointer, ev.KeyModifiers);
break;
}
diff --git a/src/Avalonia.Base/Input/IFocusManager.cs b/src/Avalonia.Base/Input/IFocusManager.cs
index 0c85cad2f7..5691172f3f 100644
--- a/src/Avalonia.Base/Input/IFocusManager.cs
+++ b/src/Avalonia.Base/Input/IFocusManager.cs
@@ -11,40 +11,12 @@ namespace Avalonia.Input
///
/// Gets the currently focused .
///
- IInputElement? Current { get; }
+ IInputElement? GetFocusedElement();
///
- /// Gets the current focus scope.
+ /// Clears currently focused element.
///
- IFocusScope? Scope { get; }
-
- ///
- /// Focuses a control.
- ///
- /// The control to focus.
- /// The method by which focus was changed.
- /// Any key modifiers active at the time of focus.
- void Focus(
- IInputElement? control,
- NavigationMethod method = NavigationMethod.Unspecified,
- KeyModifiers keyModifiers = KeyModifiers.None);
-
- ///
- /// Notifies the focus manager of a change in focus scope.
- ///
- /// The new focus scope.
- ///
- /// This should not be called by client code. It is called by an
- /// when it activates, e.g. when a Window is activated.
- ///
- void SetFocusScope(IFocusScope scope);
-
- ///
- /// Notifies the focus manager that a focus scope has been removed.
- ///
- /// The focus scope to be removed.
- /// This should not be called by client code. It is called by an
- /// when it deactivates or closes, e.g. when a Window is closed.
- void RemoveFocusScope(IFocusScope scope);
+ [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();
}
}
diff --git a/src/Avalonia.Base/Input/IFocusScope.cs b/src/Avalonia.Base/Input/IFocusScope.cs
index 56f558040e..4f7c454263 100644
--- a/src/Avalonia.Base/Input/IFocusScope.cs
+++ b/src/Avalonia.Base/Input/IFocusScope.cs
@@ -1,5 +1,8 @@
+using Avalonia.Metadata;
+
namespace Avalonia.Input
{
+ [NotClientImplementable]
public interface IFocusScope
{
}
diff --git a/src/Avalonia.Base/Input/IInputElement.cs b/src/Avalonia.Base/Input/IInputElement.cs
index 6c20d20b4d..39dc30befd 100644
--- a/src/Avalonia.Base/Input/IInputElement.cs
+++ b/src/Avalonia.Base/Input/IInputElement.cs
@@ -119,7 +119,9 @@ namespace Avalonia.Input
///
/// Focuses the control.
///
- void Focus();
+ /// The method by which focus was changed.
+ /// Any key modifiers active at the time of focus.
+ bool Focus(NavigationMethod method = NavigationMethod.Unspecified, KeyModifiers keyModifiers = KeyModifiers.None);
///
/// Gets the key bindings for the element.
diff --git a/src/Avalonia.Base/Input/IInputRoot.cs b/src/Avalonia.Base/Input/IInputRoot.cs
index 344a4eefd7..0100b22ba7 100644
--- a/src/Avalonia.Base/Input/IInputRoot.cs
+++ b/src/Avalonia.Base/Input/IInputRoot.cs
@@ -18,6 +18,14 @@ namespace Avalonia.Input
///
IKeyboardNavigationHandler KeyboardNavigationHandler { get; }
+ ///
+ /// Gets focus manager of the root.
+ ///
+ ///
+ /// Focus manager can be null only if application wasn't initialized yet.
+ ///
+ IFocusManager? FocusManager { get; }
+
///
/// Gets or sets the input element that the pointer is currently over.
///
diff --git a/src/Avalonia.Base/Input/IKeyboardDevice.cs b/src/Avalonia.Base/Input/IKeyboardDevice.cs
index 0b7b5aaecc..172b58068c 100644
--- a/src/Avalonia.Base/Input/IKeyboardDevice.cs
+++ b/src/Avalonia.Base/Input/IKeyboardDevice.cs
@@ -44,13 +44,7 @@ namespace Avalonia.Input
}
[NotClientImplementable]
- public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged
+ public interface IKeyboardDevice : IInputDevice
{
- IInputElement? FocusedElement { get; }
-
- void SetFocusedElement(
- IInputElement? element,
- NavigationMethod method,
- KeyModifiers modifiers);
}
}
diff --git a/src/Avalonia.Base/Input/InputElement.cs b/src/Avalonia.Base/Input/InputElement.cs
index 33ddbaedf9..68131e5bf7 100644
--- a/src/Avalonia.Base/Input/InputElement.cs
+++ b/src/Avalonia.Base/Input/InputElement.cs
@@ -458,9 +458,10 @@ namespace Avalonia.Input
SetAndRaise(IsEffectivelyEnabledProperty, ref _isEffectivelyEnabled, value);
PseudoClasses.Set(":disabled", !value);
- if (!IsEffectivelyEnabled && FocusManager.Instance?.Current == this)
+ if (!IsEffectivelyEnabled && FocusManager.GetFocusManager(this) is {} focusManager
+ && Equals(focusManager.GetFocusedElement(), this))
{
- FocusManager.Instance?.Focus(null);
+ focusManager.ClearFocus();
}
}
}
@@ -491,12 +492,10 @@ namespace Avalonia.Input
public GestureRecognizerCollection GestureRecognizers
=> _gestureRecognizers ?? (_gestureRecognizers = new GestureRecognizerCollection(this));
- ///
- /// Focuses the control.
- ///
- public void Focus()
+ ///
+ public bool Focus(NavigationMethod method = NavigationMethod.Unspecified, KeyModifiers keyModifiers = KeyModifiers.None)
{
- FocusManager.Instance?.Focus(this);
+ return FocusManager.GetFocusManager(this)?.Focus(this, method, keyModifiers) ?? false;
}
///
@@ -506,7 +505,7 @@ namespace Avalonia.Input
if (IsFocused)
{
- FocusManager.Instance?.Focus(null);
+ FocusManager.GetFocusManager(this)?.ClearFocus();
}
}
@@ -649,7 +648,7 @@ namespace Avalonia.Input
}
else if (change.Property == IsVisibleProperty && !change.GetNewValue() && IsFocused)
{
- FocusManager.Instance?.Focus(null);
+ FocusManager.GetFocusManager(this)?.ClearFocus();
}
}
diff --git a/src/Avalonia.Base/Input/KeyboardDevice.cs b/src/Avalonia.Base/Input/KeyboardDevice.cs
index c46834fff4..a81bc6b2e0 100644
--- a/src/Avalonia.Base/Input/KeyboardDevice.cs
+++ b/src/Avalonia.Base/Input/KeyboardDevice.cs
@@ -3,9 +3,11 @@ using System.Runtime.CompilerServices;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Avalonia.Interactivity;
+using Avalonia.Metadata;
namespace Avalonia.Input
{
+ [PrivateApi]
public class KeyboardDevice : IKeyboardDevice, INotifyPropertyChanged
{
private IInputElement? _focusedElement;
@@ -13,7 +15,7 @@ namespace Avalonia.Input
public event PropertyChangedEventHandler? PropertyChanged;
- public static IKeyboardDevice? Instance => AvaloniaLocator.Current.GetService();
+ internal static KeyboardDevice? Instance => AvaloniaLocator.Current.GetService() as KeyboardDevice;
public IInputManager? InputManager => AvaloniaLocator.Current.GetService();
diff --git a/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
index ba909de60f..e96c80da14 100644
--- a/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
+++ b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
@@ -88,7 +88,7 @@ namespace Avalonia.Input
var method = direction == NavigationDirection.Next ||
direction == NavigationDirection.Previous ?
NavigationMethod.Tab : NavigationMethod.Directional;
- FocusManager.Instance?.Focus(next, method, keyModifiers);
+ next.Focus(method, keyModifiers);
}
}
@@ -99,7 +99,7 @@ namespace Avalonia.Input
/// The event args.
protected virtual void OnKeyDown(object? sender, KeyEventArgs e)
{
- var current = FocusManager.Instance?.Current;
+ var current = FocusManager.GetFocusManager(e.Source as IInputElement)?.GetFocusedElement();
if (current != null && e.Key == Key.Tab)
{
diff --git a/src/Avalonia.Base/Input/MouseDevice.cs b/src/Avalonia.Base/Input/MouseDevice.cs
index 44412cd152..f3e77433a9 100644
--- a/src/Avalonia.Base/Input/MouseDevice.cs
+++ b/src/Avalonia.Base/Input/MouseDevice.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Avalonia.Reactive;
using Avalonia.Input.Raw;
+using Avalonia.Metadata;
using Avalonia.Platform;
using Avalonia.Utilities;
#pragma warning disable CS0618
@@ -11,6 +12,7 @@ namespace Avalonia.Input
///
/// Represents a mouse device.
///
+ [PrivateApi]
public class MouseDevice : IMouseDevice, IDisposable
{
private int _clickCount;
diff --git a/src/Avalonia.Base/Input/Navigation/TabNavigation.cs b/src/Avalonia.Base/Input/Navigation/TabNavigation.cs
index c460ecf3b3..9697e32926 100644
--- a/src/Avalonia.Base/Input/Navigation/TabNavigation.cs
+++ b/src/Avalonia.Base/Input/Navigation/TabNavigation.cs
@@ -190,9 +190,11 @@ namespace Avalonia.Input.Navigation
private static IInputElement? FocusedElement(IInputElement? e)
{
// Focus delegation is enabled only if keyboard focus is outside the container
- if (e != null && !e.IsKeyboardFocusWithin)
+ if (e != null && !e.IsKeyboardFocusWithin && e is IFocusScope scope)
{
- var focusedElement = (FocusManager.Instance as FocusManager)?.GetFocusedElement(e);
+ var focusManager = FocusManager.GetFocusManager(e);
+
+ var focusedElement = focusManager?.GetFocusedElement(scope);
if (focusedElement != null)
{
if (!IsFocusScope(e))
diff --git a/src/Avalonia.Base/Input/PenDevice.cs b/src/Avalonia.Base/Input/PenDevice.cs
index b3cd39212b..832f32fb03 100644
--- a/src/Avalonia.Base/Input/PenDevice.cs
+++ b/src/Avalonia.Base/Input/PenDevice.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Input.Raw;
+using Avalonia.Metadata;
using Avalonia.Platform;
#pragma warning disable CS0618
@@ -11,6 +12,7 @@ namespace Avalonia.Input
///
/// Represents a pen/stylus device.
///
+ [PrivateApi]
public class PenDevice : IPenDevice, IDisposable
{
private readonly Dictionary _pointers = new();
diff --git a/src/Avalonia.Base/Input/TouchDevice.cs b/src/Avalonia.Base/Input/TouchDevice.cs
index bab1b9f784..78b570da14 100644
--- a/src/Avalonia.Base/Input/TouchDevice.cs
+++ b/src/Avalonia.Base/Input/TouchDevice.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Input.Raw;
+using Avalonia.Metadata;
using Avalonia.Platform;
#pragma warning disable CS0618
@@ -14,6 +15,7 @@ namespace Avalonia.Input
///
/// This class is supposed to be used on per-toplevel basis, don't use a shared one
///
+ [PrivateApi]
public class TouchDevice : IPointerDevice, IDisposable
{
private readonly Dictionary _pointers = new Dictionary();
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index a55a47fa53..bfcd4750e3 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -3958,7 +3958,7 @@ namespace Avalonia.Controls
{
bool focusLeftDataGrid = true;
bool dataGridWillReceiveRoutedEvent = true;
- Visual focusedObject = FocusManager.Instance.Current as Visual;
+ Visual focusedObject = FocusManager.GetFocusManager(this)?.GetFocusedElement() as Visual;
DataGridColumn editingColumn = null;
while (focusedObject != null)
@@ -4865,7 +4865,8 @@ namespace Avalonia.Controls
if (!ctrl)
{
// If Enter was used by a TextBox, we shouldn't handle the key
- if (FocusManager.Instance.Current is TextBox focusedTextBox && focusedTextBox.AcceptsReturn)
+ if (FocusManager.GetFocusManager(this)?.GetFocusedElement() is TextBox focusedTextBox
+ && focusedTextBox.AcceptsReturn)
{
return false;
}
diff --git a/src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs b/src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs
index 6b9d7934bf..674389d77c 100644
--- a/src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs
+++ b/src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs
@@ -695,7 +695,7 @@ namespace Avalonia.Controls
{
Control? focusedElement = null;
- if (FocusManager.Instance?.Current is Visual child)
+ if (TopLevel.GetTopLevel(_owner)?.FocusManager?.GetFocusedElement() is Visual child)
{
var parent = child.GetVisualParent();
var owner = _owner;
diff --git a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
index 20711eecbc..d0b894101f 100644
--- a/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
@@ -762,7 +762,7 @@ namespace Avalonia.Controls
/// otherwise, false.
protected bool HasFocus()
{
- Visual? focused = FocusManager.Instance?.Current as Visual;
+ Visual? focused = FocusManager.GetFocusManager(this)?.GetFocusedElement() as Visual;
while (focused != null)
{
diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs
index 10aadfa759..6468d0b4e8 100644
--- a/src/Avalonia.Controls/Calendar/Calendar.cs
+++ b/src/Avalonia.Controls/Calendar/Calendar.cs
@@ -1567,7 +1567,7 @@ namespace Avalonia.Controls
base.OnPointerReleased(e);
if (!HasFocusInternal && e.InitialPressMouseButton == MouseButton.Left)
{
- FocusManager.Instance?.Focus(this);
+ Focus();
}
}
diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs
index f41c00662d..efa319ad54 100644
--- a/src/Avalonia.Controls/ComboBox.cs
+++ b/src/Avalonia.Controls/ComboBox.cs
@@ -230,8 +230,7 @@ namespace Avalonia.Controls
var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c));
if (firstChild != null)
{
- FocusManager.Instance?.Focus(firstChild, NavigationMethod.Directional);
- e.Handled = true;
+ e.Handled = firstChild.Focus(NavigationMethod.Directional);
}
}
}
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index 39a98bd48a..a9d8e4c9c7 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -360,7 +360,7 @@ namespace Avalonia.Controls
private void PopupOpened(object? sender, EventArgs e)
{
- _previousFocus = FocusManager.Instance?.Current;
+ _previousFocus = FocusManager.GetFocusManager(this)?.GetFocusedElement();
Focus();
_popupHostChangedHandler?.Invoke(_popup!.Host);
@@ -390,7 +390,7 @@ namespace Avalonia.Controls
}
// HACK: Reset the focus when the popup is closed. We need to fix this so it's automatic.
- FocusManager.Instance?.Focus(_previousFocus);
+ _previousFocus?.Focus();
RaiseEvent(new RoutedEventArgs
{
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
index 0ae743f30a..fb61ea679c 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
@@ -323,10 +323,11 @@ namespace Avalonia.Controls
e.Handled = true;
break;
case Key.Tab:
- if (FocusManager.Instance?.Current is IInputElement focus)
+ var focusManager = FocusManager.GetFocusManager(this);
+ if (focusManager?.GetFocusedElement() is { } focus)
{
var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
- KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
+ nextFocus?.Focus(NavigationMethod.Tab);
e.Handled = true;
}
break;
@@ -449,15 +450,15 @@ namespace Avalonia.Controls
if (monthCol < dayCol && monthCol < yearCol)
{
- KeyboardDevice.Instance?.SetFocusedElement(_monthSelector, NavigationMethod.Pointer, KeyModifiers.None);
+ _monthSelector?.Focus(NavigationMethod.Pointer);
}
else if (dayCol < monthCol && dayCol < yearCol)
{
- KeyboardDevice.Instance?.SetFocusedElement(_daySelector, NavigationMethod.Pointer, KeyModifiers.None);
+ _monthSelector?.Focus(NavigationMethod.Pointer);
}
else if (yearCol < monthCol && yearCol < dayCol)
{
- KeyboardDevice.Instance?.SetFocusedElement(_yearSelector, NavigationMethod.Pointer, KeyModifiers.None);
+ _yearSelector?.Focus(NavigationMethod.Pointer);
}
}
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
index ba06e1b5e6..929ad68c24 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
@@ -161,10 +161,10 @@ namespace Avalonia.Controls
e.Handled = true;
break;
case Key.Tab:
- if (FocusManager.Instance?.Current is IInputElement focus)
+ if (FocusManager.GetFocusManager(this)?.GetFocusedElement() is { } focus)
{
var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
- KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None);
+ nextFocus?.Focus(NavigationMethod.Tab);
e.Handled = true;
}
break;
@@ -216,7 +216,7 @@ namespace Avalonia.Controls
_periodSelector.SelectedValue = hr >= 12 ? 1 : 0;
SetGrid();
- KeyboardDevice.Instance?.SetFocusedElement(_hourSelector, NavigationMethod.Pointer, KeyModifiers.None);
+ _hourSelector?.Focus(NavigationMethod.Pointer);
}
private void SetGrid()
diff --git a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
index 5b23b5030f..7fd9fad605 100644
--- a/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
@@ -250,14 +250,14 @@ namespace Avalonia.Controls.Primitives
// Try and focus content inside Flyout
if (Popup.Child.Focusable)
{
- FocusManager.Instance?.Focus(Popup.Child);
+ Popup.Child.Focus();
}
else
{
var nextFocus = KeyboardNavigationHandler.GetNext(Popup.Child, NavigationDirection.Next);
if (nextFocus != null)
{
- FocusManager.Instance?.Focus(nextFocus);
+ nextFocus.Focus();
}
}
}
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index 1613bda45b..0b6fc2b64b 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -566,19 +566,20 @@ namespace Avalonia.Controls
{
if (!e.Handled)
{
- var focus = FocusManager.Instance;
+ var focus = FocusManager.GetFocusManager(this);
var direction = e.Key.ToNavigationDirection();
var container = Presenter?.Panel as INavigableContainer;
- if (container == null ||
- focus?.Current == null ||
+ if (focus == null ||
+ container == null ||
+ focus.GetFocusedElement() == null ||
direction == null ||
direction.Value.IsTab())
{
return;
}
- Visual? current = focus.Current as Visual;
+ Visual? current = focus.GetFocusedElement() as Visual;
while (current != null)
{
@@ -588,7 +589,7 @@ namespace Avalonia.Controls
if (next != null)
{
- focus.Focus(next, NavigationMethod.Directional, e.KeyModifiers);
+ next.Focus(NavigationMethod.Directional, e.KeyModifiers);
e.Handled = true;
}
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index 80b7841fc7..7ac219aa83 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -727,7 +727,7 @@ namespace Avalonia.Controls.Primitives
Closed?.Invoke(this, EventArgs.Empty);
- var focusCheck = FocusManager.Instance?.Current;
+ var focusCheck = FocusManager.GetFocusManager(this)?.GetFocusedElement();
// Focus is set to null as part of popup closing, so we only want to
// set focus to PlacementTarget if this is the case
@@ -744,7 +744,7 @@ namespace Avalonia.Controls.Primitives
if (e is object)
{
- FocusManager.Instance?.Focus(e);
+ e.Focus();
}
}
else
@@ -752,7 +752,7 @@ namespace Avalonia.Controls.Primitives
var anc = this.FindLogicalAncestorOfType();
if (anc != null)
{
- FocusManager.Instance?.Focus(anc);
+ anc.Focus();
}
}
}
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index 85a35a3489..67847f621b 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -452,6 +452,9 @@ namespace Avalonia.Controls
///
public IClipboard? Clipboard => PlatformImpl?.TryGetFeature();
+ ///
+ public IFocusManager? FocusManager => AvaloniaLocator.Current.GetService();
+
///
Point IRenderRoot.PointToClient(PixelPoint p)
{
@@ -725,7 +728,7 @@ namespace Avalonia.Controls
void PlatformImpl_LostFocus()
{
- var focused = (Visual?)FocusManager.Instance?.Current;
+ var focused = (Visual?)FocusManager?.GetFocusedElement();
if (focused == null)
return;
while (focused.VisualParent != null)
diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs
index ef10dcdb22..a499829d4a 100644
--- a/src/Avalonia.Controls/TreeView.cs
+++ b/src/Avalonia.Controls/TreeView.cs
@@ -560,8 +560,7 @@ namespace Avalonia.Controls
if (next != null)
{
- FocusManager.Instance?.Focus(next, NavigationMethod.Directional);
- e.Handled = true;
+ e.Handled = next.Focus(NavigationMethod.Directional);
}
}
else
diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs
index cf12c12447..a48706cb19 100644
--- a/src/Avalonia.Controls/TreeViewItem.cs
+++ b/src/Avalonia.Controls/TreeViewItem.cs
@@ -238,7 +238,7 @@ namespace Avalonia.Controls
}
else
{
- FocusManager.Instance?.Focus(treeViewItem, NavigationMethod.Directional);
+ treeViewItem.Focus(NavigationMethod.Directional);
}
return true;
diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs
index ac47e744e0..b19ad49820 100644
--- a/src/Avalonia.Controls/WindowBase.cs
+++ b/src/Avalonia.Controls/WindowBase.cs
@@ -234,7 +234,7 @@ namespace Avalonia.Controls
if (this is IFocusScope scope)
{
- FocusManager.Instance?.RemoveFocusScope(scope);
+ ((FocusManager?)FocusManager)?.RemoveFocusScope(scope);
}
base.HandleClosed();
@@ -326,7 +326,7 @@ namespace Avalonia.Controls
if (scope != null)
{
- FocusManager.Instance?.SetFocusScope(scope);
+ ((FocusManager?)FocusManager)?.SetFocusScope(scope);
}
IsActive = true;
diff --git a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
index cb896fd633..ff49d0dd87 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
@@ -86,7 +86,7 @@ namespace Avalonia.Diagnostics
private static IDisposable Open(IDevToolsTopLevelGroup topLevelGroup, DevToolsOptions options,
Window? owner, Application? app)
{
- var focussedControl = KeyboardDevice.Instance?.FocusedElement as Control;
+ var focusedControl = owner?.FocusManager?.GetFocusedElement() as Control;
AvaloniaObject root = topLevelGroup switch
{
ClassicDesktopStyleApplicationLifetimeTopLevelGroup gr => new Controls.Application(gr, app ?? Application.Current!),
@@ -98,7 +98,7 @@ namespace Avalonia.Diagnostics
if (s_open.TryGetValue(topLevelGroup, out var mainWindow))
{
mainWindow.Activate();
- mainWindow.SelectedControl(focussedControl);
+ mainWindow.SelectedControl(focusedControl);
return Disposable.Empty;
}
if (topLevelGroup.Items.Count == 1 && topLevelGroup.Items is not INotifyCollectionChanged)
@@ -110,7 +110,7 @@ namespace Avalonia.Diagnostics
if (group.Key.Items.Contains(singleTopLevel))
{
group.Value.Activate();
- group.Value.SelectedControl(focussedControl);
+ group.Value.SelectedControl(focusedControl);
return Disposable.Empty;
}
}
@@ -124,7 +124,7 @@ namespace Avalonia.Diagnostics
Tag = topLevelGroup
};
window.SetOptions(options);
- window.SelectedControl(focussedControl);
+ window.SelectedControl(focusedControl);
window.Closed += DevToolsClosed;
s_open.Add(topLevelGroup, window);
if (options.ShowAsChildWindow && owner is not null)
diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
index bc178c8ecb..c352b5f9bf 100644
--- a/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
+++ b/src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
@@ -107,7 +107,7 @@ namespace Avalonia.LinuxFramebuffer
if (_topLevel is IFocusScope scope)
{
- FocusManager.Instance?.SetFocusScope(scope);
+ ((FocusManager)_topLevel.FocusManager).SetFocusScope(scope);
}
}
diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
index f8eab5b654..be6b37bb02 100644
--- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
@@ -154,7 +154,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
var namespaces = _nsInfo.XmlNamespaces;
if (!namespaces.TryGetValue(ns, out var lst))
throw new ArgumentException("Unable to resolve namespace for type " + qualifiedTypeName);
- foreach (var entry in lst)
+ var resolvable = lst.Where(static e => e.ClrAssemblyName is { Length: > 0 });
+ foreach (var entry in resolvable)
{
var asm = Assembly.Load(new AssemblyName(entry.ClrAssemblyName));
var resolved = asm.GetType(entry.ClrNamespace + "." + name);
@@ -164,7 +165,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
throw new ArgumentException(
$"Unable to resolve type {qualifiedTypeName} from any of the following locations: " +
- string.Join(",", lst.Select(e => $"`{e.ClrAssemblyName}:{e.ClrNamespace}.{name}`")));
+ string.Join(",", resolvable.Select(e => $"`clr-namespace:{e.ClrNamespace};assembly={e.ClrAssemblyName}`")))
+ { HelpLink = "https://docs.avaloniaui.net/guides/basics/introduction-to-xaml#valid-xaml-namespaces" };
}
}
diff --git a/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs
index a8060d3fbf..8d73bde919 100644
--- a/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs
+++ b/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs
@@ -22,7 +22,7 @@ namespace Avalonia.Win32.Embedding
UnmanagedMethods.SetParent(WindowHandle, Handle);
_root.Prepare();
if (_root.IsFocused)
- FocusManager.Instance.Focus(null);
+ _root.FocusManager.ClearFocus();
_root.GotFocus += RootGotFocus;
FixPosition();
diff --git a/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs b/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
index 7e1e22579b..4f9b6c54d3 100644
--- a/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
+++ b/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
@@ -5,7 +5,7 @@ using Avalonia.Win32.Interop;
namespace Avalonia.Win32.Input
{
- class WindowsKeyboardDevice : KeyboardDevice
+ internal class WindowsKeyboardDevice : KeyboardDevice
{
private readonly byte[] _keyStates = new byte[256];
diff --git a/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs b/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs
index ac1547d09f..aa2e47de2d 100644
--- a/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs
+++ b/tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs
@@ -23,7 +23,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus();
- Assert.Same(target, FocusManager.Instance.Current);
+ Assert.Same(target, root.FocusManager.GetFocusedElement());
}
}
@@ -39,14 +39,14 @@ namespace Avalonia.Base.UnitTests.Input
Child = target = new Button() { IsVisible = false}
};
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
target.Focus();
Assert.False(target.IsFocused);
Assert.False(target.IsKeyboardFocusWithin);
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -67,14 +67,14 @@ namespace Avalonia.Base.UnitTests.Input
}
};
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
target.Focus();
Assert.False(target.IsFocused);
Assert.False(target.IsKeyboardFocusWithin);
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -100,11 +100,11 @@ namespace Avalonia.Base.UnitTests.Input
first.Focus();
- Assert.Same(first, FocusManager.Instance.Current);
+ Assert.Same(first, root.FocusManager.GetFocusedElement());
second.Focus();
- Assert.Same(first, FocusManager.Instance.Current);
+ Assert.Same(first, root.FocusManager.GetFocusedElement());
}
}
@@ -120,14 +120,14 @@ namespace Avalonia.Base.UnitTests.Input
Child = target = new Button() { IsEnabled = false }
};
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
target.Focus();
Assert.False(target.IsFocused);
Assert.False(target.IsKeyboardFocusWithin);
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -148,14 +148,14 @@ namespace Avalonia.Base.UnitTests.Input
}
};
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
target.Focus();
Assert.False(target.IsFocused);
Assert.False(target.IsKeyboardFocusWithin);
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -201,7 +201,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus();
target.IsVisible = false;
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -224,7 +224,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus();
container.IsVisible = false;
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -243,7 +243,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus();
target.IsEnabled = false;
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -266,7 +266,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus();
container.IsEnabled = false;
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -285,7 +285,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus();
root.Child = null;
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
@@ -312,13 +312,13 @@ namespace Avalonia.Base.UnitTests.Input
target2.ApplyTemplate();
- FocusManager.Instance?.Focus(target1);
+ target1.Focus();
Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus"));
Assert.False(target2.IsFocused);
Assert.False(target2.Classes.Contains(":focus"));
- FocusManager.Instance?.Focus(target2, NavigationMethod.Tab);
+ target2.Focus(NavigationMethod.Tab);
Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus"));
Assert.True(target2.IsFocused);
@@ -348,19 +348,19 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate();
target2.ApplyTemplate();
- FocusManager.Instance?.Focus(target1);
+ target1.Focus();
Assert.True(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-visible"));
Assert.False(target2.IsFocused);
Assert.False(target2.Classes.Contains(":focus-visible"));
- FocusManager.Instance?.Focus(target2, NavigationMethod.Tab);
+ target2.Focus(NavigationMethod.Tab);
Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-visible"));
Assert.True(target2.IsFocused);
Assert.True(target2.Classes.Contains(":focus-visible"));
- FocusManager.Instance?.Focus(target1, NavigationMethod.Directional);
+ target1.Focus(NavigationMethod.Directional);
Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-visible"));
Assert.False(target2.IsFocused);
@@ -390,7 +390,7 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate();
target2.ApplyTemplate();
- FocusManager.Instance?.Focus(target1);
+ target1.Focus();
Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin);
@@ -425,7 +425,7 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate();
target2.ApplyTemplate();
- FocusManager.Instance?.Focus(target1);
+ target1.Focus();
Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin);
@@ -436,7 +436,7 @@ namespace Avalonia.Base.UnitTests.Input
Assert.True(root.Classes.Contains(":focus-within"));
Assert.True(root.IsKeyboardFocusWithin);
- FocusManager.Instance?.Focus(target2);
+ target2.Focus();
Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-within"));
@@ -478,7 +478,7 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate();
target2.ApplyTemplate();
- FocusManager.Instance?.Focus(target1);
+ target1.Focus();
Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin);
@@ -534,7 +534,7 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate();
target2.ApplyTemplate();
- FocusManager.Instance?.Focus(target1);
+ target1.Focus();
Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin);
@@ -545,7 +545,7 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal(KeyboardDevice.Instance.FocusedElement, target1);
- FocusManager.Instance?.Focus(target2);
+ target2.Focus();
Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-within"));
@@ -578,9 +578,9 @@ namespace Avalonia.Base.UnitTests.Input
};
target.Focus();
- FocusManager.Instance.Focus(null);
+ root.FocusManager.ClearFocus();
- Assert.Null(FocusManager.Instance.Current);
+ Assert.Null(root.FocusManager.GetFocusedElement());
}
}
}
diff --git a/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs b/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
index 629188800a..ae6b601896 100644
--- a/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
@@ -20,7 +20,9 @@ namespace Avalonia.Base.UnitTests.Input
[Fact]
public void Close_Should_Remove_PointerOver()
{
- using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));
+ using var app = UnitTestApplication.Start(new TestServices(
+ inputManager: new InputManager(),
+ focusManager: new FocusManager()));
var renderer = RendererMocks.CreateRenderer();
var device = CreatePointerDeviceMock().Object;
diff --git a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
index 7767de11c7..db12de9db9 100644
--- a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
@@ -288,10 +288,10 @@ namespace Avalonia.Controls.UnitTests
window.Show();
button.Focus();
- Assert.True(FocusManager.Instance?.Current == button);
+ Assert.True(window.FocusManager.GetFocusedElement() == button);
button.Flyout.ShowAt(button);
Assert.False(button.IsFocused);
- Assert.True(FocusManager.Instance?.Current == flyoutTextBox);
+ Assert.True(window.FocusManager.GetFocusedElement() == flyoutTextBox);
}
}
@@ -322,10 +322,10 @@ namespace Avalonia.Controls.UnitTests
window.Content = button;
window.Show();
- FocusManager.Instance?.Focus(button);
- Assert.True(FocusManager.Instance?.Current == button);
+ button.Focus();
+ Assert.True(window.FocusManager.GetFocusedElement() == button);
button.Flyout.ShowAt(button);
- Assert.True(FocusManager.Instance?.Current == button);
+ Assert.True(window.FocusManager.GetFocusedElement() == button);
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
index 86249c66ff..58e4ec1b75 100644
--- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
@@ -576,8 +576,9 @@ namespace Avalonia.Controls.UnitTests
});
var panel = Assert.IsAssignableFrom(target.ItemsPanelRoot);
+ var focusManager = ((IInputRoot)target.VisualRoot!).FocusManager;
- Assert.Equal(panel.Children[1], FocusManager.Instance!.Current);
+ Assert.Equal(panel.Children[1], focusManager?.GetFocusedElement());
}
[Fact]
@@ -601,8 +602,9 @@ namespace Avalonia.Controls.UnitTests
});
var panel = Assert.IsAssignableFrom(target.ItemsPanelRoot);
+ var focusManager = ((IInputRoot)target.VisualRoot!).FocusManager;
- Assert.Equal(panel.Children[2], FocusManager.Instance!.Current);
+ Assert.Equal(panel.Children[2], focusManager?.GetFocusedElement());
}
[Fact]
diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
index 72f476a3b0..18c1136ccb 100644
--- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
@@ -979,7 +979,7 @@ namespace Avalonia.Controls.UnitTests
RaiseKeyEvent(button, Key.Tab);
var item = target.ContainerFromIndex(0);
- Assert.Same(item, FocusManager.Instance.Current);
+ Assert.Same(item, root.FocusManager.GetFocusedElement());
}
[Fact]
@@ -1026,17 +1026,17 @@ namespace Avalonia.Controls.UnitTests
RaiseKeyEvent(button, Key.Tab);
var item = target.ContainerFromIndex(1);
- Assert.Same(item, FocusManager.Instance.Current);
+ Assert.Same(item, root.FocusManager.GetFocusedElement());
RaiseKeyEvent(item, Key.Tab);
- Assert.Same(button, FocusManager.Instance.Current);
+ Assert.Same(button, root.FocusManager.GetFocusedElement());
target.Selection.AnchorIndex = 2;
RaiseKeyEvent(button, Key.Tab);
item = target.ContainerFromIndex(2);
- Assert.Same(item, FocusManager.Instance.Current);
+ Assert.Same(item, root.FocusManager.GetFocusedElement());
}
private static void RaiseKeyEvent(Control target, Key key, KeyModifiers inputModifiers = 0)
diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
index e5c96dcab6..eaf95b0c8c 100644
--- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
@@ -267,7 +267,7 @@ namespace Avalonia.Controls.UnitTests.Platform
target.KeyDown(item.Object, e);
parentItem.Verify(x => x.Close());
- parentItem.Verify(x => x.Focus());
+ parentItem.Verify(x => x.Focus(It.IsAny(), It.IsAny()));
Assert.True(e.Handled);
}
@@ -351,7 +351,7 @@ namespace Avalonia.Controls.UnitTests.Platform
target.KeyDown(item.Object, e);
parentItem.Verify(x => x.Close());
- parentItem.Verify(x => x.Focus());
+ parentItem.Verify(x => x.Focus(It.IsAny(), It.IsAny()));
Assert.True(e.Handled);
}
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
index 765f2d1c19..51399d1202 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
@@ -642,10 +642,11 @@ namespace Avalonia.Controls.UnitTests.Primitives
tb.Focus();
- Assert.True(FocusManager.Instance?.Current == tb);
+ var focusManager = TopLevel.GetTopLevel(tb)!.FocusManager;
+ tb = Assert.IsType(focusManager.GetFocusedElement());
//Ensure focus remains in the popup
- var nextFocus = KeyboardNavigationHandler.GetNext(FocusManager.Instance.Current, NavigationDirection.Next);
+ var nextFocus = KeyboardNavigationHandler.GetNext(tb, NavigationDirection.Next);
Assert.True(nextFocus == b);
@@ -684,7 +685,8 @@ namespace Avalonia.Controls.UnitTests.Primitives
p.Close();
- var focus = FocusManager.Instance?.Current;
+ var focusManager = window.FocusManager;
+ var focus = focusManager.GetFocusedElement();
Assert.True(focus == window);
}
}
@@ -723,7 +725,8 @@ namespace Avalonia.Controls.UnitTests.Primitives
windowTB.Focus();
- var focus = FocusManager.Instance?.Current;
+ var focusManager = window.FocusManager;
+ var focus = focusManager.GetFocusedElement();
Assert.True(focus == windowTB);
diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
index 0d3eb80ae7..398ac3ff43 100644
--- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs
@@ -461,7 +461,7 @@ namespace Avalonia.Controls.UnitTests
RaiseKeyEvent(button, Key.Tab);
var item = target.ContainerFromIndex(0);
- Assert.Same(item, FocusManager.Instance.Current);
+ Assert.Same(item, root.FocusManager.GetFocusedElement());
}
[Fact]
@@ -513,17 +513,17 @@ namespace Avalonia.Controls.UnitTests
RaiseKeyEvent(button, Key.Tab);
var item = target.ContainerFromIndex(1);
- Assert.Same(item, FocusManager.Instance.Current);
+ Assert.Same(item, root.FocusManager.GetFocusedElement());
RaiseKeyEvent(item, Key.Tab);
- Assert.Same(button, FocusManager.Instance.Current);
+ Assert.Same(button, root.FocusManager.GetFocusedElement());
target.Selection.AnchorIndex = 2;
RaiseKeyEvent(button, Key.Tab);
item = target.ContainerFromIndex(2);
- Assert.Same(item, FocusManager.Instance.Current);
+ Assert.Same(item, root.FocusManager.GetFocusedElement());
}
private static IControlTemplate TabControlTemplate()
diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
index 51300f343a..604ff1c715 100644
--- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
@@ -969,7 +969,6 @@ namespace Avalonia.Controls.UnitTests
public void Keyboard_Navigation_Should_Move_To_Last_Selected_Node()
{
using var app = Start();
- var focus = FocusManager.Instance!;
var navigation = AvaloniaLocator.Current.GetRequiredService();
var data = CreateTestTreeData();
@@ -984,6 +983,7 @@ namespace Avalonia.Controls.UnitTests
{
Children = { target, button },
});
+ var focus = root.FocusManager;
root.LayoutManager.ExecuteInitialLayoutPass();
ExpandAll(target);
@@ -994,20 +994,19 @@ namespace Avalonia.Controls.UnitTests
target.SelectedItem = item;
node.Focus();
- Assert.Same(node, focus.Current);
+ Assert.Same(node, focus.GetFocusedElement());
- navigation.Move(focus.Current!, NavigationDirection.Next);
- Assert.Same(button, focus.Current);
+ navigation.Move(focus.GetFocusedElement()!, NavigationDirection.Next);
+ Assert.Same(button, focus.GetFocusedElement());
- navigation.Move(focus.Current!, NavigationDirection.Next);
- Assert.Same(node, focus.Current);
+ navigation.Move(focus.GetFocusedElement()!, NavigationDirection.Next);
+ Assert.Same(node, focus.GetFocusedElement());
}
[Fact]
public void Keyboard_Navigation_Should_Not_Crash_If_Selected_Item_Is_not_In_Tree()
{
using var app = Start();
- var focus = FocusManager.Instance!;
var data = CreateTestTreeData();
var selectedNode = new Node { Value = "Out of Tree Selected Item" };
@@ -1025,6 +1024,7 @@ namespace Avalonia.Controls.UnitTests
{
Children = { target, button },
});
+ var focus = root.FocusManager;
root.LayoutManager.ExecuteInitialLayoutPass();
ExpandAll(target);
@@ -1035,7 +1035,7 @@ namespace Avalonia.Controls.UnitTests
target.SelectedItem = selectedNode;
node.Focus();
- Assert.Same(node, focus.Current);
+ Assert.Same(node, focus.GetFocusedElement());
}
[Fact]
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index d6318bba0b..b3a6b52d68 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -561,7 +561,7 @@ namespace Avalonia.LeakTests
var window = new Window { Focusable = true };
window.Show();
- Assert.Same(window, FocusManager.Instance.Current);
+ Assert.Same(window, window.FocusManager.GetFocusedElement());
// Context menu in resources means the baseline may not be 0.
var initialMenuCount = 0;
@@ -608,7 +608,7 @@ namespace Avalonia.LeakTests
var window = new Window { Focusable = true };
window.Show();
- Assert.Same(window, FocusManager.Instance.Current);
+ Assert.Same(window, window.FocusManager.GetFocusedElement());
// Context menu in resources means the baseline may not be 0.
var initialMenuCount = 0;
diff --git a/tests/Avalonia.UnitTests/TestRoot.cs b/tests/Avalonia.UnitTests/TestRoot.cs
index c17eeda3e1..8dabfe2197 100644
--- a/tests/Avalonia.UnitTests/TestRoot.cs
+++ b/tests/Avalonia.UnitTests/TestRoot.cs
@@ -54,6 +54,7 @@ namespace Avalonia.UnitTests
public IAccessKeyHandler AccessKeyHandler => null;
public IKeyboardNavigationHandler KeyboardNavigationHandler => null;
+ public IFocusManager FocusManager => AvaloniaLocator.Current.GetService();
public IInputElement PointerOverElement { get; set; }