Browse Source

Merge branch 'master' into fixes/1065-canscrollhorizontally

Conflicts:
	src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
pull/1338/head
Steven Kirk 9 years ago
parent
commit
a718e23b9d
  1. 3
      .ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject
  2. 6
      .ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject
  3. 12
      Avalonia.sln
  4. 4
      build.cake
  5. 4
      samples/Previewer/MainWindow.xaml.cs
  6. 4
      samples/RemoteTest/Program.cs
  7. 12
      samples/VirtualizationTest/MainWindow.xaml
  8. 18
      samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs
  9. 2
      src/Avalonia.Base/AvaloniaObject.cs
  10. 4
      src/Avalonia.Base/PriorityBindingEntry.cs
  11. 2
      src/Avalonia.Base/Threading/AvaloniaScheduler.cs
  12. 4
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  13. 4
      src/Avalonia.Base/Threading/Dispatcher.cs
  14. 4
      src/Avalonia.Base/Threading/IDispatcher.cs
  15. 2
      src/Avalonia.Controls/Carousel.cs
  16. 6
      src/Avalonia.Controls/ColumnDefinition.cs
  17. 2
      src/Avalonia.Controls/DropDown.cs
  18. 12
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  19. 27
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  20. 71
      src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs
  21. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  22. 4
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  23. 2
      src/Avalonia.Controls/Primitives/HeaderedContentControl.cs
  24. 10
      src/Avalonia.Controls/Primitives/ILogicalScrollable.cs
  25. 1
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  26. 3
      src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs
  27. 2
      src/Avalonia.Controls/Primitives/TemplatedControl.cs
  28. 6
      src/Avalonia.Controls/Primitives/Thumb.cs
  29. 1
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  30. 2
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  31. 10
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  32. 6
      src/Avalonia.Controls/RowDefinition.cs
  33. 97
      src/Avalonia.Controls/ScrollViewer.cs
  34. 4
      src/Avalonia.Controls/Shapes/Line.cs
  35. 2
      src/Avalonia.Controls/Shapes/Path.cs
  36. 29
      src/Avalonia.Controls/Shapes/Shape.cs
  37. 30
      src/Avalonia.Controls/TextBox.cs
  38. 2
      src/Avalonia.Controls/TreeView.cs
  39. 2
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  40. 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  41. 2
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  42. 2
      src/Avalonia.Diagnostics/Views/ControlDetailsView.cs
  43. 16
      src/Avalonia.HtmlRenderer/HtmlControl.cs
  44. 18
      src/Avalonia.Input/InputElement.cs
  45. 6
      src/Avalonia.Input/KeyBinding.cs
  46. 2
      src/Avalonia.Layout/LayoutManager.cs
  47. 7
      src/Avalonia.Themes.Default/ListBox.xaml
  48. 5
      src/Avalonia.Themes.Default/ScrollViewer.xaml
  49. 3
      src/Avalonia.Themes.Default/TextBox.xaml
  50. 2
      src/Avalonia.Themes.Default/TreeView.xaml
  51. 2
      src/Avalonia.Visuals/Media/Geometry.cs
  52. 2
      src/Avalonia.Visuals/Media/ImageBrush.cs
  53. 2
      src/Avalonia.Visuals/Media/MatrixTransform.cs
  54. 2
      src/Avalonia.Visuals/Media/RotateTransform.cs
  55. 4
      src/Avalonia.Visuals/Media/TranslateTransform.cs
  56. 2
      src/Avalonia.Visuals/Media/VisualBrush.cs
  57. 2
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  58. 9
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  59. 12
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  60. 17
      src/Avalonia.Visuals/Visual.cs
  61. 51
      src/Avalonia.Visuals/VisualTree/BoundsTracker.cs
  62. 5
      src/Avalonia.Visuals/VisualTree/IVisual.cs
  63. 2
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  64. 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  65. 2
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  66. 2
      src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs
  67. 2
      src/OSX/Avalonia.MonoMac/TopLevelImpl.cs
  68. 6
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  69. 2
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetSubject.cs
  70. 2
      tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs
  71. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  72. 2
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  73. 17
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs
  74. 46
      tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests_ILogicalScrollable.cs
  75. 30
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs
  76. 16
      tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj
  77. 2
      tests/Avalonia.Direct2D1.UnitTests/Properties/AssemblyInfo.cs
  78. 2
      tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
  79. 2
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AttachedProperty.cs
  80. 12
      tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs
  81. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs
  82. 6
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/NonControl.cs
  83. 2
      tests/Avalonia.RenderTests/.gitignore
  84. 33
      tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems
  85. 16
      tests/Avalonia.RenderTests/Avalonia.RenderTests.shproj
  86. 5
      tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
  87. 2
      tests/Avalonia.RenderTests/Properties/AssemblyInfo.cs
  88. 23
      tests/Avalonia.RenderTests/Shapes/PathTests.cs
  89. 15
      tests/Avalonia.RenderTests/TestBase.cs
  90. 20
      tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj
  91. 2
      tests/Avalonia.Styling.UnitTests/StyleTests.cs
  92. 4
      tests/Avalonia.UnitTests/ImmediateDispatcher.cs
  93. 4
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  94. 2
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests_HitTesting.cs
  95. 2
      tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests_HitTesting.cs
  96. 5
      tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs
  97. BIN
      tests/TestFiles/Cairo/SVGPath/SVGPath.expected.png
  98. BIN
      tests/TestFiles/Direct2D1/Shapes/Path/Path_With_Rotated_Geometry.expected.png
  99. BIN
      tests/TestFiles/Skia/Controls/TextBlock/Wrapping_NoWrap.expected.png
  100. BIN
      tests/TestFiles/Skia/Shapes/Path/Path_With_Rotated_Geometry.expected.png

3
.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject

@ -1,5 +1,8 @@
<ProjectConfiguration>
<Settings>
<AdditionalFilesToIncludeForProject>
<Value>..\TestFiles\Direct2D1\**.*</Value>
</AdditionalFilesToIncludeForProject>
<DefaultTestTimeout>3000</DefaultTestTimeout>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>

6
.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject

@ -1,7 +1,9 @@
<ProjectConfiguration>
<Settings>
<DefaultTestTimeout>1000</DefaultTestTimeout>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
<AdditionalFilesToIncludeForProject>
<Value>..\TestFiles\Skia\**.*</Value>
</AdditionalFilesToIncludeForProject>
<DefaultTestTimeout>3000</DefaultTestTimeout>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>

12
Avalonia.sln

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2008
VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
EndProject
@ -45,11 +45,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Layout.UnitTests",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Interactivity.UnitTests", "tests\Avalonia.Interactivity.UnitTests\Avalonia.Interactivity.UnitTests.csproj", "{08478EF5-44E8-42E9-92D6-15E00EC038D8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Direct2D1.RenderTests", "tests\Avalonia.Direct2D1.RenderTests\Avalonia.Direct2D1.RenderTests.csproj", "{DABFD304-D6A4-4752-8123-C2CCF7AC7831}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Input.UnitTests", "tests\Avalonia.Input.UnitTests\Avalonia.Input.UnitTests.csproj", "{AC18926A-E784-40FE-B09D-BB0FE2B599F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Direct2D1.UnitTests", "tests\Avalonia.Direct2D1.UnitTests\Avalonia.Direct2D1.UnitTests.csproj", "{EFB11458-9CDF-41C0-BE4F-44AF45A4CAB8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Markup.Xaml.UnitTests", "tests\Avalonia.Markup.Xaml.UnitTests\Avalonia.Markup.Xaml.UnitTests.csproj", "{99135EAB-653D-47E4-A378-C96E1278CA44}"
EndProject
@ -114,8 +114,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport.Te
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.DesignerSupport.TestApp", "tests\Avalonia.DesignerSupport.TestApp\Avalonia.DesignerSupport.TestApp.csproj", "{F1381F98-4D24-409A-A6C5-1C5B1E08BB08}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Avalonia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.RenderTests.shproj", "{48840EDD-24BF-495D-911E-2EB12AE75D3B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualizationTest", "samples\VirtualizationTest\VirtualizationTest.csproj", "{FBCAF3D0-2808-4934-8E96-3F607594517B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interop", "Interop", "{A0CC0258-D18C-4AB3-854F-7101680FC3F9}"
@ -176,7 +174,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Direct3DInteropSample", "sa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.Skia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Remote.Protocol", "src\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj", "{D78A720C-C0C6-478B-8564-F167F9BDD01B}"
EndProject
@ -200,7 +198,6 @@ Global
src\Shared\RenderHelpers\RenderHelpers.projitems*{3e908f67-5543-4879-a1dc-08eace79b3cd}*SharedItemsImports = 4
src\Windows\Avalonia.Win32\Avalonia.Win32.Shared.projitems*{40759a76-d0f2-464e-8000-6ff0f5c4bd7c}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{4488ad85-1495-4809-9aa4-ddfe0a48527e}*SharedItemsImports = 4
tests\Avalonia.RenderTests\Avalonia.RenderTests.projitems*{48840edd-24bf-495d-911e-2eb12ae75d3b}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{4a1abb09-9047-4bd5-a4ad-a055e52c5ee0}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7863ea94-f0fb-4386-bf8c-e5bfa761560a}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{7b92af71-6287-4693-9dcb-bd5b6e927e23}*SharedItemsImports = 4
@ -2643,7 +2640,6 @@ Global
{57E0455D-D565-44BB-B069-EE1AA20F8337} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{52F55355-D120-42AC-8116-8410A7D602FA} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F1381F98-4D24-409A-A6C5-1C5B1E08BB08} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{48840EDD-24BF-495D-911E-2EB12AE75D3B} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{FBCAF3D0-2808-4934-8E96-3F607594517B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{A0CC0258-D18C-4AB3-854F-7101680FC3F9} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{C7A69145-60B6-4882-97D6-A3921DD43978} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}

4
build.cake

@ -197,8 +197,8 @@ Task("Run-Render-Tests")
.IsDependentOn("Build")
.WithCriteria(() => !parameters.SkipTests && parameters.IsRunningOnWindows)
.Does(() => {
RunCoreTest("./tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj", parameters, true);
RunCoreTest("./tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", parameters, true);
RunCoreTest("./tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj", parameters, true);
RunCoreTest("./tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj", parameters, true);
});
Task("Run-Designer-Unit-Tests")

4
samples/Previewer/MainWindow.xaml.cs

@ -39,7 +39,7 @@ namespace Previewer
}));
new BsonTcpTransport().Listen(IPAddress.Loopback, 25000, t =>
{
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
if (_connection != null)
{
@ -61,7 +61,7 @@ namespace Previewer
private void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
{
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
if (transport != _connection)
return;

4
samples/RemoteTest/Program.cs

@ -25,7 +25,7 @@ namespace RemoteTest
var transport = new BsonTcpTransport();
transport.Listen(IPAddress.Loopback, port, sc =>
{
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
new RemoteServer(sc).Content = new MainView();
});
@ -34,7 +34,7 @@ namespace RemoteTest
var cts = new CancellationTokenSource();
transport.Connect(IPAddress.Loopback, port).ContinueWith(t =>
{
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
var window = new Window()
{

12
samples/VirtualizationTest/MainWindow.xaml

@ -21,6 +21,12 @@
<TextBox Watermark="Viewport"
UseFloatingWatermark="True"
Text="{Binding #listBox.Scroll.Viewport, Mode=OneWay}"/>
<TextBlock>Horiz. ScrollBar</TextBlock>
<DropDown Items="{Binding ScrollBarVisibilities}"
SelectedItem="{Binding HorizontalScrollBarVisibility}"/>
<TextBlock>Vert. ScrollBar</TextBlock>
<DropDown Items="{Binding ScrollBarVisibilities}"
SelectedItem="{Binding VerticalScrollBarVisibility}"/>
<TextBox Watermark="Item to Create"
UseFloatingWatermark="True"
Text="{Binding NewItemString}"/>
@ -35,7 +41,9 @@
Items="{Binding Items}"
SelectedItems="{Binding SelectedItems}"
SelectionMode="Multiple"
VirtualizationMode="{Binding VirtualizationMode}">
VirtualizationMode="{Binding VirtualizationMode}"
ScrollViewer.HorizontalScrollBarVisibility="{Binding HorizontalScrollBarVisibility, Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="{Binding VerticalScrollBarVisibility, Mode=TwoWay}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="{Binding Orientation}"/>
@ -43,7 +51,7 @@
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
<TextBlock Text="{Binding Header}" TextWrapping="Wrap"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

18
samples/VirtualizationTest/ViewModels/MainWindowViewModel.cs

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using ReactiveUI;
namespace VirtualizationTest.ViewModels
@ -17,6 +18,8 @@ namespace VirtualizationTest.ViewModels
private int _newItemIndex;
private IReactiveList<ItemViewModel> _items;
private string _prefix = "Item";
private ScrollBarVisibility _horizontalScrollBarVisibility = ScrollBarVisibility.Auto;
private ScrollBarVisibility _verticalScrollBarVisibility = ScrollBarVisibility.Auto;
private Orientation _orientation = Orientation.Vertical;
private ItemVirtualizationMode _virtualizationMode = ItemVirtualizationMode.Simple;
@ -64,6 +67,21 @@ namespace VirtualizationTest.ViewModels
public IEnumerable<Orientation> Orientations =>
Enum.GetValues(typeof(Orientation)).Cast<Orientation>();
public ScrollBarVisibility HorizontalScrollBarVisibility
{
get { return _horizontalScrollBarVisibility; }
set { this.RaiseAndSetIfChanged(ref _horizontalScrollBarVisibility, value); }
}
public ScrollBarVisibility VerticalScrollBarVisibility
{
get { return _verticalScrollBarVisibility; }
set { this.RaiseAndSetIfChanged(ref _verticalScrollBarVisibility, value); }
}
public IEnumerable<ScrollBarVisibility> ScrollBarVisibilities =>
Enum.GetValues(typeof(ScrollBarVisibility)).Cast<ScrollBarVisibility>();
public ItemVirtualizationMode VirtualizationMode
{
get { return _virtualizationMode; }

2
src/Avalonia.Base/AvaloniaObject.cs

@ -774,7 +774,7 @@ namespace Avalonia
}
else
{
Dispatcher.UIThread.InvokeAsync(Set);
Dispatcher.UIThread.Post(Set);
}
}

4
src/Avalonia.Base/PriorityBindingEntry.cs

@ -123,7 +123,7 @@ namespace Avalonia
}
else
{
Dispatcher.UIThread.InvokeAsync(Signal);
Dispatcher.UIThread.Post(Signal);
}
}
@ -135,7 +135,7 @@ namespace Avalonia
}
else
{
Dispatcher.UIThread.InvokeAsync(() => _owner.Completed(this));
Dispatcher.UIThread.Post(() => _owner.Completed(this));
}
}
}

2
src/Avalonia.Base/Threading/AvaloniaScheduler.cs

@ -33,7 +33,7 @@ namespace Avalonia.Threading
if (!Dispatcher.UIThread.CheckAccess())
{
var cancellation = new CancellationDisposable();
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
if (!cancellation.Token.IsCancellationRequested)
{

4
src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs

@ -36,7 +36,7 @@ namespace Avalonia.Threading
/// <inheritdoc/>
public override void Post(SendOrPostCallback d, object state)
{
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send);
Dispatcher.UIThread.Post(() => d(state), DispatcherPriority.Send);
}
/// <inheritdoc/>
@ -45,7 +45,7 @@ namespace Avalonia.Threading
if (Dispatcher.UIThread.CheckAccess())
d(state);
else
Dispatcher.UIThread.InvokeTaskAsync(() => d(state), DispatcherPriority.Send).Wait();
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait();
}
}
}

4
src/Avalonia.Base/Threading/Dispatcher.cs

@ -79,13 +79,13 @@ namespace Avalonia.Threading
public void RunJobs(DispatcherPriority minimumPriority) => _jobRunner.RunJobs(minimumPriority);
/// <inheritdoc/>
public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
return _jobRunner?.InvokeAsync(action, priority);
}
/// <inheritdoc/>
public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
_jobRunner?.Post(action, priority);
}

4
src/Avalonia.Base/Threading/IDispatcher.cs

@ -25,7 +25,7 @@ namespace Avalonia.Threading
/// <param name="action">The method.</param>
/// <param name="priority">The priority with which to invoke the method.</param>
/// <returns>A task that can be used to track the method's execution.</returns>
void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
/// <summary>
/// Post action that will be invoked on main thread
@ -34,6 +34,6 @@ namespace Avalonia.Threading
/// <param name="priority">The priority with which to invoke the method.</param>
// TODO: The naming of this method is confusing: the Async suffix usually means return a task.
// Remove this and rename InvokeTaskAsync as InvokeAsync. See #816.
Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal);
}
}

2
src/Avalonia.Controls/Carousel.cs

@ -24,7 +24,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="Transition"/> property.
/// </summary>
public static readonly StyledProperty<IPageTransition> TransitionProperty =
AvaloniaProperty.Register<Carousel, IPageTransition>("Transition");
AvaloniaProperty.Register<Carousel, IPageTransition>(nameof(Transition));
/// <summary>
/// The default value of <see cref="ItemsControl.ItemsPanelProperty"/> for

6
src/Avalonia.Controls/ColumnDefinition.cs

@ -12,19 +12,19 @@ namespace Avalonia.Controls
/// Defines the <see cref="MaxWidth"/> property.
/// </summary>
public static readonly StyledProperty<double> MaxWidthProperty =
AvaloniaProperty.Register<ColumnDefinition, double>("MaxWidth", double.PositiveInfinity);
AvaloniaProperty.Register<ColumnDefinition, double>(nameof(MaxWidth), double.PositiveInfinity);
/// <summary>
/// Defines the <see cref="MinWidth"/> property.
/// </summary>
public static readonly StyledProperty<double> MinWidthProperty =
AvaloniaProperty.Register<ColumnDefinition, double>("MinWidth");
AvaloniaProperty.Register<ColumnDefinition, double>(nameof(MinWidth));
/// <summary>
/// Defines the <see cref="Width"/> property.
/// </summary>
public static readonly StyledProperty<GridLength> WidthProperty =
AvaloniaProperty.Register<ColumnDefinition, GridLength>("Width", new GridLength(1, GridUnitType.Star));
AvaloniaProperty.Register<ColumnDefinition, GridLength>(nameof(Width), new GridLength(1, GridUnitType.Star));
/// <summary>
/// Initializes a new instance of the <see cref="ColumnDefinition"/> class.

2
src/Avalonia.Controls/DropDown.cs

@ -38,7 +38,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="SelectionBoxItem"/> property.
/// </summary>
public static readonly DirectProperty<DropDown, object> SelectionBoxItemProperty =
AvaloniaProperty.RegisterDirect<DropDown, object>("SelectionBoxItem", o => o.SelectionBoxItem);
AvaloniaProperty.RegisterDirect<DropDown, object>(nameof(SelectionBoxItem), o => o.SelectionBoxItem);
private bool _isDropDownOpen;
private Popup _popup;

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

@ -5,6 +5,7 @@ using System;
using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Utils;
using Avalonia.Input;
using Avalonia.Layout;
@ -97,6 +98,7 @@ namespace Avalonia.Controls.Presenters
/// <inheritdoc/>
public override Size MeasureOverride(Size availableSize)
{
var scrollable = (ILogicalScrollable)Owner;
var visualRoot = Owner.GetVisualRoot();
var maxAvailableSize = (visualRoot as WindowBase)?.PlatformImpl?.MaxClientSize
?? (visualRoot as TopLevel)?.ClientSize;
@ -115,7 +117,10 @@ namespace Avalonia.Controls.Presenters
}
}
availableSize = availableSize.WithWidth(double.PositiveInfinity);
if (scrollable.CanHorizontallyScroll)
{
availableSize = availableSize.WithWidth(double.PositiveInfinity);
}
}
else
{
@ -127,7 +132,10 @@ namespace Avalonia.Controls.Presenters
}
}
availableSize = availableSize.WithHeight(double.PositiveInfinity);
if (scrollable.CanVerticallyScroll)
{
availableSize = availableSize.WithHeight(double.PositiveInfinity);
}
}
Owner.Panel.Measure(availableSize);

27
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@ -23,6 +23,8 @@ namespace Avalonia.Controls.Presenters
defaultValue: ItemVirtualizationMode.None);
private ItemVirtualizer _virtualizer;
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
/// <summary>
/// Initializes static members of the <see cref="ItemsPresenter"/> class.
@ -46,6 +48,31 @@ namespace Avalonia.Controls.Presenters
set { SetValue(VirtualizationModeProperty, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
bool ILogicalScrollable.CanHorizontallyScroll
{
get { return _canHorizontallyScroll; }
set
{
_canHorizontallyScroll = value;
InvalidateMeasure();
}
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
bool ILogicalScrollable.CanVerticallyScroll
{
get { return _canVerticallyScroll; }
set
{
_canVerticallyScroll = value;
InvalidateMeasure();
}
}
/// <inheritdoc/>
bool ILogicalScrollable.IsLogicalScrollEnabled
{

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

@ -17,6 +17,24 @@ namespace Avalonia.Controls.Presenters
/// </summary>
public class ScrollContentPresenter : ContentPresenter, IPresenter, IScrollable
{
/// <summary>
/// Defines the <see cref="CanHorizontallyScroll"/> property.
/// </summary>
public static readonly DirectProperty<ScrollContentPresenter, bool> CanHorizontallyScrollProperty =
AvaloniaProperty.RegisterDirect<ScrollContentPresenter, bool>(
nameof(CanHorizontallyScroll),
o => o.CanHorizontallyScroll,
(o, v) => o.CanHorizontallyScroll = v);
/// <summary>
/// Defines the <see cref="CanVerticallyScroll"/> property.
/// </summary>
public static readonly DirectProperty<ScrollContentPresenter, bool> CanVerticallyScrollProperty =
AvaloniaProperty.RegisterDirect<ScrollContentPresenter, bool>(
nameof(CanVerticallyScroll),
o => o.CanVerticallyScroll,
(o, v) => o.CanVerticallyScroll = v);
/// <summary>
/// Defines the <see cref="Extent"/> property.
/// </summary>
@ -41,12 +59,8 @@ namespace Avalonia.Controls.Presenters
o => o.Viewport,
(o, v) => o.Viewport = v);
/// <summary>
/// Defines the <see cref="CanScrollHorizontally"/> property.
/// </summary>
public static readonly StyledProperty<bool> CanScrollHorizontallyProperty =
ScrollViewer.CanScrollHorizontallyProperty.AddOwner<ScrollContentPresenter>();
private bool _canHorizontallyScroll;
private bool _canVerticallyScroll;
private Size _extent;
private Vector _offset;
private IDisposable _logicalScrollSubscription;
@ -72,6 +86,24 @@ namespace Avalonia.Controls.Presenters
this.GetObservable(ChildProperty).Subscribe(UpdateScrollableSubscription);
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
public bool CanHorizontallyScroll
{
get { return _canHorizontallyScroll; }
set { SetAndRaise(CanHorizontallyScrollProperty, ref _canHorizontallyScroll, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
public bool CanVerticallyScroll
{
get { return _canVerticallyScroll; }
set { SetAndRaise(CanVerticallyScrollProperty, ref _canVerticallyScroll, value); }
}
/// <summary>
/// Gets the extent of the scrollable content.
/// </summary>
@ -99,16 +131,6 @@ namespace Avalonia.Controls.Presenters
private set { SetAndRaise(ViewportProperty, ref _viewport, value); }
}
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
public bool CanScrollHorizontally
{
get => GetValue(CanScrollHorizontallyProperty);
set => SetValue(CanScrollHorizontallyProperty, value);
}
/// <summary>
/// Attempts to bring a portion of the target visual into view by scrolling the content.
/// </summary>
/// <param name="target">The target visual.</param>
@ -181,8 +203,8 @@ namespace Avalonia.Controls.Presenters
}
var constraint = new Size(
CanScrollHorizontally ? double.PositiveInfinity : availableSize.Width,
double.PositiveInfinity);
CanHorizontallyScroll ? double.PositiveInfinity : availableSize.Width,
CanVerticallyScroll ? double.PositiveInfinity : availableSize.Height);
Child.Measure(constraint);
return Child.DesiredSize.Constrain(availableSize);
@ -197,10 +219,8 @@ namespace Avalonia.Controls.Presenters
}
var size = new Size(
CanScrollHorizontally ?
Math.Max(Child.DesiredSize.Width, finalSize.Width) :
finalSize.Width,
Math.Max(Child.DesiredSize.Height, finalSize.Height));
CanHorizontallyScroll ? Math.Max(Child.DesiredSize.Width, finalSize.Width) : finalSize.Width,
CanVerticallyScroll ? Math.Max(Child.DesiredSize.Height, finalSize.Height) : finalSize.Height);
ArrangeOverrideImpl(size, -Offset);
Viewport = finalSize;
Extent = Child.Bounds.Size;
@ -268,7 +288,12 @@ namespace Avalonia.Controls.Presenters
if (scrollable.IsLogicalScrollEnabled == true)
{
_logicalScrollSubscription = new CompositeDisposable(
this.GetObservable(OffsetProperty).Skip(1).Subscribe(x => scrollable.Offset = x),
this.GetObservable(CanHorizontallyScrollProperty)
.Subscribe(x => scrollable.CanHorizontallyScroll = x),
this.GetObservable(CanVerticallyScrollProperty)
.Subscribe(x => scrollable.CanVerticallyScroll = x),
this.GetObservable(OffsetProperty)
.Skip(1).Subscribe(x => scrollable.Offset = x),
Disposable.Create(() => scrollable.InvalidateScroll = null));
UpdateFromScrollable(scrollable);
}

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

@ -191,7 +191,7 @@ namespace Avalonia.Controls.Presenters
// The measure is currently invalid so there's no point trying to bring the
// current char into view until a measure has been carried out as the scroll
// viewer extents may not be up-to-date.
Dispatcher.UIThread.InvokeAsync(
Dispatcher.UIThread.Post(
() =>
{
var rect = FormattedText.HitTestTextPosition(caretIndex);

4
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -18,8 +18,6 @@ namespace Avalonia.Controls.Primitives
private static readonly AttachedProperty<AdornedElementInfo> s_adornedElementInfoProperty =
AvaloniaProperty.RegisterAttached<AdornerLayer, Visual, AdornedElementInfo>("AdornedElementInfo");
private readonly BoundsTracker _tracker = new BoundsTracker();
static AdornerLayer()
{
AdornedElementProperty.Changed.Subscribe(AdornedElementChanged);
@ -118,7 +116,7 @@ namespace Avalonia.Controls.Primitives
adorner.SetValue(s_adornedElementInfoProperty, info);
}
info.Subscription = _tracker.Track(adorned).Subscribe(x =>
info.Subscription = adorned.GetObservable(TransformedBoundsProperty).Subscribe(x =>
{
info.Bounds = x;
InvalidateArrange();

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

@ -12,7 +12,7 @@ namespace Avalonia.Controls.Primitives
/// Defines the <see cref="Header"/> property.
/// </summary>
public static readonly StyledProperty<object> HeaderProperty =
AvaloniaProperty.Register<ContentControl, object>("Header");
AvaloniaProperty.Register<ContentControl, object>(nameof(Header));
/// <summary>
/// Gets or sets the header content.

10
src/Avalonia.Controls/Primitives/ILogicalScrollable.cs

@ -19,6 +19,16 @@ namespace Avalonia.Controls.Primitives
/// </remarks>
public interface ILogicalScrollable : IScrollable
{
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
bool CanHorizontallyScroll { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the content can be scrolled horizontally.
/// </summary>
bool CanVerticallyScroll { get; set; }
/// <summary>
/// Gets a value indicating whether logical scrolling is enabled on the control.
/// </summary>

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

@ -99,6 +99,7 @@ namespace Avalonia.Controls.Primitives
case ScrollBarVisibility.Visible:
return true;
case ScrollBarVisibility.Disabled:
case ScrollBarVisibility.Hidden:
return false;

3
src/Avalonia.Controls/Primitives/ScrollBarVisibility.cs

@ -5,8 +5,9 @@ namespace Avalonia.Controls.Primitives
{
public enum ScrollBarVisibility
{
Disabled,
Auto,
Visible,
Hidden,
Visible,
}
}

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

@ -75,7 +75,7 @@ namespace Avalonia.Controls.Primitives
/// Defines the <see cref="Template"/> property.
/// </summary>
public static readonly StyledProperty<IControlTemplate> TemplateProperty =
AvaloniaProperty.Register<TemplatedControl, IControlTemplate>("Template");
AvaloniaProperty.Register<TemplatedControl, IControlTemplate>(nameof(Template));
/// <summary>
/// Defines the IsTemplateFocusTarget attached property.

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

@ -11,13 +11,13 @@ namespace Avalonia.Controls.Primitives
public class Thumb : TemplatedControl
{
public static readonly RoutedEvent<VectorEventArgs> DragStartedEvent =
RoutedEvent.Register<Thumb, VectorEventArgs>("DragStarted", RoutingStrategies.Bubble);
RoutedEvent.Register<Thumb, VectorEventArgs>(nameof(DragStarted), RoutingStrategies.Bubble);
public static readonly RoutedEvent<VectorEventArgs> DragDeltaEvent =
RoutedEvent.Register<Thumb, VectorEventArgs>("DragDelta", RoutingStrategies.Bubble);
RoutedEvent.Register<Thumb, VectorEventArgs>(nameof(DragDelta), RoutingStrategies.Bubble);
public static readonly RoutedEvent<VectorEventArgs> DragCompletedEvent =
RoutedEvent.Register<Thumb, VectorEventArgs>("DragCompleted", RoutingStrategies.Bubble);
RoutedEvent.Register<Thumb, VectorEventArgs>(nameof(DragCompleted), RoutingStrategies.Bubble);
private Point? _lastPoint;

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

@ -14,6 +14,7 @@ namespace Avalonia.Controls.Primitives
nameof(IsChecked),
o => o.IsChecked,
(o, v) => o.IsChecked = v,
unsetValue: false,
defaultBindingMode: BindingMode.TwoWay);
public static readonly StyledProperty<bool> IsThreeStateProperty =

2
src/Avalonia.Controls/Remote/RemoteWidget.cs

@ -18,7 +18,7 @@ namespace Avalonia.Controls.Remote
public RemoteWidget(IAvaloniaRemoteTransportConnection connection)
{
_connection = connection;
_connection.OnMessage += (t, msg) => Dispatcher.UIThread.InvokeAsync(() => OnMessage(msg));
_connection.OnMessage += (t, msg) => Dispatcher.UIThread.Post(() => OnMessage(msg));
_connection.Send(new ClientSupportedPixelFormatsMessage
{
Formats = new[]

10
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -46,16 +46,16 @@ namespace Avalonia.Controls.Remote.Server
{
_lastReceivedFrame = lastFrame.SequenceId;
}
Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
Dispatcher.UIThread.Post(RenderIfNeeded);
}
if (obj is ClientSupportedPixelFormatsMessage supportedFormats)
{
lock (_lock)
_supportedFormats = supportedFormats.Formats;
Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
Dispatcher.UIThread.Post(RenderIfNeeded);
}
if (obj is MeasureViewportMessage measure)
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
var m = Measure(new Size(measure.Width, measure.Height));
_transport.Send(new MeasureViewportMessage
@ -69,7 +69,7 @@ namespace Avalonia.Controls.Remote.Server
lock (_lock)
{
if (_pendingAllocation == null)
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
ClientViewportAllocatedMessage allocation;
lock (_lock)
@ -168,7 +168,7 @@ namespace Avalonia.Controls.Remote.Server
public override void Invalidate(Rect rect)
{
_invalidated = true;
Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
Dispatcher.UIThread.Post(RenderIfNeeded);
}
public override IMouseDevice MouseDevice { get; } = new MouseDevice();

6
src/Avalonia.Controls/RowDefinition.cs

@ -12,19 +12,19 @@ namespace Avalonia.Controls
/// Defines the <see cref="MaxHeight"/> property.
/// </summary>
public static readonly StyledProperty<double> MaxHeightProperty =
AvaloniaProperty.Register<RowDefinition, double>("MaxHeight", double.PositiveInfinity);
AvaloniaProperty.Register<RowDefinition, double>(nameof(MaxHeight), double.PositiveInfinity);
/// <summary>
/// Defines the <see cref="MinHeight"/> property.
/// </summary>
public static readonly StyledProperty<double> MinHeightProperty =
AvaloniaProperty.Register<RowDefinition, double>("MinHeight");
AvaloniaProperty.Register<RowDefinition, double>(nameof(MinHeight));
/// <summary>
/// Defines the <see cref="Height"/> property.
/// </summary>
public static readonly StyledProperty<GridLength> HeightProperty =
AvaloniaProperty.Register<RowDefinition, GridLength>("Height", new GridLength(1, GridUnitType.Star));
AvaloniaProperty.Register<RowDefinition, GridLength>(nameof(Height), new GridLength(1, GridUnitType.Star));
/// <summary>
/// Initializes a new instance of the <see cref="RowDefinition"/> class.

97
src/Avalonia.Controls/ScrollViewer.cs

@ -2,8 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
@ -15,16 +13,34 @@ namespace Avalonia.Controls
public class ScrollViewer : ContentControl, IScrollable
{
/// <summary>
/// Defines the <see cref="CanScrollHorizontally"/> property.
/// Defines the <see cref="CanHorizontallyScroll"/> property.
/// </summary>
public static readonly StyledProperty<bool> CanScrollHorizontallyProperty =
AvaloniaProperty.Register<ScrollViewer, bool>(nameof(CanScrollHorizontally), true);
/// <remarks>
/// There is no public C# accessor for this property as it is intended to be bound to by a
/// <see cref="ScrollContentPresenter"/> in the control's template.
/// </remarks>
public static readonly DirectProperty<ScrollViewer, bool> CanHorizontallyScrollProperty =
AvaloniaProperty.RegisterDirect<ScrollViewer, bool>(
nameof(CanHorizontallyScroll),
o => o.CanHorizontallyScroll);
/// <summary>
/// Defines the <see cref="CanVerticallyScroll"/> property.
/// </summary>
/// <remarks>
/// There is no public C# accessor for this property as it is intended to be bound to by a
/// <see cref="ScrollContentPresenter"/> in the control's template.
/// </remarks>
public static readonly DirectProperty<ScrollViewer, bool> CanVerticallyScrollProperty =
AvaloniaProperty.RegisterDirect<ScrollViewer, bool>(
nameof(CanVerticallyScroll),
o => o.CanVerticallyScroll);
/// <summary>
/// Defines the <see cref="Extent"/> property.
/// </summary>
public static readonly DirectProperty<ScrollViewer, Size> ExtentProperty =
AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Extent),
AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Extent),
o => o.Extent,
(o, v) => o.Extent = v);
@ -41,7 +57,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="Viewport"/> property.
/// </summary>
public static readonly DirectProperty<ScrollViewer, Size> ViewportProperty =
AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Viewport),
AvaloniaProperty.RegisterDirect<ScrollViewer, Size>(nameof(Viewport),
o => o.Viewport,
(o, v) => o.Viewport = v);
@ -85,14 +101,10 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="HorizontalScrollBarVisibility"/> property.
/// </summary>
/// <remarks>
/// There is no public C# accessor for this property as it is intended to be bound to by a
/// <see cref="ScrollContentPresenter"/> in the control's template.
/// </remarks>
public static readonly AttachedProperty<ScrollBarVisibility> HorizontalScrollBarVisibilityProperty =
AvaloniaProperty.RegisterAttached<ScrollViewer, Control, ScrollBarVisibility>(
nameof(HorizontalScrollBarVisibility),
ScrollBarVisibility.Auto);
ScrollBarVisibility.Hidden);
/// <summary>
/// Defines the VerticalScrollBarMaximum property.
@ -136,7 +148,7 @@ namespace Avalonia.Controls
/// </summary>
public static readonly AttachedProperty<ScrollBarVisibility> VerticalScrollBarVisibilityProperty =
AvaloniaProperty.RegisterAttached<ScrollViewer, Control, ScrollBarVisibility>(
nameof(VerticalScrollBarVisibility),
nameof(VerticalScrollBarVisibility),
ScrollBarVisibility.Auto);
private Size _extent;
@ -150,6 +162,8 @@ namespace Avalonia.Controls
{
AffectsValidation(ExtentProperty, OffsetProperty);
AffectsValidation(ViewportProperty, OffsetProperty);
HorizontalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>(x => x.ScrollBarVisibilityChanged);
VerticalScrollBarVisibilityProperty.Changed.AddClassHandler<ScrollViewer>(x => x.ScrollBarVisibilityChanged);
}
/// <summary>
@ -218,15 +232,6 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Gets a value indicating whether the content can be scrolled horizontally.
/// </summary>
public bool CanScrollHorizontally
{
get { return GetValue(CanScrollHorizontallyProperty); }
set { SetValue(CanScrollHorizontallyProperty, value); }
}
/// <summary>
/// Gets or sets the horizontal scrollbar visibility.
/// </summary>
@ -245,6 +250,22 @@ namespace Avalonia.Controls
set { SetValue(VerticalScrollBarVisibilityProperty, value); }
}
/// <summary>
/// Gets a value indicating whether the viewer can scroll horizontally.
/// </summary>
protected bool CanHorizontallyScroll
{
get { return HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled; }
}
/// <summary>
/// Gets a value indicating whether the viewer can scroll vertically.
/// </summary>
protected bool CanVerticallyScroll
{
get { return VerticalScrollBarVisibility != ScrollBarVisibility.Disabled; }
}
/// <summary>
/// Gets the maximum horizontal scrollbar value.
/// </summary>
@ -316,7 +337,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="control">The control to read the value from.</param>
/// <returns>The value of the property.</returns>
public ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
public static ScrollBarVisibility GetHorizontalScrollBarVisibility(Control control)
{
return control.GetValue(HorizontalScrollBarVisibilityProperty);
}
@ -326,7 +347,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="control">The control to set the value on.</param>
/// <param name="value">The value of the property.</param>
public void SetHorizontalScrollBarVisibility(Control control, ScrollBarVisibility value)
public static void SetHorizontalScrollBarVisibility(Control control, ScrollBarVisibility value)
{
control.SetValue(HorizontalScrollBarVisibilityProperty, value);
}
@ -336,7 +357,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="control">The control to read the value from.</param>
/// <returns>The value of the property.</returns>
public ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
public static ScrollBarVisibility GetVerticalScrollBarVisibility(Control control)
{
return control.GetValue(VerticalScrollBarVisibilityProperty);
}
@ -346,7 +367,7 @@ namespace Avalonia.Controls
/// </summary>
/// <param name="control">The control to set the value on.</param>
/// <param name="value">The value of the property.</param>
public void SetVerticalScrollBarVisibility(Control control, ScrollBarVisibility value)
public static void SetVerticalScrollBarVisibility(Control control, ScrollBarVisibility value)
{
control.SetValue(VerticalScrollBarVisibilityProperty, value);
}
@ -385,6 +406,30 @@ namespace Avalonia.Controls
}
}
private void ScrollBarVisibilityChanged(AvaloniaPropertyChangedEventArgs e)
{
var wasEnabled = !ScrollBarVisibility.Disabled.Equals(e.OldValue);
var isEnabled = !ScrollBarVisibility.Disabled.Equals(e.NewValue);
if (wasEnabled != isEnabled)
{
if (e.Property == HorizontalScrollBarVisibilityProperty)
{
RaisePropertyChanged(
CanHorizontallyScrollProperty,
wasEnabled,
isEnabled);
}
else if (e.Property == VerticalScrollBarVisibilityProperty)
{
RaisePropertyChanged(
CanVerticallyScrollProperty,
wasEnabled,
isEnabled);
}
}
}
private void CalculatedPropertiesChanged()
{
// Pass old values of 0 here because we don't have the old values at this point,

4
src/Avalonia.Controls/Shapes/Line.cs

@ -8,10 +8,10 @@ namespace Avalonia.Controls.Shapes
public class Line : Shape
{
public static readonly StyledProperty<Point> StartPointProperty =
AvaloniaProperty.Register<Line, Point>("StartPoint");
AvaloniaProperty.Register<Line, Point>(nameof(StartPoint));
public static readonly StyledProperty<Point> EndPointProperty =
AvaloniaProperty.Register<Line, Point>("EndPoint");
AvaloniaProperty.Register<Line, Point>(nameof(EndPoint));
static Line()
{

2
src/Avalonia.Controls/Shapes/Path.cs

@ -9,7 +9,7 @@ namespace Avalonia.Controls.Shapes
public class Path : Shape
{
public static readonly StyledProperty<Geometry> DataProperty =
AvaloniaProperty.Register<Path, Geometry>("Data");
AvaloniaProperty.Register<Path, Geometry>(nameof(Data));
static Path()
{

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

@ -12,19 +12,19 @@ namespace Avalonia.Controls.Shapes
public abstract class Shape : Control
{
public static readonly StyledProperty<IBrush> FillProperty =
AvaloniaProperty.Register<Shape, IBrush>("Fill");
AvaloniaProperty.Register<Shape, IBrush>(nameof(Fill));
public static readonly StyledProperty<Stretch> StretchProperty =
AvaloniaProperty.Register<Shape, Stretch>("Stretch");
AvaloniaProperty.Register<Shape, Stretch>(nameof(Stretch));
public static readonly StyledProperty<IBrush> StrokeProperty =
AvaloniaProperty.Register<Shape, IBrush>("Stroke");
AvaloniaProperty.Register<Shape, IBrush>(nameof(Stroke));
public static readonly StyledProperty<AvaloniaList<double>> StrokeDashArrayProperty =
AvaloniaProperty.Register<Shape, AvaloniaList<double>>("StrokeDashArray");
public static readonly StyledProperty<double> StrokeThicknessProperty =
AvaloniaProperty.Register<Shape, double>("StrokeThickness");
AvaloniaProperty.Register<Shape, double>(nameof(StrokeThickness));
private Matrix _transform = Matrix.Identity;
private Geometry _definingGeometry;
@ -61,12 +61,26 @@ namespace Avalonia.Controls.Shapes
{
get
{
if (_renderedGeometry == null)
if (_renderedGeometry == null && DefiningGeometry != null)
{
if (DefiningGeometry != null)
if (_transform == Matrix.Identity)
{
_renderedGeometry = DefiningGeometry;
}
else
{
_renderedGeometry = DefiningGeometry.Clone();
_renderedGeometry.Transform = new MatrixTransform(_transform);
if (_renderedGeometry.Transform == null ||
_renderedGeometry.Transform.Value == Matrix.Identity)
{
_renderedGeometry.Transform = new MatrixTransform(_transform);
}
else
{
_renderedGeometry.Transform = new MatrixTransform(
_renderedGeometry.Transform.Value * _transform);
}
}
}
@ -193,6 +207,7 @@ namespace Avalonia.Controls.Shapes
return finalSize;
}
private Size CalculateShapeSizeAndSetTransform(Size availableSize)
{
// This should probably use GetRenderBounds(strokeThickness) but then the calculations

30
src/Avalonia.Controls/TextBox.cs

@ -8,7 +8,6 @@ using System.Linq;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Input;
using Avalonia.Interactivity;
@ -21,13 +20,10 @@ namespace Avalonia.Controls
public class TextBox : TemplatedControl, UndoRedoHelper<TextBox.UndoRedoState>.IUndoRedoHost
{
public static readonly StyledProperty<bool> AcceptsReturnProperty =
AvaloniaProperty.Register<TextBox, bool>("AcceptsReturn");
AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsReturn));
public static readonly StyledProperty<bool> AcceptsTabProperty =
AvaloniaProperty.Register<TextBox, bool>("AcceptsTab");
public static readonly DirectProperty<TextBox, bool> CanScrollHorizontallyProperty =
AvaloniaProperty.RegisterDirect<TextBox, bool>("CanScrollHorizontally", o => o.CanScrollHorizontally);
AvaloniaProperty.Register<TextBox, bool>(nameof(AcceptsTab));
public static readonly DirectProperty<TextBox, int> CaretIndexProperty =
AvaloniaProperty.RegisterDirect<TextBox, int>(
@ -69,10 +65,10 @@ namespace Avalonia.Controls
TextBlock.TextWrappingProperty.AddOwner<TextBox>();
public static readonly StyledProperty<string> WatermarkProperty =
AvaloniaProperty.Register<TextBox, string>("Watermark");
AvaloniaProperty.Register<TextBox, string>(nameof(Watermark));
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
AvaloniaProperty.Register<TextBox, bool>("UseFloatingWatermark");
AvaloniaProperty.Register<TextBox, bool>(nameof(UseFloatingWatermark));
struct UndoRedoState : IEquatable<UndoRedoState>
{
@ -92,7 +88,6 @@ namespace Avalonia.Controls
private int _caretIndex;
private int _selectionStart;
private int _selectionEnd;
private bool _canScrollHorizontally;
private TextPresenter _presenter;
private UndoRedoHelper<UndoRedoState> _undoRedoHelper;
private bool _ignoreTextChanges;
@ -106,12 +101,11 @@ namespace Avalonia.Controls
public TextBox()
{
this.GetObservable(TextWrappingProperty)
.Select(x => x == TextWrapping.NoWrap)
.Subscribe(x => CanScrollHorizontally = x);
var horizontalScrollBarVisibility = this.GetObservable(AcceptsReturnProperty)
.Select(x => x ? ScrollBarVisibility.Auto : ScrollBarVisibility.Hidden);
var horizontalScrollBarVisibility = Observable.CombineLatest(
this.GetObservable(AcceptsReturnProperty),
this.GetObservable(TextWrappingProperty),
(acceptsReturn, wrapping) => acceptsReturn && wrapping == TextWrapping.NoWrap ?
ScrollBarVisibility.Auto : ScrollBarVisibility.Disabled);
Bind(
ScrollViewer.HorizontalScrollBarVisibilityProperty,
@ -132,12 +126,6 @@ namespace Avalonia.Controls
set { SetValue(AcceptsTabProperty, value); }
}
public bool CanScrollHorizontally
{
get { return _canScrollHorizontally; }
private set { SetAndRaise(CanScrollHorizontallyProperty, ref _canScrollHorizontally, value); }
}
public int CaretIndex
{
get

2
src/Avalonia.Controls/TreeView.cs

@ -250,7 +250,7 @@ namespace Avalonia.Controls
if (AutoScrollToSelectedItem)
{
Dispatcher.UIThread.InvokeAsync(container.ContainerControl.BringIntoView);
Dispatcher.UIThread.Post(container.ContainerControl.BringIntoView);
}
break;

2
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -3,11 +3,9 @@
using System;
using System.Collections.Specialized;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Layout;
using Avalonia.VisualTree;
namespace Avalonia.Controls
{

2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -49,7 +49,7 @@ namespace Avalonia.DesignerSupport.Remote
// In previewer mode we completely ignore client-side viewport size
if (obj is ClientViewportAllocatedMessage alloc)
{
Dispatcher.UIThread.InvokeAsync(() => SetDpi(new Vector(alloc.DpiX, alloc.DpiY)));
Dispatcher.UIThread.Post(() => SetDpi(new Vector(alloc.DpiX, alloc.DpiY)));
return;
}
base.OnMessage(transport, obj);

2
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -140,7 +140,7 @@ namespace Avalonia.DesignerSupport.Remote
};
}
private static void OnTransportMessage(IAvaloniaRemoteTransportConnection transport, object obj) => Dispatcher.UIThread.InvokeAsync(() =>
private static void OnTransportMessage(IAvaloniaRemoteTransportConnection transport, object obj) => Dispatcher.UIThread.Post(() =>
{
if (obj is ClientSupportedPixelFormatsMessage formats)
{

2
src/Avalonia.Diagnostics/Views/ControlDetailsView.cs

@ -14,7 +14,7 @@ namespace Avalonia.Diagnostics.Views
internal class ControlDetailsView : UserControl
{
private static readonly StyledProperty<ControlDetailsViewModel> ViewModelProperty =
AvaloniaProperty.Register<ControlDetailsView, ControlDetailsViewModel>("ViewModel");
AvaloniaProperty.Register<ControlDetailsView, ControlDetailsViewModel>(nameof(ViewModel));
private SimpleGrid _grid;
public ControlDetailsView()

16
src/Avalonia.HtmlRenderer/HtmlControl.cs

@ -74,29 +74,29 @@ namespace Avalonia.Controls.Html
protected Point _lastScrollOffset;
public static readonly AvaloniaProperty AvoidImagesLateLoadingProperty =
PropertyHelper.Register<HtmlControl, bool>("AvoidImagesLateLoading", false, OnAvaloniaProperty_valueChanged);
PropertyHelper.Register<HtmlControl, bool>(nameof(AvoidImagesLateLoading), false, OnAvaloniaProperty_valueChanged);
public static readonly AvaloniaProperty IsSelectionEnabledProperty =
PropertyHelper.Register<HtmlControl, bool>("IsSelectionEnabled", true, OnAvaloniaProperty_valueChanged);
PropertyHelper.Register<HtmlControl, bool>(nameof(IsSelectionEnabled), true, OnAvaloniaProperty_valueChanged);
public static readonly AvaloniaProperty IsContextMenuEnabledProperty =
PropertyHelper.Register<HtmlControl, bool>("IsContextMenuEnabled", true, OnAvaloniaProperty_valueChanged);
PropertyHelper.Register<HtmlControl, bool>(nameof(IsContextMenuEnabled), true, OnAvaloniaProperty_valueChanged);
public static readonly AvaloniaProperty BaseStylesheetProperty =
PropertyHelper.Register<HtmlControl, string>("BaseStylesheet", null, OnAvaloniaProperty_valueChanged);
PropertyHelper.Register<HtmlControl, string>(nameof(BaseStylesheet), null, OnAvaloniaProperty_valueChanged);
public static readonly AvaloniaProperty TextProperty =
PropertyHelper.Register<HtmlControl, string>("Text", null, OnAvaloniaProperty_valueChanged);
PropertyHelper.Register<HtmlControl, string>(nameof(Text), null, OnAvaloniaProperty_valueChanged);
public static readonly StyledProperty<IBrush> BackgroundProperty =
Border.BackgroundProperty.AddOwner<HtmlControl>();
public static readonly AvaloniaProperty BorderThicknessProperty =
AvaloniaProperty.Register<HtmlControl, Thickness>("BorderThickness", new Thickness(0));
AvaloniaProperty.Register<HtmlControl, Thickness>(nameof(BorderThickness), new Thickness(0));
public static readonly AvaloniaProperty BorderBrushProperty =
AvaloniaProperty.Register<HtmlControl, IBrush>("BorderBrush");
AvaloniaProperty.Register<HtmlControl, IBrush>(nameof(BorderBrush));
public static readonly AvaloniaProperty PaddingProperty =
AvaloniaProperty.Register<HtmlControl, Thickness>("Padding", new Thickness(0));
AvaloniaProperty.Register<HtmlControl, Thickness>(nameof(Padding), new Thickness(0));
public static readonly RoutedEvent LoadCompleteEvent =
RoutedEvent.Register<RoutedEventArgs>("LoadComplete", RoutingStrategies.Bubble, typeof(HtmlControl));

18
src/Avalonia.Input/InputElement.cs

@ -31,43 +31,43 @@ namespace Avalonia.Input
/// Defines the <see cref="IsEnabledCore"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsEnabledCoreProperty =
AvaloniaProperty.Register<InputElement, bool>("IsEnabledCore", true);
AvaloniaProperty.Register<InputElement, bool>(nameof(IsEnabledCore), true);
/// <summary>
/// Gets or sets associated mouse cursor.
/// </summary>
public static readonly StyledProperty<Cursor> CursorProperty =
AvaloniaProperty.Register<InputElement, Cursor>("Cursor", null, true);
AvaloniaProperty.Register<InputElement, Cursor>(nameof(Cursor), null, true);
/// <summary>
/// Defines the <see cref="IsFocused"/> property.
/// </summary>
public static readonly DirectProperty<InputElement, bool> IsFocusedProperty =
AvaloniaProperty.RegisterDirect<InputElement, bool>("IsFocused", o => o.IsFocused);
AvaloniaProperty.RegisterDirect<InputElement, bool>(nameof(IsFocused), o => o.IsFocused);
/// <summary>
/// Defines the <see cref="IsHitTestVisible"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsHitTestVisibleProperty =
AvaloniaProperty.Register<InputElement, bool>("IsHitTestVisible", true);
AvaloniaProperty.Register<InputElement, bool>(nameof(IsHitTestVisible), true);
/// <summary>
/// Defines the <see cref="IsPointerOver"/> property.
/// </summary>
public static readonly DirectProperty<InputElement, bool> IsPointerOverProperty =
AvaloniaProperty.RegisterDirect<InputElement, bool>("IsPointerOver", o => o.IsPointerOver);
AvaloniaProperty.RegisterDirect<InputElement, bool>(nameof(IsPointerOver), o => o.IsPointerOver);
/// <summary>
/// Defines the <see cref="GotFocus"/> event.
/// </summary>
public static readonly RoutedEvent<GotFocusEventArgs> GotFocusEvent =
RoutedEvent.Register<InputElement, GotFocusEventArgs>("GotFocus", RoutingStrategies.Bubble);
RoutedEvent.Register<InputElement, GotFocusEventArgs>(nameof(GotFocus), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="LostFocus"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> LostFocusEvent =
RoutedEvent.Register<InputElement, RoutedEventArgs>("LostFocus", RoutingStrategies.Bubble);
RoutedEvent.Register<InputElement, RoutedEventArgs>(nameof(LostFocus), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="KeyDown"/> event.
@ -97,13 +97,13 @@ namespace Avalonia.Input
/// Defines the <see cref="PointerEnter"/> event.
/// </summary>
public static readonly RoutedEvent<PointerEventArgs> PointerEnterEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerEnter", RoutingStrategies.Direct);
RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerEnter), RoutingStrategies.Direct);
/// <summary>
/// Defines the <see cref="PointerLeave"/> event.
/// </summary>
public static readonly RoutedEvent<PointerEventArgs> PointerLeaveEvent =
RoutedEvent.Register<InputElement, PointerEventArgs>("PointerLeave", RoutingStrategies.Direct);
RoutedEvent.Register<InputElement, PointerEventArgs>(nameof(PointerLeave), RoutingStrategies.Direct);
/// <summary>
/// Defines the <see cref="PointerMoved"/> event.

6
src/Avalonia.Input/KeyBinding.cs

@ -10,7 +10,7 @@ namespace Avalonia.Input
public class KeyBinding : AvaloniaObject
{
public static readonly StyledProperty<ICommand> CommandProperty =
AvaloniaProperty.Register<KeyBinding, ICommand>("Command");
AvaloniaProperty.Register<KeyBinding, ICommand>(nameof(Command));
public ICommand Command
{
@ -19,7 +19,7 @@ namespace Avalonia.Input
}
public static readonly StyledProperty<object> CommandParameterProperty =
AvaloniaProperty.Register<KeyBinding, object>("CommandParameter");
AvaloniaProperty.Register<KeyBinding, object>(nameof(CommandParameter));
public object CommandParameter
{
@ -28,7 +28,7 @@ namespace Avalonia.Input
}
public static readonly StyledProperty<KeyGesture> GestureProperty =
AvaloniaProperty.Register<KeyBinding, KeyGesture>("Gesture");
AvaloniaProperty.Register<KeyBinding, KeyGesture>(nameof(Gesture));
public KeyGesture Gesture
{

2
src/Avalonia.Layout/LayoutManager.cs

@ -203,7 +203,7 @@ namespace Avalonia.Layout
{
if (!_queued && !_running)
{
Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Layout);
Dispatcher.UIThread.Post(ExecuteLayoutPass, DispatcherPriority.Layout);
_queued = true;
}
}

7
src/Avalonia.Themes.Default/ListBox.xaml

@ -3,11 +3,16 @@
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Template">
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Name="PART_ScrollViewer" Background="{TemplateBinding Background}">
<ScrollViewer Name="PART_ScrollViewer"
Background="{TemplateBinding Background}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"

5
src/Avalonia.Themes.Default/ScrollViewer.xaml

@ -6,12 +6,13 @@
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto">
<ScrollContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}"
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}"
Content="{TemplateBinding Content}"
Extent="{TemplateBinding Path=Extent, Mode=TwoWay}"
Margin="{TemplateBinding Padding}"
Offset="{TemplateBinding Path=Offset, Mode=TwoWay}"
Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"
CanScrollHorizontally="{TemplateBinding CanScrollHorizontally}"/>
Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"/>
<ScrollBar Name="horizontalScrollBar"
Orientation="Horizontal"
Maximum="{TemplateBinding HorizontalScrollBarMaximum}"

3
src/Avalonia.Themes.Default/TextBox.xaml

@ -36,8 +36,7 @@
<Path Data="M14,7 A7,7 0 0,0 0,7 M0,7 A7,7 0 1,0 14,7 M7,3l0,5 M7,9l0,2" Stroke="{DynamicResource ErrorBrush}" StrokeThickness="2"/>
</Canvas>
<ScrollViewer CanScrollHorizontally="{TemplateBinding CanScrollHorizontally}"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
<ScrollViewer HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}">
<Panel>

2
src/Avalonia.Themes.Default/TreeView.xaml

@ -7,7 +7,7 @@
<ControlTemplate>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer CanScrollHorizontally="True" Background="{TemplateBinding Background}">
<ScrollViewer Background="{TemplateBinding Background}">
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"

2
src/Avalonia.Visuals/Media/Geometry.cs

@ -15,7 +15,7 @@ namespace Avalonia.Media
/// Defines the <see cref="Transform"/> property.
/// </summary>
public static readonly StyledProperty<Transform> TransformProperty =
AvaloniaProperty.Register<Geometry, Transform>("Transform");
AvaloniaProperty.Register<Geometry, Transform>(nameof(Transform));
/// <summary>
/// Initializes static members of the <see cref="Geometry"/> class.

2
src/Avalonia.Visuals/Media/ImageBrush.cs

@ -14,7 +14,7 @@ namespace Avalonia.Media
/// Defines the <see cref="Visual"/> property.
/// </summary>
public static readonly StyledProperty<IBitmap> SourceProperty =
AvaloniaProperty.Register<ImageBrush, IBitmap>("Source");
AvaloniaProperty.Register<ImageBrush, IBitmap>(nameof(Source));
/// <summary>
/// Initializes a new instance of the <see cref="ImageBrush"/> class.

2
src/Avalonia.Visuals/Media/MatrixTransform.cs

@ -15,7 +15,7 @@ namespace Avalonia.Media
/// Defines the <see cref="Matrix"/> property.
/// </summary>
public static readonly StyledProperty<Matrix> MatrixProperty =
AvaloniaProperty.Register<MatrixTransform, Matrix>("Matrix", Matrix.Identity);
AvaloniaProperty.Register<MatrixTransform, Matrix>(nameof(Matrix), Matrix.Identity);
/// <summary>
/// Initializes a new instance of the <see cref="MatrixTransform"/> class.

2
src/Avalonia.Visuals/Media/RotateTransform.cs

@ -15,7 +15,7 @@ namespace Avalonia.Media
/// Defines the <see cref="Angle"/> property.
/// </summary>
public static readonly StyledProperty<double> AngleProperty =
AvaloniaProperty.Register<RotateTransform, double>("Angle");
AvaloniaProperty.Register<RotateTransform, double>(nameof(Angle));
/// <summary>
/// Initializes a new instance of the <see cref="RotateTransform"/> class.

4
src/Avalonia.Visuals/Media/TranslateTransform.cs

@ -15,13 +15,13 @@ namespace Avalonia.Media
/// Defines the <see cref="X"/> property.
/// </summary>
public static readonly StyledProperty<double> XProperty =
AvaloniaProperty.Register<TranslateTransform, double>("X");
AvaloniaProperty.Register<TranslateTransform, double>(nameof(X));
/// <summary>
/// Defines the <see cref="Y"/> property.
/// </summary>
public static readonly StyledProperty<double> YProperty =
AvaloniaProperty.Register<TranslateTransform, double>("Y");
AvaloniaProperty.Register<TranslateTransform, double>(nameof(Y));
/// <summary>
/// Initializes a new instance of the <see cref="TranslateTransform"/> class.

2
src/Avalonia.Visuals/Media/VisualBrush.cs

@ -14,7 +14,7 @@ namespace Avalonia.Media
/// Defines the <see cref="Visual"/> property.
/// </summary>
public static readonly StyledProperty<IVisual> VisualProperty =
AvaloniaProperty.Register<VisualBrush, IVisual>("Visual");
AvaloniaProperty.Register<VisualBrush, IVisual>(nameof(Visual));
/// <summary>
/// Initializes a new instance of the <see cref="VisualBrush"/> class.

2
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -415,7 +415,7 @@ namespace Avalonia.Rendering
if (!_updateQueued && (_dirty == null || _dirty.Count > 0))
{
_updateQueued = true;
_dispatcher.InvokeAsync(UpdateScene, DispatcherPriority.Render);
_dispatcher.Post(UpdateScene, DispatcherPriority.Render);
}
Scene scene = null;

9
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -169,7 +169,7 @@ namespace Avalonia.Rendering
{
foreach (var e in visual.GetSelfAndVisualDescendants())
{
BoundsTracker.SetTransformedBounds((Visual)visual, null);
visual.TransformedBounds = null;
}
}
@ -197,7 +197,7 @@ namespace Avalonia.Rendering
if (filter?.Invoke(visual) != false)
{
bool containsPoint = BoundsTracker.GetTransformedBounds((Visual)visual)?.Contains(p) == true;
bool containsPoint = visual.TransformedBounds?.Contains(p) == true;
if ((containsPoint || !visual.ClipToBounds) && visual.VisualChildren.Count > 0)
{
@ -257,10 +257,7 @@ namespace Avalonia.Rendering
new TransformedBounds(bounds, new Rect(), context.CurrentContainerTransform);
#pragma warning restore 0618
if (visual is Visual)
{
BoundsTracker.SetTransformedBounds((Visual)visual, transformed);
}
visual.TransformedBounds = transformed;
foreach (var child in visual.VisualChildren.OrderBy(x => x, ZIndexComparer.Instance))
{

12
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -209,11 +209,8 @@ namespace Avalonia.Rendering.SceneGraph
}
catch { }
if (visual is Visual)
{
var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
BoundsTracker.SetTransformedBounds((Visual)visual, transformed);
}
var transformed = new TransformedBounds(new Rect(visual.Bounds.Size), clip, node.Transform);
visual.TransformedBounds = transformed;
if (forceRecurse)
{
@ -279,10 +276,7 @@ namespace Avalonia.Rendering.SceneGraph
scene.Layers[node.LayerRoot].Dirty.Add(node.Bounds);
if (node.Visual is Visual v)
{
BoundsTracker.SetTransformedBounds(v, null);
}
node.Visual.TransformedBounds = null;
foreach (VisualNode child in node.Children)
{

17
src/Avalonia.Visuals/Visual.cs

@ -32,6 +32,11 @@ namespace Avalonia
public static readonly DirectProperty<Visual, Rect> BoundsProperty =
AvaloniaProperty.RegisterDirect<Visual, Rect>(nameof(Bounds), o => o.Bounds);
public static readonly DirectProperty<Visual, TransformedBounds?> TransformedBoundsProperty =
AvaloniaProperty.RegisterDirect<Visual, TransformedBounds?>(
nameof(TransformedBounds),
o => o.TransformedBounds);
/// <summary>
/// Defines the <see cref="ClipToBounds"/> property.
/// </summary>
@ -87,6 +92,7 @@ namespace Avalonia
AvaloniaProperty.Register<Visual, int>(nameof(ZIndex));
private Rect _bounds;
private TransformedBounds? _transformedBounds;
private IRenderRoot _visualRoot;
private IVisual _visualParent;
@ -135,6 +141,11 @@ namespace Avalonia
protected set { SetAndRaise(BoundsProperty, ref _bounds, value); }
}
/// <summary>
/// Gets the bounds of the control relative to the window, accounting for rendering transforms.
/// </summary>
public TransformedBounds? TransformedBounds => _transformedBounds;
/// <summary>
/// Gets a value indicating whether the control should be clipped to its bounds.
/// </summary>
@ -253,6 +264,12 @@ namespace Avalonia
/// Gets the root of the visual tree, if the control is attached to a visual tree.
/// </summary>
IRenderRoot IVisual.VisualRoot => VisualRoot;
TransformedBounds? IVisual.TransformedBounds
{
get { return _transformedBounds; }
set { SetAndRaise(TransformedBoundsProperty, ref _transformedBounds, value); }
}
/// <summary>
/// Invalidates the visual and queues a repaint.

51
src/Avalonia.Visuals/VisualTree/BoundsTracker.cs

@ -1,51 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.VisualTree
{
/// <summary>
/// Tracks the bounds of a control.
/// </summary>
/// <remarks>
/// This class is used to track a controls's bounds for hit testing.
/// TODO: This shouldn't be implemented as an attached property: it would be more performant
/// to just store bounds in some sort of central repository.
/// </remarks>
public class BoundsTracker
{
/// <summary>
/// Defines the TransformedBounds attached property.
/// </summary>
private static AttachedProperty<TransformedBounds?> TransformedBoundsProperty =
AvaloniaProperty.RegisterAttached<BoundsTracker, Visual, TransformedBounds?>("TransformedBounds");
/// <summary>
/// Starts tracking the specified visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>An observable that returns the tracked bounds.</returns>
public IObservable<TransformedBounds?> Track(Visual visual)
{
return visual.GetObservable(TransformedBoundsProperty);
}
/// <summary>
/// Sets the transformed bounds of the visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="value">The transformed bounds.</param>
internal static void SetTransformedBounds(Visual visual, TransformedBounds? value)
{
visual.SetValue(TransformedBoundsProperty, value);
}
/// <summary>
/// Gets the transformed bounds of the visual.
/// </summary>
/// <param name="visual">The visual.</param>
/// <returns>The transformed bounds or null if the visual is not visible.</returns>
public static TransformedBounds? GetTransformedBounds(Visual visual) => visual.GetValue(TransformedBoundsProperty);
}
}

5
src/Avalonia.Visuals/VisualTree/IVisual.cs

@ -36,6 +36,11 @@ namespace Avalonia.VisualTree
/// </summary>
Rect Bounds { get; }
/// <summary>
/// Gets the bounds of the control relative to the window, accounting for rendering transforms.
/// </summary>
TransformedBounds? TransformedBounds { get; set; }
/// <summary>
/// Gets a value indicating whether the control should be clipped to its bounds.
/// </summary>

2
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -351,7 +351,7 @@ namespace Avalonia.Gtk3
void OnInput(RawInputEventArgs args)
{
Dispatcher.UIThread.InvokeAsync(() => Input?.Invoke(args), DispatcherPriority.Input);
Dispatcher.UIThread.Post(() => Input?.Invoke(args), DispatcherPriority.Input);
}
public Point PointToClient(Point point)

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

@ -41,7 +41,7 @@ namespace Avalonia.LinuxFramebuffer
if(_renderQueued)
return;
_renderQueued = true;
Dispatcher.UIThread.InvokeAsync(() =>
Dispatcher.UIThread.Post(() =>
{
Paint?.Invoke(new Rect(default(Point), ClientSize));
_renderQueued = false;

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

@ -62,7 +62,7 @@ public static class LinuxFramebufferPlatformExtensions
public TokenClosable(CancellationToken token)
{
token.Register(() => Dispatcher.UIThread.InvokeAsync(() => Closed?.Invoke(this, new EventArgs())));
token.Register(() => Dispatcher.UIThread.Post(() => Closed?.Invoke(this, new EventArgs())));
}
}

2
src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs

@ -104,7 +104,7 @@ namespace Avalonia.Markup.Data.Plugins
protected override void SubscribeCore(IObserver<object> observer)
{
_subscription = Instance.GetWeakObservable(_property).Subscribe(observer);
_subscription = Instance?.GetWeakObservable(_property).Subscribe(observer);
}
}
}

2
src/OSX/Avalonia.MonoMac/TopLevelImpl.cs

@ -107,7 +107,7 @@ namespace Avalonia.MonoMac
if (_nonUiRedrawQueued)
return;
_nonUiRedrawQueued = true;
Dispatcher.UIThread.InvokeAsync(
Dispatcher.UIThread.Post(
() =>
{
lock (SyncRoot)

6
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@ -506,17 +506,17 @@ namespace Avalonia.Base.UnitTests
{
public static readonly DirectProperty<Class1, string> FooProperty =
AvaloniaProperty.RegisterDirect<Class1, string>(
"Foo",
nameof(Foo),
o => o.Foo,
(o, v) => o.Foo = v,
unsetValue: "unset");
public static readonly DirectProperty<Class1, string> BarProperty =
AvaloniaProperty.RegisterDirect<Class1, string>("Bar", o => o.Bar);
AvaloniaProperty.RegisterDirect<Class1, string>(nameof(Bar), o => o.Bar);
public static readonly DirectProperty<Class1, int> BazProperty =
AvaloniaProperty.RegisterDirect<Class1, int>(
"Bar",
nameof(Baz),
o => o.Baz,
(o, v) => o.Baz = v,
unsetValue: -1);

2
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetSubject.cs

@ -37,7 +37,7 @@ namespace Avalonia.Base.UnitTests
private class Class1 : AvaloniaObject
{
public static readonly StyledProperty<string> FooProperty =
AvaloniaProperty.Register<Class1, string>("Foo", "foodefault");
AvaloniaProperty.Register<Class1, string>(nameof(Foo), "foodefault");
public string Foo
{

2
tests/Avalonia.Base.UnitTests/DirectPropertyTests.cs

@ -85,7 +85,7 @@ namespace Avalonia.Base.UnitTests
private class Class1 : AvaloniaObject
{
public static readonly DirectProperty<Class1, string> FooProperty =
AvaloniaProperty.RegisterDirect<Class1, string>("Foo", o => o.Foo, (o, v) => o.Foo = v);
AvaloniaProperty.RegisterDirect<Class1, string>(nameof(Foo), o => o.Foo, (o, v) => o.Foo = v);
private string _foo = "foo";

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

@ -281,6 +281,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
var scroller = new TestScroller
{
CanHorizontallyScroll = false,
CanVerticallyScroll = true,
Content = result = new TestItemsPresenter(useContainers)
{
Items = items,

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

@ -998,6 +998,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
var scroller = new TestScroller
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = result = new TestItemsPresenter(useContainers)
{
Items = items,

17
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Layout;
using Xunit;
@ -94,6 +95,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
TestControl content;
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = content = new TestControl(),
};
@ -110,6 +113,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
Border content;
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = content = new Border
{
Width = 150,
@ -131,8 +136,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
var child = new TestControl();
var target = new ScrollContentPresenter
{
CanVerticallyScroll = true,
Content = child,
[ScrollContentPresenter.CanScrollHorizontallyProperty] = false,
};
target.UpdateChild();
@ -147,6 +152,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
var child = new TestControl();
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = child,
};
@ -191,7 +198,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
}
[Fact]
public void Arrange_Should_Constrain_Child_Width_When_CanScrollHorizontally_False()
public void Arrange_Should_Constrain_Child_Width_When_CanHorizontallyScroll_False()
{
var child = new WrapPanel
{
@ -206,7 +213,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
var target = new ScrollContentPresenter
{
Content = child,
CanScrollHorizontally = false,
CanHorizontallyScroll = false,
};
target.UpdateChild();
@ -232,7 +239,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
var target = new ScrollContentPresenter
{
Content = child,
CanScrollHorizontally = false,
CanHorizontallyScroll = false,
};
target.UpdateChild();
@ -287,6 +294,8 @@ namespace Avalonia.Controls.UnitTests.Presenters
Border border;
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Width = 100,
Height = 100,
Content = new Decorator

46
tests/Avalonia.Controls.UnitTests/Presenters/ScrollContentPresenterTests_ILogicalScrollable.cs

@ -13,7 +13,7 @@ namespace Avalonia.Controls.UnitTests
public class ScrollContentPresenterTests_ILogicalScrollable
{
[Fact]
public void Measure_Should_Pass_Unchanged_Bounds_To_IScrollable()
public void Measure_Should_Pass_Unchanged_Bounds_To_ILogicalScrollable()
{
var scrollable = new TestScrollable();
var target = new ScrollContentPresenter
@ -28,7 +28,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Arrange_Should_Not_Offset_IScrollable_Bounds()
public void Arrange_Should_Not_Offset_ILogicalScrollable_Bounds()
{
var scrollable = new TestScrollable
{
@ -50,7 +50,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Arrange_Should_Offset_IScrollable_Bounds_When_Logical_Scroll_Disabled()
public void Arrange_Should_Offset_ILogicalScrollable_Bounds_When_Logical_Scroll_Disabled()
{
var scrollable = new TestScrollable
{
@ -59,6 +59,8 @@ namespace Avalonia.Controls.UnitTests
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = scrollable,
Offset = new Vector(25, 25),
};
@ -71,7 +73,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Arrange_Should_Not_Set_Viewport_And_Extent_With_IScrollable()
public void Arrange_Should_Not_Set_Viewport_And_Extent_With_ILogicalScrollable()
{
var target = new ScrollContentPresenter
{
@ -122,7 +124,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Extent_Offset_And_Viewport_Should_Be_Read_From_IScrollable()
public void Extent_Offset_And_Viewport_Should_Be_Read_From_ILogicalScrollable()
{
var scrollable = new TestScrollable
{
@ -152,7 +154,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Offset_Should_Be_Written_To_IScrollable()
public void Offset_Should_Be_Written_To_ILogicalScrollable()
{
var scrollable = new TestScrollable
{
@ -172,7 +174,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
public void Offset_Should_Not_Be_Written_To_IScrollable_After_Removal()
public void Offset_Should_Not_Be_Written_To_ILogicalScrollable_After_Removal()
{
var scrollable = new TestScrollable
{
@ -203,6 +205,8 @@ namespace Avalonia.Controls.UnitTests
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = scrollable,
};
@ -253,6 +257,8 @@ namespace Avalonia.Controls.UnitTests
var target = new ScrollContentPresenter
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = logicalScrollable,
};
@ -286,12 +292,38 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Rect(0, 0, 100, 100), logicalScrollable.Bounds);
}
[Fact]
public void Should_Set_ILogicalScrolable_CanHorizontallyScroll()
{
var logicalScrollable = new TestScrollable();
var target = new ScrollContentPresenter { Content = logicalScrollable };
target.UpdateChild();
Assert.False(logicalScrollable.CanHorizontallyScroll);
target.CanHorizontallyScroll = true;
Assert.True(logicalScrollable.CanHorizontallyScroll);
}
[Fact]
public void Should_Set_ILogicalScrolable_CanVerticallyScroll()
{
var logicalScrollable = new TestScrollable();
var target = new ScrollContentPresenter { Content = logicalScrollable };
target.UpdateChild();
Assert.False(logicalScrollable.CanVerticallyScroll);
target.CanVerticallyScroll = true;
Assert.True(logicalScrollable.CanVerticallyScroll);
}
private class TestScrollable : Control, ILogicalScrollable
{
private Size _extent;
private Vector _offset;
private Size _viewport;
public bool CanHorizontallyScroll { get; set; }
public bool CanVerticallyScroll { get; set; }
public bool IsLogicalScrollEnabled { get; set; } = true;
public Size AvailableSize { get; private set; }
public Action InvalidateScroll { get; set; }

30
tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

@ -1,6 +1,8 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
@ -25,6 +27,32 @@ namespace Avalonia.Controls.UnitTests
Assert.IsType<TextBlock>(target.Presenter.Child);
}
[Fact]
public void CanHorizontallyScroll_Should_Track_HorizontalScrollBarVisibility()
{
var target = new ScrollViewer();
var values = new List<bool>();
target.GetObservable(ScrollViewer.CanHorizontallyScrollProperty).Subscribe(x => values.Add(x));
target.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
target.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
Assert.Equal(new[] { true, false, true }, values);
}
[Fact]
public void CanVerticallyScroll_Should_Track_VerticalScrollBarVisibility()
{
var target = new ScrollViewer();
var values = new List<bool>();
target.GetObservable(ScrollViewer.CanVerticallyScrollProperty).Subscribe(x => values.Add(x));
target.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
target.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
Assert.Equal(new[] { true, false, true }, values);
}
[Fact]
public void Offset_Should_Be_Coerced_To_Viewport()
{
@ -59,7 +87,7 @@ namespace Avalonia.Controls.UnitTests
[~~ScrollContentPresenter.ExtentProperty] = control[~~ScrollViewer.ExtentProperty],
[~~ScrollContentPresenter.OffsetProperty] = control[~~ScrollViewer.OffsetProperty],
[~~ScrollContentPresenter.ViewportProperty] = control[~~ScrollViewer.ViewportProperty],
[~ScrollContentPresenter.CanScrollHorizontallyProperty] = control[~ScrollViewer.CanScrollHorizontallyProperty],
[~ScrollContentPresenter.CanHorizontallyScrollProperty] = control[~ScrollViewer.CanHorizontallyScrollProperty],
},
new ScrollBar
{

16
tests/Avalonia.RenderTests/Avalonia.Direct2D1.RenderTests.csproj → tests/Avalonia.Direct2D1.RenderTests/Avalonia.Direct2D1.RenderTests.csproj

@ -1,19 +1,9 @@
<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>obj-Direct2D1</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<OutputPath>bin\Direct2D\$(Configuration)</OutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<DefineConstants>$(DefineConstants);AVALONIA_DIRECT2D</DefineConstants>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Remove="obj\**" />
<None Remove="obj\**" />
<Compile Include="..\Avalonia.RenderTests\**\*.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
@ -33,7 +23,5 @@
<Import Project="..\..\build\Moq.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\XUnit.props" />
<Import Project="Avalonia.RenderTests.projitems" Label="Shared" />
<Import Project="..\..\build\Magick.NET-Q16-AnyCPU.props" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

2
tests/Avalonia.Direct2D1.UnitTests/Properties/AssemblyInfo.cs

@ -4,7 +4,5 @@
using System.Reflection;
using Xunit;
[assembly: AssemblyTitle("Avalonia.Direct2D1.UnitTests")]
// Don't run tests in parallel.
[assembly: CollectionBehavior(DisableTestParallelization = true)]

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

@ -85,7 +85,7 @@ namespace Avalonia.Layout.UnitTests
{
Width = 200,
Height = 200,
CanScrollHorizontally = true,
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Content = textBlock = new TextBlock

2
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AttachedProperty.cs

@ -136,7 +136,7 @@ namespace Avalonia.Markup.UnitTests.Data
private class Class1 : AvaloniaObject
{
public static readonly StyledProperty<Class1> NextProperty =
AvaloniaProperty.Register<Class1, Class1>("Next");
AvaloniaProperty.Register<Class1, Class1>(nameof(Next));
public Class1 Next
{

12
tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs

@ -103,20 +103,22 @@ namespace Avalonia.Markup.UnitTests.Data
{
using (var sync = UnitTestSynchronizationContext.Begin())
{
var data = new Class1();
var target = new ExpressionObserver(data, "Next^.Foo", true);
var data1 = new Class1();
var data2 = new Class2("foo");
var target = new ExpressionObserver(data1, "Next^.Foo", true);
var result = new List<object>();
var sub = target.Subscribe(x => result.Add(x));
data.Next.OnNext(new Class2("foo"));
data1.Next.OnNext(data2);
sync.ExecutePostedCallbacks();
Assert.Equal(new[] { new BindingNotification("foo") }, result);
sub.Dispose();
Assert.Equal(0, data.PropertyChangedSubscriptionCount);
Assert.Equal(0, data1.PropertyChangedSubscriptionCount);
GC.KeepAlive(data);
GC.KeepAlive(data1);
GC.KeepAlive(data2);
}
}

2
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs

@ -558,7 +558,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
private class InheritanceTest : Decorator
{
public static readonly StyledProperty<int> BazProperty =
AvaloniaProperty.Register<InheritanceTest, int>("Baz", defaultValue: 6, inherits: true);
AvaloniaProperty.Register<InheritanceTest, int>(nameof(Baz), defaultValue: 6, inherits: true);
public int Baz
{

6
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/NonControl.cs

@ -8,10 +8,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
public class NonControl : AvaloniaObject
{
public static readonly StyledProperty<Control> ControlProperty =
AvaloniaProperty.Register<NonControl, Control>("Control");
AvaloniaProperty.Register<NonControl, Control>(nameof(Control));
public static readonly StyledProperty<string> StringProperty =
AvaloniaProperty.Register<NonControl, string>("String");
AvaloniaProperty.Register<NonControl, string>(nameof(String));
//No getter or setter Avalonia property
public static readonly StyledProperty<int> FooProperty =
@ -19,7 +19,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
//getter only Avalonia property
public static readonly StyledProperty<string> BarProperty =
AvaloniaProperty.Register<NonControl, string>("Bar");
AvaloniaProperty.Register<NonControl, string>(nameof(Bar));
public Control Control
{

2
tests/Avalonia.RenderTests/.gitignore

@ -1,2 +0,0 @@
obj-Skia/
obj-Skia/*

33
tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>48840edd-24bf-495d-911e-2eb12ae75d3b</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Avalonia.RenderTests</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Controls\CustomRenderTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GeometryClippingTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Media\RadialGradientBrushTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Media\BitmapTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)OpacityMaskTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SVGPathTests.cs" />
<Compile Include="Media\FormattedTextImplTests.cs" />
<Compile Include="Controls\ImageTests.cs" />
<Compile Include="Controls\BorderTests.cs" />
<Compile Include="Media\ImageBrushTests.cs" />
<Compile Include="Media\LinearGradientBrushTests.cs" />
<Compile Include="Media\VisualBrushTests.cs" />
<Compile Include="Shapes\LineTests.cs" />
<Compile Include="Shapes\PathTests.cs" />
<Compile Include="Shapes\EllipseTests.cs" />
<Compile Include="Shapes\PolygonTests.cs" />
<Compile Include="Shapes\PolylineTests.cs" />
<Compile Include="Shapes\RectangleTests.cs" />
<Compile Include="TestBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
</Project>

16
tests/Avalonia.RenderTests/Avalonia.RenderTests.shproj

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>48840edd-24bf-495d-911e-2eb12ae75d3b</ProjectGuid>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<PropertyGroup />
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="Avalonia.RenderTests.projitems" Label="Shared" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

5
tests/Avalonia.RenderTests/Controls/TextBlockTests.cs

@ -1,6 +1,7 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
@ -20,7 +21,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
}
[Fact]
public void Wrapping_NoWrap()
public async Task Wrapping_NoWrap()
{
Decorator target = new Decorator
{
@ -38,7 +39,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
}
};
RenderToFile(target);
await RenderToFile(target);
CompareImages();
}
}

2
tests/Avalonia.RenderTests/Properties/AssemblyInfo.cs

@ -4,7 +4,5 @@
using System.Reflection;
using Xunit;
[assembly: AssemblyTitle("Avalonia.Direct2D1.RenderTests")]
// Don't run tests in parallel.
[assembly: CollectionBehavior(DisableTestParallelization = true)]

23
tests/Avalonia.RenderTests/Shapes/PathTests.cs

@ -362,5 +362,28 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
await RenderToFile(target);
CompareImages();
}
[Fact]
public async Task Path_With_Rotated_Geometry()
{
var target = new Border
{
Width = 200,
Height = 200,
Background = Brushes.White,
Child = new Path
{
Fill = Brushes.Red,
Data = new RectangleGeometry
{
Rect = new Rect(50, 50, 100, 100),
Transform = new RotateTransform(45),
}
}
};
await RenderToFile(target);
CompareImages();
}
}
}

15
tests/Avalonia.RenderTests/TestBase.cs

@ -46,7 +46,8 @@ namespace Avalonia.Direct2D1.RenderTests
public TestBase(string outputPath)
{
var testFiles = Path.GetFullPath(@"..\..\..\..\..\TestFiles\");
var testPath = GetTestsDirectory();
var testFiles = Path.Combine(testPath, "TestFiles");
#if AVALONIA_SKIA
var platform = "Skia";
#else
@ -142,6 +143,18 @@ namespace Avalonia.Direct2D1.RenderTests
}
}
private string GetTestsDirectory()
{
var path = Directory.GetCurrentDirectory();
while (path.Length > 0 && Path.GetFileName(path) != "tests")
{
path = Path.GetDirectoryName(path);
}
return path;
}
private class TestThreadingInterface : IPlatformThreadingInterface
{
public bool CurrentThreadIsLoopThread => MainThread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId;

20
tests/Avalonia.RenderTests/Avalonia.Skia.RenderTests.csproj → tests/Avalonia.Skia.RenderTests/Avalonia.Skia.RenderTests.csproj

@ -1,21 +1,10 @@
<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>obj-Skia</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
<OutputPath>bin\Skia\$(Configuration)</OutputPath>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<DefineConstants>$(DefineConstants);AVALONIA_SKIA;AVALONIA_SKIA_SKIP_FAIL</DefineConstants>
<OutputType>Library</OutputType>
<DefineConstants>AVALONIA_SKIA;AVALONIA_SKIA_SKIP_FAIL</DefineConstants>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Remove="obj\**" />
<None Remove="obj\**" />
<Compile Include="..\Avalonia.RenderTests\**\*.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.DotNetCoreRuntime\Avalonia.DotNetCoreRuntime.csproj" />
@ -35,9 +24,6 @@
<Import Project="..\..\build\Moq.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\XUnit.props" />
<Import Project="Avalonia.RenderTests.projitems" Label="Shared" />
<Import Project="..\..\build\Magick.NET-Q16-AnyCPU.props" />
<Import Project="..\..\build\SkiaSharp.props" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

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

@ -170,7 +170,7 @@ namespace Avalonia.Styling.UnitTests
private class Class1 : Control
{
public static readonly StyledProperty<string> FooProperty =
AvaloniaProperty.Register<Class1, string>("Foo", "foodefault");
AvaloniaProperty.Register<Class1, string>(nameof(Foo), "foodefault");
public string Foo
{

4
tests/Avalonia.UnitTests/ImmediateDispatcher.cs

@ -14,12 +14,12 @@ namespace Avalonia.UnitTests
return true;
}
public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
public void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
action();
}
public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
action();
return Task.FromResult<object>(null);

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

@ -24,13 +24,13 @@ namespace Avalonia.Visuals.UnitTests.Rendering
var root = new TestRoot();
var dispatcher = new Mock<IDispatcher>();
dispatcher.Setup(x => x.InvokeAsync(It.IsAny<Action>(), DispatcherPriority.Render))
dispatcher.Setup(x => x.Post(It.IsAny<Action>(), DispatcherPriority.Render))
.Callback<Action, DispatcherPriority>((a, p) => a());
CreateTargetAndRunFrame(root, dispatcher: dispatcher.Object);
dispatcher.Verify(x =>
x.InvokeAsync(
x.Post(
It.Is<Action>(a => a.Method.Name == "UpdateScene"),
DispatcherPriority.Render));
}

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

@ -372,6 +372,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering
Margin = new Thickness(0, 100, 0, 0),
Child = scroll = new ScrollContentPresenter()
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = new StackPanel()
{
Children =

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

@ -357,6 +357,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering
Margin = new Thickness(0, 100, 0, 0),
Child = scroll = new ScrollContentPresenter()
{
CanHorizontallyScroll = true,
CanVerticallyScroll = true,
Content = new StackPanel()
{
Children =

5
tests/Avalonia.Visuals.UnitTests/VisualTree/BoundsTrackerTests.cs → tests/Avalonia.Visuals.UnitTests/VisualTree/TransformedBoundsTests.cs

@ -17,14 +17,13 @@ using Avalonia.Platform;
namespace Avalonia.Visuals.UnitTests.VisualTree
{
public class BoundsTrackerTests
public class TransformedBoundsTests
{
[Fact]
public void Should_Track_Bounds()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var target = new BoundsTracker();
var control = default(Rectangle);
var tree = new Decorator
{
@ -46,7 +45,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
tree.Arrange(new Rect(0, 0, 100, 100));
ImmediateRenderer.Render(tree, context);
var track = target.Track(control);
var track = control.GetObservable(Visual.TransformedBoundsProperty);
var results = new List<TransformedBounds?>();
track.Subscribe(results.Add);

BIN
tests/TestFiles/Cairo/SVGPath/SVGPath.expected.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

BIN
tests/TestFiles/Direct2D1/Shapes/Path/Path_With_Rotated_Geometry.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
tests/TestFiles/Skia/Controls/TextBlock/Wrapping_NoWrap.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
tests/TestFiles/Skia/Shapes/Path/Path_With_Rotated_Geometry.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Loading…
Cancel
Save