Browse Source

Merge branch 'master' into strongname2

pull/4854/head
Dariusz Komosiński 5 years ago
committed by GitHub
parent
commit
d29d4cf985
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      azure-pipelines.yml
  2. BIN
      build/Assets/Icon.png
  3. 6
      build/SharedVersion.props
  4. 4
      build/SourceLink.props
  5. 1
      dirs.proj
  6. 20
      samples/BindingDemo/App.xaml.cs
  7. 8
      samples/ControlCatalog.Desktop/Program.cs
  8. 1
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  9. 10
      samples/Previewer/App.xaml.cs
  10. 15
      samples/Previewer/Program.cs
  11. 11
      samples/RenderDemo/App.xaml.cs
  12. 6
      samples/RenderDemo/Pages/GlyphRunPage.xaml
  13. 7
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
  14. 8
      samples/VirtualizationDemo/App.xaml.cs
  15. 20
      samples/VirtualizationDemo/Program.cs
  16. 8
      samples/interop/Direct3DInteropSample/App.paml.cs
  17. 21
      samples/interop/Direct3DInteropSample/Program.cs
  18. 1
      samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
  19. 1
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  20. 6
      src/Avalonia.Controls/IScrollAnchorProvider.cs
  21. 5
      src/Avalonia.Controls/Panel.cs
  22. 8
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  23. 137
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  24. 1
      src/Avalonia.Controls/Primitives/IPopupHost.cs
  25. 34
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  26. 18
      src/Avalonia.Controls/Repeater/ItemsRepeater.cs
  27. 2
      src/Avalonia.Controls/Repeater/ViewManager.cs
  28. 14
      src/Avalonia.Controls/Repeater/ViewportManager.cs
  29. 1
      src/Avalonia.Controls/Repeater/VirtualizationInfo.cs
  30. 44
      src/Avalonia.Controls/Shapes/Shape.cs
  31. 2
      src/Avalonia.Controls/Window.cs
  32. 5
      src/Avalonia.Input/KeyGesture.cs
  33. 2
      src/Avalonia.Layout/StackLayout.cs
  34. 1
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  35. 2
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  36. 7
      src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs
  37. 1
      src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs
  38. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  39. 16
      src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs
  40. 31
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs
  41. 44
      src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs
  42. 36
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  43. 1
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  44. 160
      src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs
  45. 2
      src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj
  46. 1
      src/Windows/Avalonia.Win32/Interop/TaskBarList.cs
  47. 17
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  48. 65
      src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
  49. 8
      tests/Avalonia.Animation.UnitTests/AnimatableTests.cs
  50. 4
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs
  51. 4
      tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs
  52. 2
      tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs
  53. 14
      tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs
  54. 3
      tests/Avalonia.Benchmarks/NullRenderer.cs
  55. 3
      tests/Avalonia.Benchmarks/NullThreadingPlatform.cs
  56. 2
      tests/Avalonia.Controls.UnitTests/BorderTests.cs
  57. 9
      tests/Avalonia.Controls.UnitTests/CanvasTests.cs
  58. 15
      tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs
  59. 47
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  60. 2
      tests/Avalonia.Controls.UnitTests/PanelTests.cs
  61. 10
      tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs
  62. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs
  63. 6
      tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs
  64. 57
      tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs
  65. 144
      tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs
  66. 56
      tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs
  67. 4
      tests/Avalonia.Controls.UnitTests/TextBlockTests.cs
  68. 13
      tests/Avalonia.Controls.UnitTests/ViewboxTests.cs
  69. 13
      tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs
  70. 20
      tests/Avalonia.DesignerSupport.TestApp/Program.cs
  71. 6
      tests/Avalonia.Layout.UnitTests/LayoutableTests.cs
  72. 2
      tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs
  73. 113
      tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs
  74. 7
      tests/Avalonia.LeakTests/ControlTests.cs
  75. 15
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs
  76. 2
      tests/Avalonia.RenderTests/Media/BitmapTests.cs
  77. 12
      tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs
  78. 75
      tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
  79. 74
      tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
  80. 2
      tests/Avalonia.Styling.UnitTests/StyleTests.cs
  81. 8
      tests/Avalonia.Styling.UnitTests/StylesTests.cs
  82. 2
      tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs
  83. 2
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  84. 2
      tests/Avalonia.Visuals.UnitTests/VisualTests.cs

4
azure-pipelines.yml

@ -31,6 +31,8 @@ jobs:
condition: not(canceled())
- job: macOS
variables:
SolutionDir: '$(Build.SourcesDirectory)'
pool:
vmImage: 'macOS-10.14'
steps:
@ -97,6 +99,8 @@ jobs:
- job: Windows
pool:
vmImage: 'windows-2019'
variables:
SolutionDir: '$(Build.SourcesDirectory)'
steps:
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 3.1.401'

BIN
build/Assets/Icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

6
build/SharedVersion.props

@ -10,7 +10,7 @@
<NoWarn>CS1591</NoWarn>
<LangVersion>latest</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIconUrl>https://avatars2.githubusercontent.com/u/14075148?s=200</PackageIconUrl>
<PackageIcon>Icon.png</PackageIcon>
<PackageDescription>Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.</PackageDescription>
<PackageTags>avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin</PackageTags>
<PackageReleaseNotes>https://github.com/AvaloniaUI/Avalonia/releases</PackageReleaseNotes>
@ -18,4 +18,8 @@
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\avalonia.snk</AssemblyOriginatorKeyFile>
<SignAssembly>True</SignAssembly>
</PropertyGroup>
<ItemGroup Label="PackageIcon">
<None Include="$(SolutionDir)/build/Assets/Icon.png" Pack="true" PackagePath=""/>
</ItemGroup>
</Project>

4
build/SourceLink.props

@ -1,5 +1,5 @@
<Project>
<ItemGroup>
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.0" PrivateAssets="All" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" PrivateAssets="All" />
</ItemGroup>
</Project>
</Project>

1
dirs.proj

@ -21,6 +21,7 @@
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows')) OR '$(MSBuildRuntimeType)' != 'Full'">
<ProjectReference Remove="src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj" />
<ProjectReference Remove="samples/interop/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" />
</ItemGroup>
<ItemGroup>

20
samples/BindingDemo/App.xaml.cs

@ -1,6 +1,5 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
@ -13,13 +12,20 @@ namespace BindingDemo
AvaloniaXamlLoader.Load(this);
}
private static void Main()
public override void OnFrameworkInitializationCompleted()
{
AppBuilder.Configure<App>()
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow();
base.OnFrameworkInitializationCompleted();
}
public static int Main(string[] args)
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.LogToDebug()
.Start<MainWindow>();
}
.LogToDebug();
}
}

8
samples/ControlCatalog.Desktop/Program.cs

@ -10,12 +10,8 @@ namespace ControlCatalog
internal class Program
{
[STAThread]
static void Main(string[] args)
{
// TODO: Make this work with GTK/Skia/Cairo depending on command-line args
// again.
BuildAvaloniaApp().Start<MainWindow>();
}
public static int Main(string[] args)
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
/// <summary>
/// This method is needed for IDE previewer infrastructure

1
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -11,7 +11,6 @@
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
<ProjectReference Include="..\..\src\Avalonia.X11\Avalonia.X11.csproj" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2019013001" />
</ItemGroup>

10
samples/Previewer/App.xaml.cs

@ -1,4 +1,5 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace Previewer
@ -9,6 +10,13 @@ namespace Previewer
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow();
base.OnFrameworkInitializationCompleted();
}
}
}
}

15
samples/Previewer/Program.cs

@ -1,13 +1,14 @@
using System;
using Avalonia;
using Avalonia;
namespace Previewer
{
class Program
{
static void Main(string[] args)
{
AppBuilder.Configure<App>().UsePlatformDetect().Start<MainWindow>();
}
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect();
public static int Main(string[] args)
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
}
}

11
samples/RenderDemo/App.xaml.cs

@ -1,4 +1,5 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
@ -11,9 +12,17 @@ namespace RenderDemo
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow();
base.OnFrameworkInitializationCompleted();
}
// TODO: Make this work with GTK/Skia/Cairo depending on command-line args
// again.
static void Main(string[] args) => BuildAvaloniaApp().Start<MainWindow>();
static void Main(string[] args)
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
// App configuration, used by the entry point and previewer
static AppBuilder BuildAvaloniaApp()

6
samples/RenderDemo/Pages/GlyphRunPage.xaml

@ -6,9 +6,9 @@
x:Class="RenderDemo.Pages.GlyphRunPage">
<Border
Background="White">
<DrawingPresenter
x:Name="drawingPresenter"
<Image
x:Name="imageControl"
Stretch="None">
</DrawingPresenter>
</Image>
</Border>
</UserControl>

7
samples/RenderDemo/Pages/GlyphRunPage.xaml.cs

@ -9,7 +9,7 @@ namespace RenderDemo.Pages
{
public class GlyphRunPage : UserControl
{
private DrawingPresenter _drawingPresenter;
private Image _imageControl;
private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface;
private readonly Random _rand = new Random();
private ushort[] _glyphIndices = new ushort[1];
@ -25,7 +25,8 @@ namespace RenderDemo.Pages
{
AvaloniaXamlLoader.Load(this);
_drawingPresenter = this.FindControl<DrawingPresenter>("drawingPresenter");
_imageControl = this.FindControl<Image>("imageControl");
_imageControl.Source = new DrawingImage();
DispatcherTimer.Run(() =>
{
@ -73,7 +74,7 @@ namespace RenderDemo.Pages
drawingGroup.Children.Add(geometryDrawing);
_drawingPresenter.Drawing = drawingGroup;
(_imageControl.Source as DrawingImage).Drawing = drawingGroup;
}
}
}

8
samples/VirtualizationDemo/App.xaml.cs

@ -1,4 +1,5 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace VirtualizationDemo
@ -9,5 +10,12 @@ namespace VirtualizationDemo
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow();
base.OnFrameworkInitializationCompleted();
}
}
}

20
samples/VirtualizationDemo/Program.cs

@ -1,19 +1,17 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia;
using Avalonia.ReactiveUI;
namespace VirtualizationDemo
{
class Program
{
static void Main(string[] args)
{
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.LogToDebug()
.Start<MainWindow>();
}
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseReactiveUI()
.LogToDebug();
public static int Main(string[] args)
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
}

8
samples/interop/Direct3DInteropSample/App.paml.cs

@ -1,4 +1,5 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace Direct3DInteropSample
@ -9,5 +10,12 @@ namespace Direct3DInteropSample
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow();
base.OnFrameworkInitializationCompleted();
}
}
}

21
samples/interop/Direct3DInteropSample/Program.cs

@ -1,19 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia;
using Avalonia;
namespace Direct3DInteropSample
{
class Program
{
static void Main(string[] args)
{
AppBuilder.Configure<App>()
.With(new Win32PlatformOptions {UseDeferredRendering = false})
.UseWin32().UseDirect2D1().Start<MainWindow>();
}
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.With(new Win32PlatformOptions { UseDeferredRendering = false })
.UseWin32()
.UseDirect2D1();
public static int Main(string[] args)
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
}
}

1
samples/interop/NativeEmbedSample/NativeEmbedSample.csproj

@ -9,7 +9,6 @@
<ItemGroup>
<PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
<ProjectReference Include="..\..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
<ProjectReference Include="..\..\..\src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\..\src\Avalonia.X11\Avalonia.X11.csproj" />

1
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@ -665,6 +665,7 @@ namespace Avalonia.Controls
/// <param name="dataItem">
/// The data item represented by the row that contains the intended cell.
/// </param>
/// <param name="binding">When the method returns, contains the applied binding.</param>
/// <returns>
/// A new editing element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
/// </returns>

6
src/Avalonia.Controls/IScrollAnchorProvider.cs

@ -1,4 +1,6 @@
namespace Avalonia.Controls
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
/// Specifies a contract for a scrolling control that supports scroll anchoring.
@ -8,7 +10,7 @@
/// <summary>
/// The currently chosen anchor element to use for scroll anchoring.
/// </summary>
IControl CurrentAnchor { get; }
IControl? CurrentAnchor { get; }
/// <summary>
/// Registers a control as a potential scroll anchor candidate.

5
src/Avalonia.Controls/Panel.cs

@ -137,6 +137,11 @@ namespace Avalonia.Controls
throw new NotSupportedException();
}
InvalidateMeasureOnChildrenChanged();
}
private protected virtual void InvalidateMeasureOnChildrenChanged()
{
InvalidateMeasure();
}

8
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -512,6 +512,14 @@ namespace Avalonia.Controls.Presenters
var generator = Owner.ItemContainerGenerator;
var newOffset = -1.0;
if (!panel.IsMeasureValid && panel.PreviousMeasure.HasValue)
{
//before any kind of scrolling we need to make sure panel measure is valid
//or we risk get panel into not valid state
//we make a preemptive quick measure so scrolling is valid
panel.Measure(panel.PreviousMeasure.Value);
}
if (index >= 0 && index < ItemCount)
{
if (index <= FirstIndex)

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

@ -7,6 +7,8 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Controls.Presenters
{
/// <summary>
@ -14,6 +16,8 @@ namespace Avalonia.Controls.Presenters
/// </summary>
public class ScrollContentPresenter : ContentPresenter, IPresenter, IScrollable, IScrollAnchorProvider
{
private const double EdgeDetectionTolerance = 0.1;
/// <summary>
/// Defines the <see cref="CanHorizontallyScroll"/> property.
/// </summary>
@ -64,11 +68,13 @@ namespace Avalonia.Controls.Presenters
private bool _arranging;
private Size _extent;
private Vector _offset;
private IDisposable _logicalScrollSubscription;
private IDisposable? _logicalScrollSubscription;
private Size _viewport;
private Dictionary<int, Vector> _activeLogicalGestureScrolls;
private List<IControl> _anchorCandidates;
private (IControl control, Rect bounds) _anchor;
private Dictionary<int, Vector>? _activeLogicalGestureScrolls;
private List<IControl>? _anchorCandidates;
private IControl? _anchorElement;
private Rect _anchorElementBounds;
private bool _isAnchorElementDirty;
/// <summary>
/// Initializes static members of the <see cref="ScrollContentPresenter"/> class.
@ -90,8 +96,6 @@ namespace Avalonia.Controls.Presenters
this.GetObservable(ChildProperty).Subscribe(UpdateScrollableSubscription);
}
internal event EventHandler<VectorEventArgs> PreArrange;
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
@ -138,7 +142,14 @@ namespace Avalonia.Controls.Presenters
}
/// <inheritdoc/>
IControl IScrollAnchorProvider.CurrentAnchor => _anchor.control;
IControl? IScrollAnchorProvider.CurrentAnchor
{
get
{
EnsureAnchorElementSelection();
return _anchorElement;
}
}
/// <summary>
/// Attempts to bring a portion of the target visual into view by scrolling the content.
@ -215,16 +226,18 @@ namespace Avalonia.Controls.Presenters
_anchorCandidates ??= new List<IControl>();
_anchorCandidates.Add(element);
_isAnchorElementDirty = true;
}
/// <inheritdoc/>
void IScrollAnchorProvider.UnregisterAnchorCandidate(IControl element)
{
_anchorCandidates?.Remove(element);
_isAnchorElementDirty = true;
if (_anchor.control == element)
if (_anchorElement == element)
{
_anchor = default;
_anchorElement = null;
}
}
@ -247,11 +260,6 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
PreArrange?.Invoke(this, new VectorEventArgs
{
Vector = new Vector(finalSize.Width, finalSize.Height),
});
if (_logicalScrollSubscription != null || Child == null)
{
return base.ArrangeOverride(finalSize);
@ -271,59 +279,69 @@ namespace Avalonia.Controls.Presenters
// If we have an anchor and its position relative to Child has changed during the
// arrange then that change wasn't just due to scrolling (as scrolling doesn't adjust
// relative positions within Child).
if (_anchor.control != null &&
TranslateBounds(_anchor.control, Child, out var updatedBounds) &&
updatedBounds.Position != _anchor.bounds.Position)
if (_anchorElement != null &&
TranslateBounds(_anchorElement, Child, out var updatedBounds) &&
updatedBounds.Position != _anchorElementBounds.Position)
{
var offset = updatedBounds.Position - _anchor.bounds.Position;
var offset = updatedBounds.Position - _anchorElementBounds.Position;
return offset;
}
return default;
}
// Calculate the new anchor element.
_anchor = CalculateCurrentAnchor();
var isAnchoring = Offset.X >= EdgeDetectionTolerance || Offset.Y >= EdgeDetectionTolerance;
// Do the arrange.
ArrangeOverrideImpl(size, -Offset);
if (isAnchoring)
{
// Calculate the new anchor element if necessary.
EnsureAnchorElementSelection();
// If the anchor moved during the arrange, we need to adjust the offset and do another arrange.
var anchorShift = TrackAnchor();
// Do the arrange.
ArrangeOverrideImpl(size, -Offset);
if (anchorShift != default)
{
var newOffset = Offset + anchorShift;
var newExtent = Extent;
var maxOffset = new Vector(Extent.Width - Viewport.Width, Extent.Height - Viewport.Height);
// If the anchor moved during the arrange, we need to adjust the offset and do another arrange.
var anchorShift = TrackAnchor();
if (newOffset.X > maxOffset.X)
if (anchorShift != default)
{
newExtent = newExtent.WithWidth(newOffset.X + Viewport.Width);
}
var newOffset = Offset + anchorShift;
var newExtent = Extent;
var maxOffset = new Vector(Extent.Width - Viewport.Width, Extent.Height - Viewport.Height);
if (newOffset.Y > maxOffset.Y)
{
newExtent = newExtent.WithHeight(newOffset.Y + Viewport.Height);
}
if (newOffset.X > maxOffset.X)
{
newExtent = newExtent.WithWidth(newOffset.X + Viewport.Width);
}
Extent = newExtent;
if (newOffset.Y > maxOffset.Y)
{
newExtent = newExtent.WithHeight(newOffset.Y + Viewport.Height);
}
try
{
_arranging = true;
Offset = newOffset;
}
finally
{
_arranging = false;
Extent = newExtent;
try
{
_arranging = true;
Offset = newOffset;
}
finally
{
_arranging = false;
}
ArrangeOverrideImpl(size, -Offset);
}
}
else
{
ArrangeOverrideImpl(size, -Offset);
}
Viewport = finalSize;
Extent = Child.Bounds.Size.Inflate(Child.Margin);
_isAnchorElementDirty = true;
return finalSize;
}
@ -350,7 +368,7 @@ namespace Avalonia.Controls.Presenters
{
var logicalUnits = delta.Y / LogicalScrollItemSize;
delta = delta.WithY(delta.Y - logicalUnits * LogicalScrollItemSize);
dy = logicalUnits * scrollable.ScrollSize.Height;
dy = logicalUnits * scrollable!.ScrollSize.Height;
}
else
dy = delta.Y;
@ -368,7 +386,7 @@ namespace Avalonia.Controls.Presenters
{
var logicalUnits = delta.X / LogicalScrollItemSize;
delta = delta.WithX(delta.X - logicalUnits * LogicalScrollItemSize);
dx = logicalUnits * scrollable.ScrollSize.Width;
dx = logicalUnits * scrollable!.ScrollSize.Width;
}
else
dx = delta.X;
@ -405,7 +423,7 @@ namespace Avalonia.Controls.Presenters
if (Extent.Height > Viewport.Height)
{
double height = isLogical ? scrollable.ScrollSize.Height : 50;
double height = isLogical ? scrollable!.ScrollSize.Height : 50;
y += -e.Delta.Y * height;
y = Math.Max(y, 0);
y = Math.Min(y, Extent.Height - Viewport.Height);
@ -413,7 +431,7 @@ namespace Avalonia.Controls.Presenters
if (Extent.Width > Viewport.Width)
{
double width = isLogical ? scrollable.ScrollSize.Width : 50;
double width = isLogical ? scrollable!.ScrollSize.Width : 50;
x += -e.Delta.X * width;
x = Math.Max(x, 0);
x = Math.Min(x, Extent.Width - Viewport.Width);
@ -441,7 +459,7 @@ namespace Avalonia.Controls.Presenters
private void ChildChanged(AvaloniaPropertyChangedEventArgs e)
{
UpdateScrollableSubscription((IControl)e.NewValue);
UpdateScrollableSubscription((IControl?)e.NewValue);
if (e.OldValue != null)
{
@ -449,7 +467,7 @@ namespace Avalonia.Controls.Presenters
}
}
private void UpdateScrollableSubscription(IControl child)
private void UpdateScrollableSubscription(IControl? child)
{
var scrollable = child as ILogicalScrollable;
@ -498,13 +516,17 @@ namespace Avalonia.Controls.Presenters
}
}
private (IControl, Rect) CalculateCurrentAnchor()
private void EnsureAnchorElementSelection()
{
if (_anchorCandidates == null)
if (!_isAnchorElementDirty || _anchorCandidates is null)
{
return default;
return;
}
_anchorElement = null;
_anchorElementBounds = default;
_isAnchorElementDirty = false;
var bestCandidate = default(IControl);
var bestCandidateDistance = double.MaxValue;
@ -531,10 +553,9 @@ namespace Avalonia.Controls.Presenters
// bounds aren't relative to the ScrollContentPresenter itself, if they change
// then we know it wasn't just due to scrolling.
var unscrolledBounds = TranslateBounds(bestCandidate, Child);
return (bestCandidate, unscrolledBounds);
_anchorElement = bestCandidate;
_anchorElementBounds = unscrolledBounds;
}
return default;
}
private bool GetViewportBounds(IControl element, out Rect bounds)

1
src/Avalonia.Controls/Primitives/IPopupHost.cs

@ -47,6 +47,7 @@ namespace Avalonia.Controls.Primitives
/// <param name="offset">The offset, in device-independent pixels.</param>
/// <param name="anchor">The anchor point.</param>
/// <param name="gravity">The popup gravity.</param>
/// <param name="constraintAdjustment">Defines how a popup position will be adjusted if the unadjusted position would result in the popup being partly constrained.</param>
/// <param name="rect">
/// The anchor rect. If null, the bounds of <paramref name="target"/> will be used.
/// </param>

34
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -354,19 +354,15 @@ namespace Avalonia.Controls.Primitives
/// </summary>
/// <param name="eventSource">The control that raised the event.</param>
/// <returns>The container or null if the event did not originate in a container.</returns>
protected IControl? GetContainerFromEventSource(IInteractive eventSource)
protected IControl? GetContainerFromEventSource(IInteractive? eventSource)
{
var parent = (IVisual)eventSource;
while (parent != null)
for (var current = eventSource as IVisual; current != null; current = current.VisualParent)
{
if (parent is IControl control && control.LogicalParent == this
&& ItemContainerGenerator?.IndexFromContainer(control) != -1)
if (current is IControl control && control.LogicalParent == this &&
ItemContainerGenerator?.IndexFromContainer(control) != -1)
{
return control;
}
parent = parent.VisualParent;
}
return null;
@ -670,7 +666,7 @@ namespace Avalonia.Controls.Primitives
/// false.
/// </returns>
protected bool UpdateSelectionFromEventSource(
IInteractive eventSource,
IInteractive? eventSource,
bool select = true,
bool rangeModifier = false,
bool toggleModifier = false,
@ -794,18 +790,13 @@ namespace Avalonia.Controls.Primitives
/// <param name="e">The event.</param>
private void ContainerSelectionChanged(RoutedEventArgs e)
{
if (!_ignoreContainerSelectionChanged)
if (!_ignoreContainerSelectionChanged &&
e.Source is IControl control &&
e.Source is ISelectable selectable &&
control.LogicalParent == this &&
ItemContainerGenerator?.IndexFromContainer(control) != -1)
{
var control = e.Source as IControl;
var selectable = e.Source as ISelectable;
if (control != null &&
selectable != null &&
control.LogicalParent == this &&
ItemContainerGenerator?.IndexFromContainer(control) != -1)
{
UpdateSelection(control, selectable.IsSelected);
}
UpdateSelection(control, selectable.IsSelected);
}
if (e.Source != this)
@ -824,12 +815,11 @@ namespace Avalonia.Controls.Primitives
{
try
{
var selectable = container as ISelectable;
bool result;
_ignoreContainerSelectionChanged = true;
if (selectable != null)
if (container is ISelectable selectable)
{
result = selectable.IsSelected;
selectable.IsSelected = selected;

18
src/Avalonia.Controls/Repeater/ItemsRepeater.cs

@ -267,6 +267,11 @@ namespace Avalonia.Controls
return result;
}
private protected override void InvalidateMeasureOnChildrenChanged()
{
// Don't invalidate measure when children change.
}
protected override Size MeasureOverride(Size availableSize)
{
if (_isLayoutInProgress)
@ -364,6 +369,12 @@ namespace Avalonia.Controls
{
var newBounds = element.Bounds;
virtInfo.ArrangeBounds = newBounds;
if (!virtInfo.IsRegisteredAsAnchorCandidate)
{
_viewportManager.RegisterScrollAnchorCandidate(element);
virtInfo.IsRegisteredAsAnchorCandidate = true;
}
}
}
@ -515,11 +526,14 @@ namespace Avalonia.Controls
return element;
}
internal void OnElementPrepared(IControl element, int index)
internal void OnElementPrepared(IControl element, VirtualizationInfo virtInfo)
{
_viewportManager.OnElementPrepared(element);
_viewportManager.OnElementPrepared(element, virtInfo);
if (ElementPrepared != null)
{
var index = virtInfo.Index;
if (_elementPreparedArgs == null)
{
_elementPreparedArgs = new ItemsRepeaterElementPreparedEventArgs(element, index);

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

@ -661,7 +661,7 @@ namespace Avalonia.Controls
children.Add(element);
}
repeater.OnElementPrepared(element, index);
repeater.OnElementPrepared(element, virtInfo);
// Update realized indices
_firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index);

14
src/Avalonia.Controls/Repeater/ViewportManager.cs

@ -240,9 +240,14 @@ namespace Avalonia.Controls
}
}
public void OnElementPrepared(IControl element)
public void OnElementPrepared(IControl element, VirtualizationInfo virtInfo)
{
_scroller?.RegisterAnchorCandidate(element);
// WinUI registers the element as an anchor candidate here, but I feel that's in error:
// at this point the element has not yet been positioned by the arrange pass so it will
// have its previous position, meaning that when the arrange pass moves it into its new
// position, an incorrect scroll anchoring will occur. Instead signal that it's not yet
// registered as a scroll anchor candidate.
virtInfo.IsRegisteredAsAnchorCandidate = false;
}
public void OnElementCleared(IControl element)
@ -373,6 +378,11 @@ namespace Avalonia.Controls
}
}
public void RegisterScrollAnchorCandidate(IControl element)
{
_scroller?.RegisterAnchorCandidate(element);
}
private IControl GetImmediateChildOfRepeater(IControl descendant)
{
var targetChild = descendant;

1
src/Avalonia.Controls/Repeater/VirtualizationInfo.cs

@ -38,6 +38,7 @@ namespace Avalonia.Controls
public bool IsInUniqueIdResetPool => Owner == ElementOwner.UniqueIdResetPool;
public bool MustClearDataContext { get; set; }
public bool KeepAlive { get; set; }
public bool IsRegisteredAsAnchorCandidate { get; set; }
public ElementOwner Owner { get; private set; } = ElementOwner.ElementFactory;
public string UniqueId { get; private set; }

44
src/Avalonia.Controls/Shapes/Shape.cs

@ -62,7 +62,6 @@ namespace Avalonia.Controls.Shapes
private Matrix _transform = Matrix.Identity;
private Geometry? _definingGeometry;
private Geometry? _renderedGeometry;
private bool _calculateTransformOnArrange;
static Shape()
{
@ -248,52 +247,21 @@ namespace Avalonia.Controls.Shapes
protected override Size MeasureOverride(Size availableSize)
{
bool deferCalculateTransform;
switch (Stretch)
if (DefiningGeometry is null)
{
case Stretch.Fill:
case Stretch.UniformToFill:
deferCalculateTransform = double.IsInfinity(availableSize.Width) || double.IsInfinity(availableSize.Height);
break;
case Stretch.Uniform:
deferCalculateTransform = double.IsInfinity(availableSize.Width) && double.IsInfinity(availableSize.Height);
break;
case Stretch.None:
default:
deferCalculateTransform = false;
break;
return default;
}
if (deferCalculateTransform)
{
_calculateTransformOnArrange = true;
return DefiningGeometry?.Bounds.Size ?? Size.Empty;
}
else
{
_calculateTransformOnArrange = false;
return CalculateShapeSizeAndSetTransform(availableSize);
}
return CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch).size;
}
protected override Size ArrangeOverride(Size finalSize)
{
if (_calculateTransformOnArrange)
{
_calculateTransformOnArrange = false;
CalculateShapeSizeAndSetTransform(finalSize);
}
return finalSize;
}
private Size CalculateShapeSizeAndSetTransform(Size availableSize)
{
if (DefiningGeometry != null)
{
// This should probably use GetRenderBounds(strokeThickness) but then the calculations
// will multiply the stroke thickness as well, which isn't correct.
var (size, transform) = CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch);
var (_, transform) = CalculateSizeAndTransform(finalSize, DefiningGeometry.Bounds, Stretch);
if (_transform != transform)
{
@ -301,13 +269,13 @@ namespace Avalonia.Controls.Shapes
_renderedGeometry = null;
}
return size;
return finalSize;
}
return Size.Empty;
}
internal static (Size, Matrix) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch)
internal static (Size size, Matrix transform) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch)
{
Size shapeSize = new Size(shapeBounds.Right, shapeBounds.Bottom);
Matrix translate = Matrix.Identity;

2
src/Avalonia.Controls/Window.cs

@ -451,7 +451,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="dialogResult">The dialog result.</param>
/// <remarks>
/// When the window is shown with the <see cref="ShowDialog{TResult}(IWindowImpl)"/>
/// When the window is shown with the <see cref="ShowDialog{TResult}(Window)"/>
/// or <see cref="ShowDialog{TResult}(Window)"/> method, the
/// resulting task will produce the <see cref="_dialogResult"/> value when the window
/// is closed.

5
src/Avalonia.Input/KeyGesture.cs

@ -144,7 +144,10 @@ namespace Avalonia.Input
return s.ToString();
}
public bool Matches(KeyEventArgs keyEvent) => ResolveNumPadOperationKey(keyEvent.Key) == Key && keyEvent.KeyModifiers == KeyModifiers;
public bool Matches(KeyEventArgs keyEvent) =>
keyEvent != null &&
keyEvent.KeyModifiers == KeyModifiers &&
ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(Key);
// TODO: Move that to external key parser
private static Key ParseKey(string key)

2
src/Avalonia.Layout/StackLayout.cs

@ -249,8 +249,8 @@ namespace Avalonia.Layout
realizationWindowOffsetInExtent + _orientation.MajorSize(realizationRect) >= 0 && realizationWindowOffsetInExtent <= majorSize)
{
anchorIndex = (int) (realizationWindowOffsetInExtent / averageElementSize);
offset = anchorIndex* averageElementSize + _orientation.MajorStart(lastExtent);
anchorIndex = Math.Max(0, Math.Min(itemsCount - 1, anchorIndex));
offset = anchorIndex* averageElementSize + _orientation.MajorStart(lastExtent);
}
}

1
src/Avalonia.Visuals/Media/Imaging/Bitmap.cs

@ -99,7 +99,6 @@ namespace Avalonia.Media.Imaging
/// Initializes a new instance of the <see cref="Bitmap"/> class.
/// </summary>
/// <param name="format">The pixel format.</param>
/// <param name="alphaFormat">The alpha format.</param>
/// <param name="data">The pointer to the source bytes.</param>
/// <param name="size">The size of the bitmap in device pixels.</param>
/// <param name="dpi">The DPI of the bitmap.</param>

2
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -69,7 +69,7 @@ namespace Avalonia.Platform
/// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible.
/// </remarks>
void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect,
BoxShadows boxShadow = default);
BoxShadows boxShadows = default);
/// <summary>
/// Draws text.

7
src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs

@ -13,10 +13,9 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary>
/// Initializes a new instance of the <see cref="RectangleNode"/> class.
/// </summary>
/// <param name="transform">The transform.</param>
/// <param name="transform">The transform.</param>
/// <param name="material"></param>
/// <param name="rect">The rectangle to draw.</param>
/// <param name="boxShadow">The box shadow parameters</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public ExperimentalAcrylicNode(
Matrix transform,
IExperimentalAcrylicMaterial material,
@ -44,7 +43,7 @@ namespace Avalonia.Rendering.SceneGraph
/// Determines if this draw operation equals another.
/// </summary>
/// <param name="transform">The transform of the other draw operation.</param>
/// <param name="brush">The fill of the other draw operation.</param>
/// <param name="material">The fill of the other draw operation.</param>
/// <param name="rect">The rectangle of the other draw operation.</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>

1
src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs

@ -63,7 +63,6 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="brush">The fill of the other draw operation.</param>
/// <param name="pen">The stroke of the other draw operation.</param>
/// <param name="geometry">The geometry of the other draw operation.</param>
/// <param name="boxShadow">The box shadow parameters</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent

2
src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs

@ -73,7 +73,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="brush">The fill of the other draw operation.</param>
/// <param name="pen">The stroke of the other draw operation.</param>
/// <param name="rect">The rectangle of the other draw operation.</param>
/// <param name="boxShadow">The box shadow parameters of the other draw operation</param>
/// <param name="boxShadows">The box shadow parameters of the other draw operation</param>
/// <returns>True if the draw operations are the same, otherwise false.</returns>
/// <remarks>
/// The properties of the other draw operation are passed in as arguments to prevent

16
src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs

@ -113,22 +113,22 @@ namespace Avalonia.LinuxFramebuffer.Output
[StructLayout(LayoutKind.Sequential)]
public struct drmModeConnector {
public uint connector_id;
public uint encoder_id; /**< Encoder currently connected to */
public uint encoder_id; // Encoder currently connected to
public uint connector_type;
public uint connector_type_id;
public DrmModeConnection connection;
public uint mmWidth, mmHeight; /**< HxW in millimeters */
public uint mmWidth, mmHeight; // HxW in millimeters
public DrmModeSubPixel subpixel;
public int count_modes;
public drmModeModeInfo* modes;
public int count_props;
public uint *props; /**< List of property ids */
public ulong *prop_values; /**< List of property values */
public uint *props; // List of property ids
public ulong *prop_values; // List of property values
public int count_encoders;
public uint *encoders; /**< List of encoder ids */
public uint *encoders; //List of encoder ids
}
[StructLayout(LayoutKind.Sequential)]
@ -143,14 +143,14 @@ namespace Avalonia.LinuxFramebuffer.Output
[StructLayout(LayoutKind.Sequential)]
public struct drmModeCrtc {
public uint crtc_id;
public uint buffer_id; /**< FB id to connect to 0 = disconnect */
public uint buffer_id; // FB id to connect to 0 = disconnect
public uint x, y; /**< Position on the framebuffer */
public uint x, y; // Position on the framebuffer
public uint width, height;
public int mode_valid;
public drmModeModeInfo mode;
public int gamma_size; /**< Number of gamma stops */
public int gamma_size; // Number of gamma stops
}

31
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs

@ -3,7 +3,6 @@ using System.IO;
using System.Reflection;
using System.Text;
using Avalonia.Markup.Xaml.XamlIl;
// ReSharper disable CheckNamespace
namespace Avalonia.Markup.Xaml
{
@ -13,10 +12,10 @@ namespace Avalonia.Markup.Xaml
/// Loads XAML from a string.
/// </summary>
/// <param name="xaml">The string containing the XAML.</param>
/// <param name="localAssembly">Default assembly for clr-namespace:</param>
/// <param name="rootInstance">
/// The optional instance into which the XAML should be loaded.
/// </param>
/// <param name="localAssembly">Default assembly for clr-namespace:.</param>
/// <param name="rootInstance">The optional instance into which the XAML should be loaded.</param>
/// <param name="uri">The URI of the XAML being loaded.</param>
/// <param name="designMode">Indicates whether the XAML is being loaded in design mode.</param>
/// <returns>The loaded object.</returns>
public static object Load(string xaml, Assembly localAssembly = null, object rootInstance = null, Uri uri = null, bool designMode = false)
{
@ -28,13 +27,35 @@ namespace Avalonia.Markup.Xaml
}
}
/// <summary>
/// Loads XAML from a stream.
/// </summary>
/// <param name="stream">The stream containing the XAML.</param>
/// <param name="localAssembly">Default assembly for clr-namespace:</param>
/// <param name="rootInstance">The optional instance into which the XAML should be loaded.</param>
/// <param name="uri">The URI of the XAML being loaded.</param>
/// <param name="designMode">Indicates whether the XAML is being loaded in design mode.</param>
/// <returns>The loaded object.</returns>
public static object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null,
bool designMode = false)
=> AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, designMode);
/// <summary>
/// Parse XAML from a string.
/// </summary>
/// <param name="xaml">The string containing the XAML.</param>
/// <param name="localAssembly">Default assembly for clr-namespace:.</param>
/// <returns>The loaded object.</returns>
public static object Parse(string xaml, Assembly localAssembly = null)
=> Load(xaml, localAssembly);
/// <summary>
/// Parse XAML from a string.
/// </summary>
/// <typeparam name="T">The type of the returned object.</typeparam>
/// <param name="xaml">>The string containing the XAML.</param>
/// <param name="localAssembly">>Default assembly for clr-namespace:.</param>
/// <returns>The loaded object.</returns>
public static T Parse<T>(string xaml, Assembly localAssembly = null)
=> (T)Parse(xaml, localAssembly);

44
src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs

@ -1,6 +1,5 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Media;
using Avalonia.Platform;
using HarfBuzzSharp;
using SkiaSharp;
@ -24,40 +23,33 @@ namespace Avalonia.Skia
Font.SetFunctionsOpenType();
Font.GetScale(out var xScale, out _);
DesignEmHeight = (short)Typeface.UnitsPerEm;
DesignEmHeight = (short)xScale;
var metrics = Typeface.ToFont().Metrics;
if (!Font.TryGetHorizontalFontExtents(out var fontExtents))
{
Font.TryGetVerticalFontExtents(out fontExtents);
}
const double defaultFontRenderingEmSize = 12.0;
Ascent = -fontExtents.Ascender;
Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
Descent = -fontExtents.Descender;
Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
LineGap = fontExtents.LineGap;
LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm);
if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlinePosition))
{
UnderlinePosition = underlinePosition;
}
UnderlinePosition = metrics.UnderlinePosition != null ?
(int)(metrics.UnderlinePosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
0;
if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineThickness))
{
UnderlineThickness = underlineThickness;
}
UnderlineThickness = metrics.UnderlineThickness != null ?
(int)(metrics.UnderlineThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
0;
if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughPosition))
{
StrikethroughPosition = strikethroughPosition;
}
StrikethroughPosition = metrics.StrikeoutPosition != null ?
(int)(metrics.StrikeoutPosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
0;
if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughThickness))
{
StrikethroughThickness = strikethroughThickness;
}
StrikethroughThickness = metrics.StrikeoutThickness != null ?
(int)(metrics.StrikeoutThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) :
0;
IsFixedPitch = Typeface.IsFixedPitch;
}

36
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -238,22 +238,46 @@ namespace Avalonia.Direct2D1
width = 0;
for (var i = 0; i < glyphCount; i++)
var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight);
if (glyphRun.GlyphAdvances.IsEmpty)
{
for (var i = 0; i < glyphCount; i++)
{
var advance = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale;
run.Advances[i] = advance;
width += advance;
}
}
else
{
for (var i = 0; i < glyphCount; i++)
{
var advance = (float)glyphRun.GlyphAdvances[i];
run.Advances[i] = advance;
width += advance;
}
}
if (glyphRun.GlyphOffsets.IsEmpty)
{
run.Advances[i] = (float)glyphRun.GlyphAdvances[i];
width += run.Advances[i];
return new GlyphRunImpl(run);
}
run.Offsets = new GlyphOffset[glyphCount];
for (var i = 0; i < glyphCount; i++)
{
var offset = glyphRun.GlyphOffsets[i];
var (x, y) = glyphRun.GlyphOffsets[i];
run.Offsets[i] = new GlyphOffset
{
AdvanceOffset = (float)offset.X,
AscenderOffset = (float)offset.Y
AdvanceOffset = (float)x,
AscenderOffset = (float)y
};
}

1
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -323,7 +323,6 @@ namespace Avalonia.Direct2D1.Media
/// </summary>
/// <param name="foreground">The foreground.</param>
/// <param name="glyphRun">The glyph run.</param>
/// <param name="baselineOrigin"></param>
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
using (var brush = CreateBrush(foreground, glyphRun.Size))

160
src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs

@ -1,6 +1,6 @@
using System.Globalization;
using System;
using System.Globalization;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
using Avalonia.Utilities;
@ -15,51 +15,9 @@ namespace Avalonia.Direct2D1.Media
{
using (var buffer = new Buffer())
{
buffer.ContentType = ContentType.Unicode;
FillBuffer(buffer, text);
var breakCharPosition = text.Length - 1;
var codepoint = Codepoint.ReadAt(text, breakCharPosition, out var count);
if (codepoint.IsBreakChar)
{
var breakCharCount = 1;
if (text.Length > 1)
{
var previousCodepoint = Codepoint.ReadAt(text, breakCharPosition - count, out _);
if (codepoint == '\r' && previousCodepoint == '\n'
|| codepoint == '\n' && previousCodepoint == '\r')
{
breakCharCount = 2;
}
}
if (breakCharPosition != text.Start)
{
buffer.AddUtf16(text.Buffer.Span.Slice(0, text.Length - breakCharCount));
}
var cluster = buffer.GlyphInfos.Length > 0 ?
buffer.GlyphInfos[buffer.Length - 1].Cluster + 1 :
(uint)text.Start;
switch (breakCharCount)
{
case 1:
buffer.Add('\u200C', cluster);
break;
case 2:
buffer.Add('\u200C', cluster);
buffer.Add('\u200D', cluster);
break;
}
}
else
{
buffer.AddUtf16(text.Buffer.Span);
}
buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
buffer.GuessSegmentProperties();
@ -67,44 +25,38 @@ namespace Avalonia.Direct2D1.Media
var font = ((GlyphTypefaceImpl)glyphTypeface.PlatformImpl).Font;
buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture);
font.Shape(buffer);
font.GetScale(out var scaleX, out _);
var textScale = fontRenderingEmSize / scaleX;
var len = buffer.Length;
var bufferLength = buffer.Length;
var info = buffer.GetGlyphInfoSpan();
var glyphInfos = buffer.GetGlyphInfoSpan();
var pos = buffer.GetGlyphPositionSpan();
var glyphPositions = buffer.GetGlyphPositionSpan();
var glyphIndices = new ushort[len];
var glyphIndices = new ushort[bufferLength];
var clusters = new ushort[len];
var clusters = new ushort[bufferLength];
var glyphAdvances = new double[len];
double[] glyphAdvances = null;
var glyphOffsets = new Vector[len];
Vector[] glyphOffsets = null;
for (var i = 0; i < len; i++)
for (var i = 0; i < bufferLength; i++)
{
glyphIndices[i] = (ushort)info[i].Codepoint;
clusters[i] = (ushort)(text.Start + info[i].Cluster);
var advanceX = pos[i].XAdvance * textScale;
// Depends on direction of layout
//var advanceY = pos[i].YAdvance * textScale;
glyphIndices[i] = (ushort)glyphInfos[i].Codepoint;
glyphAdvances[i] = advanceX;
clusters[i] = (ushort)glyphInfos[i].Cluster;
var offsetX = pos[i].XOffset * textScale;
var offsetY = pos[i].YOffset * textScale;
if (!glyphTypeface.IsFixedPitch)
{
SetAdvance(glyphPositions, i, textScale, ref glyphAdvances);
}
glyphOffsets[i] = new Vector(offsetX, offsetY);
SetOffset(glyphPositions, i, textScale, ref glyphOffsets);
}
return new GlyphRun(glyphTypeface, fontRenderingEmSize,
@ -115,5 +67,79 @@ namespace Avalonia.Direct2D1.Media
new ReadOnlySlice<ushort>(clusters));
}
}
private static void FillBuffer(Buffer buffer, ReadOnlySlice<char> text)
{
buffer.ContentType = ContentType.Unicode;
var i = 0;
while (i < text.Length)
{
var codepoint = Codepoint.ReadAt(text, i, out var count);
var cluster = (uint)(text.Start + i);
if (codepoint.IsBreakChar)
{
if (i + 1 < text.Length)
{
var nextCodepoint = Codepoint.ReadAt(text, i + 1, out _);
if (nextCodepoint == '\r' && codepoint == '\n' || nextCodepoint == '\n' && codepoint == '\r')
{
count++;
buffer.Add('\u200C', cluster);
buffer.Add('\u200D', cluster);
}
else
{
buffer.Add('\u200C', cluster);
}
}
else
{
buffer.Add('\u200C', cluster);
}
}
else
{
buffer.Add(codepoint, cluster);
}
i += count;
}
}
private static void SetOffset(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale,
ref Vector[] offsetBuffer)
{
var position = glyphPositions[index];
if (position.XOffset == 0 && position.YOffset == 0)
{
return;
}
offsetBuffer ??= new Vector[glyphPositions.Length];
var offsetX = position.XOffset * textScale;
var offsetY = position.YOffset * textScale;
offsetBuffer[index] = new Vector(offsetX, offsetY);
}
private static void SetAdvance(ReadOnlySpan<GlyphPosition> glyphPositions, int index, double textScale,
ref double[] advanceBuffer)
{
advanceBuffer ??= new double[glyphPositions.Length];
// Depends on direction of layout
// advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale;
advanceBuffer[index] = glyphPositions[index].XAdvance * textScale;
}
}
}

2
src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<ExtrasEnableWpfProjectSetup>true</ExtrasEnableWpfProjectSetup>
<UseWpf>true</UseWpf>
<ExtrasEnableImplicitWinFormsReferences>true</ExtrasEnableImplicitWinFormsReferences>
<UseDirect3D9>true</UseDirect3D9>
<PackageId>Avalonia.Win32.Interoperability</PackageId>

1
src/Windows/Avalonia.Win32/Interop/TaskBarList.cs

@ -13,6 +13,7 @@ namespace Avalonia.Win32.Interop
/// <summary>
/// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc
/// </summary>
/// <param name="hwnd">The window handle.</param>
/// <param name="fullscreen">Fullscreen state.</param>
public static unsafe void MarkFullscreen(IntPtr hwnd, bool fullscreen)
{

17
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -343,16 +343,21 @@ namespace Avalonia.Win32
case WindowsMessage.WM_PAINT:
{
using (_rendererLock.Lock())
using (_rendererLock.Lock())
{
if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero)
{
Paint?.Invoke(default);
var f = RenderScaling;
var r = ps.rcPaint;
Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f,
(r.bottom - r.top) / f));
EndPaint(_hwnd, ref ps);
}
ValidateRect(hWnd, IntPtr.Zero);
return IntPtr.Zero;
}
return IntPtr.Zero;
}
case WindowsMessage.WM_SIZE:
{
using (_rendererLock.Lock())

65
src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs

@ -1,5 +1,4 @@
using System;
using System.Diagnostics;
using Avalonia.Controls;
using Avalonia.Input;
using static Avalonia.Win32.Interop.UnmanagedMethods;
@ -11,70 +10,78 @@ namespace Avalonia.Win32
public partial class WindowImpl
{
// Hit test the frame for resizing and moving.
HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
private HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
{
// Get the point coordinates for the hit test.
// Get the point coordinates for the hit test (screen space).
var ptMouse = PointFromLParam(lParam);
// Get the window rectangle.
// Get the window rectangle.
GetWindowRect(hWnd, out var rcWindow);
// Get the frame rectangle, adjusted for the style without a caption.
RECT rcFrame = new RECT();
var rcFrame = new RECT();
AdjustWindowRectEx(ref rcFrame, (uint)(WindowStyles.WS_OVERLAPPEDWINDOW & ~WindowStyles.WS_CAPTION), false, 0);
RECT border_thickness = new RECT();
var borderThickness = new RECT();
if (GetStyle().HasFlag(WindowStyles.WS_THICKFRAME))
{
AdjustWindowRectEx(ref border_thickness, (uint)(GetStyle()), false, 0);
border_thickness.left *= -1;
border_thickness.top *= -1;
AdjustWindowRectEx(ref borderThickness, (uint)(GetStyle()), false, 0);
borderThickness.left *= -1;
borderThickness.top *= -1;
}
else if (GetStyle().HasFlag(WindowStyles.WS_BORDER))
{
border_thickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 };
borderThickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 };
}
if (_extendTitleBarHint >= 0)
{
border_thickness.top = (int)(_extendedMargins.Top * RenderScaling);
borderThickness.top = (int)(_extendedMargins.Top * RenderScaling);
}
// Determine if the hit test is for resizing. Default middle (1,1).
ushort uRow = 1;
ushort uCol = 1;
bool fOnResizeBorder = false;
bool onResizeBorder = false;
// Determine if the point is at the top or bottom of the window.
if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + border_thickness.top)
// Determine if the point is at the left or right of the window.
if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + borderThickness.left)
{
fOnResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top));
uRow = 0;
uCol = 0; // left side
}
else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - border_thickness.bottom)
else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - borderThickness.right)
{
uRow = 2;
uCol = 2; // right side
}
// Determine if the point is at the left or right of the window.
if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + border_thickness.left)
// Determine if the point is at the top or bottom of the window.
if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + borderThickness.top)
{
uCol = 0; // left side
onResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top));
// Two cases where we have a valid row 0 hit test:
// - window resize border (top resize border hit)
// - area below resize border that is actual titlebar (caption hit).
if (onResizeBorder || uCol == 1)
{
uRow = 0;
}
}
else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - border_thickness.right)
else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - borderThickness.bottom)
{
uCol = 2; // right side
uRow = 2;
}
// Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
HitTestValues[][] hitTests = new[]
ReadOnlySpan<HitTestValues> hitZones = stackalloc HitTestValues[]
{
new []{ HitTestValues.HTTOPLEFT, fOnResizeBorder ? HitTestValues.HTTOP : HitTestValues.HTCAPTION, HitTestValues.HTTOPRIGHT },
new []{ HitTestValues.HTLEFT, HitTestValues.HTNOWHERE, HitTestValues.HTRIGHT },
new []{ HitTestValues.HTBOTTOMLEFT, HitTestValues.HTBOTTOM, HitTestValues.HTBOTTOMRIGHT },
HitTestValues.HTTOPLEFT, onResizeBorder ? HitTestValues.HTTOP : HitTestValues.HTCAPTION,
HitTestValues.HTTOPRIGHT, HitTestValues.HTLEFT, HitTestValues.HTNOWHERE, HitTestValues.HTRIGHT,
HitTestValues.HTBOTTOMLEFT, HitTestValues.HTBOTTOM, HitTestValues.HTBOTTOMRIGHT
};
return hitTests[uRow][uCol];
var zoneIndex = uRow * 3 + uCol;
return hitZones[zoneIndex];
}
protected virtual IntPtr CustomCaptionProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool callDwp)

8
tests/Avalonia.Animation.UnitTests/AnimatableTests.cs

@ -113,7 +113,7 @@ namespace Avalonia.Animation.UnitTests
It.IsAny<IClock>(),
1.0,
0.5));
target.ResetCalls();
target.Invocations.Clear();
control.SetValue(Visual.OpacityProperty, 0.8, BindingPriority.StyleTrigger);
@ -135,7 +135,7 @@ namespace Avalonia.Animation.UnitTests
target.Setup(x => x.Apply(control, It.IsAny<IClock>(), 1.0, 0.5)).Returns(sub.Object);
control.Opacity = 0.5;
sub.ResetCalls();
sub.Invocations.Clear();
control.Opacity = 0.4;
sub.Verify(x => x.Dispose());
@ -158,7 +158,7 @@ namespace Avalonia.Animation.UnitTests
control.Opacity = 0.5;
Assert.Equal(0.9, control.Opacity);
target.ResetCalls();
target.Invocations.Clear();
control.Opacity = 0.4;
@ -182,7 +182,7 @@ namespace Avalonia.Animation.UnitTests
It.IsAny<IClock>(),
1.0,
0.5));
target.ResetCalls();
target.Invocations.Clear();
var root = (TestRoot)control.Parent;
root.Child = null;

4
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs

@ -67,7 +67,7 @@ namespace Avalonia.Base.UnitTests
public static readonly DirectProperty<Class1, string> DirectProperty =
AvaloniaProperty.RegisterDirect<Class1, string>("Styled", o => o.Direct, unsetValue: "foo");
private string _direct;
private string _direct = default;
public string Direct
{
@ -92,7 +92,7 @@ namespace Avalonia.Base.UnitTests
public static readonly DirectProperty<Class3, string> DirectProperty =
Class1.DirectProperty.AddOwner<Class3>(o => o.Direct, unsetValue: "baz");
private string _direct;
private string _direct = default;
static Class3()
{

4
tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs

@ -6,10 +6,12 @@ namespace Avalonia.Base.UnitTests.Collections
{
public class AvaloniaListExtenionsTests
{
#pragma warning disable CS0618 // Type or member is obsolete
[Fact]
public void CreateDerivedList_Creates_Initial_Items()
{
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 });
var target = source.CreateDerivedList(x => new Wrapper(x));
var result = target.Select(x => x.Value).ToList();
@ -137,6 +139,8 @@ namespace Avalonia.Base.UnitTests.Collections
Assert.Equal(source, result);
}
#pragma warning restore CS0618 // Type or member is obsolete
private class Wrapper
{

2
tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs

@ -356,7 +356,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
}
[Fact]
public async Task Null_Value_Should_Use_TargetNullValue()
public void Null_Value_Should_Use_TargetNullValue()
{
var data = new Class1 { StringValue = "foo" };

14
tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs

@ -1,12 +1,11 @@
using System;
using System.ComponentModel;
using System.Globalization;
using Avalonia.Controls;
using Avalonia.Data;
using Xunit;
using System.Windows.Input;
using System;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Layout;
using System.ComponentModel;
using Xunit;
namespace Avalonia.Base.UnitTests.Data.Converters
{
@ -251,6 +250,11 @@ namespace Avalonia.Base.UnitTests.Data.Converters
{
return obj is CustomType other && this.Value == other.Value;
}
public override int GetHashCode()
{
return 8399587^Value.GetHashCode();
}
}
private class CustomTypeConverter : TypeConverter

3
tests/Avalonia.Benchmarks/NullRenderer.cs

@ -9,8 +9,9 @@ namespace Avalonia.Benchmarks
{
public bool DrawFps { get; set; }
public bool DrawDirtyRects { get; set; }
#pragma warning disable CS0067
public event EventHandler<SceneInvalidatedEventArgs> SceneInvalidated;
#pragma warning restore CS0067
public void AddDirty(IVisual visual)
{
}

3
tests/Avalonia.Benchmarks/NullThreadingPlatform.cs

@ -23,6 +23,9 @@ namespace Avalonia.Benchmarks
public bool CurrentThreadIsLoopThread => true;
#pragma warning disable CS0067
public event Action<DispatcherPriority?> Signaled;
#pragma warning restore CS0067
}
}

2
tests/Avalonia.Controls.UnitTests/BorderTests.cs

@ -54,7 +54,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
renderer.ResetCalls();
renderer.Invocations.Clear();
((SolidColorBrush)target.Background).Color = Colors.Green;

9
tests/Avalonia.Controls.UnitTests/CanvasTests.cs

@ -1,5 +1,6 @@
using System;
using Avalonia.Controls.Shapes;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@ -9,6 +10,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Left_Property_Should_Work()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
Rectangle rect;
var target = new Canvas
{
@ -34,6 +37,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Top_Property_Should_Work()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
Rectangle rect;
var target = new Canvas
{
@ -59,6 +64,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Right_Property_Should_Work()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
Rectangle rect;
var target = new Canvas
{
@ -84,6 +91,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Bottom_Property_Should_Work()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
Rectangle rect;
var target = new Canvas
{

15
tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs

@ -1,5 +1,6 @@
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@ -171,6 +172,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_RotateTransform_90_degrees()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
25,
@ -193,6 +196,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_RotateTransform_minus_90_degrees()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
25,
@ -215,6 +220,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_ScaleTransform_x2()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
50,
@ -236,6 +243,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_SkewTransform_45_degrees()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
100,
@ -258,6 +267,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Should_Generate_SkewTransform_minus_45_degrees()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
100,
100,
@ -279,6 +290,8 @@ namespace Avalonia.Controls.UnitTests
private static void TransformMeasureSizeTest(Size size, Transform transform, Size expectedSize)
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(
size.Width,
size.Height,
@ -292,6 +305,8 @@ namespace Avalonia.Controls.UnitTests
private static void TransformRootBoundsTest(Size size, Transform transform, Rect expectedBounds)
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(size.Width, size.Height, transform);
Rect outBounds = lt.TransformRoot.Bounds;

47
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -407,6 +407,53 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(1, raised);
}
[Fact]
public void Adding_And_Selecting_Item_With_AutoScrollToSelectedItem_Should_NotHide_FirstItem()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var items = new AvaloniaList<string>();
var wnd = new Window() { Width = 100, Height = 100, IsVisible = true };
var target = new ListBox()
{
VerticalAlignment = Layout.VerticalAlignment.Top,
AutoScrollToSelectedItem = true,
Width = 50,
VirtualizationMode = ItemVirtualizationMode.Simple,
ItemTemplate = new FuncDataTemplate<object>((c, _) => new Border() { Height = 10 }),
Items = items,
};
wnd.Content = target;
var lm = wnd.LayoutManager;
lm.ExecuteInitialLayoutPass();
var panel = target.Presenter.Panel;
items.Add("Item 1");
target.Selection.Select(0);
lm.ExecuteLayoutPass();
Assert.Equal(1, panel.Children.Count);
items.Add("Item 2");
target.Selection.Select(1);
lm.ExecuteLayoutPass();
Assert.Equal(2, panel.Children.Count);
//make sure we have enough space to show all items
Assert.True(panel.Bounds.Height >= panel.Children.Sum(c => c.Bounds.Height));
//make sure we show items and they completelly visible, not only partially
Assert.True(panel.Children[0].Bounds.Top >= 0 && panel.Children[0].Bounds.Bottom <= panel.Bounds.Height, "first item is not completelly visible!");
Assert.True(panel.Children[1].Bounds.Top >= 0 && panel.Children[1].Bounds.Bottom <= panel.Bounds.Height, "second item is not completelly visible!");
}
}
private FuncControlTemplate ListBoxTemplate()
{
return new FuncControlTemplate<ListBox>((parent, scope) =>

2
tests/Avalonia.Controls.UnitTests/PanelTests.cs

@ -127,7 +127,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
renderer.ResetCalls();
renderer.Invocations.Clear();
((SolidColorBrush)target.Background).Color = Colors.Green;

10
tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs

@ -503,26 +503,26 @@ namespace Avalonia.Controls.UnitTests.Platform
target.PointerEnter(item, enter);
Assert.True(timer.ActionIsQueued);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
Mock.Get(parentItem).ResetCalls();
Mock.Get(parentItem).Invocations.Clear();
// SubMenu shown after a delay.
timer.Pulse();
Mock.Get(item).Verify(x => x.Open());
Mock.Get(item).SetupGet(x => x.IsSubMenuOpen).Returns(true);
Mock.Get(item).ResetCalls();
Mock.Get(item).Invocations.Clear();
// Pointer briefly exits item, but submenu remains open.
target.PointerLeave(item, leave);
Mock.Get(item).Verify(x => x.Close(), Times.Never);
Mock.Get(item).ResetCalls();
Mock.Get(item).Invocations.Clear();
// Pointer enters child item; is selected.
enter.Source = childItem;
target.PointerEnter(childItem, enter);
Mock.Get(item).VerifySet(x => x.SelectedItem = childItem);
Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item);
Mock.Get(item).ResetCalls();
Mock.Get(parentItem).ResetCalls();
Mock.Get(item).Invocations.Clear();
Mock.Get(parentItem).Invocations.Clear();
}
[Fact]

2
tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs

@ -212,7 +212,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
renderer.ResetCalls();
renderer.Invocations.Clear();
((SolidColorBrush)target.Background).Color = Colors.Green;

6
tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs

@ -1,4 +1,5 @@
using Avalonia.Controls.Shapes;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@ -8,6 +9,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Lays_Out_1_Child_Next_the_other()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
@ -34,6 +36,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Lays_Out_1_Child_Below_the_other()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
@ -60,6 +63,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void RelativePanel_Can_Center()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
@ -86,6 +90,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void LeftOf_Measures_Correctly()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
@ -111,6 +116,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Above_Measures_Correctly()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };

57
tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs

@ -0,0 +1,57 @@
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests.Shapes
{
public class EllipseTests
{
[Fact]
public void Measure_Does_Not_Set_RenderedGeometry_Rect()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Ellipse();
target.Measure(new Size(100, 100));
var geometry = Assert.IsType<EllipseGeometry>(target.RenderedGeometry);
Assert.Equal(default, geometry.Rect);
}
[Fact]
public void Arrange_Sets_RenderedGeometry_Properties()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Ellipse();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
var geometry = Assert.IsType<EllipseGeometry>(target.RenderedGeometry);
Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect);
}
[Fact]
public void Rearranging_Updates_RenderedGeometry_Rect()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Ellipse();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
var geometry = Assert.IsType<EllipseGeometry>(target.RenderedGeometry);
Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect);
target.Measure(new Size(200, 200));
target.Arrange(new Rect(0, 0, 200, 200));
geometry = Assert.IsType<EllipseGeometry>(target.RenderedGeometry);
Assert.Equal(new Rect(0, 0, 200, 200), geometry.Rect);
}
}
}

144
tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs

@ -34,5 +34,149 @@ namespace Avalonia.Controls.UnitTests.Shapes
root.Child = null;
}
[Theory]
[InlineData(Stretch.None, 100, 200)]
[InlineData(Stretch.Fill, 500, 500)]
[InlineData(Stretch.Uniform, 250, 500)]
[InlineData(Stretch.UniformToFill, 500, 500)]
public void Calculates_Correct_DesiredSize_For_Finite_Bounds(Stretch stretch, double expectedWidth, double expectedHeight)
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Path()
{
Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
Stretch = stretch,
};
target.Measure(new Size(500, 500));
Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize);
}
[Theory]
[InlineData(Stretch.None)]
[InlineData(Stretch.Fill)]
[InlineData(Stretch.Uniform)]
[InlineData(Stretch.UniformToFill)]
public void Calculates_Correct_DesiredSize_For_Infinite_Bounds(Stretch stretch)
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Path()
{
Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
Stretch = stretch,
};
target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Assert.Equal(new Size(100, 200), target.DesiredSize);
}
[Fact]
public void Measure_Does_Not_Update_RenderedGeometry_Transform()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Path
{
Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
Stretch = Stretch.Fill,
};
target.Measure(new Size(500, 500));
Assert.Null(target.RenderedGeometry.Transform);
}
[Theory]
[InlineData(Stretch.None, 1, 1)]
[InlineData(Stretch.Fill, 5, 2.5)]
[InlineData(Stretch.Uniform, 2.5, 2.5)]
[InlineData(Stretch.UniformToFill, 5, 5)]
public void Arrange_Updates_RenderedGeometry_Transform(Stretch stretch, double expectedScaleX, double expectedScaleY)
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Path
{
Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
Stretch = stretch,
};
target.Measure(new Size(500, 500));
target.Arrange(new Rect(0, 0, 500, 500));
if (expectedScaleX == 1 && expectedScaleY == 1)
{
Assert.Null(target.RenderedGeometry.Transform);
}
else
{
Assert.Equal(Matrix.CreateScale(expectedScaleX, expectedScaleY), target.RenderedGeometry.Transform.Value);
}
}
[Fact]
public void Arrange_Reserves_All_Of_Arrange_Rect()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
RectangleGeometry geometry;
var target = new Path
{
Data = geometry = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) },
Stretch = Stretch.Uniform,
};
target.Measure(new Size(400, 400));
target.Arrange(new Rect(0, 0, 400, 400));
Assert.Equal(new Rect(0, 0, 100, 200), geometry.Rect);
Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value);
Assert.Equal(new Rect(0, 0, 400, 400), target.Bounds);
}
[Fact]
public void Measure_Without_Arrange_Does_Not_Clear_RenderedGeometry_Transform()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Path
{
Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 100) },
Stretch = Stretch.Fill,
};
target.Measure(new Size(200, 200));
target.Arrange(new Rect(0, 0, 200, 200));
Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value);
target.Measure(new Size(300, 300));
Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value);
}
[Fact]
public void Arrange_Without_Measure_Updates_RenderedGeometry_Transform()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Path
{
Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 100) },
Stretch = Stretch.Fill,
};
target.Measure(new Size(200, 200));
target.Arrange(new Rect(0, 0, 200, 200));
Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value);
target.Arrange(new Rect(0, 0, 300, 300));
Assert.Equal(Matrix.CreateScale(3, 3), target.RenderedGeometry.Transform.Value);
}
}
}

56
tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.UnitTests;
using Moq;
@ -11,6 +8,53 @@ namespace Avalonia.Controls.UnitTests.Shapes
{
public class RectangleTests
{
[Fact]
public void Measure_Does_Not_Set_RenderedGeometry_Rect()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Rectangle();
target.Measure(new Size(100, 100));
var geometry = Assert.IsType<RectangleGeometry>(target.RenderedGeometry);
Assert.Equal(Rect.Empty, geometry.Rect);
}
[Fact]
public void Arrange_Sets_RenderedGeometry_Rect()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Rectangle();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
var geometry = Assert.IsType<RectangleGeometry>(target.RenderedGeometry);
Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect);
}
[Fact]
public void Rearranging_Updates_RenderedGeometry_Rect()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Rectangle();
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 100, 100));
var geometry = Assert.IsType<RectangleGeometry>(target.RenderedGeometry);
Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect);
target.Measure(new Size(200, 200));
target.Arrange(new Rect(0, 0, 200, 200));
geometry = Assert.IsType<RectangleGeometry>(target.RenderedGeometry);
Assert.Equal(new Rect(0, 0, 200, 200), geometry.Rect);
}
[Fact]
public void Changing_Fill_Brush_Color_Should_Invalidate_Visual()
{
@ -21,7 +65,7 @@ namespace Avalonia.Controls.UnitTests.Shapes
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
renderer.ResetCalls();
renderer.Invocations.Clear();
((SolidColorBrush)target.Fill).Color = Colors.Green;
@ -38,7 +82,7 @@ namespace Avalonia.Controls.UnitTests.Shapes
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
renderer.ResetCalls();
renderer.Invocations.Clear();
((SolidColorBrush)target.Stroke).Color = Colors.Green;

4
tests/Avalonia.Controls.UnitTests/TextBlockTests.cs

@ -37,7 +37,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
renderer.ResetCalls();
renderer.Invocations.Clear();
((SolidColorBrush)target.Background).Color = Colors.Green;
@ -54,7 +54,7 @@ namespace Avalonia.Controls.UnitTests
var root = new TestRoot(target);
var renderer = Mock.Get(root.Renderer);
renderer.ResetCalls();
renderer.Invocations.Clear();
((SolidColorBrush)target.Foreground).Color = Colors.Green;

13
tests/Avalonia.Controls.UnitTests/ViewboxTests.cs

@ -1,5 +1,6 @@
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@ -9,6 +10,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_Uniform_Child()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, 200));
@ -25,6 +28,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_None_Child()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Viewbox() { Stretch = Stretch.None, Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, 200));
@ -41,6 +46,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_Fill_Child()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Viewbox() { Stretch = Stretch.Fill, Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, 200));
@ -57,6 +64,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_UniformToFill_Child()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Viewbox() { Stretch = Stretch.UniformToFill, Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, 200));
@ -73,6 +82,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_Uniform_Child_With_Unrestricted_Width()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(double.PositiveInfinity, 200));
@ -89,6 +100,8 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Viewbox_Stretch_Uniform_Child_With_Unrestricted_Height()
{
using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } };
target.Measure(new Size(200, double.PositiveInfinity));

13
tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace Avalonia.DesignerSupport.TestApp
@ -13,5 +9,12 @@ namespace Avalonia.DesignerSupport.TestApp
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow();
base.OnFrameworkInitializationCompleted();
}
}
}

20
tests/Avalonia.DesignerSupport.TestApp/Program.cs

@ -1,24 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
namespace Avalonia.DesignerSupport.TestApp
namespace Avalonia.DesignerSupport.TestApp
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
BuildAvaloniaApp().Start<MainWindow>();
}
public static int Main(string[] args)
=> BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
private static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<App>().UsePlatformDetect();
}
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>().UsePlatformDetect();
}
}

6
tests/Avalonia.Layout.UnitTests/LayoutableTests.cs

@ -112,7 +112,7 @@ namespace Avalonia.Layout.UnitTests
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
target.ResetCalls();
target.Invocations.Clear();
control.InvalidateMeasure();
control.InvalidateMeasure();
@ -133,7 +133,7 @@ namespace Avalonia.Layout.UnitTests
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
target.ResetCalls();
target.Invocations.Clear();
control.InvalidateArrange();
control.InvalidateArrange();
@ -163,7 +163,7 @@ namespace Avalonia.Layout.UnitTests
Assert.False(control.IsMeasureValid);
Assert.True(root.IsMeasureValid);
target.ResetCalls();
target.Invocations.Clear();
root.Child = control;

2
tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs

@ -15,7 +15,9 @@ namespace Avalonia.Layout.UnitTests
[Fact]
public async Task EffectiveViewportChanged_Not_Raised_When_Control_Added_To_Tree()
{
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
await RunOnUIThread.Execute(async () =>
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
var root = CreateRoot();
var target = new Canvas();

113
tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs

@ -1,113 +0,0 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Layout.UnitTests
{
public class ShapeLayoutTests : TestWithServicesBase
{
public ShapeLayoutTests()
{
AvaloniaLocator.CurrentMutable
.Bind<IPlatformRenderInterface>().ToSingleton<MockPlatformRenderInterface>();
}
[Fact]
public void Shape_Transformation_Calculation_Should_Be_Deferred_To_Arrange_When_Strech_Is_Fill_And_Aviable_Size_Is_Infinite()
{
var shape = new Polygon()
{
Points = new List<Point>
{
new Point(0, 0),
new Point(10, 5),
new Point(0, 10)
},
Stretch = Stretch.Fill
};
var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
shape.Measure(availableSize);
Geometry postMeasureGeometry = shape.RenderedGeometry;
Transform postMeasureTransform = postMeasureGeometry.Transform;
var finalSize = new Size(100, 50);
var finalRect = new Rect(finalSize);
shape.Arrange(finalRect);
Geometry postArrangeGeometry = shape.RenderedGeometry;
Transform postArrangeTransform = postArrangeGeometry.Transform;
Assert.NotEqual(postMeasureGeometry, postArrangeGeometry);
Assert.NotEqual(postMeasureTransform, postArrangeTransform);
Assert.Equal(finalSize, shape.Bounds.Size);
}
[Fact]
public void Shape_Transformation_Calculation_Should_Not_Be_Deferred_To_Arrange_When_Strech_Is_Fill_And_Aviable_Size_Is_Finite()
{
var shape = new Polygon()
{
Points = new List<Point>
{
new Point(0, 0),
new Point(10, 5),
new Point(0, 10)
},
Stretch = Stretch.Fill
};
var availableSize = new Size(100, 50);
shape.Measure(availableSize);
Geometry postMeasureGeometry = shape.RenderedGeometry;
Transform postMeasureTransform = postMeasureGeometry.Transform;
var finalRect = new Rect(availableSize);
shape.Arrange(finalRect);
Geometry postArrangeGeometry = shape.RenderedGeometry;
Transform postArrangeTransform = postArrangeGeometry.Transform;
Assert.Equal(postMeasureGeometry, postArrangeGeometry);
Assert.Equal(postMeasureTransform, postArrangeTransform);
Assert.Equal(availableSize, shape.Bounds.Size);
}
[Fact]
public void Shape_Transformation_Calculation_Should_Not_Be_Deferred_To_Arrange_When_Strech_Is_None()
{
var shape = new Polygon()
{
Points = new List<Point>
{
new Point(0, 0),
new Point(10, 5),
new Point(0, 10)
},
Stretch = Stretch.None
};
var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
shape.Measure(availableSize);
Geometry postMeasureGeometry = shape.RenderedGeometry;
Transform postMeasureTransform = postMeasureGeometry.Transform;
var finalSize = new Size(100, 50);
var finalRect = new Rect(finalSize);
shape.Arrange(finalRect);
Geometry postArrangeGeometry = shape.RenderedGeometry;
Transform postArrangeTransform = postArrangeGeometry.Transform;
Assert.Equal(postMeasureGeometry, postArrangeGeometry);
Assert.Equal(postMeasureTransform, postArrangeTransform);
Assert.Equal(finalSize, shape.Bounds.Size);
}
}
}

7
tests/Avalonia.LeakTests/ControlTests.cs

@ -460,7 +460,7 @@ namespace Avalonia.LeakTests
AttachShowAndDetachContextMenu(window);
Mock.Get(window.PlatformImpl).ResetCalls();
Mock.Get(window.PlatformImpl).Invocations.Clear();
dotMemory.Check(memory =>
Assert.Equal(initialMenuCount, memory.GetObjects(where => where.Type.Is<ContextMenu>()).ObjectsCount));
dotMemory.Check(memory =>
@ -505,7 +505,7 @@ namespace Avalonia.LeakTests
BuildAndShowContextMenu(window);
BuildAndShowContextMenu(window);
Mock.Get(window.PlatformImpl).ResetCalls();
Mock.Get(window.PlatformImpl).Invocations.Clear();
dotMemory.Check(memory =>
Assert.Equal(initialMenuCount, memory.GetObjects(where => where.Type.Is<ContextMenu>()).ObjectsCount));
dotMemory.Check(memory =>
@ -601,8 +601,9 @@ namespace Avalonia.LeakTests
{
public bool DrawFps { get; set; }
public bool DrawDirtyRects { get; set; }
#pragma warning disable CS0067
public event EventHandler<SceneInvalidatedEventArgs> SceneInvalidated;
#pragma warning restore CS0067
public void AddDirty(IVisual visual)
{
}

15
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs

@ -168,10 +168,23 @@ namespace Avalonia.Markup.UnitTests.Parsers
private class Test : INotifyDataErrorInfo
{
private string _dataValidationError;
public bool Foo { get; set; }
public object Bar { get; set; }
public string DataValidationError { get; set; }
public string DataValidationError
{
get => _dataValidationError;
set
{
if (value == _dataValidationError)
return;
_dataValidationError = value;
ErrorsChanged?
.Invoke(this, new DataErrorsChangedEventArgs(nameof(DataValidationError)));
}
}
public bool HasErrors => !string.IsNullOrWhiteSpace(DataValidationError);
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

2
tests/Avalonia.RenderTests/Media/BitmapTests.cs

@ -102,7 +102,9 @@ namespace Avalonia.Direct2D1.RenderTests.Media
[InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgba8888)]
public void WriteableBitmapShouldBeUsable(PixelFormat fmt)
{
#pragma warning disable CS0618 // Type or member is obsolete
var writeableBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), fmt);
#pragma warning restore CS0618 // Type or member is obsolete
var data = new int[256 * 256];
for (int y = 0; y < 256; y++)

12
tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs

@ -108,7 +108,7 @@ namespace Avalonia.Styling.UnitTests
var target = new ResourceDictionary { { "foo", "bar" } };
((IResourceProvider)target).AddOwner(host.Object);
host.ResetCalls();
host.Invocations.Clear();
((IResourceProvider)target).RemoveOwner(host.Object);
host.Verify(x => x.NotifyHostedResourcesChanged(It.IsAny<ResourcesChangedEventArgs>()));
@ -120,7 +120,7 @@ namespace Avalonia.Styling.UnitTests
var host = new Mock<IResourceHost>();
var target = new ResourceDictionary(host.Object);
host.ResetCalls();
host.Invocations.Clear();
target.Add("foo", "bar");
host.Verify(x => x.NotifyHostedResourcesChanged(It.IsAny<ResourcesChangedEventArgs>()));
@ -132,7 +132,7 @@ namespace Avalonia.Styling.UnitTests
var host = new Mock<IResourceHost>();
var target = new ResourceDictionary(host.Object);
host.ResetCalls();
host.Invocations.Clear();
target.MergedDictionaries.Add(new ResourceDictionary
{
{ "foo", "bar" },
@ -149,7 +149,7 @@ namespace Avalonia.Styling.UnitTests
var host = new Mock<IResourceHost>();
var target = new ResourceDictionary(host.Object);
host.ResetCalls();
host.Invocations.Clear();
target.MergedDictionaries.Add(new ResourceDictionary());
host.Verify(
@ -169,7 +169,7 @@ namespace Avalonia.Styling.UnitTests
}
};
host.ResetCalls();
host.Invocations.Clear();
target.MergedDictionaries.RemoveAt(0);
host.Verify(
@ -189,7 +189,7 @@ namespace Avalonia.Styling.UnitTests
}
};
host.ResetCalls();
host.Invocations.Clear();
((IResourceDictionary)target.MergedDictionaries[0]).Add("foo", "bar");
host.Verify(

75
tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs

@ -1,12 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.LogicalTree;
using Xunit;
@ -85,76 +80,6 @@ namespace Avalonia.Styling.UnitTests
get => Parent;
set => ((ISetLogicalParent)this).SetParent(value);
}
public void ClearValue(AvaloniaProperty property)
{
throw new NotImplementedException();
}
public void ClearValue<T>(AvaloniaProperty<T> property)
{
throw new NotImplementedException();
}
public void AddInheritanceChild(IAvaloniaObject child)
{
throw new NotImplementedException();
}
public void RemoveInheritanceChild(IAvaloniaObject child)
{
throw new NotImplementedException();
}
public void InheritanceParentChanged<T>(StyledPropertyBase<T> property, IAvaloniaObject oldParent, IAvaloniaObject newParent)
{
throw new NotImplementedException();
}
public void InheritedPropertyChanged<T>(AvaloniaProperty<T> property, Optional<T> oldValue, Optional<T> newValue)
{
throw new NotImplementedException();
}
public void ClearValue<T>(StyledPropertyBase<T> property)
{
throw new NotImplementedException();
}
public void ClearValue<T>(DirectPropertyBase<T> property)
{
throw new NotImplementedException();
}
public T GetValue<T>(StyledPropertyBase<T> property)
{
throw new NotImplementedException();
}
public T GetValue<T>(DirectPropertyBase<T> property)
{
throw new NotImplementedException();
}
public void SetValue<T>(StyledPropertyBase<T> property, T value, BindingPriority priority = BindingPriority.LocalValue)
{
throw new NotImplementedException();
}
public void SetValue<T>(DirectPropertyBase<T> property, T value)
{
throw new NotImplementedException();
}
public IDisposable Bind<T>(StyledPropertyBase<T> property, IObservable<BindingValue<T>> source, BindingPriority priority = BindingPriority.LocalValue)
{
throw new NotImplementedException();
}
public IDisposable Bind<T>(DirectPropertyBase<T> property, IObservable<BindingValue<T>> source)
{
throw new NotImplementedException();
}
}
public class TestLogical1 : TestLogical

74
tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs

@ -1,11 +1,7 @@
using System;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.LogicalTree;
using Xunit;
@ -115,76 +111,6 @@ namespace Avalonia.Styling.UnitTests
get => Parent;
set => ((ISetLogicalParent)this).SetParent(value);
}
public void ClearValue(AvaloniaProperty property)
{
throw new NotImplementedException();
}
public void ClearValue<T>(AvaloniaProperty<T> property)
{
throw new NotImplementedException();
}
public void AddInheritanceChild(IAvaloniaObject child)
{
throw new NotImplementedException();
}
public void RemoveInheritanceChild(IAvaloniaObject child)
{
throw new NotImplementedException();
}
public void InheritanceParentChanged<T>(StyledPropertyBase<T> property, IAvaloniaObject oldParent, IAvaloniaObject newParent)
{
throw new NotImplementedException();
}
public void InheritedPropertyChanged<T>(AvaloniaProperty<T> property, Optional<T> oldValue, Optional<T> newValue)
{
throw new NotImplementedException();
}
public void ClearValue<T>(StyledPropertyBase<T> property)
{
throw new NotImplementedException();
}
public void ClearValue<T>(DirectPropertyBase<T> property)
{
throw new NotImplementedException();
}
public T GetValue<T>(StyledPropertyBase<T> property)
{
throw new NotImplementedException();
}
public T GetValue<T>(DirectPropertyBase<T> property)
{
throw new NotImplementedException();
}
public void SetValue<T>(StyledPropertyBase<T> property, T value, BindingPriority priority = BindingPriority.LocalValue)
{
throw new NotImplementedException();
}
public void SetValue<T>(DirectPropertyBase<T> property, T value)
{
throw new NotImplementedException();
}
public IDisposable Bind<T>(StyledPropertyBase<T> property, IObservable<BindingValue<T>> source, BindingPriority priority = BindingPriority.LocalValue)
{
throw new NotImplementedException();
}
public IDisposable Bind<T>(DirectPropertyBase<T> property, IObservable<BindingValue<T>> source)
{
throw new NotImplementedException();
}
}
public class TestLogical1 : TestLogical

2
tests/Avalonia.Styling.UnitTests/StyleTests.cs

@ -443,7 +443,7 @@ namespace Avalonia.Styling.UnitTests
var resources = new Mock<IResourceDictionary>();
target.Resources = resources.Object;
host.ResetCalls();
host.Invocations.Clear();
((IResourceProvider)target).AddOwner(host.Object);
resources.Verify(x => x.AddOwner(host.Object), Times.Once);
}

8
tests/Avalonia.Styling.UnitTests/StylesTests.cs

@ -15,7 +15,7 @@ namespace Avalonia.Styling.UnitTests
var style = new Mock<IStyle>();
var rp = style.As<IResourceProvider>();
host.ResetCalls();
host.Invocations.Clear();
target.Add(style.Object);
rp.Verify(x => x.AddOwner(host.Object));
@ -29,7 +29,7 @@ namespace Avalonia.Styling.UnitTests
var style = new Mock<IStyle>();
var rp = style.As<IResourceProvider>();
host.ResetCalls();
host.Invocations.Clear();
target.Add(style.Object);
target.Remove(style.Object);
@ -58,7 +58,7 @@ namespace Avalonia.Styling.UnitTests
var resources = new Mock<IResourceDictionary>();
target.Resources = resources.Object;
host.ResetCalls();
host.Invocations.Clear();
((IResourceProvider)target).AddOwner(host.Object);
resources.Verify(x => x.AddOwner(host.Object), Times.Once);
}
@ -87,7 +87,7 @@ namespace Avalonia.Styling.UnitTests
var resourceProvider = style.As<IResourceProvider>();
target.Add(style.Object);
host.ResetCalls();
host.Invocations.Clear();
((IResourceProvider)target).AddOwner(host.Object);
resourceProvider.Verify(x => x.AddOwner(host.Object), Times.Once);
}

2
tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs

@ -34,7 +34,7 @@ namespace Avalonia.UnitTests
public IGeometryImpl CreateRectangleGeometry(Rect rect)
{
return Mock.Of<IGeometryImpl>();
return Mock.Of<IGeometryImpl>(x => x.Bounds == rect);
}
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)

2
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -756,7 +756,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
private void IgnoreFirstFrame(IRenderLoopTask task, Mock<ISceneBuilder> sceneBuilder)
{
RunFrame(task);
sceneBuilder.ResetCalls();
sceneBuilder.Invocations.Clear();
}
private void RunFrame(IRenderLoopTask task)

2
tests/Avalonia.Visuals.UnitTests/VisualTests.cs

@ -173,7 +173,7 @@ namespace Avalonia.Visuals.UnitTests
};
root.Child = child;
renderer.ResetCalls();
renderer.Invocations.Clear();
root.Child = null;
renderer.Verify(x => x.AddDirty(child));

Loading…
Cancel
Save