Browse Source

Merge branch 'master' into fixes/Issue_6263

pull/11418/head
workgroupengineering 3 years ago
committed by GitHub
parent
commit
afb7be8dcb
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  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. 2
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  20. 16
      src/Avalonia.Base/Input/KeyboardNavigationHandler.cs
  21. 2
      src/Avalonia.Base/Interactivity/Interactive.cs
  22. 2
      src/Avalonia.Base/Logging/TraceLogSink.cs
  23. 2
      src/Avalonia.Base/Media/ArcSegment.cs
  24. 2
      src/Avalonia.Base/Media/BezierSegment .cs
  25. 2
      src/Avalonia.Base/Media/Imaging/Bitmap.cs
  26. 2
      src/Avalonia.Base/Media/Immutable/ImmutableTileBrush.cs
  27. 2
      src/Avalonia.Base/Media/LineSegment.cs
  28. 4
      src/Avalonia.Base/Media/PathSegment.cs
  29. 2
      src/Avalonia.Base/Media/PolyLineSegment.cs
  30. 2
      src/Avalonia.Base/Media/QuadraticBezierSegment .cs
  31. 1
      src/Avalonia.Base/Media/StreamGeometryContext.cs
  32. 2
      src/Avalonia.Base/Media/Transformation/TransformParser.cs
  33. 6
      src/Avalonia.Base/Platform/Storage/NameCollisionOption.cs
  34. 2
      src/Avalonia.Base/Rendering/ManagedDeferredRendererLock.cs
  35. 50
      src/Avalonia.Base/Rendering/RendererBase.cs
  36. 2
      src/Avalonia.Base/Rendering/Utilities/TileBrushCalculator.cs
  37. 3
      src/Avalonia.Base/Rendering/ZIndexComparer.cs
  38. 2
      src/Avalonia.Base/StyledElement.cs
  39. 2
      src/Avalonia.Base/Utilities/DisposableLock.cs
  40. 2
      src/Avalonia.Base/Utilities/NonPumpingLockHelper.cs
  41. 2
      src/Avalonia.Base/Utilities/SingleOrDictionary.cs
  42. 2
      src/Avalonia.Base/Utilities/SingleOrQueue.cs
  43. 2
      src/Avalonia.Base/Utilities/ValueSingleOrList.cs
  44. 49
      src/Avalonia.Base/Utilities/WeakTimer.cs
  45. 2
      src/Avalonia.Base/Visual.cs
  46. 4
      src/Avalonia.Controls.ItemsRepeater/Controls/RepeaterLayoutContext.cs
  47. 2
      src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
  48. 2
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  49. 2
      src/Avalonia.Controls/Primitives/AccessText.cs
  50. 2
      src/Avalonia.Controls/Primitives/OverlayPopupHost.cs
  51. 2
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  52. 2
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  53. 2
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  54. 2
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  55. 54
      src/Avalonia.Controls/ScrollViewer.cs
  56. 2
      src/Avalonia.Controls/TextBlock.cs
  57. 2
      src/Avalonia.Controls/Viewbox.cs
  58. 12
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  59. 3
      src/Avalonia.Themes.Fluent/Controls/ListBox.xaml
  60. 5
      src/Avalonia.Themes.Fluent/Controls/ScrollViewer.xaml
  61. 3
      src/Avalonia.Themes.Fluent/Controls/TextBox.xaml
  62. 3
      src/Avalonia.Themes.Fluent/Controls/TreeView.xaml
  63. 1
      src/Avalonia.Themes.Simple/Controls/ListBox.xaml
  64. 5
      src/Avalonia.Themes.Simple/Controls/ScrollViewer.xaml
  65. 1
      src/Avalonia.Themes.Simple/Controls/TextBox.xaml
  66. 1
      src/Avalonia.Themes.Simple/Controls/TreeView.xaml
  67. 4
      src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs
  68. 33
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  69. 4
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  70. 1
      src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
  71. 2
      src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs
  72. 4
      src/Skia/Avalonia.Skia/PictureRenderTarget.cs
  73. 2
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  74. 8
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  75. 20
      tests/Avalonia.Base.UnitTests/Input/KeyboardNavigationTests_Tab.cs
  76. 71
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
  77. 60
      tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs
  78. 9
      tests/Avalonia.IntegrationTests.Appium/ButtonTests.cs
  79. 6
      tests/Avalonia.IntegrationTests.Appium/PlatformFactAttribute.cs
  80. 4
      tests/Avalonia.IntegrationTests.Appium/PlatformTheoryAttribute.cs

2
packages/Avalonia/Avalonia.csproj

@ -5,7 +5,7 @@
</PropertyGroup>
<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.Build.Tasks/Avalonia.Build.Tasks.csproj">
<PrivateAssets>all</PrivateAssets>

5
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<DataObject> 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)

2
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;

3
samples/IntegrationTestApp/MainWindow.axaml

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

6
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);
}

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
/// when a control is added to the visual tree.
/// </remarks>
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.
/// </remarks>
protected void DisableTransitions()
internal void DisableTransitions()
{
if (_transitionsEnabled)
{

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

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

2
src/Avalonia.Base/AvaloniaPropertyExtensions.cs

@ -7,7 +7,7 @@ namespace Avalonia
/// <summary>
/// Extensions for <see cref="AvaloniaProperty"/>.
/// </summary>
public static class AvaloniaPropertyExtensions
internal static class AvaloniaPropertyExtensions
{
/// <summary>
/// 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
/// of Auto.
/// </summary>
public enum ClearMode
internal enum ClearMode
{
/// <summary>
/// <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>
/// <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
/// <summary>

2
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<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
private const int MaxArrayLength = 0x7FEFFFFF;

2
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<T> : IEnumerable<T>, ICollection, IReadOnlyCollection<T>, IDisposable, IDeserializationCallback
internal class PooledStack<T> : IEnumerable<T>, ICollection, IReadOnlyCollection<T>, IDisposable, IDeserializationCallback
{
[NonSerialized]
private ArrayPool<T> _pool;

2
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();

2
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";
}

2
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,

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

@ -5,7 +5,7 @@ namespace Avalonia.Diagnostics
/// <summary>
/// Provides a debug interface into <see cref="AvaloniaObject"/>.
/// </summary>
public interface IAvaloniaObjectDebug
internal interface IAvaloniaObjectDebug
{
/// <summary>
/// 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
/// <see cref="AvaloniaList{T}"/>
/// </summary>
public interface INotifyCollectionChangedDebug
internal interface INotifyCollectionChangedDebug
{
/// <summary>
/// Gets the subscriber list for the <see cref="INotifyCollectionChanged.CollectionChanged"/>

2
src/Avalonia.Base/EnumExtensions.cs

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

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

@ -9,7 +9,7 @@ namespace Avalonia.Input
/// <summary>
/// Handles access keys for a window.
/// </summary>
public class AccessKeyHandler : IAccessKeyHandler
internal class AccessKeyHandler : IAccessKeyHandler
{
/// <summary>
/// Defines the AccessKeyPressed attached event.

16
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
/// <summary>
/// Handles keyboard navigation for a window.
/// </summary>
[Unstable]
public class KeyboardNavigationHandler : IKeyboardNavigationHandler
{
/// <summary>
@ -75,13 +77,16 @@ namespace Avalonia.Input
/// <param name="direction">The direction to move.</param>
/// <param name="keyModifiers">Any key modifiers active at the time of focus.</param>
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
/// <param name="e">The event args.</param>
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);

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

@ -17,7 +17,7 @@ namespace Avalonia.Interactivity
/// <summary>
/// Gets the interactive parent of the object for bubbling and tunneling events.
/// </summary>
protected internal virtual Interactive? InteractiveParent => VisualParent as Interactive;
internal virtual Interactive? InteractiveParent => VisualParent as Interactive;
/// <summary>
/// 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
{
public class TraceLogSink : ILogSink
internal class TraceLogSink : ILogSink
{
private readonly LogEventLevel _level;
private readonly IList<string>? _areas;

2
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);
}

2
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);
}

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;
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))

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.
/// </param>
/// <param name="tileMode">The tile mode.</param>
protected internal ImmutableTileBrush(
private protected ImmutableTileBrush(
AlignmentX alignmentX,
AlignmentY alignmentY,
RelativeRect destinationRect,

2
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);
}

4
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);
}
}
}

2
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)

2
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);
}

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

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

2
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 =
{

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
{
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
{
public class TileBrushCalculator
internal class TileBrushCalculator
{
private readonly Size _imageSize;
private readonly Rect _drawRect;

3
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<Visual>
internal class ZIndexComparer : IComparer<Visual>
{
public static readonly ZIndexComparer Instance = new ZIndexComparer();
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.
/// </summary>
/// <param name="e">The event args.</param>
protected virtual void NotifyChildResourcesChanged(ResourcesChangedEventArgs e)
internal virtual void NotifyChildResourcesChanged(ResourcesChangedEventArgs e)
{
if (_logicalChildren is object)
{

2
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();

2
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
{

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

@ -11,7 +11,7 @@ namespace Avalonia.Utilities
/// </summary>
/// <typeparam name="TKey">The type of the key.</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
{
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.
/// </summary>
/// <typeparam name="T">The type of items held in the queue.</typeparam>
public class SingleOrQueue<T>
internal class SingleOrQueue<T>
{
private T? _head;
private Queue<T>? _tail;

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

@ -9,7 +9,7 @@ namespace Avalonia.Utilities
/// <remarks>
/// Once more than value has been added to this storage it will switch to using <see cref="List"/> internally.
/// </remarks>
public ref struct ValueSingleOrList<T>
internal ref struct ValueSingleOrList<T>
{
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; }
public bool HasNonUniformZIndexChildren { get; private set; }
internal bool HasNonUniformZIndexChildren { get; private set; }
/// <summary>
/// Gets a value indicating whether this control is attached to a visual root.

4
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)!;

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 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();

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

@ -302,7 +302,7 @@ namespace Avalonia.Controls.Presenters
/// <remarks>
/// This method is automatically called when the control is attached to a visual tree.
/// </remarks>
protected internal virtual void AttachToScrollViewer()
internal void AttachToScrollViewer()
{
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.
/// </summary>
/// <param name="context">The drawing context.</param>
protected internal override void RenderCore(DrawingContext context)
private protected override void RenderCore(DrawingContext context)
{
base.RenderCore(context);
int underscore = Text?.IndexOf('_') ?? -1;

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

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

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

@ -72,7 +72,7 @@ namespace Avalonia.Controls.Primitives
/// <remarks>
/// Popup events are passed to their parent window. This facilitates this.
/// </remarks>
protected internal override Interactive? InteractiveParent => (Interactive?)Parent;
internal override Interactive? InteractiveParent => (Interactive?)Parent;
/// <summary>
/// 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>
/// This method is automatically called when the control is attached to a visual tree.
/// </remarks>
protected internal virtual void AttachToScrollViewer()
internal void AttachToScrollViewer()
{
var owner = this.FindAncestorOfType<ScrollViewer>();

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

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

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

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

54
src/Avalonia.Controls/ScrollViewer.cs

@ -16,6 +16,12 @@ namespace Avalonia.Controls
[TemplatePart("PART_VerticalScrollBar", typeof(ScrollBar))]
public class ScrollViewer : ContentControl, IScrollable, IScrollAnchorProvider
{
/// <summary>
/// Defines the <see cref="BringIntoViewOnFocusChange "/> property.
/// </summary>
public static readonly AttachedProperty<bool> BringIntoViewOnFocusChangeProperty =
AvaloniaProperty.RegisterAttached<ScrollViewer, Control, bool>(nameof(BringIntoViewOnFocusChange), true);
/// <summary>
/// Defines the <see cref="Extent"/> property.
/// </summary>
@ -174,6 +180,26 @@ namespace Avalonia.Controls
remove => RemoveHandler(ScrollChangedEvent, value);
}
/// <summary>
/// Gets or sets a value that determines whether the <see cref="ScrollViewer"/> uses a
/// bring-into-view scroll behavior when an item in the view gets focus.
/// </summary>
/// <value>
/// 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.
/// </value>
/// <remarks>
/// <see cref="BringIntoViewOnFocusChange"/> can either be set explicitly on a
/// <see cref="ScrollViewer"/>, or a the attached
/// <code>ScrollViewer.BringIntoViewOnFocusChange</code> property can be set on an element
/// that hosts a <see cref="ScrollViewer"/>.
/// </remarks>
public bool BringIntoViewOnFocusChange
{
get => GetValue(BringIntoViewOnFocusChangeProperty);
set => SetValue(BringIntoViewOnFocusChangeProperty, value);
}
/// <summary>
/// Gets the extent of the scrollable content.
/// </summary>
@ -400,6 +426,26 @@ namespace Avalonia.Controls
/// </summary>
public void ScrollToEnd() => SetCurrentValue(OffsetProperty, new Vector(double.NegativeInfinity, double.PositiveInfinity));
/// <summary>
/// Gets the value of the <see cref="BringIntoViewOnFocusChange"/> attached property.
/// </summary>
/// <param name="control">The control to read the value from.</param>
/// <returns>The value of the property.</returns>
public static bool GetBringIntoViewOnFocusChange(Control control)
{
return control.GetValue(BringIntoViewOnFocusChangeProperty);
}
/// <summary>
/// Gets the value of the <see cref="BringIntoViewOnFocusChange"/> attached property.
/// </summary>
/// <param name="control">The control to set the value on.</param>
/// <param name="value">The value of the property.</param>
public static void SetBringIntoViewOnFocusChange(Control control, bool value)
{
control.SetValue(BringIntoViewOnFocusChangeProperty, value);
}
/// <summary>
/// Gets the value of the HorizontalScrollBarVisibility attached property.
/// </summary>
@ -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)

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.
internal protected virtual void RenderCore(DrawingContext context)
private protected virtual void RenderCore(DrawingContext context)
{
var background = Background;

2
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
/// </summary>
protected internal ITransform? InternalTransform
internal ITransform? InternalTransform
{
get => _containerVisual.RenderTransform;
set => _containerVisual.RenderTransform = value;

12
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);
}

3
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)}">
<ItemsPresenter Name="PART_ItemsPresenter"
AreVerticalSnapPointsRegular="{TemplateBinding AreVerticalSnapPointsRegular}"
AreHorizontalSnapPointsRegular="{TemplateBinding AreHorizontalSnapPointsRegular}"

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

@ -33,11 +33,12 @@
VerticalSnapPointsType="{TemplateBinding VerticalSnapPointsType}"
HorizontalSnapPointsAlignment="{TemplateBinding HorizontalSnapPointsAlignment}"
VerticalSnapPointsAlignment="{TemplateBinding VerticalSnapPointsAlignment}"
Padding="{TemplateBinding Padding}">
Padding="{TemplateBinding Padding}"
ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}">
<ScrollContentPresenter.GestureRecognizers>
<ScrollGestureRecognizer CanHorizontallyScroll="{Binding CanHorizontallyScroll, 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>
<ScrollBar Name="PART_HorizontalScrollBar"

3
src/Avalonia.Themes.Fluent/Controls/TextBox.xaml

@ -136,7 +136,8 @@
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}">
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
<Panel>
<TextBlock Name="PART_Watermark"
Opacity="0.5"

3
src/Avalonia.Themes.Fluent/Controls/TreeView.xaml

@ -30,7 +30,8 @@
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}">
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}">
<ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}"
Margin="{TemplateBinding Padding}" />

1
src/Avalonia.Themes.Simple/Controls/ListBox.xaml

@ -17,6 +17,7 @@
CornerRadius="{TemplateBinding CornerRadius}">
<ScrollViewer Name="PART_ScrollViewer"
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}"
Background="{TemplateBinding Background}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"

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

@ -14,11 +14,12 @@
VerticalSnapPointsType="{TemplateBinding VerticalSnapPointsType}"
HorizontalSnapPointsAlignment="{TemplateBinding HorizontalSnapPointsAlignment}"
VerticalSnapPointsAlignment="{TemplateBinding VerticalSnapPointsAlignment}"
Background="{TemplateBinding Background}">
Background="{TemplateBinding Background}"
ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}">
<ScrollContentPresenter.GestureRecognizers>
<ScrollGestureRecognizer CanHorizontallyScroll="{Binding CanHorizontallyScroll, 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>
<ScrollBar Name="PART_HorizontalScrollBar"

1
src/Avalonia.Themes.Simple/Controls/TextBox.xaml

@ -126,6 +126,7 @@
<ScrollViewer Grid.Column="1"
Grid.ColumnSpan="1"
AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">

1
src/Avalonia.Themes.Simple/Controls/TreeView.xaml

@ -15,6 +15,7 @@
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<ScrollViewer AllowAutoHide="{TemplateBinding (ScrollViewer.AllowAutoHide)}"
BringIntoViewOnFocusChange="{TemplateBinding (ScrollViewer.BringIntoViewOnFocusChange)}"
Background="{TemplateBinding Background}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
IsScrollChainingEnabled="{TemplateBinding (ScrollViewer.IsScrollChainingEnabled)}"

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

@ -40,11 +40,11 @@ namespace Avalonia.Headless.Vnc
{
Window?.MouseMove(pt);
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);
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);
_previousButtons = buttons;
}, DispatcherPriority.Input);

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

@ -7,7 +7,6 @@ using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering.Utilities;
using Avalonia.Utilities;
using Avalonia.Media.Imaging;
using SkiaSharp;
using ISceneBrush = Avalonia.Media.ISceneBrush;
@ -26,7 +25,7 @@ namespace Avalonia.Skia
private readonly Stack<double> _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.
/// </summary>
public Vector Dpi;
/// <summary>
/// Render text without Lcd rendering.
/// Render text without subpixel antialiasing.
/// </summary>
public bool DisableTextLcdRendering;
public bool DisableSubpixelTextRendering;
/// <summary>
/// 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,

4
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);

1
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
};

2
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);

4
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();
}
}

2
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,
};

8
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)
{

20
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);
}
}
}

71
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<ScrollViewer>(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<ScrollViewer>(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 &&

60
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<ItemsControl>();
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<NonRecyclingItemsControl>();
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<NonRecyclingItemsControl>();
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<ItemsControl>(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<IDataTemplate?> itemTemplate = default,
IEnumerable<Style>? styles = null)
{
var (target, scroll, itemsControl) = CreateUnrootedTarget(items, itemTemplate);
var (target, scroll, itemsControl) = CreateUnrootedTarget<ItemsControl>(items, itemTemplate);
var root = CreateRoot(itemsControl, styles);
root.LayoutManager.ExecuteInitialLayoutPass();
@ -746,9 +784,10 @@ namespace Avalonia.Controls.UnitTests
return (target, scroll, itemsControl);
}
private static (VirtualizingStackPanel, ScrollViewer, ItemsControl) CreateUnrootedTarget(
private static (VirtualizingStackPanel, ScrollViewer, T) CreateUnrootedTarget<T>(
IEnumerable<object>? items = null,
Optional<IDataTemplate?> itemTemplate = default)
where T : ItemsControl, new()
{
var target = new VirtualizingStackPanel();
@ -766,7 +805,7 @@ namespace Avalonia.Controls.UnitTests
Template = ScrollViewerTemplate(),
};
var itemsControl = new ItemsControl
var itemsControl = new T
{
ItemsSource = items,
Template = new FuncControlTemplate<ItemsControl>((_, ns) => scroll.RegisterInNameScope(ns)),
@ -840,5 +879,16 @@ namespace Avalonia.Controls.UnitTests
public event NotifyCollectionChangedEventHandler? CollectionChanged;
}
private class NonRecyclingItemsControl : ItemsControl
{
protected override Type StyleKeyOverride => typeof(ItemsControl);
protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
{
recycleKey = null;
return true;
}
}
}
}

9
tests/Avalonia.IntegrationTests.Appium/ButtonTests.cs

@ -27,6 +27,15 @@ namespace Avalonia.IntegrationTests.Appium
Assert.False(button.Enabled);
}
[Fact]
public void EffectivelyDisabledButton()
{
var button = _session.FindElementByAccessibilityId("EffectivelyDisabledButton");
Assert.Equal("Effectively Disabled Button", button.Text);
Assert.False(button.Enabled);
}
[Fact]
public void BasicButton()
{

6
tests/Avalonia.IntegrationTests.Appium/PlatformFactAttribute.cs

@ -44,11 +44,11 @@ namespace Avalonia
private bool IsSupported()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return Platforms.HasAnyFlag(TestPlatforms.Windows);
return Platforms.HasFlag(TestPlatforms.Windows);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return Platforms.HasAnyFlag(TestPlatforms.MacOS);
return Platforms.HasFlag(TestPlatforms.MacOS);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return Platforms.HasAnyFlag(TestPlatforms.Linux);
return Platforms.HasFlag(TestPlatforms.Linux);
return false;
}
}

4
tests/Avalonia.IntegrationTests.Appium/PlatformTheoryAttribute.cs

@ -27,9 +27,9 @@ namespace Avalonia.IntegrationTests.Appium
private bool IsSupported()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return Platforms.HasAnyFlag(TestPlatforms.Windows);
return Platforms.HasFlag(TestPlatforms.Windows);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
return Platforms.HasAnyFlag(TestPlatforms.MacOS);
return Platforms.HasFlag(TestPlatforms.MacOS);
return false;
}
}

Loading…
Cancel
Save