Browse Source

Merge remote-tracking branch 'origin/master' into fixes/list-selection-interactions

pull/11455/head
Max Katz 3 years ago
parent
commit
45381c3792
  1. 2
      packages/Avalonia/Avalonia.csproj
  2. 5
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  3. 2
      samples/GpuInterop/VulkanDemo/VulkanContext.cs
  4. 3
      samples/IntegrationTestApp/MainWindow.axaml
  5. 6
      samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs
  6. 4
      src/Avalonia.Base/Animation/Animatable.cs
  7. 1
      src/Avalonia.Base/Avalonia.Base.csproj
  8. 2
      src/Avalonia.Base/AvaloniaPropertyExtensions.cs
  9. 2
      src/Avalonia.Base/Collections/Pooled/ClearMode.cs
  10. 2
      src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs
  11. 2
      src/Avalonia.Base/Collections/Pooled/PooledList.cs
  12. 2
      src/Avalonia.Base/Collections/Pooled/PooledStack.cs
  13. 2
      src/Avalonia.Base/Controls/ChildNameScope.cs
  14. 2
      src/Avalonia.Base/Data/Core/CommonPropertyNames.cs
  15. 2
      src/Avalonia.Base/Data/IndexerBinding.cs
  16. 2
      src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs
  17. 2
      src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs
  18. 2
      src/Avalonia.Base/EnumExtensions.cs
  19. 6
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  20. 56
      src/Avalonia.Base/Input/FocusManager.cs
  21. 36
      src/Avalonia.Base/Input/IFocusManager.cs
  22. 3
      src/Avalonia.Base/Input/IFocusScope.cs
  23. 4
      src/Avalonia.Base/Input/IInputElement.cs
  24. 8
      src/Avalonia.Base/Input/IInputRoot.cs
  25. 8
      src/Avalonia.Base/Input/IKeyboardDevice.cs
  26. 17
      src/Avalonia.Base/Input/InputElement.cs
  27. 4
      src/Avalonia.Base/Input/KeyboardDevice.cs
  28. 18
      src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
  29. 2
      src/Avalonia.Base/Input/MouseDevice.cs
  30. 6
      src/Avalonia.Base/Input/Navigation/TabNavigation.cs
  31. 2
      src/Avalonia.Base/Input/PenDevice.cs
  32. 2
      src/Avalonia.Base/Input/TouchDevice.cs
  33. 2
      src/Avalonia.Base/Interactivity/Interactive.cs
  34. 2
      src/Avalonia.Base/Logging/TraceLogSink.cs
  35. 2
      src/Avalonia.Base/Media/ArcSegment.cs
  36. 2
      src/Avalonia.Base/Media/BezierSegment .cs
  37. 2
      src/Avalonia.Base/Media/Imaging/Bitmap.cs
  38. 2
      src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
  39. 2
      src/Avalonia.Base/Media/LineSegment.cs
  40. 4
      src/Avalonia.Base/Media/PathSegment.cs
  41. 2
      src/Avalonia.Base/Media/PolyLineSegment.cs
  42. 2
      src/Avalonia.Base/Media/QuadraticBezierSegment .cs
  43. 1
      src/Avalonia.Base/Media/StreamGeometryContext.cs
  44. 2
      src/Avalonia.Base/Media/Transformation/TransformParser.cs
  45. 6
      src/Avalonia.Base/Platform/Storage/NameCollisionOption.cs
  46. 2
      src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs
  47. 50
      src/Avalonia.Base/Rendering/RendererBase.cs
  48. 2
      src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs
  49. 3
      src/Avalonia.Base/Rendering/ZIndexComparer.cs
  50. 2
      src/Avalonia.Base/StyledElement.cs
  51. 2
      src/Avalonia.Base/Utilities/DisposableLock.cs
  52. 2
      src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs
  53. 2
      src/Avalonia.Base/Utilities/SingleOrDictionary.cs
  54. 2
      src/Avalonia.Base/Utilities/SingleOrQueue.cs
  55. 2
      src/Avalonia.Base/Utilities/ValueSingleOrList.cs
  56. 49
      src/Avalonia.Base/Utilities/WeakTimer.cs
  57. 2
      src/Avalonia.Base/Visual.cs
  58. 5
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  59. 4
      src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs
  60. 2
      src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs
  61. 2
      src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs
  62. 2
      src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
  63. 2
      src/Avalonia.Controls/Calendar/Calendar.cs
  64. 3
      src/Avalonia.Controls/ComboBox.cs
  65. 4
      src/Avalonia.Controls/ContextMenu.cs
  66. 11
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  67. 6
      src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
  68. 4
      src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs
  69. 11
      src/Avalonia.Controls/ItemsControl.cs
  70. 2
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  71. 2
      src/Avalonia.Controls/Primitives/AccessText.cs
  72. 2
      src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
  73. 6
      src/Avalonia.Controls/Primitives/Popup.cs
  74. 2
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  75. 2
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  76. 2
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  77. 2
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  78. 2
      src/Avalonia.Controls/TextBlock.cs
  79. 5
      src/Avalonia.Controls/TopLevel.cs
  80. 3
      src/Avalonia.Controls/TreeView.cs
  81. 2
      src/Avalonia.Controls/TreeViewItem.cs
  82. 2
      src/Avalonia.Controls/Viewbox.cs
  83. 14
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  84. 4
      src/Avalonia.Controls/WindowBase.cs
  85. 8
      src/Avalonia.Diagnostics/Diagnostics/DevTools.cs
  86. 5
      src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml
  87. 5
      src/Avalonia.Themes.Simple/Controls/ScrollViewer.xaml
  88. 4
      src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs
  89. 2
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  90. 6
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  91. 33
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  92. 4
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  93. 1
      src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
  94. 2
      src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs
  95. 4
      src/Skia/Avalonia.Skia/PictureRenderTarget.cs
  96. 2
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  97. 2
      src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs
  98. 8
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  99. 2
      src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
  100. 58
      tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs

2
packages/Avalonia/Avalonia.csproj

@ -5,7 +5,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia.BuildServices" Version="0.0.15" /> <PackageReference Include="Avalonia.BuildServices" Version="0.0.16" />
<ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" /> <ProjectReference Include="../../src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj" />
<ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj"> <ProjectReference Include="../../src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

5
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -1,5 +1,4 @@
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Controls; using Avalonia.Controls;
@ -23,7 +22,7 @@ namespace ControlCatalog.Pages
$"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link); $"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move); SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move);
SetupDnd("Files", d => d.Set(DataFormats.Files, new[] { Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }), DragDropEffects.Copy); SetupDnd("Files", async d => d.Set(DataFormats.Files, new[] { await (VisualRoot as TopLevel)!.StorageProvider.TryGetFileFromPathAsync(Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName) }), DragDropEffects.Copy);
} }
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects) void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)
@ -99,7 +98,7 @@ namespace ControlCatalog.Pages
{ {
if (item is IStorageFile file) if (item is IStorageFile file)
{ {
var content = await DialogsPage.ReadTextFromFile(file, 1000); var content = await DialogsPage.ReadTextFromFile(file, 500);
contentStr += $"File {item.Name}:{Environment.NewLine}{content}{Environment.NewLine}{Environment.NewLine}"; contentStr += $"File {item.Name}:{Environment.NewLine}{content}{Environment.NewLine}{Environment.NewLine}";
} }
else if (item is IStorageFolder folder) else if (item is IStorageFolder folder)

2
samples/GpuInterop/VulkanDemo/VulkanContext.cs

@ -174,7 +174,7 @@ public unsafe class VulkanContext : IDisposable
for (uint queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++) for (uint queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++)
{ {
var family = familyProperties[queueFamilyIndex]; var family = familyProperties[queueFamilyIndex];
if (!family.QueueFlags.HasAllFlags(QueueFlags.GraphicsBit)) if (!family.QueueFlags.HasFlag(QueueFlags.GraphicsBit))
continue; continue;

3
samples/IntegrationTestApp/MainWindow.axaml

@ -47,6 +47,9 @@
<Button Name="DisabledButton" IsEnabled="False"> <Button Name="DisabledButton" IsEnabled="False">
Disabled Button Disabled Button
</Button> </Button>
<Button Name="EffectivelyDisabledButton" Command="{ReflectionBinding DoesntExist}">
Effectively Disabled Button
</Button>
<Button Name="BasicButton"> <Button Name="BasicButton">
Basic Button Basic Button
</Button> </Button>

6
samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs

@ -24,19 +24,19 @@ public class PlaygroundPageViewModel : ViewModelBase
public bool Multiple public bool Multiple
{ {
get => _selectionMode.HasAnyFlag(SelectionMode.Multiple); get => _selectionMode.HasFlag(SelectionMode.Multiple);
set => SetSelectionMode(SelectionMode.Multiple, value); set => SetSelectionMode(SelectionMode.Multiple, value);
} }
public bool Toggle public bool Toggle
{ {
get => _selectionMode.HasAnyFlag(SelectionMode.Toggle); get => _selectionMode.HasFlag(SelectionMode.Toggle);
set => SetSelectionMode(SelectionMode.Toggle, value); set => SetSelectionMode(SelectionMode.Toggle, value);
} }
public bool AlwaysSelected public bool AlwaysSelected
{ {
get => _selectionMode.HasAnyFlag(SelectionMode.AlwaysSelected); get => _selectionMode.HasFlag(SelectionMode.AlwaysSelected);
set => SetSelectionMode(SelectionMode.AlwaysSelected, value); set => SetSelectionMode(SelectionMode.AlwaysSelected, value);
} }

4
src/Avalonia.Base/Animation/Animatable.cs

@ -58,7 +58,7 @@ namespace Avalonia.Animation
/// This method should not be called from user code, it will be called automatically by the framework /// This method should not be called from user code, it will be called automatically by the framework
/// when a control is added to the visual tree. /// when a control is added to the visual tree.
/// </remarks> /// </remarks>
protected void EnableTransitions() internal void EnableTransitions()
{ {
if (!_transitionsEnabled) if (!_transitionsEnabled)
{ {
@ -83,7 +83,7 @@ namespace Avalonia.Animation
/// This method should not be called from user code, it will be called automatically by the framework /// This method should not be called from user code, it will be called automatically by the framework
/// when a control is removed from the visual tree. /// when a control is removed from the visual tree.
/// </remarks> /// </remarks>
protected void DisableTransitions() internal void DisableTransitions()
{ {
if (_transitionsEnabled) if (_transitionsEnabled)
{ {

1
src/Avalonia.Base/Avalonia.Base.csproj

@ -63,6 +63,7 @@
<InternalsVisibleTo Include="Avalonia.iOS, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.iOS, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Dialogs, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Dialogs, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.LinuxFramebuffer, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="MiniMvvm, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="MiniMvvm, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="ControlCatalog, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="ControlCatalog, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" /> <InternalsVisibleTo Include="DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7" />

2
src/Avalonia.Base/AvaloniaPropertyExtensions.cs

@ -7,7 +7,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Extensions for <see cref="AvaloniaProperty"/>. /// Extensions for <see cref="AvaloniaProperty"/>.
/// </summary> /// </summary>
public static class AvaloniaPropertyExtensions internal static class AvaloniaPropertyExtensions
{ {
/// <summary> /// <summary>
/// Checks if values of given property can affect rendering (via <see cref="IAffectsRender"/>). /// Checks if values of given property can affect rendering (via <see cref="IAffectsRender"/>).

2
src/Avalonia.Base/Collections/Pooled/ClearMode.cs

@ -9,7 +9,7 @@ namespace Avalonia.Collections.Pooled
/// what each option does before using anything other than the default /// what each option does before using anything other than the default
/// of Auto. /// of Auto.
/// </summary> /// </summary>
public enum ClearMode internal enum ClearMode
{ {
/// <summary> /// <summary>
/// <para><code>Auto</code> has different behavior depending on the host project's target framework.</para> /// <para><code>Auto</code> has different behavior depending on the host project's target framework.</para>

2
src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs

@ -11,7 +11,7 @@ namespace Avalonia.Collections.Pooled
/// </summary> /// </summary>
/// <typeparam name="T">The type of elements in the read-only pooled list.</typeparam> /// <typeparam name="T">The type of elements in the read-only pooled list.</typeparam>
public interface IReadOnlyPooledList<T> : IReadOnlyList<T> internal interface IReadOnlyPooledList<T> : IReadOnlyList<T>
{ {
#pragma warning disable CS0419 #pragma warning disable CS0419
/// <summary> /// <summary>

2
src/Avalonia.Base/Collections/Pooled/PooledList.cs

@ -29,7 +29,7 @@ namespace Avalonia.Collections.Pooled
[DebuggerDisplay("Count = {Count}")] [DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(ICollectionDebugView<>))] [DebuggerTypeProxy(typeof(ICollectionDebugView<>))]
[Serializable] [Serializable]
public class PooledList<T> : IList<T>, IReadOnlyPooledList<T>, IList, IDisposable, IDeserializationCallback internal class PooledList<T> : IList<T>, IReadOnlyPooledList<T>, IList, IDisposable, IDeserializationCallback
{ {
// internal constant copied from Array.MaxArrayLength // internal constant copied from Array.MaxArrayLength
private const int MaxArrayLength = 0x7FEFFFFF; private const int MaxArrayLength = 0x7FEFFFFF;

2
src/Avalonia.Base/Collections/Pooled/PooledStack.cs

@ -29,7 +29,7 @@ namespace Avalonia.Collections.Pooled
[DebuggerTypeProxy(typeof(StackDebugView<>))] [DebuggerTypeProxy(typeof(StackDebugView<>))]
[DebuggerDisplay("Count = {Count}")] [DebuggerDisplay("Count = {Count}")]
[Serializable] [Serializable]
public class PooledStack<T> : IEnumerable<T>, ICollection, IReadOnlyCollection<T>, IDisposable, IDeserializationCallback internal class PooledStack<T> : IEnumerable<T>, ICollection, IReadOnlyCollection<T>, IDisposable, IDeserializationCallback
{ {
[NonSerialized] [NonSerialized]
private ArrayPool<T> _pool; private ArrayPool<T> _pool;

2
src/Avalonia.Base/Controls/ChildNameScope.cs

@ -3,7 +3,7 @@ using Avalonia.Utilities;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
public class ChildNameScope : INameScope internal class ChildNameScope : INameScope
{ {
private readonly INameScope _parentScope; private readonly INameScope _parentScope;
private readonly NameScope _inner = new NameScope(); private readonly NameScope _inner = new NameScope();

2
src/Avalonia.Base/Data/Core/CommonPropertyNames.cs

@ -1,6 +1,6 @@
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public static class CommonPropertyNames internal static class CommonPropertyNames
{ {
public const string IndexerName = "Item"; public const string IndexerName = "Item";
} }

2
src/Avalonia.Base/Data/IndexerBinding.cs

@ -2,7 +2,7 @@
namespace Avalonia.Data namespace Avalonia.Data
{ {
public class IndexerBinding : IBinding internal class IndexerBinding : IBinding
{ {
public IndexerBinding( public IndexerBinding(
AvaloniaObject source, AvaloniaObject source,

2
src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs

@ -5,7 +5,7 @@ namespace Avalonia.Diagnostics
/// <summary> /// <summary>
/// Provides a debug interface into <see cref="AvaloniaObject"/>. /// Provides a debug interface into <see cref="AvaloniaObject"/>.
/// </summary> /// </summary>
public interface IAvaloniaObjectDebug internal interface IAvaloniaObjectDebug
{ {
/// <summary> /// <summary>
/// Gets the subscriber list for the <see cref="AvaloniaObject.PropertyChanged"/> /// Gets the subscriber list for the <see cref="AvaloniaObject.PropertyChanged"/>

2
src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs

@ -8,7 +8,7 @@ namespace Avalonia.Diagnostics
/// Provides a debug interface into <see cref="INotifyCollectionChanged"/> subscribers on /// Provides a debug interface into <see cref="INotifyCollectionChanged"/> subscribers on
/// <see cref="AvaloniaList{T}"/> /// <see cref="AvaloniaList{T}"/>
/// </summary> /// </summary>
public interface INotifyCollectionChangedDebug internal interface INotifyCollectionChangedDebug
{ {
/// <summary> /// <summary>
/// Gets the subscriber list for the <see cref="INotifyCollectionChanged.CollectionChanged"/> /// Gets the subscriber list for the <see cref="INotifyCollectionChanged.CollectionChanged"/>

2
src/Avalonia.Base/EnumExtensions.cs

@ -6,7 +6,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Provides extension methods for enums. /// Provides extension methods for enums.
/// </summary> /// </summary>
public static class EnumExtensions internal static class EnumExtensions
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

6
src/Avalonia.Base/Input/AccessKeyHandler.cs

@ -9,7 +9,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Handles access keys for a window. /// Handles access keys for a window.
/// </summary> /// </summary>
public class AccessKeyHandler : IAccessKeyHandler internal class AccessKeyHandler : IAccessKeyHandler
{ {
/// <summary> /// <summary>
/// Defines the AccessKeyPressed attached event. /// Defines the AccessKeyPressed attached event.
@ -141,9 +141,11 @@ namespace Avalonia.Input
if (MainMenu == null || !MainMenu.IsOpen) 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. // TODO: Use FocusScopes to store the current element and restore it when context menu is closed.
// Save currently focused input element. // 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 // When Alt is pressed without a main menu, or with a closed main menu, show
// access key markers in the window (i.e. "_File"). // access key markers in the window (i.e. "_File").

56
src/Avalonia.Base/Input/FocusManager.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Metadata;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Input namespace Avalonia.Input
@ -10,6 +11,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Manages focus for the application. /// Manages focus for the application.
/// </summary> /// </summary>
[PrivateApi]
public class FocusManager : IFocusManager public class FocusManager : IFocusManager
{ {
/// <summary> /// <summary>
@ -29,15 +31,12 @@ namespace Avalonia.Input
RoutingStrategies.Tunnel); RoutingStrategies.Tunnel);
} }
/// <summary> private IInputElement? Current => KeyboardDevice.Instance?.FocusedElement;
/// Gets the instance of the <see cref="IFocusManager"/>.
/// </summary>
public static IFocusManager? Instance => AvaloniaLocator.Current.GetService<IFocusManager>();
/// <summary> /// <summary>
/// Gets the currently focused <see cref="IInputElement"/>. /// Gets the currently focused <see cref="IInputElement"/>.
/// </summary> /// </summary>
public IInputElement? Current => KeyboardDevice.Instance?.FocusedElement; public IInputElement? GetFocusedElement() => Current;
/// <summary> /// <summary>
/// Gets the current focus scope. /// Gets the current focus scope.
@ -54,7 +53,7 @@ namespace Avalonia.Input
/// <param name="control">The control to focus.</param> /// <param name="control">The control to focus.</param>
/// <param name="method">The method by which focus was changed.</param> /// <param name="method">The method by which focus was changed.</param>
/// <param name="keyModifiers">Any key modifiers active at the time of focus.</param> /// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
public void Focus( public bool Focus(
IInputElement? control, IInputElement? control,
NavigationMethod method = NavigationMethod.Unspecified, NavigationMethod method = NavigationMethod.Unspecified,
KeyModifiers keyModifiers = KeyModifiers.None) KeyModifiers keyModifiers = KeyModifiers.None)
@ -67,7 +66,7 @@ namespace Avalonia.Input
if (scope != null) if (scope != null)
{ {
Scope = scope; Scope = scope;
SetFocusedElement(scope, control, method, keyModifiers); return SetFocusedElement(scope, control, method, keyModifiers);
} }
} }
else if (Current != null) else if (Current != null)
@ -79,28 +78,29 @@ namespace Avalonia.Input
_focusScopes.TryGetValue(scope, out var element) && _focusScopes.TryGetValue(scope, out var element) &&
element != null) element != null)
{ {
Focus(element, method); return Focus(element, method);
return;
} }
} }
if (Scope is object) if (Scope is object)
{ {
// Couldn't find a focus scope, clear focus. // 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) Focus(null);
{ }
_focusScopes.TryGetValue(scope, out var result);
return result;
}
return null; public IInputElement? GetFocusedElement(IFocusScope scope)
{
_focusScopes.TryGetValue(scope, out var result);
return result;
} }
/// <summary> /// <summary>
@ -114,7 +114,7 @@ namespace Avalonia.Input
/// If the specified scope is the current <see cref="Scope"/> then the keyboard focus /// If the specified scope is the current <see cref="Scope"/> then the keyboard focus
/// will change. /// will change.
/// </remarks> /// </remarks>
public void SetFocusedElement( public bool SetFocusedElement(
IFocusScope scope, IFocusScope scope,
IInputElement? element, IInputElement? element,
NavigationMethod method = NavigationMethod.Unspecified, NavigationMethod method = NavigationMethod.Unspecified,
@ -124,7 +124,7 @@ namespace Avalonia.Input
if (element is not null && !CanFocus(element)) if (element is not null && !CanFocus(element))
{ {
return; return false;
} }
if (_focusScopes.TryGetValue(scope, out var existingElement)) if (_focusScopes.TryGetValue(scope, out var existingElement))
@ -144,6 +144,8 @@ namespace Avalonia.Input
{ {
KeyboardDevice.Instance?.SetFocusedElement(element, method, keyModifiers); KeyboardDevice.Instance?.SetFocusedElement(element, method, keyModifiers);
} }
return true;
} }
/// <summary> /// <summary>
@ -185,6 +187,20 @@ namespace Avalonia.Input
public static bool GetIsFocusScope(IInputElement e) => e is IFocusScope; public static bool GetIsFocusScope(IInputElement e) => e is IFocusScope;
/// <summary>
/// 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.
/// </summary>
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<IFocusManager>();
}
/// <summary> /// <summary>
/// Checks if the specified element can be focused. /// Checks if the specified element can be focused.
/// </summary> /// </summary>
@ -237,7 +253,7 @@ namespace Avalonia.Input
{ {
if (element is IInputElement inputElement && CanFocus(inputElement)) if (element is IInputElement inputElement && CanFocus(inputElement))
{ {
Instance?.Focus(inputElement, NavigationMethod.Pointer, ev.KeyModifiers); inputElement.Focus(NavigationMethod.Pointer, ev.KeyModifiers);
break; break;
} }

36
src/Avalonia.Base/Input/IFocusManager.cs

@ -11,40 +11,12 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Gets the currently focused <see cref="IInputElement"/>. /// Gets the currently focused <see cref="IInputElement"/>.
/// </summary> /// </summary>
IInputElement? Current { get; } IInputElement? GetFocusedElement();
/// <summary> /// <summary>
/// Gets the current focus scope. /// Clears currently focused element.
/// </summary> /// </summary>
IFocusScope? Scope { get; } [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();
/// <summary>
/// Focuses a control.
/// </summary>
/// <param name="control">The control to focus.</param>
/// <param name="method">The method by which focus was changed.</param>
/// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
void Focus(
IInputElement? control,
NavigationMethod method = NavigationMethod.Unspecified,
KeyModifiers keyModifiers = KeyModifiers.None);
/// <summary>
/// Notifies the focus manager of a change in focus scope.
/// </summary>
/// <param name="scope">The new focus scope.</param>
/// <remarks>
/// This should not be called by client code. It is called by an <see cref="IFocusScope"/>
/// when it activates, e.g. when a Window is activated.
/// </remarks>
void SetFocusScope(IFocusScope scope);
/// <summary>
/// Notifies the focus manager that a focus scope has been removed.
/// </summary>
/// <param name="scope">The focus scope to be removed.</param>
/// This should not be called by client code. It is called by an <see cref="IFocusScope"/>
/// when it deactivates or closes, e.g. when a Window is closed.
void RemoveFocusScope(IFocusScope scope);
} }
} }

3
src/Avalonia.Base/Input/IFocusScope.cs

@ -1,5 +1,8 @@
using Avalonia.Metadata;
namespace Avalonia.Input namespace Avalonia.Input
{ {
[NotClientImplementable]
public interface IFocusScope public interface IFocusScope
{ {
} }

4
src/Avalonia.Base/Input/IInputElement.cs

@ -119,7 +119,9 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Focuses the control. /// Focuses the control.
/// </summary> /// </summary>
void Focus(); /// <param name="method">The method by which focus was changed.</param>
/// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
bool Focus(NavigationMethod method = NavigationMethod.Unspecified, KeyModifiers keyModifiers = KeyModifiers.None);
/// <summary> /// <summary>
/// Gets the key bindings for the element. /// Gets the key bindings for the element.

8
src/Avalonia.Base/Input/IInputRoot.cs

@ -18,6 +18,14 @@ namespace Avalonia.Input
/// </summary> /// </summary>
IKeyboardNavigationHandler KeyboardNavigationHandler { get; } IKeyboardNavigationHandler KeyboardNavigationHandler { get; }
/// <summary>
/// Gets focus manager of the root.
/// </summary>
/// <remarks>
/// Focus manager can be null only if application wasn't initialized yet.
/// </remarks>
IFocusManager? FocusManager { get; }
/// <summary> /// <summary>
/// Gets or sets the input element that the pointer is currently over. /// Gets or sets the input element that the pointer is currently over.
/// </summary> /// </summary>

8
src/Avalonia.Base/Input/IKeyboardDevice.cs

@ -44,13 +44,7 @@ namespace Avalonia.Input
} }
[NotClientImplementable] [NotClientImplementable]
public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged public interface IKeyboardDevice : IInputDevice
{ {
IInputElement? FocusedElement { get; }
void SetFocusedElement(
IInputElement? element,
NavigationMethod method,
KeyModifiers modifiers);
} }
} }

17
src/Avalonia.Base/Input/InputElement.cs

@ -458,9 +458,10 @@ namespace Avalonia.Input
SetAndRaise(IsEffectivelyEnabledProperty, ref _isEffectivelyEnabled, value); SetAndRaise(IsEffectivelyEnabledProperty, ref _isEffectivelyEnabled, value);
PseudoClasses.Set(":disabled", !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 public GestureRecognizerCollection GestureRecognizers
=> _gestureRecognizers ?? (_gestureRecognizers = new GestureRecognizerCollection(this)); => _gestureRecognizers ?? (_gestureRecognizers = new GestureRecognizerCollection(this));
/// <summary> /// <inheritdoc />
/// Focuses the control. public bool Focus(NavigationMethod method = NavigationMethod.Unspecified, KeyModifiers keyModifiers = KeyModifiers.None)
/// </summary>
public void Focus()
{ {
FocusManager.Instance?.Focus(this); return FocusManager.GetFocusManager(this)?.Focus(this, method, keyModifiers) ?? false;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -506,7 +505,7 @@ namespace Avalonia.Input
if (IsFocused) if (IsFocused)
{ {
FocusManager.Instance?.Focus(null); FocusManager.GetFocusManager(this)?.ClearFocus();
} }
} }
@ -649,7 +648,7 @@ namespace Avalonia.Input
} }
else if (change.Property == IsVisibleProperty && !change.GetNewValue<bool>() && IsFocused) else if (change.Property == IsVisibleProperty && !change.GetNewValue<bool>() && IsFocused)
{ {
FocusManager.Instance?.Focus(null); FocusManager.GetFocusManager(this)?.ClearFocus();
} }
} }

4
src/Avalonia.Base/Input/KeyboardDevice.cs

@ -3,9 +3,11 @@ using System.Runtime.CompilerServices;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Input.TextInput; using Avalonia.Input.TextInput;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Metadata;
namespace Avalonia.Input namespace Avalonia.Input
{ {
[PrivateApi]
public class KeyboardDevice : IKeyboardDevice, INotifyPropertyChanged public class KeyboardDevice : IKeyboardDevice, INotifyPropertyChanged
{ {
private IInputElement? _focusedElement; private IInputElement? _focusedElement;
@ -13,7 +15,7 @@ namespace Avalonia.Input
public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangedEventHandler? PropertyChanged;
public static IKeyboardDevice? Instance => AvaloniaLocator.Current.GetService<IKeyboardDevice>(); internal static KeyboardDevice? Instance => AvaloniaLocator.Current.GetService<IKeyboardDevice>() as KeyboardDevice;
public IInputManager? InputManager => AvaloniaLocator.Current.GetService<IInputManager>(); public IInputManager? InputManager => AvaloniaLocator.Current.GetService<IInputManager>();

18
src/Avalonia.Base/Input/KeyboardNavigationHandler.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Avalonia.Input.Navigation; using Avalonia.Input.Navigation;
using Avalonia.Metadata;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Input namespace Avalonia.Input
@ -8,6 +9,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Handles keyboard navigation for a window. /// Handles keyboard navigation for a window.
/// </summary> /// </summary>
[Unstable]
public class KeyboardNavigationHandler : IKeyboardNavigationHandler public class KeyboardNavigationHandler : IKeyboardNavigationHandler
{ {
/// <summary> /// <summary>
@ -75,20 +77,23 @@ namespace Avalonia.Input
/// <param name="direction">The direction to move.</param> /// <param name="direction">The direction to move.</param>
/// <param name="keyModifiers">Any key modifiers active at the time of focus.</param> /// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
public void Move( public void Move(
IInputElement element, IInputElement? element,
NavigationDirection direction, NavigationDirection direction,
KeyModifiers keyModifiers = KeyModifiers.None) KeyModifiers keyModifiers = KeyModifiers.None)
{ {
element = element ?? throw new ArgumentNullException(nameof(element)); if (element is null && _owner is null)
{
return;
}
var next = GetNext(element, direction); var next = GetNext(element ?? _owner!, direction);
if (next != null) if (next != null)
{ {
var method = direction == NavigationDirection.Next || var method = direction == NavigationDirection.Next ||
direction == NavigationDirection.Previous ? direction == NavigationDirection.Previous ?
NavigationMethod.Tab : NavigationMethod.Directional; NavigationMethod.Tab : NavigationMethod.Directional;
FocusManager.Instance?.Focus(next, method, keyModifiers); next.Focus(method, keyModifiers);
} }
} }
@ -99,10 +104,9 @@ namespace Avalonia.Input
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void OnKeyDown(object? sender, KeyEventArgs e) protected virtual void OnKeyDown(object? sender, KeyEventArgs e)
{ {
var current = FocusManager.Instance?.Current; if (e.Key == Key.Tab)
if (current != null && e.Key == Key.Tab)
{ {
var current = FocusManager.GetFocusManager(e.Source as IInputElement)?.GetFocusedElement();
var direction = (e.KeyModifiers & KeyModifiers.Shift) == 0 ? var direction = (e.KeyModifiers & KeyModifiers.Shift) == 0 ?
NavigationDirection.Next : NavigationDirection.Previous; NavigationDirection.Next : NavigationDirection.Previous;
Move(current, direction, e.KeyModifiers); Move(current, direction, e.KeyModifiers);

2
src/Avalonia.Base/Input/MouseDevice.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Utilities; using Avalonia.Utilities;
#pragma warning disable CS0618 #pragma warning disable CS0618
@ -11,6 +12,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Represents a mouse device. /// Represents a mouse device.
/// </summary> /// </summary>
[PrivateApi]
public class MouseDevice : IMouseDevice, IDisposable public class MouseDevice : IMouseDevice, IDisposable
{ {
private int _clickCount; private int _clickCount;

6
src/Avalonia.Base/Input/Navigation/TabNavigation.cs

@ -190,9 +190,11 @@ namespace Avalonia.Input.Navigation
private static IInputElement? FocusedElement(IInputElement? e) private static IInputElement? FocusedElement(IInputElement? e)
{ {
// Focus delegation is enabled only if keyboard focus is outside the container // 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 (focusedElement != null)
{ {
if (!IsFocusScope(e)) if (!IsFocusScope(e))

2
src/Avalonia.Base/Input/PenDevice.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
#pragma warning disable CS0618 #pragma warning disable CS0618
@ -11,6 +12,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// Represents a pen/stylus device. /// Represents a pen/stylus device.
/// </summary> /// </summary>
[PrivateApi]
public class PenDevice : IPenDevice, IDisposable public class PenDevice : IPenDevice, IDisposable
{ {
private readonly Dictionary<long, Pointer> _pointers = new(); private readonly Dictionary<long, Pointer> _pointers = new();

2
src/Avalonia.Base/Input/TouchDevice.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.Metadata;
using Avalonia.Platform; using Avalonia.Platform;
#pragma warning disable CS0618 #pragma warning disable CS0618
@ -14,6 +15,7 @@ namespace Avalonia.Input
/// <remarks> /// <remarks>
/// This class is supposed to be used on per-toplevel basis, don't use a shared one /// This class is supposed to be used on per-toplevel basis, don't use a shared one
/// </remarks> /// </remarks>
[PrivateApi]
public class TouchDevice : IPointerDevice, IDisposable public class TouchDevice : IPointerDevice, IDisposable
{ {
private readonly Dictionary<long, Pointer> _pointers = new Dictionary<long, Pointer>(); private readonly Dictionary<long, Pointer> _pointers = new Dictionary<long, Pointer>();

2
src/Avalonia.Base/Interactivity/Interactive.cs

@ -17,7 +17,7 @@ namespace Avalonia.Interactivity
/// <summary> /// <summary>
/// Gets the interactive parent of the object for bubbling and tunneling events. /// Gets the interactive parent of the object for bubbling and tunneling events.
/// </summary> /// </summary>
protected internal virtual Interactive? InteractiveParent => VisualParent as Interactive; internal virtual Interactive? InteractiveParent => VisualParent as Interactive;
/// <summary> /// <summary>
/// Adds a handler for the specified routed event. /// Adds a handler for the specified routed event.

2
src/Avalonia.Base/Logging/TraceLogSink.cs

@ -6,7 +6,7 @@ using Avalonia.Utilities;
namespace Avalonia.Logging namespace Avalonia.Logging
{ {
public class TraceLogSink : ILogSink internal class TraceLogSink : ILogSink
{ {
private readonly LogEventLevel _level; private readonly LogEventLevel _level;
private readonly IList<string>? _areas; private readonly IList<string>? _areas;

2
src/Avalonia.Base/Media/ArcSegment.cs

@ -95,7 +95,7 @@ namespace Avalonia.Media
set { SetValue(SweepDirectionProperty, value); } set { SetValue(SweepDirectionProperty, value); }
} }
protected internal override void ApplyTo(StreamGeometryContext ctx) internal override void ApplyTo(StreamGeometryContext ctx)
{ {
ctx.ArcTo(Point, Size, RotationAngle, IsLargeArc, SweepDirection); ctx.ArcTo(Point, Size, RotationAngle, IsLargeArc, SweepDirection);
} }

2
src/Avalonia.Base/Media/BezierSegment .cs

@ -56,7 +56,7 @@ namespace Avalonia.Media
set { SetValue(Point3Property, value); } set { SetValue(Point3Property, value); }
} }
protected internal override void ApplyTo(StreamGeometryContext ctx) internal override void ApplyTo(StreamGeometryContext ctx)
{ {
ctx.CubicBezierTo(Point1, Point2, Point3); ctx.CubicBezierTo(Point1, Point2, Point3);
} }

2
src/Avalonia.Base/Media/Imaging/Bitmap.cs

@ -173,7 +173,7 @@ namespace Avalonia.Media.Imaging
public virtual PixelFormat? Format => (PlatformImpl.Item as IReadableBitmapImpl)?.Format; public virtual PixelFormat? Format => (PlatformImpl.Item as IReadableBitmapImpl)?.Format;
protected internal unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride, private protected unsafe void CopyPixelsCore(PixelRect sourceRect, IntPtr buffer, int bufferSize, int stride,
ILockedFramebuffer fb) ILockedFramebuffer fb)
{ {
if ((sourceRect.Width <= 0 || sourceRect.Height <= 0) && (sourceRect.X != 0 || sourceRect.Y != 0)) if ((sourceRect.Width <= 0 || sourceRect.Height <= 0) && (sourceRect.X != 0 || sourceRect.Y != 0))

2
src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs

@ -21,7 +21,7 @@ namespace Avalonia.Media.Immutable
/// How the source rectangle will be stretched to fill the destination rect. /// How the source rectangle will be stretched to fill the destination rect.
/// </param> /// </param>
/// <param name="tileMode">The tile mode.</param> /// <param name="tileMode">The tile mode.</param>
protected internal ImmutableTileBrush( private protected ImmutableTileBrush(
AlignmentX alignmentX, AlignmentX alignmentX,
AlignmentY alignmentY, AlignmentY alignmentY,
RelativeRect destinationRect, RelativeRect destinationRect,

2
src/Avalonia.Base/Media/LineSegment.cs

@ -22,7 +22,7 @@ namespace Avalonia.Media
set { SetValue(PointProperty, value); } set { SetValue(PointProperty, value); }
} }
protected internal override void ApplyTo(StreamGeometryContext ctx) internal override void ApplyTo(StreamGeometryContext ctx)
{ {
ctx.LineTo(Point); ctx.LineTo(Point);
} }

4
src/Avalonia.Base/Media/PathSegment.cs

@ -2,6 +2,6 @@ namespace Avalonia.Media
{ {
public abstract class PathSegment : AvaloniaObject public abstract class PathSegment : AvaloniaObject
{ {
protected internal abstract void ApplyTo(StreamGeometryContext ctx); internal abstract void ApplyTo(StreamGeometryContext ctx);
} }
} }

2
src/Avalonia.Base/Media/PolyLineSegment.cs

@ -44,7 +44,7 @@ namespace Avalonia.Media
Points = new Points(points); Points = new Points(points);
} }
protected internal override void ApplyTo(StreamGeometryContext ctx) internal override void ApplyTo(StreamGeometryContext ctx)
{ {
var points = Points; var points = Points;
if (points.Count > 0) if (points.Count > 0)

2
src/Avalonia.Base/Media/QuadraticBezierSegment .cs

@ -40,7 +40,7 @@ namespace Avalonia.Media
set { SetValue(Point2Property, value); } set { SetValue(Point2Property, value); }
} }
protected internal override void ApplyTo(StreamGeometryContext ctx) internal override void ApplyTo(StreamGeometryContext ctx)
{ {
ctx.QuadraticBezierTo(Point1, Point2); ctx.QuadraticBezierTo(Point1, Point2);
} }

1
src/Avalonia.Base/Media/StreamGeometryContext.cs

@ -10,7 +10,6 @@ namespace Avalonia.Media
/// of <see cref="StreamGeometryContext"/> is obtained by calling /// of <see cref="StreamGeometryContext"/> is obtained by calling
/// <see cref="StreamGeometry.Open"/>. /// <see cref="StreamGeometry.Open"/>.
/// </remarks> /// </remarks>
/// TODO: This class is just a wrapper around IStreamGeometryContextImpl: is it needed?
public class StreamGeometryContext : IGeometryContext public class StreamGeometryContext : IGeometryContext
{ {
private readonly IStreamGeometryContextImpl _impl; private readonly IStreamGeometryContextImpl _impl;

2
src/Avalonia.Base/Media/Transformation/TransformParser.cs

@ -4,7 +4,7 @@ using Avalonia.Utilities;
namespace Avalonia.Media.Transformation namespace Avalonia.Media.Transformation
{ {
public static class TransformParser internal static class TransformParser
{ {
private static readonly (string, TransformFunction)[] s_functionMapping = private static readonly (string, TransformFunction)[] s_functionMapping =
{ {

6
src/Avalonia.Base/Platform/Storage/NameCollisionOption.cs

@ -1,6 +0,0 @@
namespace Avalonia.Platform.Storage;
public class NameCollisionOption
{
}

2
src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs

@ -4,7 +4,7 @@ using Avalonia.Utilities;
namespace Avalonia.Rendering namespace Avalonia.Rendering
{ {
public class ManagedDeferredRendererLock : DisposableLock, IDeferredRendererLock internal class ManagedDeferredRendererLock : DisposableLock, IDeferredRendererLock
{ {
} }

50
src/Avalonia.Base/Rendering/RendererBase.cs

@ -1,50 +0,0 @@
using System;
using System.Diagnostics;
using System.Globalization;
using Avalonia.Media;
namespace Avalonia.Rendering
{
public class RendererBase
{
private readonly bool _useManualFpsCounting;
private static int s_fontSize = 18;
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
private int _framesThisSecond;
private int _fps;
private TimeSpan _lastFpsUpdate;
public RendererBase(bool useManualFpsCounting = false)
{
_useManualFpsCounting = useManualFpsCounting;
}
protected void FpsTick() => _framesThisSecond++;
protected void RenderFps(DrawingContext context, Rect clientRect, int? layerCount)
{
var now = _stopwatch.Elapsed;
var elapsed = now - _lastFpsUpdate;
if (!_useManualFpsCounting)
++_framesThisSecond;
if (elapsed.TotalSeconds > 1)
{
_fps = (int)(_framesThisSecond / elapsed.TotalSeconds);
_framesThisSecond = 0;
_lastFpsUpdate = now;
}
var text = layerCount.HasValue ? FormattableString.Invariant($"Layers: {layerCount} FPS: {_fps:000}") : FormattableString.Invariant($"FPS: {_fps:000}");
var formattedText = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Typeface.Default, s_fontSize, Brushes.White);
var rect = new Rect(clientRect.Right - formattedText.Width, 0, formattedText.Width, formattedText.Height);
context.DrawRectangle(Brushes.Black, null, rect);
context.DrawText(formattedText, rect.TopLeft);
}
}
}

2
src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs

@ -2,7 +2,7 @@
namespace Avalonia.Rendering.Utilities namespace Avalonia.Rendering.Utilities
{ {
public class TileBrushCalculator internal class TileBrushCalculator
{ {
private readonly Size _imageSize; private readonly Size _imageSize;
private readonly Rect _drawRect; private readonly Rect _drawRect;

3
src/Avalonia.Base/Rendering/ZIndexComparer.cs

@ -1,10 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.VisualTree;
namespace Avalonia.Rendering namespace Avalonia.Rendering
{ {
public class ZIndexComparer : IComparer<Visual> internal class ZIndexComparer : IComparer<Visual>
{ {
public static readonly ZIndexComparer Instance = new ZIndexComparer(); public static readonly ZIndexComparer Instance = new ZIndexComparer();
public static readonly Comparison<Visual> ComparisonInstance = Instance.Compare; public static readonly Comparison<Visual> ComparisonInstance = Instance.Compare;

2
src/Avalonia.Base/StyledElement.cs

@ -556,7 +556,7 @@ namespace Avalonia
/// Notifies child controls that a change has been made to resources that apply to them. /// Notifies child controls that a change has been made to resources that apply to them.
/// </summary> /// </summary>
/// <param name="e">The event args.</param> /// <param name="e">The event args.</param>
protected virtual void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) internal virtual void NotifyChildResourcesChanged(ResourcesChangedEventArgs e)
{ {
if (_logicalChildren is object) if (_logicalChildren is object)
{ {

2
src/Avalonia.Base/Utilities/DisposableLock.cs

@ -3,7 +3,7 @@ using System.Threading;
namespace Avalonia.Utilities namespace Avalonia.Utilities
{ {
public class DisposableLock internal class DisposableLock
{ {
private readonly object _lock = new object(); private readonly object _lock = new object();

2
src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs

@ -3,7 +3,7 @@ using Avalonia.Threading;
namespace Avalonia.Utilities namespace Avalonia.Utilities
{ {
public class NonPumpingLockHelper internal class NonPumpingLockHelper
{ {
public interface IHelperImpl public interface IHelperImpl
{ {

2
src/Avalonia.Base/Utilities/SingleOrDictionary.cs

@ -11,7 +11,7 @@ namespace Avalonia.Utilities
/// </summary> /// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam> /// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam> /// <typeparam name="TValue">The type of the value.</typeparam>
public class SingleOrDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>> internal class SingleOrDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
where TKey : notnull where TKey : notnull
{ {
private KeyValuePair<TKey, TValue>? _singleValue; private KeyValuePair<TKey, TValue>? _singleValue;

2
src/Avalonia.Base/Utilities/SingleOrQueue.cs

@ -7,7 +7,7 @@ namespace Avalonia.Utilities
/// FIFO Queue optimized for holding zero or one items. /// FIFO Queue optimized for holding zero or one items.
/// </summary> /// </summary>
/// <typeparam name="T">The type of items held in the queue.</typeparam> /// <typeparam name="T">The type of items held in the queue.</typeparam>
public class SingleOrQueue<T> internal class SingleOrQueue<T>
{ {
private T? _head; private T? _head;
private Queue<T>? _tail; private Queue<T>? _tail;

2
src/Avalonia.Base/Utilities/ValueSingleOrList.cs

@ -9,7 +9,7 @@ namespace Avalonia.Utilities
/// <remarks> /// <remarks>
/// Once more than value has been added to this storage it will switch to using <see cref="List"/> internally. /// Once more than value has been added to this storage it will switch to using <see cref="List"/> internally.
/// </remarks> /// </remarks>
public ref struct ValueSingleOrList<T> internal ref struct ValueSingleOrList<T>
{ {
private bool _isSingleSet; private bool _isSingleSet;

49
src/Avalonia.Base/Utilities/WeakTimer.cs

@ -1,49 +0,0 @@
using System;
using Avalonia.Threading;
namespace Avalonia.Utilities
{
public class WeakTimer
{
public interface IWeakTimerSubscriber
{
bool Tick();
}
private readonly WeakReference<IWeakTimerSubscriber> _subscriber;
private DispatcherTimer _timer;
public WeakTimer(IWeakTimerSubscriber subscriber)
{
_subscriber = new WeakReference<IWeakTimerSubscriber>(subscriber);
_timer = new DispatcherTimer();
_timer.Tick += delegate { OnTick(); };
}
private void OnTick()
{
if (!_subscriber.TryGetTarget(out var subscriber) || !subscriber.Tick())
Stop();
}
public TimeSpan Interval
{
get { return _timer.Interval; }
set { _timer.Interval = value; }
}
public void Start() => _timer.Start();
public void Stop() => _timer.Stop();
public static WeakTimer StartWeakTimer(IWeakTimerSubscriber subscriber, TimeSpan interval)
{
var timer = new WeakTimer(subscriber) {Interval = interval};
timer.Start();
return timer;
}
}
}

2
src/Avalonia.Base/Visual.cs

@ -321,7 +321,7 @@ namespace Avalonia
internal RenderOptions RenderOptions { get; set; } internal RenderOptions RenderOptions { get; set; }
public bool HasNonUniformZIndexChildren { get; private set; } internal bool HasNonUniformZIndexChildren { get; private set; }
/// <summary> /// <summary>
/// Gets a value indicating whether this control is attached to a visual root. /// Gets a value indicating whether this control is attached to a visual root.

5
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -3958,7 +3958,7 @@ namespace Avalonia.Controls
{ {
bool focusLeftDataGrid = true; bool focusLeftDataGrid = true;
bool dataGridWillReceiveRoutedEvent = true; bool dataGridWillReceiveRoutedEvent = true;
Visual focusedObject = FocusManager.Instance.Current as Visual; Visual focusedObject = FocusManager.GetFocusManager(this)?.GetFocusedElement() as Visual;
DataGridColumn editingColumn = null; DataGridColumn editingColumn = null;
while (focusedObject != null) while (focusedObject != null)
@ -4865,7 +4865,8 @@ namespace Avalonia.Controls
if (!ctrl) if (!ctrl)
{ {
// If Enter was used by a TextBox, we shouldn't handle the key // 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; return false;
} }

4
src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs

@ -53,8 +53,8 @@ namespace Avalonia.Controls
{ {
return _owner.GetElementImpl( return _owner.GetElementImpl(
index, index,
options.HasAllFlags(ElementRealizationOptions.ForceCreate), options.HasFlag(ElementRealizationOptions.ForceCreate),
options.HasAllFlags(ElementRealizationOptions.SuppressAutoRecycle)); options.HasFlag(ElementRealizationOptions.SuppressAutoRecycle));
} }
protected override object GetItemAtCore(int index) => _owner.ItemsSourceView!.GetAt(index)!; protected override object GetItemAtCore(int index) => _owner.ItemsSourceView!.GetAt(index)!;

2
src/Avalonia.Controls.ItemsRepeater/Controls/ViewManager.cs

@ -695,7 +695,7 @@ namespace Avalonia.Controls
{ {
Control? focusedElement = null; Control? focusedElement = null;
if (FocusManager.Instance?.Current is Visual child) if (TopLevel.GetTopLevel(_owner)?.FocusManager?.GetFocusedElement() is Visual child)
{ {
var parent = child.GetVisualParent(); var parent = child.GetVisualParent();
var owner = _owner; var owner = _owner;

2
src/Avalonia.Controls/AutoCompleteBox/AutoCompleteBox.cs

@ -762,7 +762,7 @@ namespace Avalonia.Controls
/// otherwise, false.</returns> /// otherwise, false.</returns>
protected bool HasFocus() protected bool HasFocus()
{ {
Visual? focused = FocusManager.Instance?.Current as Visual; Visual? focused = FocusManager.GetFocusManager(this)?.GetFocusedElement() as Visual;
while (focused != null) while (focused != null)
{ {

2
src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs

@ -151,7 +151,7 @@ namespace Avalonia.Automation.Peers
protected override bool HasKeyboardFocusCore() => Owner.IsFocused; protected override bool HasKeyboardFocusCore() => Owner.IsFocused;
protected override bool IsContentElementCore() => true; protected override bool IsContentElementCore() => true;
protected override bool IsControlElementCore() => true; protected override bool IsControlElementCore() => true;
protected override bool IsEnabledCore() => Owner.IsEnabled; protected override bool IsEnabledCore() => Owner.IsEffectivelyEnabled;
protected override bool IsKeyboardFocusableCore() => Owner.Focusable; protected override bool IsKeyboardFocusableCore() => Owner.Focusable;
protected override void SetFocusCore() => Owner.Focus(); protected override void SetFocusCore() => Owner.Focus();

2
src/Avalonia.Controls/Calendar/Calendar.cs

@ -1567,7 +1567,7 @@ namespace Avalonia.Controls
base.OnPointerReleased(e); base.OnPointerReleased(e);
if (!HasFocusInternal && e.InitialPressMouseButton == MouseButton.Left) if (!HasFocusInternal && e.InitialPressMouseButton == MouseButton.Left)
{ {
FocusManager.Instance?.Focus(this); Focus();
} }
} }

3
src/Avalonia.Controls/ComboBox.cs

@ -230,8 +230,7 @@ namespace Avalonia.Controls
var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c)); var firstChild = Presenter?.Panel?.Children.FirstOrDefault(c => CanFocus(c));
if (firstChild != null) if (firstChild != null)
{ {
FocusManager.Instance?.Focus(firstChild, NavigationMethod.Directional); e.Handled = firstChild.Focus(NavigationMethod.Directional);
e.Handled = true;
} }
} }
} }

4
src/Avalonia.Controls/ContextMenu.cs

@ -360,7 +360,7 @@ namespace Avalonia.Controls
private void PopupOpened(object? sender, EventArgs e) private void PopupOpened(object? sender, EventArgs e)
{ {
_previousFocus = FocusManager.Instance?.Current; _previousFocus = FocusManager.GetFocusManager(this)?.GetFocusedElement();
Focus(); Focus();
_popupHostChangedHandler?.Invoke(_popup!.Host); _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. // 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 RaiseEvent(new RoutedEventArgs
{ {

11
src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs

@ -323,10 +323,11 @@ namespace Avalonia.Controls
e.Handled = true; e.Handled = true;
break; break;
case Key.Tab: 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); var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None); nextFocus?.Focus(NavigationMethod.Tab);
e.Handled = true; e.Handled = true;
} }
break; break;
@ -449,15 +450,15 @@ namespace Avalonia.Controls
if (monthCol < dayCol && monthCol < yearCol) if (monthCol < dayCol && monthCol < yearCol)
{ {
KeyboardDevice.Instance?.SetFocusedElement(_monthSelector, NavigationMethod.Pointer, KeyModifiers.None); _monthSelector?.Focus(NavigationMethod.Pointer);
} }
else if (dayCol < monthCol && dayCol < yearCol) else if (dayCol < monthCol && dayCol < yearCol)
{ {
KeyboardDevice.Instance?.SetFocusedElement(_daySelector, NavigationMethod.Pointer, KeyModifiers.None); _monthSelector?.Focus(NavigationMethod.Pointer);
} }
else if (yearCol < monthCol && yearCol < dayCol) else if (yearCol < monthCol && yearCol < dayCol)
{ {
KeyboardDevice.Instance?.SetFocusedElement(_yearSelector, NavigationMethod.Pointer, KeyModifiers.None); _yearSelector?.Focus(NavigationMethod.Pointer);
} }
} }

6
src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs

@ -161,10 +161,10 @@ namespace Avalonia.Controls
e.Handled = true; e.Handled = true;
break; break;
case Key.Tab: case Key.Tab:
if (FocusManager.Instance?.Current is IInputElement focus) if (FocusManager.GetFocusManager(this)?.GetFocusedElement() is { } focus)
{ {
var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next); var nextFocus = KeyboardNavigationHandler.GetNext(focus, NavigationDirection.Next);
KeyboardDevice.Instance?.SetFocusedElement(nextFocus, NavigationMethod.Tab, KeyModifiers.None); nextFocus?.Focus(NavigationMethod.Tab);
e.Handled = true; e.Handled = true;
} }
break; break;
@ -216,7 +216,7 @@ namespace Avalonia.Controls
_periodSelector.SelectedValue = hr >= 12 ? 1 : 0; _periodSelector.SelectedValue = hr >= 12 ? 1 : 0;
SetGrid(); SetGrid();
KeyboardDevice.Instance?.SetFocusedElement(_hourSelector, NavigationMethod.Pointer, KeyModifiers.None); _hourSelector?.Focus(NavigationMethod.Pointer);
} }
private void SetGrid() private void SetGrid()

4
src/Avalonia.Controls/Flyouts/PopupFlyoutBase.cs

@ -250,14 +250,14 @@ namespace Avalonia.Controls.Primitives
// Try and focus content inside Flyout // Try and focus content inside Flyout
if (Popup.Child.Focusable) if (Popup.Child.Focusable)
{ {
FocusManager.Instance?.Focus(Popup.Child); Popup.Child.Focus();
} }
else else
{ {
var nextFocus = KeyboardNavigationHandler.GetNext(Popup.Child, NavigationDirection.Next); var nextFocus = KeyboardNavigationHandler.GetNext(Popup.Child, NavigationDirection.Next);
if (nextFocus != null) if (nextFocus != null)
{ {
FocusManager.Instance?.Focus(nextFocus); nextFocus.Focus();
} }
} }
} }

11
src/Avalonia.Controls/ItemsControl.cs

@ -590,19 +590,20 @@ namespace Avalonia.Controls
{ {
if (!e.Handled) if (!e.Handled)
{ {
var focus = FocusManager.Instance; var focus = FocusManager.GetFocusManager(this);
var direction = e.Key.ToNavigationDirection(); var direction = e.Key.ToNavigationDirection();
var container = Presenter?.Panel as INavigableContainer; var container = Presenter?.Panel as INavigableContainer;
if (container == null || if (focus == null ||
focus?.Current == null || container == null ||
focus.GetFocusedElement() == null ||
direction == null || direction == null ||
direction.Value.IsTab()) direction.Value.IsTab())
{ {
return; return;
} }
Visual? current = focus.Current as Visual; Visual? current = focus.GetFocusedElement() as Visual;
while (current != null) while (current != null)
{ {
@ -612,7 +613,7 @@ namespace Avalonia.Controls
if (next != null) if (next != null)
{ {
focus.Focus(next, NavigationMethod.Directional, e.KeyModifiers); next.Focus(NavigationMethod.Directional, e.KeyModifiers);
e.Handled = true; e.Handled = true;
} }

2
src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs

@ -302,7 +302,7 @@ namespace Avalonia.Controls.Presenters
/// <remarks> /// <remarks>
/// This method is automatically called when the control is attached to a visual tree. /// This method is automatically called when the control is attached to a visual tree.
/// </remarks> /// </remarks>
protected internal virtual void AttachToScrollViewer() internal void AttachToScrollViewer()
{ {
var owner = this.FindAncestorOfType<ScrollViewer>(); var owner = this.FindAncestorOfType<ScrollViewer>();

2
src/Avalonia.Controls/Primitives/AccessText.cs

@ -60,7 +60,7 @@ namespace Avalonia.Controls.Primitives
/// Renders the <see cref="AccessText"/> to a drawing context. /// Renders the <see cref="AccessText"/> to a drawing context.
/// </summary> /// </summary>
/// <param name="context">The drawing context.</param> /// <param name="context">The drawing context.</param>
protected internal override void RenderCore(DrawingContext context) private protected override void RenderCore(DrawingContext context)
{ {
base.RenderCore(context); base.RenderCore(context);
int underscore = Text?.IndexOf('_') ?? -1; int underscore = Text?.IndexOf('_') ?? -1;

2
src/Avalonia.Controls/Primitives/OverlayPopupHost.cs

@ -51,7 +51,7 @@ namespace Avalonia.Controls.Primitives
} }
/// <inheritdoc /> /// <inheritdoc />
protected internal override Interactive? InteractiveParent => Parent as Interactive; internal override Interactive? InteractiveParent => Parent as Interactive;
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() => Hide(); public void Dispose() => Hide();

6
src/Avalonia.Controls/Primitives/Popup.cs

@ -727,7 +727,7 @@ namespace Avalonia.Controls.Primitives
Closed?.Invoke(this, EventArgs.Empty); 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 // Focus is set to null as part of popup closing, so we only want to
// set focus to PlacementTarget if this is the case // set focus to PlacementTarget if this is the case
@ -744,7 +744,7 @@ namespace Avalonia.Controls.Primitives
if (e is object) if (e is object)
{ {
FocusManager.Instance?.Focus(e); e.Focus();
} }
} }
else else
@ -752,7 +752,7 @@ namespace Avalonia.Controls.Primitives
var anc = this.FindLogicalAncestorOfType<Control>(); var anc = this.FindLogicalAncestorOfType<Control>();
if (anc != null) if (anc != null)
{ {
FocusManager.Instance?.Focus(anc); anc.Focus();
} }
} }
} }

2
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -72,7 +72,7 @@ namespace Avalonia.Controls.Primitives
/// <remarks> /// <remarks>
/// Popup events are passed to their parent window. This facilitates this. /// Popup events are passed to their parent window. This facilitates this.
/// </remarks> /// </remarks>
protected internal override Interactive? InteractiveParent => (Interactive?)Parent; internal override Interactive? InteractiveParent => (Interactive?)Parent;
/// <summary> /// <summary>
/// Gets the control that is hosting the popup root. /// Gets the control that is hosting the popup root.

2
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -200,7 +200,7 @@ namespace Avalonia.Controls.Primitives
/// <remarks> /// <remarks>
/// This method is automatically called when the control is attached to a visual tree. /// This method is automatically called when the control is attached to a visual tree.
/// </remarks> /// </remarks>
protected internal virtual void AttachToScrollViewer() internal void AttachToScrollViewer()
{ {
var owner = this.FindAncestorOfType<ScrollViewer>(); var owner = this.FindAncestorOfType<ScrollViewer>();

2
src/Avalonia.Controls/Primitives/TemplatedControl.cs

@ -318,7 +318,7 @@ namespace Avalonia.Controls.Primitives
} }
/// <inheritdoc /> /// <inheritdoc />
protected sealed override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) internal sealed override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e)
{ {
var count = VisualChildren.Count; var count = VisualChildren.Count;

2
src/Avalonia.Controls/Primitives/VisualLayerManager.cs

@ -104,7 +104,7 @@ namespace Avalonia.Controls.Primitives
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) internal override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e)
{ {
foreach (var l in _layers) foreach (var l in _layers)
((ILogical)l).NotifyResourcesChanged(e); ((ILogical)l).NotifyResourcesChanged(e);

2
src/Avalonia.Controls/TextBlock.cs

@ -555,7 +555,7 @@ namespace Avalonia.Controls
} }
// Workaround to seal Render method, we need to make so because AccessText was overriding Render method which is sealed now. // Workaround to seal Render method, we need to make so because AccessText was overriding Render method which is sealed now.
internal protected virtual void RenderCore(DrawingContext context) private protected virtual void RenderCore(DrawingContext context)
{ {
var background = Background; var background = Background;

5
src/Avalonia.Controls/TopLevel.cs

@ -452,6 +452,9 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public IClipboard? Clipboard => PlatformImpl?.TryGetFeature<IClipboard>(); public IClipboard? Clipboard => PlatformImpl?.TryGetFeature<IClipboard>();
/// <inheritdoc />
public IFocusManager? FocusManager => AvaloniaLocator.Current.GetService<IFocusManager>();
/// <inheritdoc/> /// <inheritdoc/>
Point IRenderRoot.PointToClient(PixelPoint p) Point IRenderRoot.PointToClient(PixelPoint p)
{ {
@ -725,7 +728,7 @@ namespace Avalonia.Controls
void PlatformImpl_LostFocus() void PlatformImpl_LostFocus()
{ {
var focused = (Visual?)FocusManager.Instance?.Current; var focused = (Visual?)FocusManager?.GetFocusedElement();
if (focused == null) if (focused == null)
return; return;
while (focused.VisualParent != null) while (focused.VisualParent != null)

3
src/Avalonia.Controls/TreeView.cs

@ -560,8 +560,7 @@ namespace Avalonia.Controls
if (next != null) if (next != null)
{ {
FocusManager.Instance?.Focus(next, NavigationMethod.Directional); e.Handled = next.Focus(NavigationMethod.Directional);
e.Handled = true;
} }
} }
else else

2
src/Avalonia.Controls/TreeViewItem.cs

@ -238,7 +238,7 @@ namespace Avalonia.Controls
} }
else else
{ {
FocusManager.Instance?.Focus(treeViewItem, NavigationMethod.Directional); treeViewItem.Focus(NavigationMethod.Directional);
} }
return true; return true;

2
src/Avalonia.Controls/Viewbox.cs

@ -82,7 +82,7 @@ namespace Avalonia.Controls
/// Gets or sets the transform applied to the container visual that /// Gets or sets the transform applied to the container visual that
/// hosts the child of the Viewbox /// hosts the child of the Viewbox
/// </summary> /// </summary>
protected internal ITransform? InternalTransform internal ITransform? InternalTransform
{ {
get => _containerVisual.RenderTransform; get => _containerVisual.RenderTransform;
set => _containerVisual.RenderTransform = value; set => _containerVisual.RenderTransform = value;

14
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -356,7 +356,7 @@ namespace Avalonia.Controls
{ {
var items = Items; var items = Items;
if (_isInLayout || index < 0 || index >= items.Count || _realizedElements is null) if (_isInLayout || index < 0 || index >= items.Count || _realizedElements is null || !IsEffectivelyVisible)
return null; return null;
if (GetRealizedElement(index) is Control element) if (GetRealizedElement(index) is Control element)
@ -682,9 +682,12 @@ namespace Avalonia.Controls
_scrollViewer?.UnregisterAnchorCandidate(element); _scrollViewer?.UnregisterAnchorCandidate(element);
var recycleKey = element.GetValue(RecycleKeyProperty); var recycleKey = element.GetValue(RecycleKeyProperty);
Debug.Assert(recycleKey is not null);
if (recycleKey == s_itemIsItsOwnContainer) if (recycleKey is null)
{
RemoveInternalChild(element);
}
else if (recycleKey == s_itemIsItsOwnContainer)
{ {
element.IsVisible = false; element.IsVisible = false;
} }
@ -707,9 +710,8 @@ namespace Avalonia.Controls
Debug.Assert(ItemContainerGenerator is not null); Debug.Assert(ItemContainerGenerator is not null);
var recycleKey = element.GetValue(RecycleKeyProperty); var recycleKey = element.GetValue(RecycleKeyProperty);
Debug.Assert(recycleKey is not null);
if (recycleKey is null || recycleKey == s_itemIsItsOwnContainer)
if (recycleKey == s_itemIsItsOwnContainer)
{ {
RemoveInternalChild(element); RemoveInternalChild(element);
} }

4
src/Avalonia.Controls/WindowBase.cs

@ -234,7 +234,7 @@ namespace Avalonia.Controls
if (this is IFocusScope scope) if (this is IFocusScope scope)
{ {
FocusManager.Instance?.RemoveFocusScope(scope); ((FocusManager?)FocusManager)?.RemoveFocusScope(scope);
} }
base.HandleClosed(); base.HandleClosed();
@ -326,7 +326,7 @@ namespace Avalonia.Controls
if (scope != null) if (scope != null)
{ {
FocusManager.Instance?.SetFocusScope(scope); ((FocusManager?)FocusManager)?.SetFocusScope(scope);
} }
IsActive = true; IsActive = true;

8
src/Avalonia.Diagnostics/Diagnostics/DevTools.cs

@ -86,7 +86,7 @@ namespace Avalonia.Diagnostics
private static IDisposable Open(IDevToolsTopLevelGroup topLevelGroup, DevToolsOptions options, private static IDisposable Open(IDevToolsTopLevelGroup topLevelGroup, DevToolsOptions options,
Window? owner, Application? app) Window? owner, Application? app)
{ {
var focussedControl = KeyboardDevice.Instance?.FocusedElement as Control; var focusedControl = owner?.FocusManager?.GetFocusedElement() as Control;
AvaloniaObject root = topLevelGroup switch AvaloniaObject root = topLevelGroup switch
{ {
ClassicDesktopStyleApplicationLifetimeTopLevelGroup gr => new Controls.Application(gr, app ?? Application.Current!), ClassicDesktopStyleApplicationLifetimeTopLevelGroup gr => new Controls.Application(gr, app ?? Application.Current!),
@ -98,7 +98,7 @@ namespace Avalonia.Diagnostics
if (s_open.TryGetValue(topLevelGroup, out var mainWindow)) if (s_open.TryGetValue(topLevelGroup, out var mainWindow))
{ {
mainWindow.Activate(); mainWindow.Activate();
mainWindow.SelectedControl(focussedControl); mainWindow.SelectedControl(focusedControl);
return Disposable.Empty; return Disposable.Empty;
} }
if (topLevelGroup.Items.Count == 1 && topLevelGroup.Items is not INotifyCollectionChanged) if (topLevelGroup.Items.Count == 1 && topLevelGroup.Items is not INotifyCollectionChanged)
@ -110,7 +110,7 @@ namespace Avalonia.Diagnostics
if (group.Key.Items.Contains(singleTopLevel)) if (group.Key.Items.Contains(singleTopLevel))
{ {
group.Value.Activate(); group.Value.Activate();
group.Value.SelectedControl(focussedControl); group.Value.SelectedControl(focusedControl);
return Disposable.Empty; return Disposable.Empty;
} }
} }
@ -124,7 +124,7 @@ namespace Avalonia.Diagnostics
Tag = topLevelGroup Tag = topLevelGroup
}; };
window.SetOptions(options); window.SetOptions(options);
window.SelectedControl(focussedControl); window.SelectedControl(focusedControl);
window.Closed += DevToolsClosed; window.Closed += DevToolsClosed;
s_open.Add(topLevelGroup, window); s_open.Add(topLevelGroup, window);
if (options.ShowAsChildWindow && owner is not null) if (options.ShowAsChildWindow && owner is not null)

5
src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml

@ -33,11 +33,12 @@
VerticalSnapPointsType="{TemplateBinding VerticalSnapPointsType}" VerticalSnapPointsType="{TemplateBinding VerticalSnapPointsType}"
HorizontalSnapPointsAlignment="{TemplateBinding HorizontalSnapPointsAlignment}" HorizontalSnapPointsAlignment="{TemplateBinding HorizontalSnapPointsAlignment}"
VerticalSnapPointsAlignment="{TemplateBinding VerticalSnapPointsAlignment}" VerticalSnapPointsAlignment="{TemplateBinding VerticalSnapPointsAlignment}"
Padding="{TemplateBinding Padding}"> Padding="{TemplateBinding Padding}"
ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}">
<ScrollContentPresenter.GestureRecognizers> <ScrollContentPresenter.GestureRecognizers>
<ScrollGestureRecognizer CanHorizontallyScroll="{Binding CanHorizontallyScroll, ElementName=PART_ContentPresenter}" <ScrollGestureRecognizer CanHorizontallyScroll="{Binding CanHorizontallyScroll, ElementName=PART_ContentPresenter}"
CanVerticallyScroll="{Binding CanVerticallyScroll, ElementName=PART_ContentPresenter}" CanVerticallyScroll="{Binding CanVerticallyScroll, ElementName=PART_ContentPresenter}"
IsScrollInertiaEnabled="{Binding IsScrollInertiaEnabled, RelativeSource={RelativeSource TemplatedParent}}" /> IsScrollInertiaEnabled="{Binding (ScrollViewer.IsScrollInertiaEnabled), ElementName=PART_ContentPresenter}"/>
</ScrollContentPresenter.GestureRecognizers> </ScrollContentPresenter.GestureRecognizers>
</ScrollContentPresenter> </ScrollContentPresenter>
<ScrollBar Name="PART_HorizontalScrollBar" <ScrollBar Name="PART_HorizontalScrollBar"

5
src/Avalonia.Themes.Simple/Controls/ScrollViewer.xaml

@ -14,11 +14,12 @@
VerticalSnapPointsType="{TemplateBinding VerticalSnapPointsType}" VerticalSnapPointsType="{TemplateBinding VerticalSnapPointsType}"
HorizontalSnapPointsAlignment="{TemplateBinding HorizontalSnapPointsAlignment}" HorizontalSnapPointsAlignment="{TemplateBinding HorizontalSnapPointsAlignment}"
VerticalSnapPointsAlignment="{TemplateBinding VerticalSnapPointsAlignment}" VerticalSnapPointsAlignment="{TemplateBinding VerticalSnapPointsAlignment}"
Background="{TemplateBinding Background}"> Background="{TemplateBinding Background}"
ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}">
<ScrollContentPresenter.GestureRecognizers> <ScrollContentPresenter.GestureRecognizers>
<ScrollGestureRecognizer CanHorizontallyScroll="{Binding CanHorizontallyScroll, ElementName=PART_ContentPresenter}" <ScrollGestureRecognizer CanHorizontallyScroll="{Binding CanHorizontallyScroll, ElementName=PART_ContentPresenter}"
CanVerticallyScroll="{Binding CanVerticallyScroll, ElementName=PART_ContentPresenter}" CanVerticallyScroll="{Binding CanVerticallyScroll, ElementName=PART_ContentPresenter}"
IsScrollInertiaEnabled="{Binding IsScrollInertiaEnabled, RelativeSource={RelativeSource TemplatedParent}}" /> IsScrollInertiaEnabled="{Binding (ScrollViewer.IsScrollInertiaEnabled), ElementName=PART_ContentPresenter}"/>
</ScrollContentPresenter.GestureRecognizers> </ScrollContentPresenter.GestureRecognizers>
</ScrollContentPresenter> </ScrollContentPresenter>
<ScrollBar Name="PART_HorizontalScrollBar" <ScrollBar Name="PART_HorizontalScrollBar"

4
src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs

@ -40,11 +40,11 @@ namespace Avalonia.Headless.Vnc
{ {
Window?.MouseMove(pt); Window?.MouseMove(pt);
foreach (var btn in CheckedButtons) foreach (var btn in CheckedButtons)
if (_previousButtons.HasAllFlags(btn) && !buttons.HasAllFlags(btn)) if (_previousButtons.HasFlag(btn) && !buttons.HasFlag(btn))
Window?.MouseUp(pt, TranslateButton(btn), modifiers); Window?.MouseUp(pt, TranslateButton(btn), modifiers);
foreach (var btn in CheckedButtons) foreach (var btn in CheckedButtons)
if (!_previousButtons.HasAllFlags(btn) && buttons.HasAllFlags(btn)) if (!_previousButtons.HasFlag(btn) && buttons.HasFlag(btn))
Window?.MouseDown(pt, TranslateButton(btn), modifiers); Window?.MouseDown(pt, TranslateButton(btn), modifiers);
_previousButtons = buttons; _previousButtons = buttons;
}, DispatcherPriority.Input); }, DispatcherPriority.Input);

2
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -107,7 +107,7 @@ namespace Avalonia.LinuxFramebuffer
if (_topLevel is IFocusScope scope) if (_topLevel is IFocusScope scope)
{ {
FocusManager.Instance?.SetFocusScope(scope); ((FocusManager)_topLevel.FocusManager).SetFocusScope(scope);
} }
} }

6
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@ -154,7 +154,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
var namespaces = _nsInfo.XmlNamespaces; var namespaces = _nsInfo.XmlNamespaces;
if (!namespaces.TryGetValue(ns, out var lst)) if (!namespaces.TryGetValue(ns, out var lst))
throw new ArgumentException("Unable to resolve namespace for type " + qualifiedTypeName); 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 asm = Assembly.Load(new AssemblyName(entry.ClrAssemblyName));
var resolved = asm.GetType(entry.ClrNamespace + "." + name); var resolved = asm.GetType(entry.ClrNamespace + "." + name);
@ -164,7 +165,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
throw new ArgumentException( throw new ArgumentException(
$"Unable to resolve type {qualifiedTypeName} from any of the following locations: " + $"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" };
} }
} }

33
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -7,7 +7,6 @@ using Avalonia.Media;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.Utilities; using Avalonia.Rendering.Utilities;
using Avalonia.Utilities; using Avalonia.Utilities;
using Avalonia.Media.Imaging;
using SkiaSharp; using SkiaSharp;
using ISceneBrush = Avalonia.Media.ISceneBrush; using ISceneBrush = Avalonia.Media.ISceneBrush;
@ -26,7 +25,7 @@ namespace Avalonia.Skia
private readonly Stack<double> _opacityStack = new(); private readonly Stack<double> _opacityStack = new();
private readonly Matrix? _postTransform; private readonly Matrix? _postTransform;
private double _currentOpacity = 1.0f; private double _currentOpacity = 1.0f;
private readonly bool _canTextUseLcdRendering; private readonly bool _disableSubpixelTextRendering;
private Matrix _currentTransform; private Matrix _currentTransform;
private bool _disposed; private bool _disposed;
private GRContext? _grContext; private GRContext? _grContext;
@ -59,11 +58,11 @@ namespace Avalonia.Skia
/// Dpi of drawings. /// Dpi of drawings.
/// </summary> /// </summary>
public Vector Dpi; public Vector Dpi;
/// <summary> /// <summary>
/// Render text without Lcd rendering. /// Render text without subpixel antialiasing.
/// </summary> /// </summary>
public bool DisableTextLcdRendering; public bool DisableSubpixelTextRendering;
/// <summary> /// <summary>
/// GPU-accelerated context (optional) /// GPU-accelerated context (optional)
@ -135,7 +134,7 @@ namespace Avalonia.Skia
_dpi = createInfo.Dpi; _dpi = createInfo.Dpi;
_disposables = disposables; _disposables = disposables;
_canTextUseLcdRendering = !createInfo.DisableTextLcdRendering; _disableSubpixelTextRendering = createInfo.DisableSubpixelTextRendering;
_grContext = createInfo.GrContext; _grContext = createInfo.GrContext;
_gpu = createInfo.Gpu; _gpu = createInfo.Gpu;
if (_grContext != null) if (_grContext != null)
@ -519,7 +518,23 @@ namespace Avalonia.Skia
{ {
var glyphRunImpl = (GlyphRunImpl)glyphRun; var glyphRunImpl = (GlyphRunImpl)glyphRun;
var textBlob = glyphRunImpl.GetTextBlob(RenderOptions); var textRenderOptions = RenderOptions;
if (_disableSubpixelTextRendering)
{
switch (textRenderOptions.TextRenderingMode)
{
case TextRenderingMode.Unspecified
when textRenderOptions.EdgeMode == EdgeMode.Antialias || textRenderOptions.EdgeMode == EdgeMode.Unspecified:
case TextRenderingMode.SubpixelAntialias:
{
textRenderOptions = textRenderOptions with { TextRenderingMode = TextRenderingMode.Antialias };
break;
}
}
}
var textBlob = glyphRunImpl.GetTextBlob(textRenderOptions);
Canvas.DrawText(textBlob, (float)glyphRun.BaselineOrigin.X, Canvas.DrawText(textBlob, (float)glyphRun.BaselineOrigin.X,
(float)glyphRun.BaselineOrigin.Y, paintWrapper.Paint); (float)glyphRun.BaselineOrigin.Y, paintWrapper.Paint);
@ -969,6 +984,7 @@ namespace Avalonia.Skia
using (var ctx = intermediate.CreateDrawingContext()) using (var ctx = intermediate.CreateDrawingContext())
{ {
ctx.RenderOptions = RenderOptions;
ctx.Clear(Colors.Transparent); ctx.Clear(Colors.Transparent);
content.Render(ctx, rect.TopLeft == default ? null : Matrix.CreateTranslation(-rect.X, -rect.Y)); content.Render(ctx, rect.TopLeft == default ? null : Matrix.CreateTranslation(-rect.X, -rect.Y));
} }
@ -997,6 +1013,7 @@ namespace Avalonia.Skia
using var pictureTarget = new PictureRenderTarget(_gpu, _grContext, _dpi); using var pictureTarget = new PictureRenderTarget(_gpu, _grContext, _dpi);
using (var ctx = pictureTarget.CreateDrawingContext(calc.IntermediateSize)) using (var ctx = pictureTarget.CreateDrawingContext(calc.IntermediateSize))
{ {
ctx.RenderOptions = RenderOptions;
ctx.PushClip(calc.IntermediateClip); ctx.PushClip(calc.IntermediateClip);
content.Render(ctx, transform); content.Render(ctx, transform);
ctx.PopClip(); ctx.PopClip();
@ -1283,7 +1300,7 @@ namespace Avalonia.Skia
Height = pixelSize.Height, Height = pixelSize.Height,
Dpi = _dpi, Dpi = _dpi,
Format = format, Format = format,
DisableTextLcdRendering = !_canTextUseLcdRendering, DisableTextLcdRendering = isLayer ? _disableSubpixelTextRendering : true,
GrContext = _grContext, GrContext = _grContext,
Gpu = _gpu, Gpu = _gpu,
Session = _session, Session = _session,

4
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Controls.Platform.Surfaces; using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering;
using SkiaSharp; using SkiaSharp;
namespace Avalonia.Skia namespace Avalonia.Skia
@ -54,8 +53,7 @@ namespace Avalonia.Skia
var createInfo = new DrawingContextImpl.CreateInfo var createInfo = new DrawingContextImpl.CreateInfo
{ {
Surface = _framebufferSurface, Surface = _framebufferSurface,
Dpi = framebuffer.Dpi, Dpi = framebuffer.Dpi
DisableTextLcdRendering = true
}; };
return new DrawingContextImpl(createInfo, _preFramebufferCopyHandler, canvas, framebuffer); return new DrawingContextImpl(createInfo, _preFramebufferCopyHandler, canvas, framebuffer);

1
src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs

@ -30,7 +30,6 @@ namespace Avalonia.Skia
GrContext = session.GrContext, GrContext = session.GrContext,
Surface = session.SkSurface, Surface = session.SkSurface,
Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor, Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor,
DisableTextLcdRendering = true,
Gpu = _skiaGpu, Gpu = _skiaGpu,
CurrentSession = session CurrentSession = session
}; };

2
src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs

@ -20,7 +20,7 @@ namespace Avalonia.Skia.Helpers
{ {
Canvas = canvas, Canvas = canvas,
Dpi = dpi, Dpi = dpi,
DisableTextLcdRendering = true, DisableSubpixelTextRendering = true,
}; };
return new DrawingContextImpl(createInfo); return new DrawingContextImpl(createInfo);

4
src/Skia/Avalonia.Skia/PictureRenderTarget.cs

@ -39,7 +39,7 @@ internal class PictureRenderTarget : IDisposable
{ {
Canvas = canvas, Canvas = canvas,
Dpi = _dpi, Dpi = _dpi,
DisableTextLcdRendering = true, DisableSubpixelTextRendering = true,
GrContext = _grContext, GrContext = _grContext,
Gpu = _gpu, Gpu = _gpu,
}; };
@ -52,4 +52,4 @@ internal class PictureRenderTarget : IDisposable
} }
public void Dispose() => _picture?.Dispose(); public void Dispose() => _picture?.Dispose();
} }

2
src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs

@ -106,7 +106,7 @@ namespace Avalonia.Skia
{ {
Surface = _surface.Surface, Surface = _surface.Surface,
Dpi = Dpi, Dpi = Dpi,
DisableTextLcdRendering = _disableLcdRendering, DisableSubpixelTextRendering = _disableLcdRendering,
GrContext = _grContext, GrContext = _grContext,
Gpu = _gpu, Gpu = _gpu,
}; };

2
src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs

@ -22,7 +22,7 @@ namespace Avalonia.Win32.Embedding
UnmanagedMethods.SetParent(WindowHandle, Handle); UnmanagedMethods.SetParent(WindowHandle, Handle);
_root.Prepare(); _root.Prepare();
if (_root.IsFocused) if (_root.IsFocused)
FocusManager.Instance.Focus(null); _root.FocusManager.ClearFocus();
_root.GotFocus += RootGotFocus; _root.GotFocus += RootGotFocus;
FixPosition(); FixPosition();

8
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@ -141,13 +141,13 @@ namespace Avalonia.Win32.Interop.Wpf
{ {
var state = Keyboard.Modifiers; var state = Keyboard.Modifiers;
var rv = default(RawInputModifiers); var rv = default(RawInputModifiers);
if (state.HasAllFlags(ModifierKeys.Windows)) if (state.HasFlag(ModifierKeys.Windows))
rv |= RawInputModifiers.Meta; rv |= RawInputModifiers.Meta;
if (state.HasAllFlags(ModifierKeys.Alt)) if (state.HasFlag(ModifierKeys.Alt))
rv |= RawInputModifiers.Alt; rv |= RawInputModifiers.Alt;
if (state.HasAllFlags(ModifierKeys.Control)) if (state.HasFlag(ModifierKeys.Control))
rv |= RawInputModifiers.Control; rv |= RawInputModifiers.Control;
if (state.HasAllFlags(ModifierKeys.Shift)) if (state.HasFlag(ModifierKeys.Shift))
rv |= RawInputModifiers.Shift; rv |= RawInputModifiers.Shift;
if (e != null) if (e != null)
{ {

2
src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs

@ -5,7 +5,7 @@ using Avalonia.Win32.Interop;
namespace Avalonia.Win32.Input namespace Avalonia.Win32.Input
{ {
class WindowsKeyboardDevice : KeyboardDevice internal class WindowsKeyboardDevice : KeyboardDevice
{ {
private readonly byte[] _keyStates = new byte[256]; private readonly byte[] _keyStates = new byte[256];

58
tests/Avalonia.Base.UnitTests/Input/InputElement_Focus.cs

@ -23,7 +23,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus(); 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} Child = target = new Button() { IsVisible = false}
}; };
Assert.Null(FocusManager.Instance.Current); Assert.Null(root.FocusManager.GetFocusedElement());
target.Focus(); target.Focus();
Assert.False(target.IsFocused); Assert.False(target.IsFocused);
Assert.False(target.IsKeyboardFocusWithin); 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(); target.Focus();
Assert.False(target.IsFocused); Assert.False(target.IsFocused);
Assert.False(target.IsKeyboardFocusWithin); 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(); first.Focus();
Assert.Same(first, FocusManager.Instance.Current); Assert.Same(first, root.FocusManager.GetFocusedElement());
second.Focus(); 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 } Child = target = new Button() { IsEnabled = false }
}; };
Assert.Null(FocusManager.Instance.Current); Assert.Null(root.FocusManager.GetFocusedElement());
target.Focus(); target.Focus();
Assert.False(target.IsFocused); Assert.False(target.IsFocused);
Assert.False(target.IsKeyboardFocusWithin); 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(); target.Focus();
Assert.False(target.IsFocused); Assert.False(target.IsFocused);
Assert.False(target.IsKeyboardFocusWithin); 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.Focus();
target.IsVisible = false; target.IsVisible = false;
Assert.Null(FocusManager.Instance.Current); Assert.Null(root.FocusManager.GetFocusedElement());
} }
} }
@ -224,7 +224,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus(); target.Focus();
container.IsVisible = false; 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.Focus();
target.IsEnabled = false; target.IsEnabled = false;
Assert.Null(FocusManager.Instance.Current); Assert.Null(root.FocusManager.GetFocusedElement());
} }
} }
@ -266,7 +266,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus(); target.Focus();
container.IsEnabled = false; container.IsEnabled = false;
Assert.Null(FocusManager.Instance.Current); Assert.Null(root.FocusManager.GetFocusedElement());
} }
} }
@ -285,7 +285,7 @@ namespace Avalonia.Base.UnitTests.Input
target.Focus(); target.Focus();
root.Child = null; root.Child = null;
Assert.Null(FocusManager.Instance.Current); Assert.Null(root.FocusManager.GetFocusedElement());
} }
} }
@ -312,13 +312,13 @@ namespace Avalonia.Base.UnitTests.Input
target2.ApplyTemplate(); target2.ApplyTemplate();
FocusManager.Instance?.Focus(target1); target1.Focus();
Assert.True(target1.IsFocused); Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus")); Assert.True(target1.Classes.Contains(":focus"));
Assert.False(target2.IsFocused); Assert.False(target2.IsFocused);
Assert.False(target2.Classes.Contains(":focus")); Assert.False(target2.Classes.Contains(":focus"));
FocusManager.Instance?.Focus(target2, NavigationMethod.Tab); target2.Focus(NavigationMethod.Tab);
Assert.False(target1.IsFocused); Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus")); Assert.False(target1.Classes.Contains(":focus"));
Assert.True(target2.IsFocused); Assert.True(target2.IsFocused);
@ -348,19 +348,19 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate(); target1.ApplyTemplate();
target2.ApplyTemplate(); target2.ApplyTemplate();
FocusManager.Instance?.Focus(target1); target1.Focus();
Assert.True(target1.IsFocused); Assert.True(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-visible")); Assert.False(target1.Classes.Contains(":focus-visible"));
Assert.False(target2.IsFocused); Assert.False(target2.IsFocused);
Assert.False(target2.Classes.Contains(":focus-visible")); Assert.False(target2.Classes.Contains(":focus-visible"));
FocusManager.Instance?.Focus(target2, NavigationMethod.Tab); target2.Focus(NavigationMethod.Tab);
Assert.False(target1.IsFocused); Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-visible")); Assert.False(target1.Classes.Contains(":focus-visible"));
Assert.True(target2.IsFocused); Assert.True(target2.IsFocused);
Assert.True(target2.Classes.Contains(":focus-visible")); Assert.True(target2.Classes.Contains(":focus-visible"));
FocusManager.Instance?.Focus(target1, NavigationMethod.Directional); target1.Focus(NavigationMethod.Directional);
Assert.True(target1.IsFocused); Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-visible")); Assert.True(target1.Classes.Contains(":focus-visible"));
Assert.False(target2.IsFocused); Assert.False(target2.IsFocused);
@ -390,7 +390,7 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate(); target1.ApplyTemplate();
target2.ApplyTemplate(); target2.ApplyTemplate();
FocusManager.Instance?.Focus(target1); target1.Focus();
Assert.True(target1.IsFocused); Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within")); Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin); Assert.True(target1.IsKeyboardFocusWithin);
@ -425,7 +425,7 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate(); target1.ApplyTemplate();
target2.ApplyTemplate(); target2.ApplyTemplate();
FocusManager.Instance?.Focus(target1); target1.Focus();
Assert.True(target1.IsFocused); Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within")); Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin); Assert.True(target1.IsKeyboardFocusWithin);
@ -436,7 +436,7 @@ namespace Avalonia.Base.UnitTests.Input
Assert.True(root.Classes.Contains(":focus-within")); Assert.True(root.Classes.Contains(":focus-within"));
Assert.True(root.IsKeyboardFocusWithin); Assert.True(root.IsKeyboardFocusWithin);
FocusManager.Instance?.Focus(target2); target2.Focus();
Assert.False(target1.IsFocused); Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-within")); Assert.False(target1.Classes.Contains(":focus-within"));
@ -478,7 +478,7 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate(); target1.ApplyTemplate();
target2.ApplyTemplate(); target2.ApplyTemplate();
FocusManager.Instance?.Focus(target1); target1.Focus();
Assert.True(target1.IsFocused); Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within")); Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin); Assert.True(target1.IsKeyboardFocusWithin);
@ -534,7 +534,7 @@ namespace Avalonia.Base.UnitTests.Input
target1.ApplyTemplate(); target1.ApplyTemplate();
target2.ApplyTemplate(); target2.ApplyTemplate();
FocusManager.Instance?.Focus(target1); target1.Focus();
Assert.True(target1.IsFocused); Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within")); Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin); Assert.True(target1.IsKeyboardFocusWithin);
@ -545,7 +545,7 @@ namespace Avalonia.Base.UnitTests.Input
Assert.Equal(KeyboardDevice.Instance.FocusedElement, target1); Assert.Equal(KeyboardDevice.Instance.FocusedElement, target1);
FocusManager.Instance?.Focus(target2); target2.Focus();
Assert.False(target1.IsFocused); Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-within")); Assert.False(target1.Classes.Contains(":focus-within"));
@ -578,9 +578,9 @@ namespace Avalonia.Base.UnitTests.Input
}; };
target.Focus(); target.Focus();
FocusManager.Instance.Focus(null); root.FocusManager.ClearFocus();
Assert.Null(FocusManager.Instance.Current); Assert.Null(root.FocusManager.GetFocusedElement());
} }
} }
} }

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save