diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 8ac2fb28ed..8f1b39ae12 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,7 @@ - + all diff --git a/src/Avalonia.Base/Diagnostics/AppliedStyle.cs b/src/Avalonia.Base/Diagnostics/AppliedStyle.cs new file mode 100644 index 0000000000..1718f6b043 --- /dev/null +++ b/src/Avalonia.Base/Diagnostics/AppliedStyle.cs @@ -0,0 +1,18 @@ +using Avalonia.Styling; + +namespace Avalonia.Diagnostics +{ + public class AppliedStyle + { + private readonly IStyleInstance _instance; + + internal AppliedStyle(IStyleInstance instance) + { + _instance = instance; + } + + public bool HasActivator => _instance.HasActivator; + public bool IsActive => _instance.IsActive; + public StyleBase Style => (StyleBase)_instance.Source; + } +} diff --git a/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs b/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs index 984b145e68..90326891c6 100644 --- a/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs +++ b/src/Avalonia.Base/Diagnostics/StyleDiagnostics.cs @@ -11,9 +11,9 @@ namespace Avalonia.Diagnostics /// /// Currently applied styles. /// - public IReadOnlyList AppliedStyles { get; } + public IReadOnlyList AppliedStyles { get; } - public StyleDiagnostics(IReadOnlyList appliedStyles) + public StyleDiagnostics(IReadOnlyList appliedStyles) { AppliedStyles = appliedStyles; } 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.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 196fa850d6..1a0b3bcea6 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -420,12 +420,12 @@ namespace Avalonia internal StyleDiagnostics GetStyleDiagnosticsInternal() { - var styles = new List(); + var styles = new List(); foreach (var frame in GetValueStore().Frames) { if (frame is IStyleInstance style) - styles.Add(style); + styles.Add(new(style)); } return new StyleDiagnostics(styles); diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs index 487198a861..7bc0777c61 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivator.cs @@ -15,8 +15,7 @@ namespace Avalonia.Styling.Activators /// - The activation state can be re-evaluated at any time by calling /// - No error or completion messages /// - [Unstable] - public interface IStyleActivator : IDisposable + internal interface IStyleActivator : IDisposable { /// /// Gets a value indicating whether the style is subscribed. diff --git a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs index 142a3c3517..6d49485c20 100644 --- a/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs +++ b/src/Avalonia.Base/Styling/Activators/IStyleActivatorSink.cs @@ -5,8 +5,7 @@ namespace Avalonia.Styling.Activators /// /// Receives notifications from an . /// - [Unstable] - public interface IStyleActivatorSink + internal interface IStyleActivatorSink { /// /// Called when the subscribed activator value changes. diff --git a/src/Avalonia.Base/Styling/ChildSelector.cs b/src/Avalonia.Base/Styling/ChildSelector.cs index 400ba18530..ac28d2bc46 100644 --- a/src/Avalonia.Base/Styling/ChildSelector.cs +++ b/src/Avalonia.Base/Styling/ChildSelector.cs @@ -19,13 +19,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => _parent.InTemplate; + internal override bool InTemplate => _parent.InTemplate; /// - public override bool IsCombinator => true; + internal override bool IsCombinator => true; /// - public override Type? TargetType => null; + internal override Type? TargetType => null; public override string ToString(Style? owner) { @@ -37,7 +37,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var controlParent = ((ILogical)control).LogicalParent; @@ -64,7 +64,7 @@ namespace Avalonia.Styling } } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => _parent; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/DescendentSelector.cs b/src/Avalonia.Base/Styling/DescendentSelector.cs index 20874a6877..6706eb4441 100644 --- a/src/Avalonia.Base/Styling/DescendentSelector.cs +++ b/src/Avalonia.Base/Styling/DescendentSelector.cs @@ -17,13 +17,13 @@ namespace Avalonia.Styling } /// - public override bool IsCombinator => true; + internal override bool IsCombinator => true; /// - public override bool InTemplate => _parent.InTemplate; + internal override bool InTemplate => _parent.InTemplate; /// - public override Type? TargetType => null; + internal override Type? TargetType => null; public override string ToString(Style? owner) { @@ -35,7 +35,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var c = (ILogical)control; var descendantMatches = new OrActivatorBuilder(); @@ -69,7 +69,7 @@ namespace Avalonia.Styling } } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => _parent; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/ISetter.cs b/src/Avalonia.Base/Styling/ISetter.cs deleted file mode 100644 index 22af90b446..0000000000 --- a/src/Avalonia.Base/Styling/ISetter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Avalonia.Metadata; - -namespace Avalonia.Styling -{ - /// - /// Represents a setter for a . - /// - [NotClientImplementable] - public interface ISetter - { - /// - /// Instances a setter on a control. - /// - /// The style which contains the setter. - /// The control. - /// An . - /// - /// This method should return an which can be used to apply - /// the setter to the specified control. - /// - ISetterInstance Instance(IStyleInstance styleInstance, StyledElement target); - } -} diff --git a/src/Avalonia.Base/Styling/ISetterInstance.cs b/src/Avalonia.Base/Styling/ISetterInstance.cs index 4a65d6deeb..f8a7e7d346 100644 --- a/src/Avalonia.Base/Styling/ISetterInstance.cs +++ b/src/Avalonia.Base/Styling/ISetterInstance.cs @@ -3,7 +3,7 @@ namespace Avalonia.Styling { /// - /// Represents an that has been instanced on a control. + /// Represents a that has been instanced on a control. /// [Unstable] public interface ISetterInstance diff --git a/src/Avalonia.Base/Styling/ISetterValue.cs b/src/Avalonia.Base/Styling/ISetterValue.cs index 0fd245a429..800d8275b5 100644 --- a/src/Avalonia.Base/Styling/ISetterValue.cs +++ b/src/Avalonia.Base/Styling/ISetterValue.cs @@ -3,13 +3,13 @@ namespace Avalonia.Styling { /// - /// Customizes the behavior of a class when added as a value to an . + /// Customizes the behavior of a class when added as a value to a . /// public interface ISetterValue { /// /// Notifies that the object has been added as a setter value. /// - void Initialize(ISetter setter); + void Initialize(SetterBase setter); } } diff --git a/src/Avalonia.Base/Styling/IStyleInstance.cs b/src/Avalonia.Base/Styling/IStyleInstance.cs index 749a2c84d5..72cc3d6901 100644 --- a/src/Avalonia.Base/Styling/IStyleInstance.cs +++ b/src/Avalonia.Base/Styling/IStyleInstance.cs @@ -5,8 +5,7 @@ namespace Avalonia.Styling /// /// Represents a that has been instanced on a control. /// - [Unstable] - public interface IStyleInstance + internal interface IStyleInstance { /// /// Gets the source style. diff --git a/src/Avalonia.Base/Styling/NestingSelector.cs b/src/Avalonia.Base/Styling/NestingSelector.cs index deb688ca4d..4a6b0cfda9 100644 --- a/src/Avalonia.Base/Styling/NestingSelector.cs +++ b/src/Avalonia.Base/Styling/NestingSelector.cs @@ -7,13 +7,13 @@ namespace Avalonia.Styling /// internal class NestingSelector : Selector { - public override bool InTemplate => false; - public override bool IsCombinator => false; - public override Type? TargetType => null; + internal override bool InTemplate => false; + internal override bool IsCombinator => false; + internal override Type? TargetType => null; public override string ToString(Style? owner) => owner?.Parent?.ToString() ?? "^"; - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { if (parent is Style s && s.Selector is not null) { @@ -32,7 +32,7 @@ namespace Avalonia.Styling "Nesting selector was specified but cannot determine parent selector."); } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => null; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => null; } } diff --git a/src/Avalonia.Base/Styling/NotSelector.cs b/src/Avalonia.Base/Styling/NotSelector.cs index f6b1288ac5..9a541cbba7 100644 --- a/src/Avalonia.Base/Styling/NotSelector.cs +++ b/src/Avalonia.Base/Styling/NotSelector.cs @@ -26,13 +26,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => _argument.InTemplate; + internal override bool InTemplate => _argument.InTemplate; /// - public override bool IsCombinator => false; + internal override bool IsCombinator => false; /// - public override Type? TargetType => _previous?.TargetType; + internal override Type? TargetType => _previous?.TargetType; /// public override string ToString(Style? owner) @@ -45,7 +45,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var innerResult = _argument.Match(control, parent, subscribe); @@ -66,7 +66,7 @@ namespace Avalonia.Styling } } - protected override Selector? MovePrevious() => _previous; - protected override Selector? MovePreviousOrParent() => _previous; + private protected override Selector? MovePrevious() => _previous; + private protected override Selector? MovePreviousOrParent() => _previous; } } diff --git a/src/Avalonia.Base/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs index 532179bb2c..bf6247aba1 100644 --- a/src/Avalonia.Base/Styling/NthChildSelector.cs +++ b/src/Avalonia.Base/Styling/NthChildSelector.cs @@ -12,7 +12,7 @@ namespace Avalonia.Styling /// /// Element indices are 1-based. /// - public class NthChildSelector : Selector + internal class NthChildSelector : Selector { private const string NthChildSelectorName = "nth-child"; private const string NthLastChildSelectorName = "nth-last-child"; @@ -39,16 +39,16 @@ namespace Avalonia.Styling } - public override bool InTemplate => _previous?.InTemplate ?? false; + internal override bool InTemplate => _previous?.InTemplate ?? false; - public override bool IsCombinator => false; + internal override bool IsCombinator => false; - public override Type? TargetType => _previous?.TargetType; + internal override Type? TargetType => _previous?.TargetType; public int Step { get; } public int Offset { get; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { if (!(control is ILogical logical)) { @@ -103,8 +103,8 @@ namespace Avalonia.Styling return match ? SelectorMatch.AlwaysThisInstance : SelectorMatch.NeverThisInstance; } - protected override Selector? MovePrevious() => _previous; - protected override Selector? MovePreviousOrParent() => _previous; + private protected override Selector? MovePrevious() => _previous; + private protected override Selector? MovePreviousOrParent() => _previous; public override string ToString(Style? owner) { diff --git a/src/Avalonia.Base/Styling/NthLastChildSelector.cs b/src/Avalonia.Base/Styling/NthLastChildSelector.cs index 6f6abbae6a..aa62ad2b2c 100644 --- a/src/Avalonia.Base/Styling/NthLastChildSelector.cs +++ b/src/Avalonia.Base/Styling/NthLastChildSelector.cs @@ -8,7 +8,7 @@ namespace Avalonia.Styling /// /// Element indices are 1-based. /// - public class NthLastChildSelector : NthChildSelector + internal class NthLastChildSelector : NthChildSelector { /// /// Creates an instance of diff --git a/src/Avalonia.Base/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs index 53e4baa2c4..cc77aa9fcf 100644 --- a/src/Avalonia.Base/Styling/OrSelector.cs +++ b/src/Avalonia.Base/Styling/OrSelector.cs @@ -36,13 +36,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => false; + internal override bool InTemplate => false; /// - public override bool IsCombinator => false; + internal override bool IsCombinator => false; /// - public override Type? TargetType => _targetType ??= EvaluateTargetType(); + internal override Type? TargetType => _targetType ??= EvaluateTargetType(); /// public override string ToString(Style? owner) @@ -55,7 +55,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var activators = new OrActivatorBuilder(); var neverThisInstance = false; @@ -94,8 +94,8 @@ namespace Avalonia.Styling } } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => null; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => null; internal override void ValidateNestingSelector(bool inControlTheme) { diff --git a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs index 0865a7a8b0..3a50923094 100644 --- a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs +++ b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs @@ -28,13 +28,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => _previous?.InTemplate ?? false; + internal override bool InTemplate => _previous?.InTemplate ?? false; /// - public override bool IsCombinator => false; + internal override bool IsCombinator => false; /// - public override Type? TargetType => _previous?.TargetType; + internal override Type? TargetType => _previous?.TargetType; /// public override string ToString(Style? owner) @@ -73,7 +73,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { if (subscribe) { @@ -88,8 +88,8 @@ namespace Avalonia.Styling } - protected override Selector? MovePrevious() => _previous; - protected override Selector? MovePreviousOrParent() => _previous; + private protected override Selector? MovePrevious() => _previous; + private protected override Selector? MovePreviousOrParent() => _previous; [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.TypeConvertionSupressWarningMessage)] [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = TrimmingMessages.TypeConvertionSupressWarningMessage)] diff --git a/src/Avalonia.Base/Styling/Selector.cs b/src/Avalonia.Base/Styling/Selector.cs index c83950f72d..4e1c338553 100644 --- a/src/Avalonia.Base/Styling/Selector.cs +++ b/src/Avalonia.Base/Styling/Selector.cs @@ -14,7 +14,7 @@ namespace Avalonia.Styling /// Gets a value indicating whether either this selector or a previous selector has moved /// into a template. /// - public abstract bool InTemplate { get; } + internal abstract bool InTemplate { get; } /// /// Gets a value indicating whether this selector is a combinator. @@ -22,12 +22,12 @@ namespace Avalonia.Styling /// /// A combinator is a selector such as Child or Descendent which links simple selectors. /// - public abstract bool IsCombinator { get; } + internal abstract bool IsCombinator { get; } /// /// Gets the target type of the selector, if available. /// - public abstract Type? TargetType { get; } + internal abstract Type? TargetType { get; } /// /// Tries to match the selector with a control. @@ -41,7 +41,7 @@ namespace Avalonia.Styling /// or simply return an immediate result. /// /// A . - public SelectorMatch Match(StyledElement control, IStyle? parent = null, bool subscribe = true) + internal SelectorMatch Match(StyledElement control, IStyle? parent = null, bool subscribe = true) { // First match the selector until a combinator is found. Selectors are stored from // right-to-left, so MatchUntilCombinator reverses this order because the type selector @@ -88,17 +88,17 @@ namespace Avalonia.Styling /// or simply return an immediate result. /// /// A . - protected abstract SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe); + private protected abstract SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe); /// /// Moves to the previous selector. /// - protected abstract Selector? MovePrevious(); + private protected abstract Selector? MovePrevious(); /// /// Moves to the previous selector or the parent selector. /// - protected abstract Selector? MovePreviousOrParent(); + private protected abstract Selector? MovePreviousOrParent(); internal virtual void ValidateNestingSelector(bool inControlTheme) { diff --git a/src/Avalonia.Base/Styling/SelectorMatch.cs b/src/Avalonia.Base/Styling/SelectorMatch.cs index 2eac04301a..cbcab612d6 100644 --- a/src/Avalonia.Base/Styling/SelectorMatch.cs +++ b/src/Avalonia.Base/Styling/SelectorMatch.cs @@ -8,7 +8,7 @@ namespace Avalonia.Styling /// /// Describes how a matches a control and its type. /// - public enum SelectorMatchResult + internal enum SelectorMatchResult { /// /// The selector never matches this type. @@ -43,7 +43,7 @@ namespace Avalonia.Styling /// A selector match describes whether and how a matches a control, and /// in addition whether the selector can ever match a control of the same type. /// - public readonly record struct SelectorMatch + internal readonly record struct SelectorMatch { /// /// A selector match with the result of . diff --git a/src/Avalonia.Base/Styling/Setter.cs b/src/Avalonia.Base/Styling/Setter.cs index 9b009be6d2..e5b2bed738 100644 --- a/src/Avalonia.Base/Styling/Setter.cs +++ b/src/Avalonia.Base/Styling/Setter.cs @@ -14,7 +14,7 @@ namespace Avalonia.Styling /// A is used to set a value on a /// depending on a condition. /// - public class Setter : ISetter, IValueEntry, ISetterInstance, IAnimationSetter + public class Setter : SetterBase, IValueEntry, ISetterInstance, IAnimationSetter { private object? _value; private DirectPropertySetterInstance? _direct; @@ -66,7 +66,7 @@ namespace Avalonia.Styling void IValueEntry.Unsubscribe() { } [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)] - ISetterInstance ISetter.Instance(IStyleInstance instance, StyledElement target) + internal override ISetterInstance Instance(IStyleInstance instance, StyledElement target) { if (target is not AvaloniaObject ao) throw new InvalidOperationException("Don't know how to instance a style on this type."); diff --git a/src/Avalonia.Base/Styling/SetterBase.cs b/src/Avalonia.Base/Styling/SetterBase.cs new file mode 100644 index 0000000000..24cd525130 --- /dev/null +++ b/src/Avalonia.Base/Styling/SetterBase.cs @@ -0,0 +1,12 @@ +namespace Avalonia.Styling +{ + /// + /// Represents the base class for value setters. + /// + public abstract class SetterBase + { + internal abstract ISetterInstance Instance( + IStyleInstance styleInstance, + StyledElement target); + } +} diff --git a/src/Avalonia.Base/Styling/StyleBase.cs b/src/Avalonia.Base/Styling/StyleBase.cs index 7dfa516bce..318e8d6890 100644 --- a/src/Avalonia.Base/Styling/StyleBase.cs +++ b/src/Avalonia.Base/Styling/StyleBase.cs @@ -16,7 +16,7 @@ namespace Avalonia.Styling private IResourceHost? _owner; private StyleChildren? _children; private IResourceDictionary? _resources; - private List? _setters; + private List? _setters; private List? _animations; private StyleInstance? _sharedInstance; @@ -60,7 +60,7 @@ namespace Avalonia.Styling } } - public IList Setters => _setters ??= new List(); + public IList Setters => _setters ??= new(); public IList Animations => _animations ??= new List(); bool IResourceNode.HasResources => _resources?.Count > 0; @@ -69,7 +69,7 @@ namespace Avalonia.Styling internal bool HasChildren => _children?.Count > 0; internal bool HasSettersOrAnimations => _setters?.Count > 0 || _animations?.Count > 0; - public void Add(ISetter setter) => Setters.Add(setter); + public void Add(SetterBase setter) => Setters.Add(setter); public void Add(IStyle style) => Children.Add(style); public event EventHandler? OwnerChanged; diff --git a/src/Avalonia.Base/Styling/TemplateSelector.cs b/src/Avalonia.Base/Styling/TemplateSelector.cs index b253efc6d2..1fa2ca2d0f 100644 --- a/src/Avalonia.Base/Styling/TemplateSelector.cs +++ b/src/Avalonia.Base/Styling/TemplateSelector.cs @@ -18,13 +18,13 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => true; + internal override bool InTemplate => true; /// - public override bool IsCombinator => true; + internal override bool IsCombinator => true; /// - public override Type? TargetType => null; + internal override Type? TargetType => null; public override string ToString(Style? owner) { @@ -36,7 +36,7 @@ namespace Avalonia.Styling return _selectorString; } - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { var templatedParent = control.TemplatedParent as StyledElement; @@ -48,7 +48,7 @@ namespace Avalonia.Styling return _parent.Match(templatedParent, parent, subscribe); } - protected override Selector? MovePrevious() => null; - protected override Selector? MovePreviousOrParent() => _parent; + private protected override Selector? MovePrevious() => null; + private protected override Selector? MovePreviousOrParent() => _parent; } } diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs index 2bd05242f5..81b204761b 100644 --- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs @@ -58,7 +58,7 @@ namespace Avalonia.Styling } /// - public override bool InTemplate => _previous?.InTemplate ?? false; + internal override bool InTemplate => _previous?.InTemplate ?? false; /// /// Gets the name of the control to match. @@ -66,10 +66,10 @@ namespace Avalonia.Styling public string? Name { get; set; } /// - public override Type? TargetType => _targetType ?? _previous?.TargetType; + internal override Type? TargetType => _targetType ?? _previous?.TargetType; /// - public override bool IsCombinator => false; + internal override bool IsCombinator => false; /// /// Whether the selector matches the concrete or any object which @@ -89,7 +89,7 @@ namespace Avalonia.Styling } /// - protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) + private protected override SelectorMatch Evaluate(StyledElement control, IStyle? parent, bool subscribe) { if (TargetType != null) { @@ -134,8 +134,8 @@ namespace Avalonia.Styling return Name == null ? SelectorMatch.AlwaysThisType : SelectorMatch.AlwaysThisInstance; } - protected override Selector? MovePrevious() => _previous; - protected override Selector? MovePreviousOrParent() => _previous; + private protected override Selector? MovePrevious() => _previous; + private protected override Selector? MovePreviousOrParent() => _previous; private string BuildSelectorString(Style? owner) { 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 97a8c6fe97..a9d8e4c9c7 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -285,7 +285,7 @@ namespace Avalonia.Controls } } - void ISetterValue.Initialize(ISetter setter) + void ISetterValue.Initialize(SetterBase setter) { // ContextMenu can be assigned to the ContextMenu property in a setter. This overrides // the behavior defined in Control which requires controls to be wrapped in a