Browse Source

Merge branch 'master' into refactor/grokys-internalize

pull/11437/head
Max Katz 3 years ago
committed by GitHub
parent
commit
7ec8ad38af
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/Avalonia/Avalonia.csproj
  2. 12
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  3. 33
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  4. 4
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  5. 1
      src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
  6. 2
      src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs
  7. 4
      src/Skia/Avalonia.Skia/PictureRenderTarget.cs
  8. 2
      src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
  9. 60
      tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.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>

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

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

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

Loading…
Cancel
Save