Browse Source

Merge branch 'master' into fixes/3965-popup-modal-interaction

pull/4099/head
Steven Kirk 6 years ago
parent
commit
a0ef74dd4c
  1. 2
      .gitmodules
  2. 40
      Avalonia.sln
  3. 1
      Avalonia.v3.ncrunchsolution
  4. 1
      Directory.Build.props
  5. 2
      NuGet.Config
  6. 12
      build/ApiDiff.props
  7. 2
      build/SkiaSharp.props
  8. 2
      dirs.proj
  9. 16
      native/Avalonia.Native/src/OSX/Screens.mm
  10. 13
      native/Avalonia.Native/src/OSX/window.mm
  11. 4
      nukebuild/Build.cs
  12. 2
      readme.md
  13. 24
      samples/ControlCatalog/App.xaml.cs
  14. 1
      samples/ControlCatalog/MainView.xaml
  15. 42
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  16. 6
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  17. 8
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  18. 31
      samples/ControlCatalog/Pages/MenuPage.xaml
  19. 10
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  20. 36
      samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
  21. 5
      samples/RenderDemo/App.xaml
  22. 125
      samples/RenderDemo/SideBar.xaml
  23. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  24. 3
      src/Avalonia.Animation/Avalonia.Animation.csproj
  25. 3
      src/Avalonia.Base/Avalonia.Base.csproj
  26. 61
      src/Avalonia.Base/Threading/AvaloniaScheduler.cs
  27. 27
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  28. 10
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  29. 2
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  30. 1
      src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
  31. 6
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  32. 3
      src/Avalonia.Controls/Avalonia.Controls.csproj
  33. 6
      src/Avalonia.Controls/Chrome/CaptionButtons.cs
  34. 126
      src/Avalonia.Controls/Chrome/TitleBar.cs
  35. 16
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  36. 31
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  37. 51
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  38. 38
      src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
  39. 2
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  40. 1
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  41. 10
      src/Avalonia.Controls/MenuItem.cs
  42. 6
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  43. 5
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  44. 5
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  45. 21
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  46. 2
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  47. 7
      src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
  48. 4
      src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
  49. 20
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  50. 28
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  51. 2
      src/Avalonia.Controls/ProgressBar.cs
  52. 2
      src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
  53. 487
      src/Avalonia.Controls/RelativePanel.cs
  54. 2
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  55. 27
      src/Avalonia.Controls/Repeater/ElementFactory.cs
  56. 66
      src/Avalonia.Controls/Repeater/IElementFactory.cs
  57. 19
      src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
  58. 4
      src/Avalonia.Controls/Repeater/ItemsRepeater.cs
  59. 12
      src/Avalonia.Controls/Repeater/RecyclePool.cs
  60. 119
      src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs
  61. 31
      src/Avalonia.Controls/Repeater/ViewManager.cs
  62. 29
      src/Avalonia.Controls/Templates/FuncDataTemplate.cs
  63. 6
      src/Avalonia.Controls/Templates/FuncTemplate`2.cs
  64. 12
      src/Avalonia.Controls/Templates/IDataTemplate.cs
  65. 25
      src/Avalonia.Controls/Templates/IRecyclingDataTemplate.cs
  66. 2
      src/Avalonia.Controls/TextBox.cs
  67. 25
      src/Avalonia.Controls/TickBar.cs
  68. 1
      src/Avalonia.Controls/ToolTip.cs
  69. 21
      src/Avalonia.Controls/ToolTipService.cs
  70. 4
      src/Avalonia.Controls/TopLevel.cs
  71. 20
      src/Avalonia.Controls/Window.cs
  72. 2
      src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj
  73. 7
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  74. 10
      src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs
  75. 28
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json
  76. 2
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js
  77. 1
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  78. 7
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  79. 3
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  80. 2
      src/Avalonia.Desktop/Avalonia.Desktop.csproj
  81. 1
      src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj
  82. 2
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  83. 2
      src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
  84. 2
      src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
  85. 5
      src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
  86. 2
      src/Avalonia.FreeDesktop/DBusHelper.cs
  87. 3
      src/Avalonia.Headless/Avalonia.Headless.csproj
  88. 9
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  89. 1
      src/Avalonia.Input/Avalonia.Input.csproj
  90. 1
      src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
  91. 1
      src/Avalonia.Layout/Avalonia.Layout.csproj
  92. 44
      src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
  93. 15
      src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs
  94. 5
      src/Avalonia.Native/OsxUnicodeKeys.cs
  95. 2
      src/Avalonia.Native/PopupImpl.cs
  96. 18
      src/Avalonia.Native/WindowImplBase.cs
  97. 3
      src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
  98. 1
      src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
  99. 2
      src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
  100. 1
      src/Avalonia.Styling/Avalonia.Styling.csproj

2
.gitmodules

@ -2,5 +2,5 @@
path = nukebuild/Numerge
url = https://github.com/kekekeks/Numerge.git
[submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"]
path = src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
path = src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
url = https://github.com/kekekeks/XamlX.git

40
Avalonia.sln

@ -123,10 +123,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
build\AndroidWorkarounds.props = build\AndroidWorkarounds.props
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
build\BuildTargets.targets = build\BuildTargets.targets
build\CoreLibraries.props = build\CoreLibraries.props
build\EmbedXaml.props = build\EmbedXaml.props
build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
build\iOSWorkarounds.props = build\iOSWorkarounds.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
@ -136,17 +139,25 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\NetCore.props = build\NetCore.props
build\NetFX.props = build\NetFX.props
build\ReactiveUI.props = build\ReactiveUI.props
build\ReferenceCoreLibraries.props = build\ReferenceCoreLibraries.props
build\Rx.props = build\Rx.props
build\SampleApp.props = build\SampleApp.props
build\SharedVersion.props = build\SharedVersion.props
build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.props = build\SkiaSharp.props
build\SourceLink.props = build\SourceLink.props
build\System.Drawing.Common.props = build\System.Drawing.Common.props
build\System.Memory.props = build\System.Memory.props
build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
build\XUnit.props = build\XUnit.props
build\ApiDiff.props = build\ApiDiff.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
ProjectSection(SolutionItems) = preProject
build\UnitTests.NetCore.targets = build\UnitTests.NetCore.targets
build\BuildTargets.targets = build\BuildTargets.targets
build\LegacyProject.targets = build\LegacyProject.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}"
@ -211,6 +222,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless", "src\Av
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.Vnc", "src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -1998,6 +2011,30 @@ Global
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.Build.0 = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.ActiveCfg = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.Build.0 = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.ActiveCfg = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.Build.0 = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -2056,6 +2093,7 @@ Global
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

1
Avalonia.v3.ncrunchsolution

@ -3,6 +3,7 @@
<AdditionalFilesToIncludeForSolution>
<Value>tests\TestFiles\**.*</Value>
<Value>src\Avalonia.Build.Tasks\bin\Debug\netstandard2.0\Avalonia.Build.Tasks.dll</Value>
<Value>src\Avalonia.Build.Tasks\bin\Debug\netstandard2.0\Mono.Cecil.dll</Value>
</AdditionalFilesToIncludeForSolution>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<ProjectConfigStoragePathRelativeToSolutionDir>.ncrunch</ProjectConfigStoragePathRelativeToSolutionDir>

1
Directory.Build.props

@ -1,5 +1,6 @@
<Project>
<PropertyGroup>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath>
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
</PropertyGroup>
</Project>

2
NuGet.Config

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
</packageSources>
</configuration>

12
build/ApiDiff.props

@ -0,0 +1,12 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ApiContractPackageVersion>0.10.0-preview2</ApiContractPackageVersion>
<NugetPackageName Condition="'$(PackageId)' != ''">$(PackageId)</NugetPackageName>
<NugetPackageName Condition="'$(PackageId)' == ''">Avalonia</NugetPackageName>
</PropertyGroup>
<ItemGroup>
<PackageDownload Include="$(NugetPackageName)" Version="[$(ApiContractPackageVersion)]" />
<PackageReference Include="Microsoft.DotNet.ApiCompat" Version="5.0.0-beta.20372.2" PrivateAssets="All" />
<ResolvedMatchingContract Include="$(NuGetPackageRoot)\$(NugetPackageName.ToLower())\$(ApiContractPackageVersion)\lib\$(TargetFramework)\$(AssemblyName).dll" />
</ItemGroup>
</Project>

2
build/SkiaSharp.props

@ -1,6 +1,6 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.80.0" />
<PackageReference Include="SkiaSharp" Version="2.80.1" />
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="2.80.0" />
</ItemGroup>
</Project>

2
dirs.proj

@ -6,7 +6,7 @@
<ProjectReference Include="packages/**/*.*proj" />
<ProjectReference Remove="**/*.shproj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/XamlIl/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
</ItemGroup>
<!--<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">-->
<ItemGroup>

16
native/Avalonia.Native/src/OSX/Screens.mm

@ -4,6 +4,14 @@ class Screens : public ComSingleObject<IAvnScreens, &IID_IAvnScreens>
{
public:
FORWARD_IUNKNOWN()
private:
CGFloat PrimaryDisplayHeight()
{
return NSMaxY([[[NSScreen screens] firstObject] frame]);
}
public:
virtual HRESULT GetScreenCount (int* ret) override
{
@autoreleasepool
@ -25,15 +33,15 @@ class Screens : public ComSingleObject<IAvnScreens, &IID_IAvnScreens>
auto screen = [[NSScreen screens] objectAtIndex:index];
ret->Bounds.X = [screen frame].origin.x;
ret->Bounds.Y = [screen frame].origin.y;
ret->Bounds.Height = [screen frame].size.height;
ret->Bounds.Width = [screen frame].size.width;
ret->Bounds.X = [screen frame].origin.x;
ret->Bounds.Y = PrimaryDisplayHeight() - [screen frame].origin.y - ret->Bounds.Height;
ret->WorkingArea.X = [screen visibleFrame].origin.x;
ret->WorkingArea.Y = [screen visibleFrame].origin.y;
ret->WorkingArea.Height = [screen visibleFrame].size.height;
ret->WorkingArea.Width = [screen visibleFrame].size.width;
ret->WorkingArea.X = [screen visibleFrame].origin.x;
ret->WorkingArea.Y = ret->Bounds.Height - [screen visibleFrame].origin.y - ret->WorkingArea.Height;
ret->PixelDensity = [screen backingScaleFactor];

13
native/Avalonia.Native/src/OSX/window.mm

@ -1291,10 +1291,15 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_parent->UpdateCursor();
auto fsize = [self convertSizeToBacking: [self frame].size];
_lastPixelSize.Width = (int)fsize.width;
_lastPixelSize.Height = (int)fsize.height;
[self updateRenderTarget];
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height)
{
_lastPixelSize.Width = (int)fsize.width;
_lastPixelSize.Height = (int)fsize.height;
[self updateRenderTarget];
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
}
}
- (void)updateLayer

4
nukebuild/Build.cs

@ -128,7 +128,9 @@ partial class Build : NukeBuild
{
var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp";
NpmTasks.NpmInstall(c => c.SetWorkingDirectory(webappDir));
NpmTasks.NpmInstall(c => c
.SetWorkingDirectory(webappDir)
.SetArgumentConfigurator(a => a.Add("--silent")));
NpmTasks.NpmRun(c => c
.SetWorkingDirectory(webappDir)
.SetCommand("dist"));

2
readme.md

@ -1,4 +1,4 @@
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [![Discord](https://img.shields.io/badge/discord-join%20chat-46BC99)]( https://aka.ms/dotnet-discord) [![Build Status](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_apis/build/status/AvaloniaUI.Avalonia)](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [![Backers on Open Collective](https://opencollective.com/Avalonia/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/Avalonia/sponsors/badge.svg)](#sponsors) ![License](https://img.shields.io/github/license/avaloniaui/avalonia.svg)
<br />
[![NuGet](https://img.shields.io/nuget/v/Avalonia.svg)](https://www.nuget.org/packages/Avalonia) [![downloads](https://img.shields.io/nuget/dt/avalonia)](https://www.nuget.org/packages/Avalonia) [![MyGet](https://img.shields.io/myget/avalonia-ci/vpre/Avalonia.svg?label=myget)](https://www.myget.org/gallery/avalonia-ci) ![Size](https://img.shields.io/github/repo-size/avaloniaui/avalonia.svg)

24
samples/ControlCatalog/App.xaml.cs

@ -29,11 +29,19 @@ namespace ControlCatalog
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default")
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default")
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
},
};
@ -41,11 +49,19 @@ namespace ControlCatalog
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default")
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default")
Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
},
};

1
samples/ControlCatalog/MainView.xaml

@ -2,7 +2,6 @@
xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainView"
Background="Black"
Foreground="{DynamicResource ThemeForegroundBrush}"
FontSize="{DynamicResource FontSizeNormal}">
<Grid>

42
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

@ -1,6 +1,30 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ItemsRepeaterPage">
<UserControl.Resources>
<RecyclePool x:Key="RecyclePool" />
<DataTemplate x:Key="odd">
<TextBlock Background="Yellow"
Foreground="Black"
Height="{Binding Height}"
Text="{Binding Text}"/>
</DataTemplate>
<DataTemplate x:Key="even">
<TextBlock Background="Wheat"
Foreground="Black"
Height="{Binding Height}"
Text="{Binding Text}"/>
</DataTemplate>
<RecyclingElementFactory x:Key="elementFactory"
RecyclePool="{StaticResource RecyclePool}"
SelectTemplateKey="OnSelectTemplateKey">
<RecyclingElementFactory.Templates>
<StaticResource x:Key="odd" ResourceKey="odd" />
<StaticResource x:Key="even" ResourceKey="even" />
</RecyclingElementFactory.Templates>
</RecyclingElementFactory>
</UserControl.Resources>
<DockPanel>
<StackPanel DockPanel.Dock="Top" Spacing="4" Margin="0 0 0 16">
<TextBlock Classes="h1">ItemsRepeater</TextBlock>
@ -12,8 +36,6 @@
<ComboBoxItem>Stack - Horizontal</ComboBoxItem>
<ComboBoxItem>UniformGrid - Vertical</ComboBoxItem>
<ComboBoxItem>UniformGrid - Horizontal</ComboBoxItem>
<ComboBoxItem>WrapLayout - Horizontal</ComboBoxItem>
<ComboBoxItem>WrapLayout - Veritcal</ComboBoxItem>
</ComboBox>
<Button Command="{Binding AddItem}">Add Item</Button>
<Button Command="{Binding RandomizeHeights}">Randomize Heights</Button>
@ -25,20 +47,8 @@
<ScrollViewer Name="scroller"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ItemsRepeater Name="repeater" Background="Transparent" Items="{Binding Items}">
<ItemsRepeater.Styles>
<Style Selector="Border:pointerover">
<Setter Property="Width" Value="200" />
</Style>
</ItemsRepeater.Styles>
<ItemsRepeater.ItemTemplate>
<DataTemplate>
<Border Background="Purple" BorderThickness="2" BorderBrush="Green" MinHeight="100" MinWidth="100">
<TextBlock Focusable="True" Height="{Binding Height}" Text="{Binding Text}" Foreground="White" />
</Border>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<ItemsRepeater Name="repeater" Background="Transparent" Items="{Binding Items}"
ItemTemplate="{StaticResource elementFactory}"/>
</ScrollViewer>
</Border>
</DockPanel>

6
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs

@ -38,6 +38,12 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this);
}
public void OnSelectTemplateKey(object sender, SelectTemplateEventArgs e)
{
var item = (ItemsRepeaterPageViewModel.Item)e.DataContext;
e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
}
private void LayoutChanged(object sender, SelectionChangedEventArgs e)
{
if (_repeater == null)

8
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -10,7 +10,13 @@
HorizontalAlignment="Center"
Spacing="16">
<StackPanel Orientation="Vertical" Spacing="8">
<ListBox Items="{Binding Items}" SelectedItem="{Binding SelectedItem}" AutoScrollToSelectedItem="True" SelectionMode="{Binding SelectionMode}" Width="250" Height="350"></ListBox>
<ListBox Items="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
SelectedItems="{Binding SelectedItems}"
AutoScrollToSelectedItem="True"
SelectionMode="{Binding SelectionMode}"
Width="250"
Height="350"/>
<Button Command="{Binding AddItemCommand}">Add</Button>

31
samples/ControlCatalog/Pages/MenuPage.xaml

@ -20,7 +20,9 @@
<Separator/>
<MenuItem Header="Menu with _Submenu">
<MenuItem Header="Submenu _1"/>
<MenuItem Header="Submenu _2"/>
<MenuItem Header="Submenu _2 with Submenu">
<MenuItem Header="Submenu Level 2" />
</MenuItem>
</MenuItem>
<MenuItem Header="Menu Item with _Icon" InputGesture="Ctrl+Shift+B">
<MenuItem.Icon>
@ -52,6 +54,33 @@
</Menu.Styles>
</Menu>
</StackPanel>
<StackPanel>
<TextBlock Classes="h3" Margin="4 8">Mixed</TextBlock>
<Menu>
<MenuItem Header="_File">
<MenuItem Header="_New" CommandParameter="{Binding}" InputGesture="Ctrl+N"/>
<Separator/>
<MenuItem Header="_Open..." InputGesture="Ctrl+O"/>
<Separator/>
<MenuItem Header="Execu_te Script..." />
<Separator/>
<MenuItem Header="_Recent" Items="{Binding RecentItems}">
<MenuItem.Styles>
<Style Selector="MenuItem">
<Setter Property="Header" Value="{Binding Header}"/>
</Style>
</MenuItem.Styles>
</MenuItem>
<Separator/>
<MenuItem Header="E_xit" InputGesture="Alt+F4"/>
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About"/>
</MenuItem>
</Menu>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

10
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

@ -62,13 +62,9 @@ namespace ControlCatalog.ViewModels
public class Item : ReactiveObject
{
private double _height = double.NaN;
private int _index;
public Item(int index)
{
_index = index;
}
public Item(int index) => Index = index;
public int Index { get; }
public string Text { get; set; }
public double Height
@ -76,8 +72,6 @@ namespace ControlCatalog.ViewModels
get => _height;
set => this.RaiseAndSetIfChanged(ref _height, value);
}
public IBrush Background => ((_index % 2) == 0) ? Brushes.Yellow : Brushes.Wheat;
}
}
}

36
samples/ControlCatalog/ViewModels/MenuPageViewModel.cs

@ -17,6 +17,23 @@ namespace ControlCatalog.ViewModels
SaveCommand = ReactiveCommand.Create(Save, Observable.Return(false));
OpenRecentCommand = ReactiveCommand.Create<string>(OpenRecent);
var recentItems = new[]
{
new MenuItemViewModel
{
Header = "File1.txt",
Command = OpenRecentCommand,
CommandParameter = @"c:\foo\File1.txt"
},
new MenuItemViewModel
{
Header = "File2.txt",
Command = OpenRecentCommand,
CommandParameter = @"c:\foo\File2.txt"
},
};
RecentItems = recentItems;
MenuItems = new[]
{
new MenuItemViewModel
@ -24,27 +41,13 @@ namespace ControlCatalog.ViewModels
Header = "_File",
Items = new[]
{
new MenuItemViewModel { Header = "_Open...", Command = OpenCommand },
new MenuItemViewModel { Header = "O_pen...", Command = OpenCommand },
new MenuItemViewModel { Header = "Save", Command = SaveCommand },
new MenuItemViewModel { Header = "-" },
new MenuItemViewModel
{
Header = "Recent",
Items = new[]
{
new MenuItemViewModel
{
Header = "File1.txt",
Command = OpenRecentCommand,
CommandParameter = @"c:\foo\File1.txt"
},
new MenuItemViewModel
{
Header = "File2.txt",
Command = OpenRecentCommand,
CommandParameter = @"c:\foo\File2.txt"
},
}
Items = recentItems
},
}
},
@ -61,6 +64,7 @@ namespace ControlCatalog.ViewModels
}
public IReadOnlyList<MenuItemViewModel> MenuItems { get; set; }
public IReadOnlyList<MenuItemViewModel> RecentItems { get; set; }
public ReactiveCommand<Unit, Unit> OpenCommand { get; }
public ReactiveCommand<Unit, Unit> SaveCommand { get; }
public ReactiveCommand<string, Unit> OpenRecentCommand { get; }

5
samples/RenderDemo/App.xaml

@ -3,8 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.App">
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/FluentLight.xaml"/>
<StyleInclude Source="avares://RenderDemo/SideBar.xaml"/>
</Application.Styles>
</Application>
</Application>

125
samples/RenderDemo/SideBar.xaml

@ -1,65 +1,68 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/>
<Setter Property="Background" Value="{DynamicResource SystemAccentColor}"/>
<Setter Property="Template">
<ControlTemplate>
<Border
Margin="{TemplateBinding Margin}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ScrollViewer
Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
Background="{TemplateBinding Background}">
<ItemsPresenter
Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}">
</ItemsPresenter>
</ScrollViewer>
<ContentPresenter
Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectedContent}"
ContentTemplate="{TemplateBinding SelectedContentTemplate}">
</ContentPresenter>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/>
<Setter Property="Background" Value="{DynamicResource SystemAccentColor}"/>
<Setter Property="Template">
<ControlTemplate>
<Border
Margin="{TemplateBinding Margin}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ScrollViewer
Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="{TemplateBinding (ScrollViewer.HorizontalScrollBarVisibility)}"
VerticalScrollBarVisibility="{TemplateBinding (ScrollViewer.VerticalScrollBarVisibility)}"
Background="{TemplateBinding Background}">
<ItemsPresenter
Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
ItemTemplate="{TemplateBinding ItemTemplate}">
</ItemsPresenter>
</ScrollViewer>
<ContentPresenter
Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectedContent}"
ContentTemplate="{TemplateBinding SelectedContentTemplate}">
</ContentPresenter>
</DockPanel>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="TabControl.sidebar > TabItem">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.150"/>
</Transitions>
</Setter>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SystemAccentColorLight1}"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Opacity" Value="0.5"/>
<Setter Property="Transitions">
<Transitions>
<DoubleTransition Property="Opacity" Duration="0:0:0.150"/>
</Transitions>
</Setter>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ Border#PART_SelectedPipe">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource SystemAccentColorLight2}"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/>
</Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ Border#PART_LayoutRoot">
<Setter Property="Background" Value="{DynamicResource SystemAccentColorLight1}"/>
</Style>
</Styles>

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -126,7 +126,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Visibility = ViewStates.Visible;
}
public double Scaling => 1;
public double RenderScaling => 1;
void Draw()
{

3
src/Avalonia.Animation/Avalonia.Animation.csproj

@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

3
src/Avalonia.Base/Avalonia.Base.csproj

@ -3,7 +3,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Avalonia.Base</AssemblyName>
<RootNamespace>Avalonia</RootNamespace>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj"/>
@ -13,4 +13,5 @@
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\JetBrains.Annotations.props" />
<Import Project="..\..\build\System.Memory.props" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

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

@ -9,6 +9,16 @@ namespace Avalonia.Threading
/// </summary>
public class AvaloniaScheduler : LocalScheduler
{
/// <summary>
/// Users can schedule actions on the dispatcher thread while being on the correct thread already.
/// We are optimizing this case by invoking user callback immediately which can lead to stack overflows in certain cases.
/// To prevent this we are limiting amount of reentrant calls to <see cref="Schedule{TState}"/> before we will
/// schedule on a dispatcher anyway.
/// </summary>
private const int MaxReentrantSchedules = 32;
private int _reentrancyGuard;
/// <summary>
/// The instance of the <see cref="AvaloniaScheduler"/>.
/// </summary>
@ -24,31 +34,58 @@ namespace Avalonia.Threading
/// <inheritdoc/>
public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
var composite = new CompositeDisposable(2);
IDisposable PostOnDispatcher()
{
var composite = new CompositeDisposable(2);
var cancellation = new CancellationDisposable();
Dispatcher.UIThread.Post(() =>
{
if (!cancellation.Token.IsCancellationRequested)
{
composite.Add(action(this, state));
}
}, DispatcherPriority.DataBind);
composite.Add(cancellation);
return composite;
}
if (dueTime == TimeSpan.Zero)
{
if (!Dispatcher.UIThread.CheckAccess())
{
var cancellation = new CancellationDisposable();
Dispatcher.UIThread.Post(() =>
{
if (!cancellation.Token.IsCancellationRequested)
{
composite.Add(action(this, state));
}
}, DispatcherPriority.DataBind);
composite.Add(cancellation);
return PostOnDispatcher();
}
else
{
return action(this, state);
if (_reentrancyGuard >= MaxReentrantSchedules)
{
return PostOnDispatcher();
}
try
{
_reentrancyGuard++;
return action(this, state);
}
finally
{
_reentrancyGuard--;
}
}
}
else
{
var composite = new CompositeDisposable(2);
composite.Add(DispatcherTimer.RunOnce(() => composite.Add(action(this, state)), dueTime));
return composite;
}
return composite;
}
}
}

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

@ -1,3 +1,5 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Threading;
namespace Avalonia.Threading
@ -7,6 +9,20 @@ namespace Avalonia.Threading
/// </summary>
public class AvaloniaSynchronizationContext : SynchronizationContext
{
public interface INonPumpingPlatformWaitProvider
{
int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
}
private readonly INonPumpingPlatformWaitProvider _waitProvider;
public AvaloniaSynchronizationContext(INonPumpingPlatformWaitProvider waitProvider)
{
_waitProvider = waitProvider;
if (_waitProvider != null)
SetWaitNotificationRequired();
}
/// <summary>
/// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer.
/// </summary>
@ -22,7 +38,8 @@ namespace Avalonia.Threading
return;
}
SetSynchronizationContext(new AvaloniaSynchronizationContext());
SetSynchronizationContext(new AvaloniaSynchronizationContext(AvaloniaLocator.Current
.GetService<INonPumpingPlatformWaitProvider>()));
}
/// <inheritdoc/>
@ -39,5 +56,13 @@ namespace Avalonia.Threading
else
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait();
}
[PrePrepareMethod]
public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
{
if (_waitProvider != null)
return _waitProvider.Wait(waitHandles, waitAll, millisecondsTimeout);
return base.Wait(waitHandles, waitAll, millisecondsTimeout);
}
}
}

10
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -17,14 +17,14 @@
<Compile Include="../Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs">
<Link>Shared/AvaloniaResourceXamlInfo.cs</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/**/*.cs">
<Compile Include="../Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/**/*.cs">
<Link>XamlIlExtensions/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="external/cecil/**/*.*" />
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlX\**\*.cs">
<Compile Include="../Markup/Avalonia.Markup.Xaml.Loader/xamlil.github\src\XamlX\**\*.cs">
<Link>XamlIl/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlX.IL.Cecil\**\*.cs">
<Compile Include="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX.IL.Cecil\**\*.cs">
<Link>XamlIl.Cecil/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs">
@ -57,8 +57,8 @@
<Compile Include="../Avalonia.Base/Utilities/StyleClassParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<PackageReference Include="Avalonia.Unofficial.Cecil" Version="20190417.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" PrivateAssets="All" />
</ItemGroup>

2
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -128,7 +128,7 @@ namespace Avalonia.Build.Tasks
asm.MainModule.Types.Add(typeDef);
var builder = typeSystem.CreateTypeBuilder(typeDef);
foreach (var res in group.Resources.Where(CheckXamlName))
foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x=>x.FilePath.ToLowerInvariant()))
{
try
{

1
src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj

@ -22,4 +22,5 @@
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\JetBrains.Annotations.props" />
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

6
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -3922,6 +3922,12 @@ namespace Avalonia.Controls
dataGridCell: editingCell);
EditingRow.InvalidateDesiredHeight();
var column = editingCell.OwningColumn;
if (column.Width.IsSizeToCells || column.Width.IsAuto)
{// Invalidate desired width and force recalculation
column.SetWidthDesiredValue(0);
EditingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width);
}
}
// We're done, so raise the CellEditEnded event

3
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
@ -14,4 +14,5 @@
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\JetBrains.Annotations.props" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

6
src/Avalonia.Controls/Chrome/CaptionButtons.cs

@ -38,12 +38,10 @@ namespace Avalonia.Controls.Chrome
{
if (_disposables != null)
{
var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow);
layer?.Children.Remove(this);
_disposables.Dispose();
_disposables = null;
_hostWindow = null;
}
}

126
src/Avalonia.Controls/Chrome/TitleBar.cs

@ -12,106 +12,86 @@ namespace Avalonia.Controls.Chrome
public class TitleBar : TemplatedControl
{
private CompositeDisposable? _disposables;
private readonly Window? _hostWindow;
private CaptionButtons? _captionButtons;
public TitleBar(Window hostWindow)
private void UpdateSize(Window window)
{
_hostWindow = hostWindow;
}
public TitleBar()
{
}
public void Attach()
{
if (_disposables == null)
{
var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow);
layer?.Children.Add(this);
if (_hostWindow != null)
{
_disposables = new CompositeDisposable
{
_hostWindow.GetObservable(Window.WindowDecorationMarginProperty)
.Subscribe(x => UpdateSize()),
_hostWindow.GetObservable(Window.ExtendClientAreaTitleBarHeightHintProperty)
.Subscribe(x => UpdateSize()),
_hostWindow.GetObservable(Window.OffScreenMarginProperty)
.Subscribe(x => UpdateSize()),
_hostWindow.GetObservable(Window.WindowStateProperty)
.Subscribe(x =>
{
PseudoClasses.Set(":minimized", x == WindowState.Minimized);
PseudoClasses.Set(":normal", x == WindowState.Normal);
PseudoClasses.Set(":maximized", x == WindowState.Maximized);
PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
})
};
_captionButtons?.Attach(_hostWindow);
}
UpdateSize();
}
}
private void UpdateSize()
{
if (_hostWindow != null)
if (window != null)
{
Margin = new Thickness(
_hostWindow.OffScreenMargin.Left,
_hostWindow.OffScreenMargin.Top,
_hostWindow.OffScreenMargin.Right,
_hostWindow.OffScreenMargin.Bottom);
window.OffScreenMargin.Left,
window.OffScreenMargin.Top,
window.OffScreenMargin.Right,
window.OffScreenMargin.Bottom);
if (_hostWindow.WindowState != WindowState.FullScreen)
if (window.WindowState != WindowState.FullScreen)
{
Height = _hostWindow.WindowDecorationMargin.Top;
Height = window.WindowDecorationMargin.Top;
if (_captionButtons != null)
{
_captionButtons.Height = Height;
}
}
IsVisible = window.PlatformImpl.NeedsManagedDecorations;
}
}
public void Detach()
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_disposables != null)
{
var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow);
layer?.Children.Remove(this);
base.OnApplyTemplate(e);
_disposables.Dispose();
_disposables = null;
_captionButtons?.Detach();
_captionButtons = e.NameScope.Get<CaptionButtons>("PART_CaptionButtons");
_captionButtons?.Detach();
if (VisualRoot is Window window)
{
_captionButtons?.Attach(window);
UpdateSize(window);
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnApplyTemplate(e);
_captionButtons = e.NameScope.Get<CaptionButtons>("PART_CaptionButtons");
base.OnAttachedToVisualTree(e);
if (_hostWindow != null)
if (VisualRoot is Window window)
{
_captionButtons.Attach(_hostWindow);
_disposables = new CompositeDisposable
{
window.GetObservable(Window.WindowDecorationMarginProperty)
.Subscribe(x => UpdateSize(window)),
window.GetObservable(Window.ExtendClientAreaTitleBarHeightHintProperty)
.Subscribe(x => UpdateSize(window)),
window.GetObservable(Window.OffScreenMarginProperty)
.Subscribe(x => UpdateSize(window)),
window.GetObservable(Window.ExtendClientAreaChromeHintsProperty)
.Subscribe(x => UpdateSize(window)),
window.GetObservable(Window.WindowStateProperty)
.Subscribe(x =>
{
PseudoClasses.Set(":minimized", x == WindowState.Minimized);
PseudoClasses.Set(":normal", x == WindowState.Normal);
PseudoClasses.Set(":maximized", x == WindowState.Maximized);
PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
}),
window.GetObservable(Window.IsExtendedIntoWindowDecorationsProperty)
.Subscribe(x => UpdateSize(window))
};
}
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
UpdateSize();
_disposables?.Dispose();
_captionButtons?.Detach();
_captionButtons = null;
}
}
}

16
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -88,7 +88,7 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect<DatePicker, DateTimeOffset?>(nameof(SelectedDate),
x => x.SelectedDate, (x, v) => x.SelectedDate = v);
//Template Items
// Template Items
private Button _flyoutButton;
private TextBlock _dayText;
private TextBlock _monthText;
@ -359,10 +359,14 @@ namespace Avalonia.Controls
}
}
Grid.SetColumn(_spacer1, 1);
Grid.SetColumn(_spacer2, 3);
_spacer1.IsVisible = columnIndex > 1;
_spacer2.IsVisible = columnIndex > 2;
var isSpacer1Visible = columnIndex > 1;
var isSpacer2Visible = columnIndex > 2;
// ternary conditional operator is used to make sure grid cells will be validated
Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
_spacer1.IsVisible = isSpacer1Visible;
_spacer2.IsVisible = isSpacer2Visible;
}
private void SetSelectedDateText()
@ -398,7 +402,7 @@ namespace Avalonia.Controls
var deltaY = _presenter.GetOffsetForPopup();
//The extra 5 px I think is related to default popup placement behavior
// The extra 5 px I think is related to default popup placement behavior
_popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);

31
src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs

@ -77,7 +77,7 @@ namespace Avalonia.Controls
DatePicker.YearVisibleProperty.AddOwner<DatePickerPresenter>(x =>
x.YearVisible, (x, v) => x.YearVisible = v);
//Template Items
// Template Items
private Grid _pickerContainer;
private Button _acceptButton;
private Button _dismissButton;
@ -107,7 +107,7 @@ namespace Avalonia.Controls
private bool _yearVisible = true;
private DateTimeOffset _syncDate;
private GregorianCalendar _calendar;
private readonly GregorianCalendar _calendar;
private bool _suppressUpdateSelection;
public DatePickerPresenter()
@ -234,7 +234,7 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
//These are requirements, so throw if not found
// These are requirements, so throw if not found
_pickerContainer = e.NameScope.Get<Grid>("PickerContainer");
_monthHost = e.NameScope.Get<Panel>("MonthHost");
_dayHost = e.NameScope.Get<Panel>("DayHost");
@ -326,7 +326,7 @@ namespace Avalonia.Controls
/// </summary>
private void InitPicker()
{
//OnApplyTemplate must've been called before we can init here...
// OnApplyTemplate must've been called before we can init here...
if (_pickerContainer == null)
return;
@ -344,12 +344,11 @@ namespace Avalonia.Controls
SetGrid();
//Date should've been set when we reach this point
// Date should've been set when we reach this point
var dt = Date;
if (DayVisible)
{
GregorianCalendar gc = new GregorianCalendar();
var maxDays = gc.GetDaysInMonth(dt.Year, dt.Month);
var maxDays = _calendar.GetDaysInMonth(dt.Year, dt.Month);
_daySelector.MaximumValue = maxDays;
_daySelector.MinimumValue = 1;
_daySelector.SelectedValue = dt.Day;
@ -407,10 +406,14 @@ namespace Avalonia.Controls
}
}
Grid.SetColumn(_spacer1, 1);
Grid.SetColumn(_spacer2, 3);
_spacer1.IsVisible = columnIndex > 1;
_spacer2.IsVisible = columnIndex > 2;
var isSpacer1Visible = columnIndex > 1;
var isSpacer2Visible = columnIndex > 2;
// ternary conditional operator is used to make sure grid cells will be validated
Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
_spacer1.IsVisible = isSpacer1Visible;
_spacer2.IsVisible = isSpacer2Visible;
}
private void SetInitialFocus()
@ -433,12 +436,12 @@ namespace Avalonia.Controls
}
}
private void OnDismissButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
private void OnDismissButtonClicked(object sender, RoutedEventArgs e)
{
OnDismiss();
}
private void OnAcceptButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
private void OnAcceptButtonClicked(object sender, RoutedEventArgs e)
{
Date = _syncDate;
OnConfirmed();
@ -471,7 +474,7 @@ namespace Avalonia.Controls
_syncDate = newDate;
//We don't need to update the days if not displaying day, not february
// We don't need to update the days if not displaying day, not february
if (!DayVisible || _syncDate.Month != 2)
return;

51
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -15,7 +15,7 @@ namespace Avalonia.Controls
/// Defines the <see cref="MinuteIncrement"/> property
/// </summary>
public static readonly DirectProperty<TimePicker, int> MinuteIncrementProperty =
AvaloniaProperty.RegisterDirect<TimePicker, int>(nameof(MinuteIncrement),
AvaloniaProperty.RegisterDirect<TimePicker, int>(nameof(MinuteIncrement),
x => x.MinuteIncrement, (x, v) => x.MinuteIncrement = v);
/// <summary>
@ -34,17 +34,17 @@ namespace Avalonia.Controls
/// Defines the <see cref="ClockIdentifier"/> property
/// </summary>
public static readonly DirectProperty<TimePicker, string> ClockIdentifierProperty =
AvaloniaProperty.RegisterDirect<TimePicker, string>(nameof(ClockIdentifier),
AvaloniaProperty.RegisterDirect<TimePicker, string>(nameof(ClockIdentifier),
x => x.ClockIdentifier, (x, v) => x.ClockIdentifier = v);
/// <summary>
/// Defines the <see cref="SelectedTime"/> property
/// </summary>
public static readonly DirectProperty<TimePicker, TimeSpan?> SelectedTimeProperty =
AvaloniaProperty.RegisterDirect<TimePicker, TimeSpan?>(nameof(SelectedTime),
AvaloniaProperty.RegisterDirect<TimePicker, TimeSpan?>(nameof(SelectedTime),
x => x.SelectedTime, (x, v) => x.SelectedTime = v);
//Template Items
// Template Items
private TimePickerPresenter _presenter;
private Button _flyoutButton;
private Border _firstPickerHost;
@ -52,7 +52,7 @@ namespace Avalonia.Controls
private Border _thirdPickerHost;
private TextBlock _hourText;
private TextBlock _minuteText;
public TextBlock _periodText;
private TextBlock _periodText;
private Rectangle _firstSplitter;
private Rectangle _secondSplitter;
private Grid _contentGrid;
@ -145,7 +145,7 @@ namespace Avalonia.Controls
if (_flyoutButton != null)
_flyoutButton.Click -= OnFlyoutButtonClicked;
if(_presenter != null)
if (_presenter != null)
{
_presenter.Confirmed -= OnConfirmed;
_presenter.Dismissed -= OnDismissPicker;
@ -170,7 +170,6 @@ namespace Avalonia.Controls
_popup = e.NameScope.Find<Popup>("Popup");
_presenter = e.NameScope.Find<TimePickerPresenter>("PickerPresenter");
if (_flyoutButton != null)
_flyoutButton.Click += OnFlyoutButtonClicked;
@ -185,7 +184,6 @@ namespace Avalonia.Controls
_presenter[!TimePickerPresenter.MinuteIncrementProperty] = this[!MinuteIncrementProperty];
_presenter[!TimePickerPresenter.ClockIdentifierProperty] = this[!ClockIdentifierProperty];
}
}
private void SetGrid()
@ -195,30 +193,19 @@ namespace Avalonia.Controls
bool use24HourClock = ClockIdentifier == "24HourClock";
if (!use24HourClock)
{
_contentGrid.ColumnDefinitions = new ColumnDefinitions("*,Auto,*,Auto,*");
_thirdPickerHost.IsVisible = true;
_secondSplitter.IsVisible = true;
var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
_contentGrid.ColumnDefinitions = new ColumnDefinitions(columnsD);
Grid.SetColumn(_firstPickerHost, 0);
Grid.SetColumn(_secondPickerHost, 2);
Grid.SetColumn(_thirdPickerHost, 4);
_thirdPickerHost.IsVisible = !use24HourClock;
_secondSplitter.IsVisible = !use24HourClock;
Grid.SetColumn(_firstSplitter, 1);
Grid.SetColumn(_secondSplitter, 3);
}
else
{
_contentGrid.ColumnDefinitions = new ColumnDefinitions("*,Auto,*");
_thirdPickerHost.IsVisible = false;
_secondSplitter.IsVisible = false;
Grid.SetColumn(_firstPickerHost, 0);
Grid.SetColumn(_secondPickerHost, 2);
Grid.SetColumn(_firstPickerHost, 0);
Grid.SetColumn(_secondPickerHost, 2);
Grid.SetColumn(_thirdPickerHost, use24HourClock ? 0 : 4);
Grid.SetColumn(_firstSplitter, 1);
}
Grid.SetColumn(_firstSplitter, 1);
Grid.SetColumn(_secondSplitter, use24HourClock ? 0 : 3);
}
private void SetSelectedTimeText()
@ -237,14 +224,13 @@ namespace Avalonia.Controls
hr = hr > 12 ? hr - 12 : hr == 0 ? 12 : hr;
newTime = new TimeSpan(hr, newTime.Minutes, 0);
}
_hourText.Text = newTime.ToString("%h");
_hourText.Text = newTime.ToString("%h");
_minuteText.Text = newTime.ToString("mm");
PseudoClasses.Set(":hasnotime", false);
_periodText.Text = time.Value.Hours >= 12 ? CultureInfo.CurrentCulture.DateTimeFormat.PMDesignator :
CultureInfo.CurrentCulture.DateTimeFormat.AMDesignator;
}
else
{
@ -262,7 +248,7 @@ namespace Avalonia.Controls
SelectedTimeChanged?.Invoke(this, new TimePickerSelectedValueChangedEventArgs(oldTime, newTime));
}
private void OnFlyoutButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
private void OnFlyoutButtonClicked(object sender, Interactivity.RoutedEventArgs e)
{
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
@ -270,7 +256,7 @@ namespace Avalonia.Controls
var deltaY = _presenter.GetOffsetForPopup();
//The extra 5 px I think is related to default popup placement behavior
// The extra 5 px I think is related to default popup placement behavior
_popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
@ -287,6 +273,5 @@ namespace Avalonia.Controls
_popup.Close();
SelectedTime = _presenter.Time;
}
}
}

38
src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs

@ -39,7 +39,7 @@ namespace Avalonia.Controls
KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Cycle);
}
//TemplateItems
// TemplateItems
private Grid _pickerContainer;
private Button _acceptButton;
private Button _dismissButton;
@ -55,8 +55,8 @@ namespace Avalonia.Controls
private Button _minuteDownButton;
private Button _periodDownButton;
//Backing Fields
private TimeSpan _Time;
// Backing Fields
private TimeSpan _time;
private int _minuteIncrement = 1;
private string _clockIdentifier = "12HourClock";
@ -83,7 +83,7 @@ namespace Avalonia.Controls
get => _clockIdentifier;
set
{
if (string.IsNullOrEmpty(value) || value == "" || !(value == "12HourClock" || value == "24HourClock"))
if (string.IsNullOrEmpty(value) || !(value == "12HourClock" || value == "24HourClock"))
throw new ArgumentException("Invalid ClockIdentifier");
SetAndRaise(ClockIdentifierProperty, ref _clockIdentifier, value);
InitPicker();
@ -95,10 +95,10 @@ namespace Avalonia.Controls
/// </summary>
public TimeSpan Time
{
get => _Time;
get => _time;
set
{
SetAndRaise(TimeProperty, ref _Time, value);
SetAndRaise(TimeProperty, ref _time, value);
InitPicker();
}
}
@ -213,26 +213,24 @@ namespace Avalonia.Controls
private void SetGrid()
{
if (ClockIdentifier == "12HourClock")
{
_pickerContainer.ColumnDefinitions = new ColumnDefinitions("*,Auto,*,Auto,*");
_spacer2.IsVisible = true;
_periodHost.IsVisible = true;
}
else
{
_pickerContainer.ColumnDefinitions = new ColumnDefinitions("*,Auto,*");
_spacer2.IsVisible = false;
_periodHost.IsVisible = false;
}
bool use24HourClock = ClockIdentifier == "24HourClock";
var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
_pickerContainer.ColumnDefinitions = new ColumnDefinitions(columnsD);
_spacer2.IsVisible = !use24HourClock;
_periodHost.IsVisible = !use24HourClock;
Grid.SetColumn(_spacer2, use24HourClock ? 0 : 3);
Grid.SetColumn(_periodHost, use24HourClock ? 0 : 4);
}
private void OnDismissButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
private void OnDismissButtonClicked(object sender, RoutedEventArgs e)
{
OnDismiss();
}
private void OnAcceptButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
private void OnAcceptButtonClicked(object sender, RoutedEventArgs e)
{
OnConfirmed();
}

2
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@ -35,7 +35,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
}
}
public double Scaling
public double RenderScaling
{
get { return _scaling; }
set

1
src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs

@ -142,7 +142,6 @@ namespace Avalonia.Controls.Generators
private readonly IDataTemplate _inner;
public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner;
public IControl Build(object param) => _inner.Build(param);
public bool SupportsRecycling => _inner.SupportsRecycling;
public bool Match(object data) => _inner.Match(data);
public InstancedBinding ItemsSelector(object item) => null;
}

10
src/Avalonia.Controls/MenuItem.cs

@ -98,8 +98,7 @@ namespace Avalonia.Controls
private ICommand? _command;
private bool _commandCanExecute = true;
private Popup? _popup;
private IDisposable? _gridHack;
private Popup _popup;
/// <summary>
/// Initializes static members of the <see cref="MenuItem"/> class.
@ -325,9 +324,6 @@ namespace Avalonia.Controls
{
Command.CanExecuteChanged -= CanExecuteChanged;
}
_gridHack?.Dispose();
_gridHack = null;
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
@ -347,9 +343,7 @@ namespace Avalonia.Controls
// the WPF codebase:
//
// https://github.com/dotnet/wpf/blob/89537909bdf36bc918e88b37751add46a8980bb0/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/MenuItem.cs#L2126-L2141
_gridHack = Bind(
DefinitionBase.PrivateSharedSizeScopeProperty,
parent.GetBindingObservable(DefinitionBase.PrivateSharedSizeScopeProperty));
SetValue(DefinitionBase.PrivateSharedSizeScopeProperty, parent.GetValue(DefinitionBase.PrivateSharedSizeScopeProperty));
}
}

6
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -23,10 +23,10 @@ namespace Avalonia.Platform
Size ClientSize { get; }
/// <summary>
/// Gets the scaling factor for the toplevel.
/// Gets the scaling factor for the toplevel. This is used for rendering.
/// </summary>
double Scaling { get; }
double RenderScaling { get; }
/// <summary>
/// The list of native platform's surfaces that can be consumed by rendering subsystems.
/// </summary>

5
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -13,6 +13,11 @@ namespace Avalonia.Platform
/// Hides the window.
/// </summary>
void Hide();
/// <summary>
/// Gets the scaling factor for Window positioning and sizing.
/// </summary>
double DesktopScaling { get; }
/// <summary>
/// Gets the position of the window in device pixels.

5
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

@ -155,6 +155,11 @@ namespace Avalonia.Controls.Presenters
}
}
protected override void PanelCreated(IPanel panel)
{
ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
/// Moves to the selected page, animating if a <see cref="PageTransition"/> is set.
/// </summary>

21
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@ -86,7 +86,7 @@ namespace Avalonia.Controls.Presenters
private IControl _child;
private bool _createdChild;
private IDataTemplate _dataTemplate;
private IRecyclingDataTemplate _recyclingDataTemplate;
private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper();
/// <summary>
@ -281,7 +281,7 @@ namespace Avalonia.Controls.Presenters
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
_dataTemplate = null;
_recyclingDataTemplate = null;
_createdChild = false;
InvalidateMeasure();
}
@ -307,22 +307,21 @@ namespace Avalonia.Controls.Presenters
{
var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default;
// We have content and it isn't a control, so if the new data template is the same
// as the old data template, try to recycle the existing child control to display
// the new data.
if (dataTemplate == _dataTemplate && dataTemplate.SupportsRecycling)
if (dataTemplate is IRecyclingDataTemplate rdt)
{
newChild = oldChild;
var toRecycle = rdt == _recyclingDataTemplate ? oldChild : null;
newChild = rdt.Build(content, toRecycle);
_recyclingDataTemplate = rdt;
}
else
{
_dataTemplate = dataTemplate;
newChild = _dataTemplate.Build(content);
newChild = dataTemplate.Build(content);
_recyclingDataTemplate = null;
}
}
else
{
_dataTemplate = null;
_recyclingDataTemplate = null;
}
return newChild;
@ -422,7 +421,7 @@ namespace Avalonia.Controls.Presenters
LogicalChildren.Remove(Child);
((ISetInheritanceParent)Child).SetParent(Child.Parent);
Child = null;
_dataTemplate = null;
_recyclingDataTemplate = null;
}
InvalidateMeasure();

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

@ -229,8 +229,6 @@ namespace Avalonia.Controls.Presenters
}
PanelCreated(Panel);
ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>

7
src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs

@ -8,7 +8,7 @@ namespace Avalonia.Controls.Primitives
{
public class ChromeOverlayLayer : Panel, ICustomSimpleHitTest
{
public static ChromeOverlayLayer? GetOverlayLayer(IVisual visual)
public static Panel? GetOverlayLayer(IVisual visual)
{
foreach (var v in visual.GetVisualAncestors())
if (v is VisualLayerManager vlm)
@ -24,6 +24,11 @@ namespace Avalonia.Controls.Primitives
return null;
}
public void Add(Control c)
{
base.Children.Add(c);
}
public bool HitTest(Point point) => Children.HitTestCustom(point);
}
}

4
src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs

@ -40,9 +40,9 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
public void MoveAndResize(Point devicePoint, Size virtualSize)
{
_moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.Scaling);
_moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.RenderScaling);
}
public virtual double Scaling => _parent.Scaling;
public virtual double Scaling => _parent.DesktopScaling;
}
}

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

@ -692,14 +692,24 @@ namespace Avalonia.Controls.Primitives
}
}
foreach (var i in e.SelectedIndices)
if (e.SelectedIndices.Count > 0 || e.DeselectedIndices.Count > 0)
{
Mark(i.GetAt(0), true);
}
foreach (var i in e.SelectedIndices)
{
Mark(i.GetAt(0), true);
}
foreach (var i in e.DeselectedIndices)
foreach (var i in e.DeselectedIndices)
{
Mark(i.GetAt(0), false);
}
}
else if (e.DeselectedItems.Count > 0)
{
Mark(i.GetAt(0), false);
// (De)selected indices being empty means that a selected item was removed from
// the Items (it can't tell us the index of the item because the index is no longer
// valid). In this case, we just update the selection state of all containers.
UpdateContainerSelection();
}
var newSelectedIndex = SelectedIndex;

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

@ -13,10 +13,12 @@ namespace Avalonia.Controls.Primitives
private ILogicalRoot _logicalRoot;
private readonly List<Control> _layers = new List<Control>();
public static readonly StyledProperty<ChromeOverlayLayer> ChromeOverlayLayerProperty =
AvaloniaProperty.Register<VisualLayerManager, ChromeOverlayLayer>(nameof(ChromeOverlayLayer));
public bool IsPopup { get; set; }
public AdornerLayer AdornerLayer
{
get
@ -32,10 +34,19 @@ namespace Avalonia.Controls.Primitives
{
get
{
var rv = FindLayer<ChromeOverlayLayer>();
if (rv == null)
AddLayer(rv = new ChromeOverlayLayer(), ChromeZIndex);
return rv;
var current = GetValue(ChromeOverlayLayerProperty);
if (current is null)
{
var chromeOverlayLayer = new ChromeOverlayLayer();
AddLayer(chromeOverlayLayer, ChromeZIndex);
SetValue(ChromeOverlayLayerProperty, chromeOverlayLayer);
current = chromeOverlayLayer;
}
return current;
}
}
@ -46,7 +57,7 @@ namespace Avalonia.Controls.Primitives
if (IsPopup)
return null;
var rv = FindLayer<OverlayLayer>();
if(rv == null)
if (rv == null)
AddLayer(rv = new OverlayLayer(), OverlayZIndex);
return rv;
}
@ -88,7 +99,8 @@ namespace Avalonia.Controls.Primitives
layer.ZIndex = zindex;
VisualChildren.Add(layer);
if (((ILogical)this).IsAttachedToLogicalTree)
((ILogical)layer).NotifyAttachedToLogicalTree(new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this));
((ILogical)layer).NotifyAttachedToLogicalTree(
new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this));
InvalidateArrange();
}

2
src/Avalonia.Controls/ProgressBar.cs

@ -215,7 +215,7 @@ namespace Avalonia.Controls
TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166%
// Remove these properties when we switch to fluent as default and removed the old one.
IndeterminateStartingOffset = -(dim / 5d);
IndeterminateStartingOffset = -dim;
IndeterminateEndingOffset = dim;
var padding = Padding;

2
src/Avalonia.Controls/RelativePanel.AttachedProperties.cs

@ -33,7 +33,7 @@ namespace Avalonia.Controls
AlignVerticalCenterWithProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
BelowProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
LeftOfProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
LeftOfProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
RightOfProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
}
/// <summary>

487
src/Avalonia.Controls/RelativePanel.cs

@ -1,4 +1,4 @@
/// Ported from https://github.com/HandyOrg/HandyControl/blob/master/src/Shared/HandyControl_Shared/Controls/Panel/RelativePanel.cs
// Ported from https://github.com/HandyOrg/HandyControl/blob/master/src/Shared/HandyControl_Shared/Controls/Panel/RelativePanel.cs
using System;
using System.Collections.Generic;
using System.Linq;
@ -14,25 +14,28 @@ namespace Avalonia.Controls
public RelativePanel() => _childGraph = new Graph();
protected override Size MeasureOverride(Size availableSize)
private Layoutable? GetDependencyElement(AvaloniaProperty property, AvaloniaObject child)
{
foreach (var child in Children)
var dependency = child.GetValue(property);
if (dependency is Layoutable layoutable)
{
child?.Measure(availableSize);
if (Children.Contains((ILayoutable)layoutable))
return layoutable;
throw new ArgumentException($"RelativePanel error: Element does not exist in the current context: {property.Name}");
}
return availableSize;
return null;
}
protected override Size ArrangeOverride(Size arrangeSize)
protected override Size MeasureOverride(Size availableSize)
{
_childGraph.Reset(arrangeSize);
foreach (var child in Children.OfType<Layoutable>())
_childGraph.Clear();
foreach (Layoutable child in Children)
{
if (child == null)
continue;
var node = _childGraph.AddNode(child);
node.AlignLeftWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignLeftWithProperty, child));
@ -47,63 +50,44 @@ namespace Avalonia.Controls
node.AlignHorizontalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignHorizontalCenterWithProperty, child));
node.AlignVerticalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignVerticalCenterWithProperty, child));
}
if (_childGraph.CheckCyclic())
{
throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete.");
}
_childGraph.Measure(availableSize);
var size = new Size();
foreach (var child in Children)
{
if (child.Bounds.Bottom > size.Height)
{
size = size.WithHeight(child.Bounds.Bottom);
}
_childGraph.Reset(false);
var boundingSize = _childGraph.GetBoundingSize(Width.IsNaN(), Height.IsNaN());
_childGraph.Reset();
_childGraph.Measure(boundingSize);
return boundingSize;
}
if (child.Bounds.Right > size.Width)
{
size = size.WithWidth(child.Bounds.Right);
}
}
protected override Size ArrangeOverride(Size arrangeSize)
{
_childGraph.GetNodes().Do(node => node.Arrange(arrangeSize));
return arrangeSize;
}
if (VerticalAlignment == VerticalAlignment.Stretch)
{
size = size.WithHeight(arrangeSize.Height);
}
private class GraphNode
{
public bool Measured { get; set; }
if (HorizontalAlignment == HorizontalAlignment.Stretch)
{
size = size.WithWidth(arrangeSize.Width);
}
public Layoutable Element { get; }
return size;
}
private bool HorizontalOffsetFlag { get; set; }
private Layoutable? GetDependencyElement(AvaloniaProperty property, AvaloniaObject child)
{
var dependency = child.GetValue(property);
private bool VerticalOffsetFlag { get; set; }
if (dependency is Layoutable layoutable)
{
if (Children.Contains((ILayoutable)layoutable))
return layoutable;
private Size BoundingSize { get; set; }
throw new ArgumentException($"RelativePanel error: Element does not exist in the current context: {property.Name}");
}
public Size OriginDesiredSize { get; set; }
return null;
}
public double Left { get; set; } = double.NaN;
private class GraphNode
{
public Point Position { get; set; }
public double Top { get; set; } = double.NaN;
public bool Arranged { get; set; }
public double Right { get; set; } = double.NaN;
public Layoutable Element { get; }
public double Bottom { get; set; } = double.NaN;
public HashSet<GraphNode> OutgoingNodes { get; }
@ -132,19 +116,106 @@ namespace Avalonia.Controls
OutgoingNodes = new HashSet<GraphNode>();
Element = element;
}
public void Arrange(Size arrangeSize) => Element.Arrange(new Rect(Left, Top, Math.Max(arrangeSize.Width - Left - Right, 0), Math.Max(arrangeSize.Height - Top - Bottom, 0)));
public void Reset(bool clearPos)
{
if (clearPos)
{
Left = double.NaN;
Top = double.NaN;
Right = double.NaN;
Bottom = double.NaN;
}
Measured = false;
}
public Size GetBoundingSize()
{
if (Left < 0 || Top < 0) return default;
if (Measured)
return BoundingSize;
if (!OutgoingNodes.Any())
{
BoundingSize = Element.DesiredSize;
Measured = true;
}
else
{
BoundingSize = GetBoundingSize(this, Element.DesiredSize, OutgoingNodes);
Measured = true;
}
return BoundingSize;
}
private static Size GetBoundingSize(GraphNode prevNode, Size prevSize, IEnumerable<GraphNode> nodes)
{
foreach (var node in nodes)
{
if (node.Measured || !node.OutgoingNodes.Any())
{
if (prevNode.LeftOfNode != null && prevNode.LeftOfNode == node ||
prevNode.RightOfNode != null && prevNode.RightOfNode == node)
{
prevSize = prevSize.WithWidth(prevSize.Width + node.BoundingSize.Width);
if (GetAlignHorizontalCenterWithPanel(node.Element) || node.HorizontalOffsetFlag)
{
prevSize = prevSize.WithWidth(prevSize.Width + prevNode.OriginDesiredSize.Width);
prevNode.HorizontalOffsetFlag = true;
}
if (node.VerticalOffsetFlag)
{
prevNode.VerticalOffsetFlag = true;
}
}
if (prevNode.AboveNode != null && prevNode.AboveNode == node ||
prevNode.BelowNode != null && prevNode.BelowNode == node)
{
prevSize = prevSize.WithHeight(prevSize.Height + node.BoundingSize.Height);
if (GetAlignVerticalCenterWithPanel(node.Element) || node.VerticalOffsetFlag)
{
prevSize = prevSize.WithHeight(prevSize.Height + node.OriginDesiredSize.Height);
prevNode.VerticalOffsetFlag = true;
}
if (node.HorizontalOffsetFlag)
{
prevNode.HorizontalOffsetFlag = true;
}
}
}
else
{
return GetBoundingSize(node, prevSize, node.OutgoingNodes);
}
}
return prevSize;
}
}
private class Graph
{
private readonly Dictionary<AvaloniaObject, GraphNode> _nodeDic;
private Size _arrangeSize;
private Size AvailableSize { get; set; }
public Graph() => _nodeDic = new Dictionary<AvaloniaObject, GraphNode>();
public IEnumerable<GraphNode> GetNodes() => _nodeDic.Values;
public Graph()
public void Clear()
{
_nodeDic = new Dictionary<AvaloniaObject, GraphNode>();
AvailableSize = new Size();
_nodeDic.Clear();
}
public void Reset(bool clearPos = true) => _nodeDic.Values.Do(node => node.Reset(clearPos));
public GraphNode? AddLink(GraphNode from, Layoutable? to)
{
if (to == null)
@ -177,177 +248,289 @@ namespace Avalonia.Controls
return _nodeDic[value];
}
public void Reset(Size arrangeSize)
public void Measure(Size availableSize)
{
_arrangeSize = arrangeSize;
_nodeDic.Clear();
AvailableSize = availableSize;
Measure(_nodeDic.Values, null);
}
public bool CheckCyclic() => CheckCyclic(_nodeDic.Values, null);
private bool CheckCyclic(IEnumerable<GraphNode> nodes, HashSet<Layoutable>? set)
private void Measure(IEnumerable<GraphNode> nodes, HashSet<AvaloniaObject>? set)
{
set ??= new HashSet<Layoutable>();
set ??= new HashSet<AvaloniaObject>();
foreach (var node in nodes)
{
if (!node.Arranged && node.OutgoingNodes.Count == 0)
if (!node.Measured && !node.OutgoingNodes.Any())
{
ArrangeChild(node, true);
MeasureChild(node);
continue;
}
if (node.OutgoingNodes.All(item => item.Arranged))
if (node.OutgoingNodes.All(item => item.Measured))
{
ArrangeChild(node);
MeasureChild(node);
continue;
}
if (!set.Add(node.Element))
return true;
throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete.");
Measure(node.OutgoingNodes, set);
return CheckCyclic(node.OutgoingNodes, set);
if (!node.Measured)
{
MeasureChild(node);
}
}
return false;
}
private void ArrangeChild(GraphNode node, bool ignoneSibling = false)
private void MeasureChild(GraphNode node)
{
var child = node.Element;
var childSize = child.DesiredSize;
var childPos = new Point();
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
node.OriginDesiredSize = child.DesiredSize;
if (GetAlignHorizontalCenterWithPanel(child))
var alignLeftWithPanel = GetAlignLeftWithPanel(child);
var alignTopWithPanel = GetAlignTopWithPanel(child);
var alignRightWithPanel = GetAlignRightWithPanel(child);
var alignBottomWithPanel = GetAlignBottomWithPanel(child);
if (alignLeftWithPanel)
node.Left = 0;
if (alignTopWithPanel)
node.Top = 0;
if (alignRightWithPanel)
node.Right = 0;
if (alignBottomWithPanel)
node.Bottom = 0;
if (node.AlignLeftWithNode != null)
{
childPos = childPos.WithX((_arrangeSize.Width - childSize.Width) / 2);
node.Left = node.Left.IsNaN() ? node.AlignLeftWithNode.Left : node.AlignLeftWithNode.Left * 0.5;
}
if (GetAlignVerticalCenterWithPanel(child))
if (node.AlignTopWithNode != null)
{
childPos = childPos.WithY((_arrangeSize.Height - childSize.Height) / 2);
node.Top = node.Top.IsNaN() ? node.AlignTopWithNode.Top : node.AlignTopWithNode.Top * 0.5;
}
var alignLeftWithPanel = GetAlignLeftWithPanel(child);
var alignTopWithPanel = GetAlignTopWithPanel(child);
var alignRightWithPanel = GetAlignRightWithPanel(child);
var alignBottomWithPanel = GetAlignBottomWithPanel(child);
if (node.AlignRightWithNode != null)
{
node.Right = node.Right.IsNaN()
? node.AlignRightWithNode.Right
: node.AlignRightWithNode.Right * 0.5;
}
if (!ignoneSibling)
if (node.AlignBottomWithNode != null)
{
if (node.LeftOfNode != null)
{
childPos = childPos.WithX(node.LeftOfNode.Position.X - childSize.Width);
}
node.Bottom = node.Bottom.IsNaN()
? node.AlignBottomWithNode.Bottom
: node.AlignBottomWithNode.Bottom * 0.5;
}
if (node.AboveNode != null)
{
childPos = childPos.WithY(node.AboveNode.Position.Y - childSize.Height);
}
var availableHeight = AvailableSize.Height - node.Top - node.Bottom;
if (availableHeight.IsNaN())
{
availableHeight = AvailableSize.Height;
if (node.RightOfNode != null)
if (!node.Top.IsNaN() && node.Bottom.IsNaN())
{
childPos = childPos.WithX(node.RightOfNode.Position.X + node.RightOfNode.Element.DesiredSize.Width);
availableHeight -= node.Top;
}
if (node.BelowNode != null)
else if (node.Top.IsNaN() && !node.Bottom.IsNaN())
{
childPos = childPos.WithY(node.BelowNode.Position.Y + node.BelowNode.Element.DesiredSize.Height);
availableHeight -= node.Bottom;
}
}
var availableWidth = AvailableSize.Width - node.Left - node.Right;
if (availableWidth.IsNaN())
{
availableWidth = AvailableSize.Width;
if (node.AlignHorizontalCenterWith != null)
if (!node.Left.IsNaN() && node.Right.IsNaN())
{
childPos = childPos.WithX(node.AlignHorizontalCenterWith.Position.X +
(node.AlignHorizontalCenterWith.Element.DesiredSize.Width - childSize.Width) / 2);
availableWidth -= node.Left;
}
if (node.AlignVerticalCenterWith != null)
else if (node.Left.IsNaN() && !node.Right.IsNaN())
{
childPos = childPos.WithY(node.AlignVerticalCenterWith.Position.Y +
(node.AlignVerticalCenterWith.Element.DesiredSize.Height - childSize.Height) / 2);
availableWidth -= node.Right;
}
}
if (node.AlignLeftWithNode != null)
child.Measure(new Size(Math.Max(availableWidth, 0), Math.Max(availableHeight, 0)));
var childSize = child.DesiredSize;
if (node.LeftOfNode != null && node.Left.IsNaN())
{
node.Left = node.LeftOfNode.Left - childSize.Width;
}
if (node.AboveNode != null && node.Top.IsNaN())
{
node.Top = node.AboveNode.Top - childSize.Height;
}
if (node.RightOfNode != null)
{
if (node.Right.IsNaN())
{
childPos = childPos.WithX(node.AlignLeftWithNode.Position.X);
node.Right = node.RightOfNode.Right - childSize.Width;
}
if (node.AlignTopWithNode != null)
if (node.Left.IsNaN())
{
childPos = childPos.WithY(node.AlignTopWithNode.Position.Y);
node.Left = AvailableSize.Width - node.RightOfNode.Right;
}
}
if (node.AlignRightWithNode != null)
if (node.BelowNode != null)
{
if (node.Bottom.IsNaN())
{
childPos = childPos.WithX(node.AlignRightWithNode.Element.DesiredSize.Width + node.AlignRightWithNode.Position.X - childSize.Width);
node.Bottom = node.BelowNode.Bottom - childSize.Height;
}
if (node.AlignBottomWithNode != null)
if (node.Top.IsNaN())
{
childPos = childPos.WithY(node.AlignBottomWithNode.Element.DesiredSize.Height + node.AlignBottomWithNode.Position.Y - childSize.Height);
node.Top = AvailableSize.Height - node.BelowNode.Bottom;
}
}
if (alignLeftWithPanel)
if (node.AlignHorizontalCenterWith != null)
{
if (node.AlignRightWithNode != null)
{
childPos = childPos.WithX((node.AlignRightWithNode.Element.DesiredSize.Width + node.AlignRightWithNode.Position.X - childSize.Width) / 2);
}
var halfWidthLeft = (AvailableSize.Width + node.AlignHorizontalCenterWith.Left - node.AlignHorizontalCenterWith.Right - childSize.Width) * 0.5;
var halfWidthRight = (AvailableSize.Width - node.AlignHorizontalCenterWith.Left + node.AlignHorizontalCenterWith.Right - childSize.Width) * 0.5;
if (node.Left.IsNaN())
node.Left = halfWidthLeft;
else
{
childPos = childPos.WithX(0);
}
node.Left = (node.Left + halfWidthLeft) * 0.5;
if (node.Right.IsNaN())
node.Right = halfWidthRight;
else
node.Right = (node.Right + halfWidthRight) * 0.5;
}
if (alignTopWithPanel)
if (node.AlignVerticalCenterWith != null)
{
if (node.AlignBottomWithNode != null)
{
childPos = childPos.WithY((node.AlignBottomWithNode.Element.DesiredSize.Height + node.AlignBottomWithNode.Position.Y - childSize.Height) / 2);
}
var halfHeightTop = (AvailableSize.Height + node.AlignVerticalCenterWith.Top - node.AlignVerticalCenterWith.Bottom - childSize.Height) * 0.5;
var halfHeightBottom = (AvailableSize.Height - node.AlignVerticalCenterWith.Top + node.AlignVerticalCenterWith.Bottom - childSize.Height) * 0.5;
if (node.Top.IsNaN())
node.Top = halfHeightTop;
else
{
childPos = childPos.WithY(0);
}
node.Top = (node.Top + halfHeightTop) * 0.5;
if (node.Bottom.IsNaN())
node.Bottom = halfHeightBottom;
else
node.Bottom = (node.Bottom + halfHeightBottom) * 0.5;
}
if (alignRightWithPanel)
if (GetAlignHorizontalCenterWithPanel(child))
{
if (alignLeftWithPanel)
{
childPos = childPos.WithX((_arrangeSize.Width - childSize.Width) / 2);
}
else if (node.AlignLeftWithNode == null)
{
childPos = childPos.WithX(_arrangeSize.Width - childSize.Width);
}
var halfSubWidth = (AvailableSize.Width - childSize.Width) * 0.5;
if (node.Left.IsNaN())
node.Left = halfSubWidth;
else
{
childPos = childPos.WithX((_arrangeSize.Width + node.AlignLeftWithNode.Position.X - childSize.Width) / 2);
}
node.Left = (node.Left + halfSubWidth) * 0.5;
if (node.Right.IsNaN())
node.Right = halfSubWidth;
else
node.Right = (node.Right + halfSubWidth) * 0.5;
}
if (alignBottomWithPanel)
if (GetAlignVerticalCenterWithPanel(child))
{
if (alignTopWithPanel)
{
childPos = childPos.WithY((_arrangeSize.Height - childSize.Height) / 2);
}
else if (node.AlignTopWithNode == null)
var halfSubHeight = (AvailableSize.Height - childSize.Height) * 0.5;
if (node.Top.IsNaN())
node.Top = halfSubHeight;
else
node.Top = (node.Top + halfSubHeight) * 0.5;
if (node.Bottom.IsNaN())
node.Bottom = halfSubHeight;
else
node.Bottom = (node.Bottom + halfSubHeight) * 0.5;
}
if (node.Left.IsNaN())
{
if (!node.Right.IsNaN())
node.Left = AvailableSize.Width - node.Right - childSize.Width;
else
{
childPos = childPos.WithY(_arrangeSize.Height - childSize.Height);
node.Left = 0;
node.Right = AvailableSize.Width - childSize.Width;
}
}
else if (!node.Left.IsNaN() && node.Right.IsNaN())
{
node.Right = AvailableSize.Width - node.Left - childSize.Width;
}
if (node.Top.IsNaN())
{
if (!node.Bottom.IsNaN())
node.Top = AvailableSize.Height - node.Bottom - childSize.Height;
else
{
childPos = childPos.WithY((_arrangeSize.Height + node.AlignTopWithNode.Position.Y - childSize.Height) / 2);
node.Top = 0;
node.Bottom = AvailableSize.Height - childSize.Height;
}
}
else if (!node.Top.IsNaN() && node.Bottom.IsNaN())
{
node.Bottom = AvailableSize.Height - node.Top - childSize.Height;
}
node.Measured = true;
}
public Size GetBoundingSize(bool calcWidth, bool calcHeight)
{
var boundingSize = new Size();
foreach (var node in _nodeDic.Values)
{
var size = node.GetBoundingSize();
boundingSize = boundingSize.WithWidth(Math.Max(boundingSize.Width, size.Width));
boundingSize = boundingSize.WithHeight(Math.Max(boundingSize.Height, size.Height));
}
boundingSize = boundingSize.WithWidth(calcWidth ? boundingSize.Width : AvailableSize.Width);
boundingSize = boundingSize.WithHeight(calcHeight ? boundingSize.Height : AvailableSize.Height);
return boundingSize;
}
}
}
internal static partial class Extensions
{
/// <summary>
/// Returns a value that indicates whether the specified value is not a number ().
/// </summary>
/// <param name="d">A double-precision floating-point number.</param>
/// <returns>true if evaluates to ; otherwise, false.</returns>
public static bool IsNaN(this double d)
{
return double.IsNaN(d);
}
child.Arrange(new Rect(childPos.X, childPos.Y, childSize.Width, childSize.Height));
node.Position = childPos;
node.Arranged = true;
public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> predicate)
{
var enumerable = source as IList<TSource> ?? source.ToList();
foreach (var item in enumerable)
{
predicate.Invoke(item);
}
return enumerable;
}
}
}

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

@ -192,7 +192,7 @@ namespace Avalonia.Controls.Remote.Server
GetAvaloniaInputModifiers(pressed.Modifiers)));
}, DispatcherPriority.Input);
}
if (obj is PointerPressedEventMessage released)
if (obj is PointerReleasedEventMessage released)
{
Dispatcher.UIThread.Post(() =>
{

27
src/Avalonia.Controls/Repeater/ElementFactory.cs

@ -0,0 +1,27 @@
using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
public abstract class ElementFactory : IElementFactory
{
public IControl Build(object data)
{
return GetElementCore(new ElementFactoryGetArgs { Data = data });
}
public IControl GetElement(ElementFactoryGetArgs args)
{
return GetElementCore(args);
}
public bool Match(object data) => true;
public void RecycleElement(ElementFactoryRecycleArgs args)
{
RecycleElementCore(args);
}
protected abstract IControl GetElementCore(ElementFactoryGetArgs args);
protected abstract void RecycleElementCore(ElementFactoryRecycleArgs args);
}
}

66
src/Avalonia.Controls/Repeater/IElementFactory.cs

@ -0,0 +1,66 @@
using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
/// <summary>
/// Represents the optional arguments to use when calling an implementation of the
/// <see cref="IElementFactory"/>'s <see cref="IElementFactory.GetElement"/> method.
/// </summary>
public class ElementFactoryGetArgs
{
/// <summary>
/// Gets or sets the data item for which an appropriate element tree should be realized
/// when calling <see cref="IElementFactory.GetElement"/>.
/// </summary>
public object Data { get; set; }
/// <summary>
/// Gets or sets the <see cref="IControl"/> that is expected to be the parent of the
/// realized element from <see cref="IElementFactory.GetElement"/>.
/// </summary>
public IControl Parent { get; set; }
/// <summary>
/// Gets or sets the index of the item that should be realized.
/// </summary>
public int Index { get; set; }
}
/// <summary>
/// Represents the optional arguments to use when calling an implementation of the
/// <see cref="IElementFactory"/>'s <see cref="IElementFactory.GetElement"/> method.
/// </summary>
public class ElementFactoryRecycleArgs
{
/// <summary>
/// Gets or sets the <see cref="IControl"/> to recycle when calling
/// <see cref="IElementFactory.RecycleElement"/>.
/// </summary>
public IControl Element { get; set; }
/// <summary>
/// Gets or sets the <see cref="IControl"/> that is expected to be the parent of the
/// realized element from <see cref="IElementFactory.GetElement"/>.
/// </summary>
public IControl Parent { get; set; }
}
/// <summary>
/// A data template that supports creating and recyling elements for an <see cref="ItemsRepeater"/>.
/// </summary>
public interface IElementFactory : IDataTemplate
{
/// <summary>
/// Gets an <see cref="IControl"/>.
/// </summary>
/// <param name="args">The element args.</param>
public IControl GetElement(ElementFactoryGetArgs args);
/// <summary>
/// Recycles an <see cref="IControl"/> that was previously retrieved using
/// <see cref="GetElement"/>.
/// </summary>
/// <param name="args">The recycle args.</param>
public void RecycleElement(ElementFactoryRecycleArgs args);
}
}

19
src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs

@ -7,13 +7,26 @@ using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
internal class ItemTemplateWrapper
internal class ItemTemplateWrapper : IElementFactory
{
private readonly IDataTemplate _dataTemplate;
public ItemTemplateWrapper(IDataTemplate dataTemplate) => _dataTemplate = dataTemplate;
public IControl GetElement(IControl parent, object data)
public IControl Build(object param) => GetElement(null, param);
public bool Match(object data) => _dataTemplate.Match(data);
public IControl GetElement(ElementFactoryGetArgs args)
{
return GetElement(args.Parent, args.Data);
}
public void RecycleElement(ElementFactoryRecycleArgs args)
{
RecycleElement(args.Parent, args.Element);
}
private IControl GetElement(IControl parent, object data)
{
var selectedTemplate = _dataTemplate;
var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);
@ -37,7 +50,7 @@ namespace Avalonia.Controls
return element;
}
public void RecycleElement(IControl parent, IControl element)
private void RecycleElement(IControl parent, IControl element)
{
var selectedTemplate = _dataTemplate;
var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);

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

@ -141,7 +141,7 @@ namespace Avalonia.Controls
/// </summary>
public ItemsSourceView ItemsSourceView { get; private set; }
internal ItemTemplateWrapper ItemTemplateShim { get; set; }
internal IElementFactory ItemTemplateShim { get; set; }
internal Point LayoutOrigin { get; set; }
internal object LayoutState { get; set; }
internal IControl MadeAnchor => _viewportManager.MadeAnchor;
@ -664,7 +664,7 @@ namespace Avalonia.Controls
}
}
ItemTemplateShim = new ItemTemplateWrapper(newValue);
ItemTemplateShim = newValue as IElementFactory ?? new ItemTemplateWrapper(newValue);
InvalidateMeasure();
}

12
src/Avalonia.Controls/Repeater/RecyclePool.cs

@ -11,10 +11,13 @@ using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
internal class RecyclePool
public class RecyclePool
{
public static readonly AttachedProperty<IDataTemplate> OriginTemplateProperty =
AvaloniaProperty.RegisterAttached<Control, IDataTemplate>("OriginTemplate", typeof(RecyclePool));
internal static readonly AttachedProperty<IDataTemplate> OriginTemplateProperty =
AvaloniaProperty.RegisterAttached<RecyclePool, Control, IDataTemplate>("OriginTemplate");
internal static readonly AttachedProperty<string> ReuseKeyProperty =
AvaloniaProperty.RegisterAttached<RecyclePool, Control, string>("ReuseKey", string.Empty);
private static ConditionalWeakTable<IDataTemplate, RecyclePool> s_pools = new ConditionalWeakTable<IDataTemplate, RecyclePool>();
private readonly Dictionary<string, List<ElementInfo>> _elements = new Dictionary<string, List<ElementInfo>>();
@ -77,6 +80,9 @@ namespace Avalonia.Controls
return null;
}
internal string GetReuseKey(IControl element) => element.GetValue(ReuseKeyProperty);
internal void SetReuseKey(IControl element, string value) => element.SetValue(ReuseKeyProperty, value);
private IPanel EnsureOwnerIsPanelOrNull(IControl owner)
{
if (owner is IPanel panel)

119
src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs

@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Templates;
#nullable enable
namespace Avalonia.Controls
{
public class SelectTemplateEventArgs : EventArgs
{
public string? TemplateKey { get; set; }
public object? DataContext { get; internal set; }
public IControl? Owner { get; internal set; }
}
public class RecyclingElementFactory : ElementFactory
{
private RecyclePool? _recyclePool;
private IDictionary<string, IDataTemplate>? _templates;
private SelectTemplateEventArgs? _args;
public RecyclingElementFactory()
{
Templates = new Dictionary<string, IDataTemplate>();
}
public RecyclePool RecyclePool
{
get => _recyclePool ??= new RecyclePool();
set => _recyclePool = value ?? throw new ArgumentNullException(nameof(value));
}
public IDictionary<string, IDataTemplate> Templates
{
get => _templates ??= new Dictionary<string, IDataTemplate>();
set => _templates = value ?? throw new ArgumentNullException(nameof(value));
}
public event EventHandler<SelectTemplateEventArgs>? SelectTemplateKey;
protected override IControl GetElementCore(ElementFactoryGetArgs args)
{
if (_templates == null || _templates.Count == 0)
{
throw new InvalidOperationException("Templates cannot be empty.");
}
var templateKey = Templates.Count == 1 ?
Templates.First().Key :
OnSelectTemplateKeyCore(args.Data, args.Parent);
if (string.IsNullOrEmpty(templateKey))
{
// Note: We could allow null/whitespace, which would work as long as
// the recycle pool is not shared. in order to make this work in all cases
// currently we validate that a valid template key is provided.
throw new InvalidOperationException("Template key cannot be null or empty.");
}
// Get an element from the Recycle Pool or create one
var element = RecyclePool.TryGetElement(templateKey, args.Parent);
if (element is null)
{
// No need to call HasKey if there is only one template.
if (Templates.Count > 1 && !Templates.ContainsKey(templateKey))
{
var message = $"No templates of key '{templateKey}' were found in the templates collection.";
throw new InvalidOperationException(message);
}
var dataTemplate = Templates[templateKey];
element = dataTemplate.Build(args.Data);
// Associate ReuseKey with element
RecyclePool.SetReuseKey(element, templateKey);
}
return element;
}
protected override void RecycleElementCore(ElementFactoryRecycleArgs args)
{
var element = args.Element;
var key = RecyclePool.GetReuseKey(element);
RecyclePool.PutElement(element, key, args.Parent);
}
protected virtual string OnSelectTemplateKeyCore(object dataContext, IControl owner)
{
if (SelectTemplateKey is object)
{
_args ??= new SelectTemplateEventArgs();
_args.TemplateKey = null;
_args.DataContext = dataContext;
_args.Owner = owner;
try
{
SelectTemplateKey(this, _args);
}
finally
{
_args.DataContext = null;
_args.Owner = null;
}
}
if (string.IsNullOrEmpty(_args?.TemplateKey))
{
throw new InvalidOperationException(
"Please provide a valid template identifier in the handler for the SelectTemplateKey event.");
}
return _args!.TemplateKey!;
}
}
}

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

@ -6,11 +6,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Logging;
using Avalonia.VisualTree;
@ -26,6 +24,8 @@ namespace Avalonia.Controls
private readonly UniqueIdElementPool _resetPool;
private IControl _lastFocusedElement;
private bool _isDataSourceStableResetPending;
private ElementFactoryGetArgs _elementFactoryGetArgs;
private ElementFactoryRecycleArgs _elementFactoryRecycleArgs;
private int _firstRealizedElementIndexHeldByLayout = FirstRealizedElementIndexDefault;
private int _lastRealizedElementIndexHeldByLayout = LastRealizedElementIndexDefault;
private bool _eventsSubscribed;
@ -134,7 +134,14 @@ namespace Avalonia.Controls
if (_owner.ItemTemplateShim != null)
{
_owner.ItemTemplateShim.RecycleElement(_owner, element);
var context = _elementFactoryRecycleArgs ??= new ElementFactoryRecycleArgs();
context.Element = element;
context.Parent = _owner;
_owner.ItemTemplateShim.RecycleElement(context);
context.Element = null;
context.Parent = null;
}
else
{
@ -579,7 +586,7 @@ namespace Avalonia.Controls
var data = _owner.ItemsSourceView.GetAt(index);
var providedElementFactory = _owner.ItemTemplateShim;
ItemTemplateWrapper GetElementFactory()
IElementFactory GetElementFactory()
{
if (providedElementFactory == null)
{
@ -602,7 +609,20 @@ namespace Avalonia.Controls
}
var elementFactory = GetElementFactory();
return elementFactory.GetElement(_owner, data);
var args = _elementFactoryGetArgs ??= new ElementFactoryGetArgs();
try
{
args.Data = data;
args.Parent = _owner;
args.Index = index;
return elementFactory.GetElement(args);
}
finally
{
args.Data = null;
args.Parent = null;
}
}
var element = GetElement();
@ -732,6 +752,7 @@ namespace Avalonia.Controls
{
_owner.GotFocus += OnFocusChanged;
_owner.LostFocus += OnFocusChanged;
_eventsSubscribed = true;
}
}

29
src/Avalonia.Controls/Templates/FuncDataTemplate.cs

@ -6,7 +6,7 @@ namespace Avalonia.Controls.Templates
/// <summary>
/// Builds a control for a piece of data.
/// </summary>
public class FuncDataTemplate : FuncTemplate<object, IControl>, IDataTemplate
public class FuncDataTemplate : FuncTemplate<object, IControl>, IRecyclingDataTemplate
{
/// <summary>
/// The default data template used in the case where no matching data template is found.
@ -30,10 +30,8 @@ namespace Avalonia.Controls.Templates
},
true);
/// <summary>
/// The implementation of the <see cref="Match"/> method.
/// </summary>
private readonly Func<object, bool> _match;
private readonly bool _supportsRecycling;
/// <summary>
/// Initializes a new instance of the <see cref="FuncDataTemplate"/> class.
@ -70,12 +68,9 @@ namespace Avalonia.Controls.Templates
Contract.Requires<ArgumentNullException>(match != null);
_match = match;
SupportsRecycling = supportsRecycling;
_supportsRecycling = supportsRecycling;
}
/// <inheritdoc/>
public bool SupportsRecycling { get; }
/// <summary>
/// Checks to see if this data template matches the specified data.
/// </summary>
@ -88,6 +83,24 @@ namespace Avalonia.Controls.Templates
return _match(data);
}
/// <summary>
/// Creates or recycles a control to display the specified data.
/// </summary>
/// <param name="data">The data to display.</param>
/// <param name="existing">An optional control to recycle.</param>
/// <returns>
/// The <paramref name="existing"/> control if supplied and applicable to
/// <paramref name="data"/>, otherwise a new control.
/// </returns>
/// <remarks>
/// The caller should ensure that any control passed to <paramref name="existing"/>
/// originated from the same data template.
/// </remarks>
public IControl Build(object data, IControl existing)
{
return _supportsRecycling && existing is object ? existing : Build(data);
}
/// <summary>
/// Determines of an object is of the specified type.
/// </summary>

6
src/Avalonia.Controls/Templates/FuncTemplate`2.cs

@ -1,5 +1,7 @@
using System;
#nullable enable
namespace Avalonia.Controls.Templates
{
/// <summary>
@ -18,9 +20,7 @@ namespace Avalonia.Controls.Templates
/// <param name="func">The function used to create the control.</param>
public FuncTemplate(Func<TParam, INameScope, TControl> func)
{
Contract.Requires<ArgumentNullException>(func != null);
_func = func;
_func = func ?? throw new ArgumentNullException(nameof(func));
}
/// <summary>

12
src/Avalonia.Controls/Templates/IDataTemplate.cs

@ -1,3 +1,7 @@
using System;
#nullable enable
namespace Avalonia.Controls.Templates
{
/// <summary>
@ -5,12 +9,6 @@ namespace Avalonia.Controls.Templates
/// </summary>
public interface IDataTemplate : ITemplate<object, IControl>
{
/// <summary>
/// Gets a value indicating whether the data template supports recycling of the generated
/// control.
/// </summary>
bool SupportsRecycling { get; }
/// <summary>
/// Checks to see if this data template matches the specified data.
/// </summary>
@ -20,4 +18,4 @@ namespace Avalonia.Controls.Templates
/// </returns>
bool Match(object data);
}
}
}

25
src/Avalonia.Controls/Templates/IRecyclingDataTemplate.cs

@ -0,0 +1,25 @@
#nullable enable
namespace Avalonia.Controls.Templates
{
/// <summary>
/// An <see cref="IDataTemplate"/> that supports recycling existing elements.
/// </summary>
public interface IRecyclingDataTemplate : IDataTemplate
{
/// <summary>
/// Creates or recycles a control to display the specified data.
/// </summary>
/// <param name="data">The data to display.</param>
/// <param name="existing">An optional control to recycle.</param>
/// <returns>
/// The <paramref name="existing"/> control if supplied and applicable to
/// <paramref name="data"/>, otherwise a new control.
/// </returns>
/// <remarks>
/// The caller should ensure that any control passed to <paramref name="existing"/>
/// originated from the same data template.
/// </remarks>
IControl Build(object data, IControl? existing);
}
}

2
src/Avalonia.Controls/TextBox.cs

@ -134,7 +134,7 @@ namespace Avalonia.Controls
{
if (acceptsReturn)
{
return wrapping == TextWrapping.NoWrap ?
return wrapping != TextWrapping.Wrap ?
ScrollBarVisibility.Auto :
ScrollBarVisibility.Disabled;
}

25
src/Avalonia.Controls/TickBar.cs

@ -39,11 +39,15 @@ namespace Avalonia.Controls
{
static TickBar()
{
AffectsRender<TickBar>(ReservedSpaceProperty,
AffectsRender<TickBar>(FillProperty,
IsDirectionReversedProperty,
ReservedSpaceProperty,
MaximumProperty,
MinimumProperty,
OrientationProperty,
TickFrequencyProperty);
PlacementProperty,
TickFrequencyProperty,
TicksProperty);
}
public TickBar() : base()
@ -137,7 +141,7 @@ namespace Avalonia.Controls
/// <summary>
/// The Ticks property contains collection of value of type Double which
/// are the logical positions use to draw the ticks.
/// The property value is a <see cref="DoubleCollection" />.
/// The property value is a <see cref="AvaloniaList{T}" />.
/// </summary>
public AvaloniaList<double> Ticks
{
@ -169,7 +173,6 @@ namespace Avalonia.Controls
public static readonly StyledProperty<TickBarPlacement> PlacementProperty =
AvaloniaProperty.Register<TickBar, TickBarPlacement>(nameof(Placement), 0d);
/// <summary>
/// Placement property specified how the Tick will be placed.
/// This property affects the way ticks are drawn.
@ -189,7 +192,7 @@ namespace Avalonia.Controls
/// <summary>
/// TickBar will use ReservedSpaceProperty for left and right spacing (for horizontal orientation) or
/// tob and bottom spacing (for vertical orienation).
/// top and bottom spacing (for vertical orienation).
/// The space on both sides of TickBar is half of specified ReservedSpace.
/// This property has type of <see cref="Rect" />.
/// </summary>
@ -201,7 +204,7 @@ namespace Avalonia.Controls
/// <summary>
/// Draw ticks.
/// Ticks can be draw in 8 diffrent ways depends on Placment property and IsDirectionReversed property.
/// Ticks can be draw in 8 different ways depends on Placement property and IsDirectionReversed property.
///
/// This function also draw selection-tick(s) if IsSelectionRangeEnabled is 'true' and
/// SelectionStart and SelectionEnd are valid.
@ -211,9 +214,7 @@ namespace Avalonia.Controls
///
/// The secondary ticks (all other ticks, including selection-tics) height will be 75% of TickBar's render size.
///
/// Brush that use to fill ticks is specified by Shape.Fill property.
///
/// Pen that use to draw ticks is specified by Shape.Pen property.
/// Brush that use to fill ticks is specified by Fill property.
/// </summary>
public override void Render(DrawingContext dc)
{
@ -222,7 +223,6 @@ namespace Avalonia.Controls
var tickLen = 0.0d; // Height for Primary Tick (for Mininum and Maximum value)
var tickLen2 = 0.0d; // Height for Secondary Tick
var logicalToPhysical = 1.0;
var progression = 1.0d;
var startPoint = new Point();
var endPoint = new Point();
var rSpace = Orientation == Orientation.Horizontal ? ReservedSpace.Width : ReservedSpace.Height;
@ -242,7 +242,6 @@ namespace Avalonia.Controls
startPoint = new Point(halfReservedSpace, size.Height);
endPoint = new Point(halfReservedSpace + size.Width, size.Height);
logicalToPhysical = size.Width / range;
progression = 1;
break;
case TickBarPlacement.Bottom:
@ -255,7 +254,6 @@ namespace Avalonia.Controls
startPoint = new Point(halfReservedSpace, 0d);
endPoint = new Point(halfReservedSpace + size.Width, 0d);
logicalToPhysical = size.Width / range;
progression = 1;
break;
case TickBarPlacement.Left:
@ -269,7 +267,6 @@ namespace Avalonia.Controls
startPoint = new Point(size.Width, size.Height + halfReservedSpace);
endPoint = new Point(size.Width, halfReservedSpace);
logicalToPhysical = size.Height / range * -1;
progression = -1;
break;
case TickBarPlacement.Right:
@ -282,7 +279,6 @@ namespace Avalonia.Controls
startPoint = new Point(0d, size.Height + halfReservedSpace);
endPoint = new Point(0d, halfReservedSpace);
logicalToPhysical = size.Height / range * -1;
progression = -1;
break;
};
@ -291,7 +287,6 @@ namespace Avalonia.Controls
// Invert direciton of the ticks
if (IsDirectionReversed)
{
progression *= -progression;
logicalToPhysical *= -1;
// swap startPoint & endPoint

1
src/Avalonia.Controls/ToolTip.cs

@ -66,6 +66,7 @@ namespace Avalonia.Controls
static ToolTip()
{
TipProperty.Changed.Subscribe(ToolTipService.Instance.TipChanged);
IsOpenProperty.Changed.Subscribe(ToolTipService.Instance.TipOpenChanged);
IsOpenProperty.Changed.Subscribe(IsOpenChanged);
}

21
src/Avalonia.Controls/ToolTipService.cs

@ -37,6 +37,27 @@ namespace Avalonia.Controls
}
}
internal void TipOpenChanged(AvaloniaPropertyChangedEventArgs e)
{
var control = (Control)e.Sender;
if (e.OldValue is false && e.NewValue is true)
{
control.DetachedFromVisualTree += ControlDetaching;
}
else if(e.OldValue is true && e.NewValue is false)
{
control.DetachedFromVisualTree -= ControlDetaching;
}
}
private void ControlDetaching(object sender, VisualTreeAttachmentEventArgs e)
{
var control = (Control)sender;
control.DetachedFromVisualTree -= ControlDetaching;
Close(control);
}
/// <summary>
/// Called when the pointer enters a control with an attached tooltip.
/// </summary>

4
src/Avalonia.Controls/TopLevel.cs

@ -280,10 +280,10 @@ namespace Avalonia.Controls
}
/// <inheritdoc/>
double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1;
double ILayoutRoot.LayoutScaling => PlatformImpl?.RenderScaling ?? 1;
/// <inheritdoc/>
double IRenderRoot.RenderScaling => PlatformImpl?.Scaling ?? 1;
double IRenderRoot.RenderScaling => PlatformImpl?.RenderScaling ?? 1;
IStyleHost IStyleHost.StylingParent => _globalStyles;

20
src/Avalonia.Controls/Window.cs

@ -6,6 +6,7 @@ using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Chrome;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
@ -70,8 +71,7 @@ namespace Avalonia.Controls
/// </summary>
public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot
{
private readonly List<(Window child, bool isDialog)> _children = new List<(Window, bool)>();
private TitleBar _managedTitleBar;
private readonly List<(Window child, bool isDialog)> _children = new List<(Window, bool)>();
private bool _isExtendedIntoWindowDecorations;
private Thickness _windowDecorationMargin;
private Thickness _offScreenMargin;
@ -552,20 +552,6 @@ namespace Avalonia.Controls
IsExtendedIntoWindowDecorations = isExtended;
WindowDecorationMargin = PlatformImpl.ExtendedMargins;
OffScreenMargin = PlatformImpl.OffScreenMargin;
if (PlatformImpl.NeedsManagedDecorations)
{
if (_managedTitleBar == null)
{
_managedTitleBar = new TitleBar(this);
_managedTitleBar.Attach();
}
}
else
{
_managedTitleBar?.Detach();
_managedTitleBar = null;
}
}
/// <summary>
@ -818,7 +804,7 @@ namespace Avalonia.Controls
private void SetWindowStartupLocation(IWindowBaseImpl owner = null)
{
var scaling = owner?.Scaling ?? PlatformImpl?.Scaling ?? 1;
var scaling = owner?.DesktopScaling ?? PlatformImpl?.DesktopScaling ?? 1;
// TODO: We really need non-client size here.
var rect = new PixelRect(

2
src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj

@ -9,6 +9,7 @@
<Version>0.7.0</Version>
<NoWarn>CS1591</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NugetPackageName>Avalonia</NugetPackageName>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Remote\HtmlTransport\webapp\build\**\*.gz" />
@ -26,4 +27,5 @@
<ProjectReference Include="..\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

7
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -18,10 +18,11 @@ namespace Avalonia.DesignerSupport
Control control;
using (PlatformManager.DesignerMode())
{
var loader = new AvaloniaXamlLoader() {IsDesignMode = true};
var loader = AvaloniaLocator.Current.GetService<AvaloniaXamlLoader.IRuntimeXamlLoader>();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
if (loader == null)
throw new XamlLoadException("Runtime XAML loader is not registered");
Uri baseUri = null;
if (assemblyPath != null)
@ -34,7 +35,7 @@ namespace Avalonia.DesignerSupport
}
var localAsm = assemblyPath != null ? Assembly.LoadFile(Path.GetFullPath(assemblyPath)) : null;
var loaded = loader.Load(stream, localAsm, null, baseUri);
var loaded = loader.Load(stream, localAsm, null, baseUri, true);
var style = loaded as IStyle;
if (style != null)
{

10
src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs

@ -9,12 +9,14 @@ namespace Avalonia.DesignerSupport.Remote
{
class FileWatcherTransport : IAvaloniaRemoteTransportConnection, ITransportWithEnforcedMethod
{
private readonly string _appPath;
private string _path;
private string _lastContents;
private bool _disposed;
public FileWatcherTransport(Uri file)
public FileWatcherTransport(Uri file, string appPath)
{
_appPath = appPath;
_path = file.LocalPath;
}
@ -73,7 +75,11 @@ namespace Avalonia.DesignerSupport.Remote
{
Console.WriteLine("Triggering XAML update");
_lastContents = data;
_onMessage?.Invoke(this, new UpdateXamlMessage { Xaml = data });
_onMessage?.Invoke(this, new UpdateXamlMessage
{
Xaml = data,
AssemblyPath = _appPath
});
}
await Task.Delay(100);

28
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json

@ -3564,12 +3564,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -3584,17 +3586,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -3711,7 +3716,8 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -3723,6 +3729,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -3737,6 +3744,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -3744,12 +3752,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -3768,6 +3778,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -3848,7 +3859,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -3860,6 +3872,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -3981,6 +3994,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",

2
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js

@ -93,7 +93,7 @@ const config = {
plugins:
[
new Printer(),
new CleanWebpackPlugin([path.resolve(__dirname, 'build')]),
new CleanWebpackPlugin([path.resolve(__dirname, 'build')], { verbose: false }),
new MiniCssExtractPlugin({
filename: "[name].[chunkhash]h" +
".css",

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

@ -36,6 +36,7 @@ namespace Avalonia.DesignerSupport.Remote
{
}
public double DesktopScaling => 1.0;
public PixelPoint Position { get; set; }
public Action<PixelPoint> PositionChanged { get; set; }
public Action Deactivated { get; set; }

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

@ -112,8 +112,9 @@ namespace Avalonia.DesignerSupport.Remote
return rv;
}
static IAvaloniaRemoteTransportConnection CreateTransport(Uri transport)
static IAvaloniaRemoteTransportConnection CreateTransport(CommandLineArgs args)
{
var transport = args.Transport;
if (transport.Scheme == "tcp-bson")
{
return new BsonTcpTransport().Connect(IPAddress.Parse(transport.Host), transport.Port).Result;
@ -121,7 +122,7 @@ namespace Avalonia.DesignerSupport.Remote
if (transport.Scheme == "file")
{
return new FileWatcherTransport(transport);
return new FileWatcherTransport(transport, args.AppPath);
}
PrintUsage();
return null;
@ -160,7 +161,7 @@ namespace Avalonia.DesignerSupport.Remote
public static void Main(string[] cmdline)
{
var args = ParseCommandLineArgs(cmdline);
var transport = CreateTransport(args.Transport);
var transport = CreateTransport(args);
if (transport is ITransportWithEnforcedMethod enforcedMethod)
args.Method = enforcedMethod.PreviewerMethod;
var asm = Assembly.LoadFile(System.IO.Path.GetFullPath(args.AppPath));

3
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -21,7 +21,8 @@ namespace Avalonia.DesignerSupport.Remote
public IPlatformHandle Handle { get; }
public Size MaxAutoSizeHint { get; }
public Size ClientSize { get; }
public double Scaling { get; } = 1.0;
public double RenderScaling { get; } = 1.0;
public double DesktopScaling => 1.0;
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }

2
src/Avalonia.Desktop/Avalonia.Desktop.csproj

@ -11,4 +11,6 @@
<ProjectReference Include="../../packages/Avalonia/Avalonia.csproj" />
<ProjectReference Include="../Avalonia.X11/Avalonia.X11.csproj" />
</ItemGroup>
<Import Project="..\..\build\ApiDiff.props" />
</Project>

1
src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj

@ -17,4 +17,5 @@
<Import Project="..\..\build\NetCore.props" />
<Import Project="..\..\build\NetFX.props" />
<Import Project="..\Shared\PlatformSupport\PlatformSupport.projitems" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

2
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Avalonia</RootNamespace>
<PackageId>Avalonia.Diagnostics</PackageId>
</PropertyGroup>
<ItemGroup>
<Compile Update="**\*.xaml.cs">
@ -28,4 +29,5 @@
<Import Project="..\..\build\EmbedXaml.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\BuildTargets.targets" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

2
src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs

@ -7,8 +7,6 @@ namespace Avalonia.Diagnostics
{
internal class ViewLocator : IDataTemplate
{
public bool SupportsRecycling => false;
public IControl Build(object data)
{
var name = data.GetType().FullName.Replace("ViewModel", "View");

2
src/Avalonia.Dialogs/Avalonia.Dialogs.csproj

@ -15,4 +15,6 @@
<ItemGroup>
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
</ItemGroup>
<Import Project="..\..\build\ApiDiff.props" />
</Project>

5
src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
@ -10,6 +10,5 @@
<ItemGroup>
<PackageReference Include="Tmds.DBus" Version="0.7.0" />
</ItemGroup>
</ItemGroup>
</Project>

2
src/Avalonia.FreeDesktop/DBusHelper.cs

@ -43,7 +43,7 @@ namespace Avalonia.FreeDesktop
public void Initialized()
{
lock (_lock)
_ctx = new AvaloniaSynchronizationContext();
_ctx = new AvaloniaSynchronizationContext(null);
}
}
public static Connection Connection { get; private set; }

3
src/Avalonia.Headless/Avalonia.Headless.csproj

@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
</ItemGroup>
</ItemGroup>
</Project>

9
src/Avalonia.Headless/HeadlessWindowImpl.cs

@ -41,7 +41,8 @@ namespace Avalonia.Headless
}
public Size ClientSize { get; set; }
public double Scaling { get; } = 1;
public double RenderScaling { get; } = 1;
public double DesktopScaling => RenderScaling;
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }
@ -62,9 +63,9 @@ namespace Avalonia.Headless
public IInputRoot InputRoot { get; set; }
public Point PointToClient(PixelPoint point) => point.ToPoint(Scaling);
public Point PointToClient(PixelPoint point) => point.ToPoint(RenderScaling);
public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, Scaling);
public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
public void SetCursor(IPlatformHandle cursor)
{
@ -201,7 +202,7 @@ namespace Avalonia.Headless
public ILockedFramebuffer Lock()
{
var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, Scaling), new Vector(96, 96) * Scaling);
var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, RenderScaling), new Vector(96, 96) * RenderScaling);
var fb = bmp.Lock();
return new FramebufferProxy(fb, () =>
{

1
src/Avalonia.Input/Avalonia.Input.csproj

@ -10,4 +10,5 @@
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

1
src/Avalonia.Interactivity/Avalonia.Interactivity.csproj

@ -11,4 +11,5 @@
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

1
src/Avalonia.Layout/Avalonia.Layout.csproj

@ -8,4 +8,5 @@
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

44
src/Avalonia.Native/AvaloniaNativeMenuExporter.cs

@ -11,7 +11,7 @@ namespace Avalonia.Native
class AvaloniaNativeMenuExporter : ITopLevelNativeMenuExporter
{
private IAvaloniaNativeFactory _factory;
private bool _resetQueued;
private bool _resetQueued = true;
private bool _exported = false;
private IAvnWindow _nativeWindow;
private NativeMenu _menu;
@ -39,8 +39,7 @@ namespace Avalonia.Native
public void SetNativeMenu(NativeMenu menu)
{
_menu = menu == null ? new NativeMenu() : menu;
DoLayoutReset();
DoLayoutReset(true);
}
internal void UpdateIfNeeded()
@ -74,31 +73,34 @@ namespace Avalonia.Native
return result;
}
void DoLayoutReset()
private void DoLayoutReset(bool forceUpdate = false)
{
_resetQueued = false;
if (_nativeWindow is null)
if (_resetQueued || forceUpdate)
{
var appMenu = NativeMenu.GetMenu(Application.Current);
_resetQueued = false;
if (appMenu == null)
if (_nativeWindow is null)
{
appMenu = CreateDefaultAppMenu();
NativeMenu.SetMenu(Application.Current, appMenu);
}
var appMenu = NativeMenu.GetMenu(Application.Current);
SetMenu(appMenu);
}
else
{
if (_menu != null)
if (appMenu == null)
{
appMenu = CreateDefaultAppMenu();
NativeMenu.SetMenu(Application.Current, appMenu);
}
SetMenu(appMenu);
}
else
{
SetMenu(_nativeWindow, _menu);
if (_menu != null)
{
SetMenu(_nativeWindow, _menu);
}
}
}
_exported = true;
_exported = true;
}
}
internal void QueueReset()
@ -106,7 +108,7 @@ namespace Avalonia.Native
if (_resetQueued)
return;
_resetQueued = true;
Dispatcher.UIThread.Post(DoLayoutReset, DispatcherPriority.Background);
Dispatcher.UIThread.Post(() => DoLayoutReset(), DispatcherPriority.Background);
}
private void SetMenu(NativeMenu menu)

15
src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs

@ -1,15 +0,0 @@
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Platform;
namespace Avalonia.Native
{
class OsxManagedPopupPositionerPopupImplHelper : ManagedPopupPositionerPopupImplHelper
{
public OsxManagedPopupPositionerPopupImplHelper(IWindowBaseImpl parent, MoveResizeDelegate moveResize) : base(parent, moveResize)
{
}
public override double Scaling => 1;
}
}

5
src/Avalonia.Native/OsxUnicodeKeys.cs

@ -140,6 +140,11 @@ namespace Avalonia.Native.Interop
}
else
{
if (key >= Key.D0 && key <= Key.D9)
{
return key.ToString().Replace("D", "");
}
return key.ToString().ToLower();
}
}

2
src/Avalonia.Native/PopupImpl.cs

@ -26,7 +26,7 @@ namespace Avalonia.Native
var context = _opts.UseGpu ? glFeature?.DeferredContext : null;
Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context);
}
PopupPositioner = new ManagedPopupPositioner(new OsxManagedPopupPositionerPopupImplHelper(parent, MoveResize));
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
}
private void MoveResize(PixelPoint position, Size size, double scaling)

18
src/Avalonia.Native/WindowImplBase.cs

@ -81,7 +81,7 @@ namespace Avalonia.Native
_glSurface = new GlPlatformSurface(window, _glContext);
Screen = new ScreenImpl(screens);
_savedLogicalSize = ClientSize;
_savedScaling = Scaling;
_savedScaling = RenderScaling;
_nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost());
var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
@ -300,7 +300,15 @@ namespace Avalonia.Native
public IRenderer CreateRenderer(IRenderRoot root)
{
if (_deferredRendering)
return new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>());
{
var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
var customRendererFactory = AvaloniaLocator.Current.GetService<IRendererFactory>();
if (customRendererFactory != null)
return customRendererFactory.Create(root, loop);
return new DeferredRenderer(root, loop);
}
return new ImmediateRenderer(root);
}
@ -369,7 +377,9 @@ namespace Avalonia.Native
_native.SetTopMost(value);
}
public double Scaling => _native?.GetScaling() ?? 1;
public double RenderScaling => _native?.GetScaling() ?? 1;
public double DesktopScaling => 1;
public Action Deactivated { get; set; }
public Action Activated { get; set; }
@ -432,7 +442,7 @@ namespace Avalonia.Native
TransparencyLevel = transparencyLevel;
_native.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur);
_native?.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur);
TransparencyLevelChanged?.Invoke(TransparencyLevel);
}
}

3
src/Avalonia.OpenGL/Avalonia.OpenGL.csproj

@ -9,6 +9,5 @@
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
</ItemGroup>
</Project>

1
src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj

@ -8,4 +8,5 @@
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\ReactiveUI.props" />
<Import Project="..\..\build\ApiDiff.props" />
</Project>

2
src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj

@ -4,8 +4,10 @@
<DefineConstants>AVALONIA_REMOTE_PROTOCOL;$(DefineConstants)</DefineConstants>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
<PackageId>Avalonia.Remote.Protocol</PackageId>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Avalonia.Input\Key.cs" />
</ItemGroup>
<Import Project="..\..\build\ApiDiff.props" />
</Project>

1
src/Avalonia.Styling/Avalonia.Styling.csproj

@ -8,4 +8,5 @@
<ProjectReference Include="..\Avalonia.Animation\Avalonia.Animation.csproj" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
</ItemGroup>
<Import Project="..\..\build\ApiDiff.props" />
</Project>

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

Loading…
Cancel
Save