diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 8ac2fb28ed..8f1b39ae12 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -5,7 +5,7 @@ - + all diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs b/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs index 7fb5bec589..aef82768c8 100644 --- a/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Linq; using System.Reflection; using Avalonia.Controls; @@ -23,7 +22,7 @@ namespace ControlCatalog.Pages $"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link); 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 factory, DragDropEffects effects) @@ -99,7 +98,7 @@ namespace ControlCatalog.Pages { 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}"; } else if (item is IStorageFolder folder) diff --git a/samples/GpuInterop/VulkanDemo/VulkanContext.cs b/samples/GpuInterop/VulkanDemo/VulkanContext.cs index 1d44549089..a810a4b9f7 100644 --- a/samples/GpuInterop/VulkanDemo/VulkanContext.cs +++ b/samples/GpuInterop/VulkanDemo/VulkanContext.cs @@ -174,7 +174,7 @@ public unsafe class VulkanContext : IDisposable for (uint queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount; queueFamilyIndex++) { var family = familyProperties[queueFamilyIndex]; - if (!family.QueueFlags.HasAllFlags(QueueFlags.GraphicsBit)) + if (!family.QueueFlags.HasFlag(QueueFlags.GraphicsBit)) continue; diff --git a/samples/IntegrationTestApp/MainWindow.axaml b/samples/IntegrationTestApp/MainWindow.axaml index d8d9678a2d..1c88f2c95f 100644 --- a/samples/IntegrationTestApp/MainWindow.axaml +++ b/samples/IntegrationTestApp/MainWindow.axaml @@ -47,6 +47,9 @@ + diff --git a/samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs b/samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs index 98ab91b0a6..748654594b 100644 --- a/samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs +++ b/samples/VirtualizationDemo/ViewModels/PlaygroundPageViewModel.cs @@ -24,19 +24,19 @@ public class PlaygroundPageViewModel : ViewModelBase public bool Multiple { - get => _selectionMode.HasAnyFlag(SelectionMode.Multiple); + get => _selectionMode.HasFlag(SelectionMode.Multiple); set => SetSelectionMode(SelectionMode.Multiple, value); } public bool Toggle { - get => _selectionMode.HasAnyFlag(SelectionMode.Toggle); + get => _selectionMode.HasFlag(SelectionMode.Toggle); set => SetSelectionMode(SelectionMode.Toggle, value); } public bool AlwaysSelected { - get => _selectionMode.HasAnyFlag(SelectionMode.AlwaysSelected); + get => _selectionMode.HasFlag(SelectionMode.AlwaysSelected); set => SetSelectionMode(SelectionMode.AlwaysSelected, value); } diff --git a/src/Avalonia.Base/Animation/Animatable.cs b/src/Avalonia.Base/Animation/Animatable.cs index 5208c8b218..98d4a71415 100644 --- a/src/Avalonia.Base/Animation/Animatable.cs +++ b/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 /// when a control is added to the visual tree. /// - protected void EnableTransitions() + internal void EnableTransitions() { 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 /// when a control is removed from the visual tree. /// - protected void DisableTransitions() + internal void DisableTransitions() { if (_transitionsEnabled) { diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index eafff3b780..ddc2b7effb 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -63,6 +63,7 @@ + diff --git a/src/Avalonia.Base/AvaloniaPropertyExtensions.cs b/src/Avalonia.Base/AvaloniaPropertyExtensions.cs index 6aaa224b00..2c005637dc 100644 --- a/src/Avalonia.Base/AvaloniaPropertyExtensions.cs +++ b/src/Avalonia.Base/AvaloniaPropertyExtensions.cs @@ -7,7 +7,7 @@ namespace Avalonia /// /// Extensions for . /// - public static class AvaloniaPropertyExtensions + internal static class AvaloniaPropertyExtensions { /// /// Checks if values of given property can affect rendering (via ). diff --git a/src/Avalonia.Base/Collections/Pooled/ClearMode.cs b/src/Avalonia.Base/Collections/Pooled/ClearMode.cs index d78ac8feab..518542bf07 100644 --- a/src/Avalonia.Base/Collections/Pooled/ClearMode.cs +++ b/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 /// of Auto. /// - public enum ClearMode + internal enum ClearMode { /// /// Auto has different behavior depending on the host project's target framework. diff --git a/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs b/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs index 7a233a62ab..88f022f114 100644 --- a/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs +++ b/src/Avalonia.Base/Collections/Pooled/IReadOnlyPooledList.cs @@ -11,7 +11,7 @@ namespace Avalonia.Collections.Pooled /// /// The type of elements in the read-only pooled list. - public interface IReadOnlyPooledList : IReadOnlyList + internal interface IReadOnlyPooledList : IReadOnlyList { #pragma warning disable CS0419 /// diff --git a/src/Avalonia.Base/Collections/Pooled/PooledList.cs b/src/Avalonia.Base/Collections/Pooled/PooledList.cs index 150fe5f7a9..73e1b28798 100644 --- a/src/Avalonia.Base/Collections/Pooled/PooledList.cs +++ b/src/Avalonia.Base/Collections/Pooled/PooledList.cs @@ -29,7 +29,7 @@ namespace Avalonia.Collections.Pooled [DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(ICollectionDebugView<>))] [Serializable] - public class PooledList : IList, IReadOnlyPooledList, IList, IDisposable, IDeserializationCallback + internal class PooledList : IList, IReadOnlyPooledList, IList, IDisposable, IDeserializationCallback { // internal constant copied from Array.MaxArrayLength private const int MaxArrayLength = 0x7FEFFFFF; diff --git a/src/Avalonia.Base/Collections/Pooled/PooledStack.cs b/src/Avalonia.Base/Collections/Pooled/PooledStack.cs index 3d29c43051..7a8a6ff67b 100644 --- a/src/Avalonia.Base/Collections/Pooled/PooledStack.cs +++ b/src/Avalonia.Base/Collections/Pooled/PooledStack.cs @@ -29,7 +29,7 @@ namespace Avalonia.Collections.Pooled [DebuggerTypeProxy(typeof(StackDebugView<>))] [DebuggerDisplay("Count = {Count}")] [Serializable] - public class PooledStack : IEnumerable, ICollection, IReadOnlyCollection, IDisposable, IDeserializationCallback + internal class PooledStack : IEnumerable, ICollection, IReadOnlyCollection, IDisposable, IDeserializationCallback { [NonSerialized] private ArrayPool _pool; diff --git a/src/Avalonia.Base/Controls/ChildNameScope.cs b/src/Avalonia.Base/Controls/ChildNameScope.cs index 1ceaff924c..eeca120973 100644 --- a/src/Avalonia.Base/Controls/ChildNameScope.cs +++ b/src/Avalonia.Base/Controls/ChildNameScope.cs @@ -3,7 +3,7 @@ using Avalonia.Utilities; namespace Avalonia.Controls { - public class ChildNameScope : INameScope + internal class ChildNameScope : INameScope { private readonly INameScope _parentScope; private readonly NameScope _inner = new NameScope(); diff --git a/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs b/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs index da6f407d81..14659fe01c 100644 --- a/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs +++ b/src/Avalonia.Base/Data/Core/CommonPropertyNames.cs @@ -1,6 +1,6 @@ namespace Avalonia.Data.Core { - public static class CommonPropertyNames + internal static class CommonPropertyNames { public const string IndexerName = "Item"; } diff --git a/src/Avalonia.Base/Data/IndexerBinding.cs b/src/Avalonia.Base/Data/IndexerBinding.cs index 83ef8f76b4..a1b9e8b151 100644 --- a/src/Avalonia.Base/Data/IndexerBinding.cs +++ b/src/Avalonia.Base/Data/IndexerBinding.cs @@ -2,7 +2,7 @@ namespace Avalonia.Data { - public class IndexerBinding : IBinding + internal class IndexerBinding : IBinding { public IndexerBinding( AvaloniaObject source, diff --git a/src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs b/src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs index 75c1e3432b..671bbfbdfc 100644 --- a/src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs +++ b/src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs @@ -5,7 +5,7 @@ namespace Avalonia.Diagnostics /// /// Provides a debug interface into . /// - public interface IAvaloniaObjectDebug + internal interface IAvaloniaObjectDebug { /// /// Gets the subscriber list for the diff --git a/src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs b/src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs index 00f2949b08..f43e17325b 100644 --- a/src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs +++ b/src/Avalonia.Base/Diagnostics/INotifyCollectionChangedDebug.cs @@ -8,7 +8,7 @@ namespace Avalonia.Diagnostics /// Provides a debug interface into subscribers on /// /// - public interface INotifyCollectionChangedDebug + internal interface INotifyCollectionChangedDebug { /// /// Gets the subscriber list for the diff --git a/src/Avalonia.Base/EnumExtensions.cs b/src/Avalonia.Base/EnumExtensions.cs index 9b74266b09..c2b29c6620 100644 --- a/src/Avalonia.Base/EnumExtensions.cs +++ b/src/Avalonia.Base/EnumExtensions.cs @@ -6,7 +6,7 @@ namespace Avalonia /// /// Provides extension methods for enums. /// - public static class EnumExtensions + internal static class EnumExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Avalonia.Base/Input/AccessKeyHandler.cs b/src/Avalonia.Base/Input/AccessKeyHandler.cs index 23d1f51730..2b8786089f 100644 --- a/src/Avalonia.Base/Input/AccessKeyHandler.cs +++ b/src/Avalonia.Base/Input/AccessKeyHandler.cs @@ -9,7 +9,7 @@ namespace Avalonia.Input /// /// Handles access keys for a window. /// - public class AccessKeyHandler : IAccessKeyHandler + internal class AccessKeyHandler : IAccessKeyHandler { /// /// Defines the AccessKeyPressed attached event. diff --git a/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs index e96c80da14..7a83501ce4 100644 --- a/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs +++ b/src/Avalonia.Base/Input/KeyboardNavigationHandler.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using Avalonia.Input.Navigation; +using Avalonia.Metadata; using Avalonia.VisualTree; namespace Avalonia.Input @@ -8,6 +9,7 @@ namespace Avalonia.Input /// /// Handles keyboard navigation for a window. /// + [Unstable] public class KeyboardNavigationHandler : IKeyboardNavigationHandler { /// @@ -75,13 +77,16 @@ namespace Avalonia.Input /// The direction to move. /// Any key modifiers active at the time of focus. public void Move( - IInputElement element, + IInputElement? element, NavigationDirection direction, 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) { @@ -99,10 +104,9 @@ namespace Avalonia.Input /// The event args. protected virtual void OnKeyDown(object? sender, KeyEventArgs e) { - var current = FocusManager.GetFocusManager(e.Source as IInputElement)?.GetFocusedElement(); - - if (current != null && e.Key == Key.Tab) + if (e.Key == Key.Tab) { + var current = FocusManager.GetFocusManager(e.Source as IInputElement)?.GetFocusedElement(); var direction = (e.KeyModifiers & KeyModifiers.Shift) == 0 ? NavigationDirection.Next : NavigationDirection.Previous; Move(current, direction, e.KeyModifiers); diff --git a/src/Avalonia.Base/Interactivity/Interactive.cs b/src/Avalonia.Base/Interactivity/Interactive.cs index 821e00d784..0dfaae0fc1 100644 --- a/src/Avalonia.Base/Interactivity/Interactive.cs +++ b/src/Avalonia.Base/Interactivity/Interactive.cs @@ -17,7 +17,7 @@ namespace Avalonia.Interactivity /// /// Gets the interactive parent of the object for bubbling and tunneling events. /// - protected internal virtual Interactive? InteractiveParent => VisualParent as Interactive; + internal virtual Interactive? InteractiveParent => VisualParent as Interactive; /// /// Adds a handler for the specified routed event. diff --git a/src/Avalonia.Base/Logging/TraceLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs index a1b4dfe3aa..1fee35bf21 100644 --- a/src/Avalonia.Base/Logging/TraceLogSink.cs +++ b/src/Avalonia.Base/Logging/TraceLogSink.cs @@ -6,7 +6,7 @@ using Avalonia.Utilities; namespace Avalonia.Logging { - public class TraceLogSink : ILogSink + internal class TraceLogSink : ILogSink { private readonly LogEventLevel _level; private readonly IList? _areas; diff --git a/src/Avalonia.Base/Media/ArcSegment.cs b/src/Avalonia.Base/Media/ArcSegment.cs index b7dbd4925b..ee353b0a89 100644 --- a/src/Avalonia.Base/Media/ArcSegment.cs +++ b/src/Avalonia.Base/Media/ArcSegment.cs @@ -95,7 +95,7 @@ namespace Avalonia.Media set { SetValue(SweepDirectionProperty, value); } } - protected internal override void ApplyTo(StreamGeometryContext ctx) + internal override void ApplyTo(StreamGeometryContext ctx) { ctx.ArcTo(Point, Size, RotationAngle, IsLargeArc, SweepDirection); } diff --git a/src/Avalonia.Base/Media/BezierSegment .cs b/src/Avalonia.Base/Media/BezierSegment .cs index 64ea2924cc..31efe1ec23 100644 --- a/src/Avalonia.Base/Media/BezierSegment .cs +++ b/src/Avalonia.Base/Media/BezierSegment .cs @@ -56,7 +56,7 @@ namespace Avalonia.Media set { SetValue(Point3Property, value); } } - protected internal override void ApplyTo(StreamGeometryContext ctx) + internal override void ApplyTo(StreamGeometryContext ctx) { ctx.CubicBezierTo(Point1, Point2, Point3); } diff --git a/src/Avalonia.Base/Media/Imaging/Bitmap.cs b/src/Avalonia.Base/Media/Imaging/Bitmap.cs index fc31077221..fbe9370edc 100644 --- a/src/Avalonia.Base/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Base/Media/Imaging/Bitmap.cs @@ -173,7 +173,7 @@ namespace Avalonia.Media.Imaging 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) { if ((sourceRect.Width <= 0 || sourceRect.Height <= 0) && (sourceRect.X != 0 || sourceRect.Y != 0)) diff --git a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs b/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs index 2781141f06..df6d14ecf9 100644 --- a/src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs +++ b/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. /// /// The tile mode. - protected internal ImmutableTileBrush( + private protected ImmutableTileBrush( AlignmentX alignmentX, AlignmentY alignmentY, RelativeRect destinationRect, diff --git a/src/Avalonia.Base/Media/LineSegment.cs b/src/Avalonia.Base/Media/LineSegment.cs index 5729ab2c3b..68193bb770 100644 --- a/src/Avalonia.Base/Media/LineSegment.cs +++ b/src/Avalonia.Base/Media/LineSegment.cs @@ -22,7 +22,7 @@ namespace Avalonia.Media set { SetValue(PointProperty, value); } } - protected internal override void ApplyTo(StreamGeometryContext ctx) + internal override void ApplyTo(StreamGeometryContext ctx) { ctx.LineTo(Point); } diff --git a/src/Avalonia.Base/Media/PathSegment.cs b/src/Avalonia.Base/Media/PathSegment.cs index 89a33815ae..0b517e56f3 100644 --- a/src/Avalonia.Base/Media/PathSegment.cs +++ b/src/Avalonia.Base/Media/PathSegment.cs @@ -2,6 +2,6 @@ namespace Avalonia.Media { public abstract class PathSegment : AvaloniaObject { - protected internal abstract void ApplyTo(StreamGeometryContext ctx); + internal abstract void ApplyTo(StreamGeometryContext ctx); } -} \ No newline at end of file +} diff --git a/src/Avalonia.Base/Media/PolyLineSegment.cs b/src/Avalonia.Base/Media/PolyLineSegment.cs index 51bf13d7cb..49c34fea66 100644 --- a/src/Avalonia.Base/Media/PolyLineSegment.cs +++ b/src/Avalonia.Base/Media/PolyLineSegment.cs @@ -44,7 +44,7 @@ namespace Avalonia.Media Points = new Points(points); } - protected internal override void ApplyTo(StreamGeometryContext ctx) + internal override void ApplyTo(StreamGeometryContext ctx) { var points = Points; if (points.Count > 0) diff --git a/src/Avalonia.Base/Media/QuadraticBezierSegment .cs b/src/Avalonia.Base/Media/QuadraticBezierSegment .cs index 9dc24e2a93..01d22f2043 100644 --- a/src/Avalonia.Base/Media/QuadraticBezierSegment .cs +++ b/src/Avalonia.Base/Media/QuadraticBezierSegment .cs @@ -40,7 +40,7 @@ namespace Avalonia.Media set { SetValue(Point2Property, value); } } - protected internal override void ApplyTo(StreamGeometryContext ctx) + internal override void ApplyTo(StreamGeometryContext ctx) { ctx.QuadraticBezierTo(Point1, Point2); } diff --git a/src/Avalonia.Base/Media/StreamGeometryContext.cs b/src/Avalonia.Base/Media/StreamGeometryContext.cs index ed6065eae4..8b51bf3676 100644 --- a/src/Avalonia.Base/Media/StreamGeometryContext.cs +++ b/src/Avalonia.Base/Media/StreamGeometryContext.cs @@ -10,7 +10,6 @@ namespace Avalonia.Media /// of is obtained by calling /// . /// - /// TODO: This class is just a wrapper around IStreamGeometryContextImpl: is it needed? public class StreamGeometryContext : IGeometryContext { private readonly IStreamGeometryContextImpl _impl; diff --git a/src/Avalonia.Base/Media/Transformation/TransformParser.cs b/src/Avalonia.Base/Media/Transformation/TransformParser.cs index 85f4f5fec1..cfbc5b23c4 100644 --- a/src/Avalonia.Base/Media/Transformation/TransformParser.cs +++ b/src/Avalonia.Base/Media/Transformation/TransformParser.cs @@ -4,7 +4,7 @@ using Avalonia.Utilities; namespace Avalonia.Media.Transformation { - public static class TransformParser + internal static class TransformParser { private static readonly (string, TransformFunction)[] s_functionMapping = { diff --git a/src/Avalonia.Base/Platform/Storage/NameCollisionOption.cs b/src/Avalonia.Base/Platform/Storage/NameCollisionOption.cs deleted file mode 100644 index 4a0d0c634f..0000000000 --- a/src/Avalonia.Base/Platform/Storage/NameCollisionOption.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Avalonia.Platform.Storage; - -public class NameCollisionOption -{ - -} diff --git a/src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs b/src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs index 158fecbfc6..0397a32bfd 100644 --- a/src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs +++ b/src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs @@ -4,7 +4,7 @@ using Avalonia.Utilities; namespace Avalonia.Rendering { - public class ManagedDeferredRendererLock : DisposableLock, IDeferredRendererLock + internal class ManagedDeferredRendererLock : DisposableLock, IDeferredRendererLock { } diff --git a/src/Avalonia.Base/Rendering/RendererBase.cs b/src/Avalonia.Base/Rendering/RendererBase.cs deleted file mode 100644 index f0ddbd3626..0000000000 --- a/src/Avalonia.Base/Rendering/RendererBase.cs +++ /dev/null @@ -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); - } - } -} diff --git a/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs b/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs index 6b34b44337..439bda4494 100644 --- a/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs +++ b/src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs @@ -2,7 +2,7 @@ namespace Avalonia.Rendering.Utilities { - public class TileBrushCalculator + internal class TileBrushCalculator { private readonly Size _imageSize; private readonly Rect _drawRect; diff --git a/src/Avalonia.Base/Rendering/ZIndexComparer.cs b/src/Avalonia.Base/Rendering/ZIndexComparer.cs index c9240668b4..8009dc8573 100644 --- a/src/Avalonia.Base/Rendering/ZIndexComparer.cs +++ b/src/Avalonia.Base/Rendering/ZIndexComparer.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; -using Avalonia.VisualTree; namespace Avalonia.Rendering { - public class ZIndexComparer : IComparer + internal class ZIndexComparer : IComparer { public static readonly ZIndexComparer Instance = new ZIndexComparer(); public static readonly Comparison ComparisonInstance = Instance.Compare; diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 1a0b3bcea6..b98e378338 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/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. /// /// The event args. - protected virtual void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) + internal virtual void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) { if (_logicalChildren is object) { diff --git a/src/Avalonia.Base/Utilities/DisposableLock.cs b/src/Avalonia.Base/Utilities/DisposableLock.cs index c6f01b24fa..3202a7610d 100644 --- a/src/Avalonia.Base/Utilities/DisposableLock.cs +++ b/src/Avalonia.Base/Utilities/DisposableLock.cs @@ -3,7 +3,7 @@ using System.Threading; namespace Avalonia.Utilities { - public class DisposableLock + internal class DisposableLock { private readonly object _lock = new object(); diff --git a/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs b/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs index 55fd9a7957..61ce6e7b71 100644 --- a/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs +++ b/src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs @@ -3,7 +3,7 @@ using Avalonia.Threading; namespace Avalonia.Utilities { - public class NonPumpingLockHelper + internal class NonPumpingLockHelper { public interface IHelperImpl { diff --git a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs index 0eb7ae2e31..5edddc1a6d 100644 --- a/src/Avalonia.Base/Utilities/SingleOrDictionary.cs +++ b/src/Avalonia.Base/Utilities/SingleOrDictionary.cs @@ -11,7 +11,7 @@ namespace Avalonia.Utilities /// /// The type of the key. /// The type of the value. - public class SingleOrDictionary : IEnumerable> + internal class SingleOrDictionary : IEnumerable> where TKey : notnull { private KeyValuePair? _singleValue; diff --git a/src/Avalonia.Base/Utilities/SingleOrQueue.cs b/src/Avalonia.Base/Utilities/SingleOrQueue.cs index 99a92f6788..2a608dbbfd 100644 --- a/src/Avalonia.Base/Utilities/SingleOrQueue.cs +++ b/src/Avalonia.Base/Utilities/SingleOrQueue.cs @@ -7,7 +7,7 @@ namespace Avalonia.Utilities /// FIFO Queue optimized for holding zero or one items. /// /// The type of items held in the queue. - public class SingleOrQueue + internal class SingleOrQueue { private T? _head; private Queue? _tail; diff --git a/src/Avalonia.Base/Utilities/ValueSingleOrList.cs b/src/Avalonia.Base/Utilities/ValueSingleOrList.cs index c1e5d912a8..22afa39067 100644 --- a/src/Avalonia.Base/Utilities/ValueSingleOrList.cs +++ b/src/Avalonia.Base/Utilities/ValueSingleOrList.cs @@ -9,7 +9,7 @@ namespace Avalonia.Utilities /// /// Once more than value has been added to this storage it will switch to using internally. /// - public ref struct ValueSingleOrList + internal ref struct ValueSingleOrList { private bool _isSingleSet; diff --git a/src/Avalonia.Base/Utilities/WeakTimer.cs b/src/Avalonia.Base/Utilities/WeakTimer.cs deleted file mode 100644 index ab784fca4d..0000000000 --- a/src/Avalonia.Base/Utilities/WeakTimer.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using Avalonia.Threading; - -namespace Avalonia.Utilities -{ - public class WeakTimer - { - public interface IWeakTimerSubscriber - { - bool Tick(); - } - - private readonly WeakReference _subscriber; - private DispatcherTimer _timer; - - public WeakTimer(IWeakTimerSubscriber subscriber) - { - _subscriber = new WeakReference(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; - } - - } -} diff --git a/src/Avalonia.Base/Visual.cs b/src/Avalonia.Base/Visual.cs index 30c89d186f..50b23f158d 100644 --- a/src/Avalonia.Base/Visual.cs +++ b/src/Avalonia.Base/Visual.cs @@ -321,7 +321,7 @@ namespace Avalonia internal RenderOptions RenderOptions { get; set; } - public bool HasNonUniformZIndexChildren { get; private set; } + internal bool HasNonUniformZIndexChildren { get; private set; } /// /// Gets a value indicating whether this control is attached to a visual root. diff --git a/src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs b/src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs index 49c8b9ff92..d03949c1c2 100644 --- a/src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs +++ b/src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs @@ -53,8 +53,8 @@ namespace Avalonia.Controls { return _owner.GetElementImpl( index, - options.HasAllFlags(ElementRealizationOptions.ForceCreate), - options.HasAllFlags(ElementRealizationOptions.SuppressAutoRecycle)); + options.HasFlag(ElementRealizationOptions.ForceCreate), + options.HasFlag(ElementRealizationOptions.SuppressAutoRecycle)); } protected override object GetItemAtCore(int index) => _owner.ItemsSourceView!.GetAt(index)!; diff --git a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs index d04dfec3e8..c55bd0f3e5 100644 --- a/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs +++ b/src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs @@ -151,7 +151,7 @@ namespace Avalonia.Automation.Peers protected override bool HasKeyboardFocusCore() => Owner.IsFocused; protected override bool IsContentElementCore() => 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 void SetFocusCore() => Owner.Focus(); diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 1704965fd7..3ee50ab547 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -302,7 +302,7 @@ namespace Avalonia.Controls.Presenters /// /// This method is automatically called when the control is attached to a visual tree. /// - protected internal virtual void AttachToScrollViewer() + internal void AttachToScrollViewer() { var owner = this.FindAncestorOfType(); diff --git a/src/Avalonia.Controls/Primitives/AccessText.cs b/src/Avalonia.Controls/Primitives/AccessText.cs index ed3412bb45..c5dbc3000f 100644 --- a/src/Avalonia.Controls/Primitives/AccessText.cs +++ b/src/Avalonia.Controls/Primitives/AccessText.cs @@ -60,7 +60,7 @@ namespace Avalonia.Controls.Primitives /// Renders the to a drawing context. /// /// The drawing context. - protected internal override void RenderCore(DrawingContext context) + private protected override void RenderCore(DrawingContext context) { base.RenderCore(context); int underscore = Text?.IndexOf('_') ?? -1; diff --git a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs index 7ed055f2e5..99fd4cd1dc 100644 --- a/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/OverlayPopupHost.cs @@ -51,7 +51,7 @@ namespace Avalonia.Controls.Primitives } /// - protected internal override Interactive? InteractiveParent => Parent as Interactive; + internal override Interactive? InteractiveParent => Parent as Interactive; /// public void Dispose() => Hide(); diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index a2034a2dbb..aef97bce36 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -72,7 +72,7 @@ namespace Avalonia.Controls.Primitives /// /// Popup events are passed to their parent window. This facilitates this. /// - protected internal override Interactive? InteractiveParent => (Interactive?)Parent; + internal override Interactive? InteractiveParent => (Interactive?)Parent; /// /// Gets the control that is hosting the popup root. diff --git a/src/Avalonia.Controls/Primitives/ScrollBar.cs b/src/Avalonia.Controls/Primitives/ScrollBar.cs index 37aa1ebffd..86bea8daa5 100644 --- a/src/Avalonia.Controls/Primitives/ScrollBar.cs +++ b/src/Avalonia.Controls/Primitives/ScrollBar.cs @@ -200,7 +200,7 @@ namespace Avalonia.Controls.Primitives /// /// This method is automatically called when the control is attached to a visual tree. /// - protected internal virtual void AttachToScrollViewer() + internal void AttachToScrollViewer() { var owner = this.FindAncestorOfType(); diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index 44d68fcf30..bef02567bc 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -318,7 +318,7 @@ namespace Avalonia.Controls.Primitives } /// - protected sealed override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) + internal sealed override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) { var count = VisualChildren.Count; diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs index 35676474dd..bc47462b56 100644 --- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs +++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs @@ -104,7 +104,7 @@ namespace Avalonia.Controls.Primitives } /// - protected override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) + internal override void NotifyChildResourcesChanged(ResourcesChangedEventArgs e) { foreach (var l in _layers) ((ILogical)l).NotifyResourcesChanged(e); diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index a7188c6226..1b9cb7de34 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -16,6 +16,12 @@ namespace Avalonia.Controls [TemplatePart("PART_VerticalScrollBar", typeof(ScrollBar))] public class ScrollViewer : ContentControl, IScrollable, IScrollAnchorProvider { + /// + /// Defines the property. + /// + public static readonly AttachedProperty BringIntoViewOnFocusChangeProperty = + AvaloniaProperty.RegisterAttached(nameof(BringIntoViewOnFocusChange), true); + /// /// Defines the property. /// @@ -174,6 +180,26 @@ namespace Avalonia.Controls remove => RemoveHandler(ScrollChangedEvent, value); } + /// + /// Gets or sets a value that determines whether the uses a + /// bring-into-view scroll behavior when an item in the view gets focus. + /// + /// + /// true to use a behavior that brings focused items into view. false to use a behavior + /// that focused items do not automatically scroll into view. The default is true. + /// + /// + /// can either be set explicitly on a + /// , or a the attached + /// ScrollViewer.BringIntoViewOnFocusChange property can be set on an element + /// that hosts a . + /// + public bool BringIntoViewOnFocusChange + { + get => GetValue(BringIntoViewOnFocusChangeProperty); + set => SetValue(BringIntoViewOnFocusChangeProperty, value); + } + /// /// Gets the extent of the scrollable content. /// @@ -400,6 +426,26 @@ namespace Avalonia.Controls /// public void ScrollToEnd() => SetCurrentValue(OffsetProperty, new Vector(double.NegativeInfinity, double.PositiveInfinity)); + /// + /// Gets the value of the attached property. + /// + /// The control to read the value from. + /// The value of the property. + public static bool GetBringIntoViewOnFocusChange(Control control) + { + return control.GetValue(BringIntoViewOnFocusChangeProperty); + } + + /// + /// Gets the value of the attached property. + /// + /// The control to set the value on. + /// The value of the property. + public static void SetBringIntoViewOnFocusChange(Control control, bool value) + { + control.SetValue(BringIntoViewOnFocusChangeProperty, value); + } + /// /// Gets the value of the HorizontalScrollBarVisibility attached property. /// @@ -696,6 +742,14 @@ namespace Avalonia.Controls } } + protected override void OnGotFocus(GotFocusEventArgs e) + { + base.OnGotFocus(e); + + if (e.Source != this && e.Source is Control c && BringIntoViewOnFocusChange) + c.BringIntoView(); + } + protected override void OnKeyDown(KeyEventArgs e) { if (e.Key == Key.PageUp) diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index adef052db8..c1edf23c89 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/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. - internal protected virtual void RenderCore(DrawingContext context) + private protected virtual void RenderCore(DrawingContext context) { var background = Background; diff --git a/src/Avalonia.Controls/Viewbox.cs b/src/Avalonia.Controls/Viewbox.cs index 1518fb49e3..b21a04c1d4 100644 --- a/src/Avalonia.Controls/Viewbox.cs +++ b/src/Avalonia.Controls/Viewbox.cs @@ -82,7 +82,7 @@ namespace Avalonia.Controls /// Gets or sets the transform applied to the container visual that /// hosts the child of the Viewbox /// - protected internal ITransform? InternalTransform + internal ITransform? InternalTransform { get => _containerVisual.RenderTransform; set => _containerVisual.RenderTransform = value; diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs index 0580761631..2f275431ff 100644 --- a/src/Avalonia.Controls/VirtualizingStackPanel.cs +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -662,9 +662,12 @@ namespace Avalonia.Controls _scrollViewer?.UnregisterAnchorCandidate(element); 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; } @@ -687,9 +690,8 @@ namespace Avalonia.Controls Debug.Assert(ItemContainerGenerator is not null); var recycleKey = element.GetValue(RecycleKeyProperty); - Debug.Assert(recycleKey is not null); - - if (recycleKey == s_itemIsItsOwnContainer) + + if (recycleKey is null || recycleKey == s_itemIsItsOwnContainer) { RemoveInternalChild(element); } diff --git a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml index e2273cd487..05bbbe9558 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ListBox.xaml @@ -36,7 +36,8 @@ VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}" IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}" IsScrollInertiaEnabled="{TemplateBinding (ScrollViewer.IsScrollInertiaEnabled)}" - AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"> + AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}" + BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}"> + Padding="{TemplateBinding Padding}" + ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}"> + IsScrollInertiaEnabled="{Binding (ScrollViewer.IsScrollInertiaEnabled), ElementName=PART_ContentPresenter}"/> + AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}" + BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}"> + AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}" + BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}"> diff --git a/src/Avalonia.Themes.Simple/Controls/ListBox.xaml b/src/Avalonia.Themes.Simple/Controls/ListBox.xaml index eaa1f914ca..a51fcbf72e 100644 --- a/src/Avalonia.Themes.Simple/Controls/ListBox.xaml +++ b/src/Avalonia.Themes.Simple/Controls/ListBox.xaml @@ -17,6 +17,7 @@ CornerRadius="{TemplateBinding CornerRadius}"> + Background="{TemplateBinding Background}" + ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}"> + IsScrollInertiaEnabled="{Binding (ScrollViewer.IsScrollInertiaEnabled), ElementName=PART_ContentPresenter}"/> diff --git a/src/Avalonia.Themes.Simple/Controls/TreeView.xaml b/src/Avalonia.Themes.Simple/Controls/TreeView.xaml index 8630c14aa2..f2f451fe6f 100644 --- a/src/Avalonia.Themes.Simple/Controls/TreeView.xaml +++ b/src/Avalonia.Themes.Simple/Controls/TreeView.xaml @@ -15,6 +15,7 @@ BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}"> _opacityStack = new(); private readonly Matrix? _postTransform; private double _currentOpacity = 1.0f; - private readonly bool _canTextUseLcdRendering; + private readonly bool _disableSubpixelTextRendering; private Matrix _currentTransform; private bool _disposed; private GRContext? _grContext; @@ -59,11 +58,11 @@ namespace Avalonia.Skia /// Dpi of drawings. /// public Vector Dpi; - + /// - /// Render text without Lcd rendering. + /// Render text without subpixel antialiasing. /// - public bool DisableTextLcdRendering; + public bool DisableSubpixelTextRendering; /// /// GPU-accelerated context (optional) @@ -135,7 +134,7 @@ namespace Avalonia.Skia _dpi = createInfo.Dpi; _disposables = disposables; - _canTextUseLcdRendering = !createInfo.DisableTextLcdRendering; + _disableSubpixelTextRendering = createInfo.DisableSubpixelTextRendering; _grContext = createInfo.GrContext; _gpu = createInfo.Gpu; if (_grContext != null) @@ -519,7 +518,23 @@ namespace Avalonia.Skia { 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, (float)glyphRun.BaselineOrigin.Y, paintWrapper.Paint); @@ -969,6 +984,7 @@ namespace Avalonia.Skia using (var ctx = intermediate.CreateDrawingContext()) { + ctx.RenderOptions = RenderOptions; ctx.Clear(Colors.Transparent); 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 ctx = pictureTarget.CreateDrawingContext(calc.IntermediateSize)) { + ctx.RenderOptions = RenderOptions; ctx.PushClip(calc.IntermediateClip); content.Render(ctx, transform); ctx.PopClip(); @@ -1283,7 +1300,7 @@ namespace Avalonia.Skia Height = pixelSize.Height, Dpi = _dpi, Format = format, - DisableTextLcdRendering = !_canTextUseLcdRendering, + DisableTextLcdRendering = isLayer ? _disableSubpixelTextRendering : true, GrContext = _grContext, Gpu = _gpu, Session = _session, diff --git a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs index a22b67e09e..64afaa6817 100644 --- a/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using Avalonia.Reactive; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Platform; -using Avalonia.Rendering; using SkiaSharp; namespace Avalonia.Skia @@ -54,8 +53,7 @@ namespace Avalonia.Skia var createInfo = new DrawingContextImpl.CreateInfo { Surface = _framebufferSurface, - Dpi = framebuffer.Dpi, - DisableTextLcdRendering = true + Dpi = framebuffer.Dpi }; return new DrawingContextImpl(createInfo, _preFramebufferCopyHandler, canvas, framebuffer); diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs index ce292eb826..6db6083ba7 100644 --- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs @@ -30,7 +30,6 @@ namespace Avalonia.Skia GrContext = session.GrContext, Surface = session.SkSurface, Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor, - DisableTextLcdRendering = true, Gpu = _skiaGpu, CurrentSession = session }; diff --git a/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs b/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs index b774ddf411..6a726dc9dc 100644 --- a/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs +++ b/src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs @@ -20,7 +20,7 @@ namespace Avalonia.Skia.Helpers { Canvas = canvas, Dpi = dpi, - DisableTextLcdRendering = true, + DisableSubpixelTextRendering = true, }; return new DrawingContextImpl(createInfo); diff --git a/src/Skia/Avalonia.Skia/PictureRenderTarget.cs b/src/Skia/Avalonia.Skia/PictureRenderTarget.cs index 280b7c27cd..02cc1c0676 100644 --- a/src/Skia/Avalonia.Skia/PictureRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/PictureRenderTarget.cs @@ -39,7 +39,7 @@ internal class PictureRenderTarget : IDisposable { Canvas = canvas, Dpi = _dpi, - DisableTextLcdRendering = true, + DisableSubpixelTextRendering = true, GrContext = _grContext, Gpu = _gpu, }; @@ -52,4 +52,4 @@ internal class PictureRenderTarget : IDisposable } public void Dispose() => _picture?.Dispose(); -} \ No newline at end of file +} diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs index 92210c30e2..c695e8ba41 100644 --- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs @@ -106,7 +106,7 @@ namespace Avalonia.Skia { Surface = _surface.Surface, Dpi = Dpi, - DisableTextLcdRendering = _disableLcdRendering, + DisableSubpixelTextRendering = _disableLcdRendering, GrContext = _grContext, Gpu = _gpu, }; diff --git a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs index 24fd7e3933..8025779c90 100644 --- a/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs +++ b/src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs @@ -141,13 +141,13 @@ namespace Avalonia.Win32.Interop.Wpf { var state = Keyboard.Modifiers; var rv = default(RawInputModifiers); - if (state.HasAllFlags(ModifierKeys.Windows)) + if (state.HasFlag(ModifierKeys.Windows)) rv |= RawInputModifiers.Meta; - if (state.HasAllFlags(ModifierKeys.Alt)) + if (state.HasFlag(ModifierKeys.Alt)) rv |= RawInputModifiers.Alt; - if (state.HasAllFlags(ModifierKeys.Control)) + if (state.HasFlag(ModifierKeys.Control)) rv |= RawInputModifiers.Control; - if (state.HasAllFlags(ModifierKeys.Shift)) + if (state.HasFlag(ModifierKeys.Shift)) rv |= RawInputModifiers.Shift; if (e != null) { diff --git a/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs index 34a9947d28..0b3d1a275b 100644 --- a/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs +++ b/tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Base.UnitTests.Input @@ -1253,5 +1254,24 @@ namespace Avalonia.Base.UnitTests.Input Assert.Same(expected, result); } + + [Fact] + public void Focuses_First_Child_From_No_Focus() + { + using var app = UnitTestApplication.Start(TestServices.RealFocus); + var button = new Button(); + var root = new TestRoot(button); + var target = new KeyboardNavigationHandler(); + + target.SetOwner(root); + + root.RaiseEvent(new KeyEventArgs + { + RoutedEvent = InputElement.KeyDownEvent, + Key = Key.Tab, + }); + + Assert.True(button.IsFocused); + } } } diff --git a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs index 82c4b5f8f0..9a7d51d86d 100644 --- a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs @@ -358,6 +358,77 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(100, thumb.Bounds.Top); } + [Fact] + public void BringIntoViewOnFocusChange_Scrolls_Child_Control_Into_View_When_Focused() + { + using var app = UnitTestApplication.Start(TestServices.RealFocus); + var content = new StackPanel + { + Children = + { + new Button + { + Width = 100, + Height = 900, + }, + new Button + { + Width = 100, + Height = 900, + }, + } + }; + + var target = new ScrollViewer + { + Template = new FuncControlTemplate(CreateTemplate), + Content = content, + }; + var root = new TestRoot(target); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + var button = (Button)content.Children[1]; + button.Focus(); + + Assert.Equal(new Vector(0, 800), target.Offset); + } + + [Fact] + public void BringIntoViewOnFocusChange_False_Does_Not_Scroll_Child_Control_Into_View_When_Focused() + { + var content = new StackPanel + { + Children = + { + new Button + { + Width = 100, + Height = 900, + }, + new Button + { + Width = 100, + Height = 900, + }, + } + }; + + var target = new ScrollViewer + { + Template = new FuncControlTemplate(CreateTemplate), + Content = content, + }; + var root = new TestRoot(target); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + var button = (Button)content.Children[1]; + button.Focus(); + + Assert.Equal(new Vector(0, 0), target.Offset); + } + private Point GetRootPoint(Visual control, Point p) { if (control.GetVisualRoot() is Visual root && diff --git a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs index 1fddaab910..c5a6ecc376 100644 --- a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs @@ -646,7 +646,7 @@ namespace Avalonia.Controls.UnitTests { // Issue #11272 using var app = App(); - var (_, _, itemsControl) = CreateUnrootedTarget(); + var (_, _, itemsControl) = CreateUnrootedTarget(); var container = new Decorator { Margin = new Thickness(100) }; var root = new TestRoot(true, container); @@ -657,11 +657,49 @@ namespace Avalonia.Controls.UnitTests root.LayoutManager.ExecuteLayoutPass(); } + [Fact] + public void Supports_Null_Recycle_Key_When_Scrolling() + { + using var app = App(); + var (_, scroll, itemsControl) = CreateUnrootedTarget(); + var root = CreateRoot(itemsControl); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + var firstItem = itemsControl.ContainerFromIndex(0)!; + scroll.Offset = new(0, 20); + + Layout(itemsControl); + + Assert.Null(firstItem.Parent); + Assert.Null(firstItem.VisualParent); + Assert.DoesNotContain(firstItem, itemsControl.ItemsPanelRoot!.Children); + } + + [Fact] + public void Supports_Null_Recycle_Key_When_Clearing_Items() + { + using var app = App(); + var (_, _, itemsControl) = CreateUnrootedTarget(); + var root = CreateRoot(itemsControl); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + var firstItem = itemsControl.ContainerFromIndex(0)!; + itemsControl.ItemsSource = null; + + Layout(itemsControl); + + Assert.Null(firstItem.Parent); + Assert.Null(firstItem.VisualParent); + Assert.Empty(itemsControl.ItemsPanelRoot!.Children); + } + [Fact] public void ScrollIntoView_On_Effectively_Invisible_Panel_Does_Not_Create_Ghost_Elements() { var items = new[] { "foo", "bar", "baz" }; - var (target, _, itemsControl) = CreateUnrootedTarget(items: items); + var (target, _, itemsControl) = CreateUnrootedTarget(items: items); var container = new Decorator { Margin = new Thickness(100), Child = itemsControl }; var root = new TestRoot(true, container); @@ -738,7 +776,7 @@ namespace Avalonia.Controls.UnitTests Optional itemTemplate = default, IEnumerable