Browse Source

Merge pull request #1 from AvaloniaUI/master

get latest master
pull/5561/head
Splitwirez 6 years ago
committed by GitHub
parent
commit
ec818b3e5a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .gitmodules
  2. 91
      Avalonia.sln
  3. 1
      Avalonia.v3.ncrunchsolution
  4. 1
      Directory.Build.props
  5. 4
      build/HarfBuzzSharp.props
  6. 4
      build/SkiaSharp.props
  7. 2
      dirs.proj
  8. 13
      native/Avalonia.Native/inc/avalonia-native.h
  9. 16
      native/Avalonia.Native/src/OSX/Screens.mm
  10. 13
      native/Avalonia.Native/src/OSX/window.h
  11. 302
      native/Avalonia.Native/src/OSX/window.mm
  12. 2
      nukebuild/Build.cs
  13. 76
      nukebuild/BuildTasksPatcher.cs
  14. 3
      nukebuild/_build.csproj
  15. 2
      readme.md
  16. 5
      samples/BindingDemo/MainWindow.xaml
  17. 4
      samples/BindingDemo/ViewModels/MainWindowViewModel.cs
  18. 3
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  19. 55
      samples/ControlCatalog.NetCore/Program.cs
  20. 38
      samples/ControlCatalog/App.xaml.cs
  21. 6
      samples/ControlCatalog/MainView.xaml
  22. 7
      samples/ControlCatalog/MainView.xaml.cs
  23. 41
      samples/ControlCatalog/MainWindow.xaml
  24. 139
      samples/ControlCatalog/Pages/AcrylicPage.xaml
  25. 19
      samples/ControlCatalog/Pages/AcrylicPage.xaml.cs
  26. 12
      samples/ControlCatalog/Pages/BorderPage.xaml
  27. 14
      samples/ControlCatalog/Pages/ButtonPage.xaml
  28. 2
      samples/ControlCatalog/Pages/ComboBoxPage.xaml
  29. 4
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  30. 6
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  31. 36
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
  32. 26
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  33. 8
      samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml
  34. 8
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  35. 60
      samples/ControlCatalog/Pages/RelativePanelPage.axaml
  36. 19
      samples/ControlCatalog/Pages/RelativePanelPage.axaml.cs
  37. 69
      samples/ControlCatalog/Pages/TextBlockPage.xaml
  38. 4
      samples/ControlCatalog/Pages/ToolTipPage.xaml
  39. 19
      samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml
  40. 19
      samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml.cs
  41. 6
      samples/ControlCatalog/SideBar.xaml
  42. 10
      samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
  43. 65
      samples/ControlCatalog/ViewModels/MainWindowViewModel.cs
  44. 5
      samples/RenderDemo/App.xaml
  45. 4
      samples/RenderDemo/Pages/ClippingPage.xaml
  46. 125
      samples/RenderDemo/SideBar.xaml
  47. 1
      samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
  48. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  49. 8
      src/Avalonia.Base/AvaloniaProperty.cs
  50. 73
      src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs
  51. 32
      src/Avalonia.Base/Data/Core/ExpressionObserver.cs
  52. 14
      src/Avalonia.Base/Data/Core/IPropertyInfo.cs
  53. 2
      src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs
  54. 32
      src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs
  55. 91
      src/Avalonia.Base/Data/Core/PropertyPath.cs
  56. 37
      src/Avalonia.Base/Data/Core/StreamNode.cs
  57. 27
      src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
  58. 20
      src/Avalonia.Base/Utilities/CharacterReader.cs
  59. 46
      src/Avalonia.Base/Utilities/KeywordParser.cs
  60. 46
      src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
  61. 2
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.Helpers.cs
  62. 60
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  63. 8
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  64. 5
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  65. 5
      src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs
  66. 2
      src/Avalonia.Controls.DataGrid/Themes/Default.xaml
  67. 23
      src/Avalonia.Controls/AcrylicPlatformCompensationLevels.cs
  68. 84
      src/Avalonia.Controls/Chrome/CaptionButtons.cs
  69. 97
      src/Avalonia.Controls/Chrome/TitleBar.cs
  70. 31
      src/Avalonia.Controls/ComboBox.cs
  71. 16
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  72. 31
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  73. 51
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  74. 38
      src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
  75. 5
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  76. 117
      src/Avalonia.Controls/ExperimentalAcrylicBorder.cs
  77. 1
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  78. 38
      src/Avalonia.Controls/Platform/ExtendClientAreaChromeHints.cs
  79. 11
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  80. 5
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  81. 46
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  82. 5
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  83. 22
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  84. 2
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  85. 4
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  86. 21
      src/Avalonia.Controls/Primitives/AccessText.cs
  87. 34
      src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
  88. 4
      src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
  89. 20
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  90. 9
      src/Avalonia.Controls/Primitives/Track.cs
  91. 36
      src/Avalonia.Controls/Primitives/VisualLayerManager.cs
  92. 1
      src/Avalonia.Controls/Properties/AssemblyInfo.cs
  93. 546
      src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
  94. 536
      src/Avalonia.Controls/RelativePanel.cs
  95. 2
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  96. 27
      src/Avalonia.Controls/Repeater/ElementFactory.cs
  97. 66
      src/Avalonia.Controls/Repeater/IElementFactory.cs
  98. 19
      src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
  99. 4
      src/Avalonia.Controls/Repeater/ItemsRepeater.cs
  100. 12
      src/Avalonia.Controls/Repeater/RecyclePool.cs

4
.gitmodules

@ -2,5 +2,5 @@
path = nukebuild/Numerge path = nukebuild/Numerge
url = https://github.com/kekekeks/Numerge.git url = https://github.com/kekekeks/Numerge.git
[submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"] [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/XamlIl.git url = https://github.com/kekekeks/XamlX.git

91
Avalonia.sln

@ -123,10 +123,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
build\AndroidWorkarounds.props = build\AndroidWorkarounds.props
build\Base.props = build\Base.props build\Base.props = build\Base.props
build\Binding.props = build\Binding.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\HarfBuzzSharp.props = build\HarfBuzzSharp.props
build\iOSWorkarounds.props = build\iOSWorkarounds.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
@ -136,17 +139,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\NetCore.props = build\NetCore.props build\NetCore.props = build\NetCore.props
build\NetFX.props = build\NetFX.props build\NetFX.props = build\NetFX.props
build\ReactiveUI.props = build\ReactiveUI.props build\ReactiveUI.props = build\ReactiveUI.props
build\ReferenceCoreLibraries.props = build\ReferenceCoreLibraries.props
build\Rx.props = build\Rx.props build\Rx.props = build\Rx.props
build\SampleApp.props = build\SampleApp.props build\SampleApp.props = build\SampleApp.props
build\SharedVersion.props = build\SharedVersion.props
build\SharpDX.props = build\SharpDX.props build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.props = build\SkiaSharp.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\System.Memory.props = build\System.Memory.props
build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
build\XUnit.props = build\XUnit.props build\XUnit.props = build\XUnit.props
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
build\UnitTests.NetCore.targets = build\UnitTests.NetCore.targets build\UnitTests.NetCore.targets = build\UnitTests.NetCore.targets
build\BuildTargets.targets = build\BuildTargets.targets
build\LegacyProject.targets = build\LegacyProject.targets
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}"
@ -207,6 +217,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeEmbedSample", "sample
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless", "src\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}"
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 Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -1826,6 +1842,54 @@ Global
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhone.Build.0 = Release|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhone.Build.0 = Release|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhone.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhone.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|Any CPU.Build.0 = Release|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhone.ActiveCfg = Release|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhone.Build.0 = Release|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhone.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhone.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|Any CPU.Build.0 = Release|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhone.ActiveCfg = Release|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhone.Build.0 = Release|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU {4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
@ -1946,6 +2010,30 @@ Global
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.Build.0 = Release|Any CPU {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.ActiveCfg = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -2004,6 +2092,7 @@ Global
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {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} {3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

1
Avalonia.v3.ncrunchsolution

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

1
Directory.Build.props

@ -1,5 +1,6 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)build-intermediate/nuget</PackageOutputPath> <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> </PropertyGroup>
</Project> </Project>

4
build/HarfBuzzSharp.props

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

4
build/SkiaSharp.props

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

2
dirs.proj

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

13
native/Avalonia.Native/inc/avalonia-native.h

@ -205,6 +205,15 @@ enum AvnMenuItemToggleType
Radio Radio
}; };
enum AvnExtendClientAreaChromeHints
{
AvnNoChrome = 0,
AvnSystemChrome = 0x01,
AvnPreferSystemChrome = 0x02,
AvnOSXThickTitleBar = 0x08,
AvnDefaultChrome = AvnSystemChrome,
};
AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown
{ {
public: public:
@ -279,6 +288,10 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
virtual HRESULT SetWindowState(AvnWindowState state) = 0; virtual HRESULT SetWindowState(AvnWindowState state) = 0;
virtual HRESULT GetWindowState(AvnWindowState*ret) = 0; virtual HRESULT GetWindowState(AvnWindowState*ret) = 0;
virtual HRESULT TakeFocusFromChildren() = 0; virtual HRESULT TakeFocusFromChildren() = 0;
virtual HRESULT SetExtendClientArea (bool enable) = 0;
virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) = 0;
virtual HRESULT GetExtendTitleBarHeight (double*ret) = 0;
virtual HRESULT SetExtendTitleBarHeight (double value) = 0;
}; };
AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown

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

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

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

@ -3,9 +3,6 @@
class WindowBaseImpl; class WindowBaseImpl;
@interface AutoFitContentVisualEffectView : NSVisualEffectView
@end
@interface AvnView : NSView<NSTextInputClient, NSDraggingDestination> @interface AvnView : NSView<NSTextInputClient, NSDraggingDestination>
-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(NSEvent* _Nonnull) lastMouseDownEvent; -(NSEvent* _Nonnull) lastMouseDownEvent;
@ -15,6 +12,14 @@ class WindowBaseImpl;
-(AvnPixelSize) getPixelSize; -(AvnPixelSize) getPixelSize;
@end @end
@interface AutoFitContentView : NSView
-(AutoFitContentView* _Nonnull) initWithContent: (NSView* _Nonnull) content;
-(void) ShowTitleBar: (bool) show;
-(void) SetTitleBarHeightHint: (double) height;
-(void) SetContent: (NSView* _Nonnull) content;
-(void) ShowBlur: (bool) show;
@end
@interface AvnWindow : NSWindow <NSWindowDelegate> @interface AvnWindow : NSWindow <NSWindowDelegate>
+(void) closeAll; +(void) closeAll;
-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent; -(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
@ -27,6 +32,8 @@ class WindowBaseImpl;
-(void) showWindowMenuWithAppMenu; -(void) showWindowMenuWithAppMenu;
-(void) applyMenu:(NSMenu* _Nullable)menu; -(void) applyMenu:(NSMenu* _Nullable)menu;
-(double) getScaling; -(double) getScaling;
-(double) getExtendedTitleBarHeight;
-(void) setIsExtended:(bool)value;
@end @end
struct INSWindowHolder struct INSWindowHolder

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

@ -6,8 +6,6 @@
#include <OpenGL/gl.h> #include <OpenGL/gl.h>
#include "rendertarget.h" #include "rendertarget.h"
class WindowBaseImpl : public virtual ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder class WindowBaseImpl : public virtual ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder
{ {
private: private:
@ -20,7 +18,7 @@ public:
View = NULL; View = NULL;
Window = NULL; Window = NULL;
} }
NSVisualEffectView* VisualEffect; AutoFitContentView* StandardContainer;
AvnView* View; AvnView* View;
AvnWindow* Window; AvnWindow* Window;
ComPtr<IAvnWindowBaseEvents> BaseEvents; ComPtr<IAvnWindowBaseEvents> BaseEvents;
@ -39,6 +37,7 @@ public:
_glContext = gl; _glContext = gl;
renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl]; renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl];
View = [[AvnView alloc] initWithParent:this]; View = [[AvnView alloc] initWithParent:this];
StandardContainer = [[AutoFitContentView new] initWithContent:View];
Window = [[AvnWindow alloc] initWithParent:this]; Window = [[AvnWindow alloc] initWithParent:this];
@ -49,12 +48,8 @@ public:
[Window setStyleMask:NSWindowStyleMaskBorderless]; [Window setStyleMask:NSWindowStyleMaskBorderless];
[Window setBackingType:NSBackingStoreBuffered]; [Window setBackingType:NSBackingStoreBuffered];
VisualEffect = [AutoFitContentVisualEffectView new]; [Window setOpaque:false];
[VisualEffect setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; [Window setContentView: StandardContainer];
[VisualEffect setMaterial:NSVisualEffectMaterialLight];
[VisualEffect setAutoresizesSubviews:true];
[Window setContentView: View];
} }
virtual HRESULT ObtainNSWindowHandle(void** ret) override virtual HRESULT ObtainNSWindowHandle(void** ret) override
@ -410,12 +405,7 @@ public:
virtual HRESULT SetBlurEnabled (bool enable) override virtual HRESULT SetBlurEnabled (bool enable) override
{ {
[Window setContentView: enable ? VisualEffect : View]; [StandardContainer ShowBlur:enable];
if(enable)
{
[VisualEffect addSubview:View];
}
return S_OK; return S_OK;
} }
@ -492,6 +482,8 @@ private:
bool _inSetWindowState; bool _inSetWindowState;
NSRect _preZoomSize; NSRect _preZoomSize;
bool _transitioningWindowState; bool _transitioningWindowState;
bool _isClientAreaExtended;
AvnExtendClientAreaChromeHints _extendClientHints;
FORWARD_IUNKNOWN() FORWARD_IUNKNOWN()
BEGIN_INTERFACE_MAP() BEGIN_INTERFACE_MAP()
@ -505,6 +497,8 @@ private:
ComPtr<IAvnWindowEvents> WindowEvents; ComPtr<IAvnWindowEvents> WindowEvents;
WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
{ {
_isClientAreaExtended = false;
_extendClientHints = AvnDefaultChrome;
_fullScreenActive = false; _fullScreenActive = false;
_canResize = true; _canResize = true;
_decorations = SystemDecorationsFull; _decorations = SystemDecorationsFull;
@ -523,8 +517,20 @@ private:
if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) {
NSView *titlebarView = [subview subviews][0]; NSView *titlebarView = [subview subviews][0];
for (id button in titlebarView.subviews) { for (id button in titlebarView.subviews) {
if ([button isKindOfClass:[NSButton class]]) { if ([button isKindOfClass:[NSButton class]])
[button setHidden: (_decorations != SystemDecorationsFull)]; {
if(_isClientAreaExtended)
{
auto wantsChrome = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
[button setHidden: !wantsChrome];
}
else
{
[button setHidden: (_decorations != SystemDecorationsFull)];
}
[button setWantsLayer:true];
} }
} }
} }
@ -600,6 +606,35 @@ private:
if(_lastWindowState != state) if(_lastWindowState != state)
{ {
if(_isClientAreaExtended)
{
if(_lastWindowState == FullScreen)
{
// we exited fs.
if(_extendClientHints & AvnOSXThickTitleBar)
{
Window.toolbar = [NSToolbar new];
Window.toolbar.showsBaselineSeparator = false;
}
[Window setTitlebarAppearsTransparent:true];
[StandardContainer setFrameSize: StandardContainer.frame.size];
}
else if(state == FullScreen)
{
// we entered fs.
if(_extendClientHints & AvnOSXThickTitleBar)
{
Window.toolbar = nullptr;
}
[Window setTitlebarAppearsTransparent:false];
[StandardContainer setFrameSize: StandardContainer.frame.size];
}
}
_lastWindowState = state; _lastWindowState = state;
WindowEvents->WindowStateChanged(state); WindowEvents->WindowStateChanged(state);
} }
@ -656,8 +691,6 @@ private:
return S_OK; return S_OK;
} }
auto currentFrame = [Window frame];
UpdateStyle(); UpdateStyle();
HideOrShowTrafficLights(); HideOrShowTrafficLights();
@ -790,6 +823,81 @@ private:
return S_OK; return S_OK;
if([Window isKeyWindow]) if([Window isKeyWindow])
[Window makeFirstResponder: View]; [Window makeFirstResponder: View];
return S_OK;
}
virtual HRESULT SetExtendClientArea (bool enable) override
{
_isClientAreaExtended = enable;
if(enable)
{
Window.titleVisibility = NSWindowTitleHidden;
[Window setTitlebarAppearsTransparent:true];
auto wantsTitleBar = (_extendClientHints & AvnSystemChrome) || (_extendClientHints & AvnPreferSystemChrome);
if (wantsTitleBar)
{
[StandardContainer ShowTitleBar:true];
}
else
{
[StandardContainer ShowTitleBar:false];
}
if(_extendClientHints & AvnOSXThickTitleBar)
{
Window.toolbar = [NSToolbar new];
Window.toolbar.showsBaselineSeparator = false;
}
else
{
Window.toolbar = nullptr;
}
}
else
{
Window.titleVisibility = NSWindowTitleVisible;
Window.toolbar = nullptr;
[Window setTitlebarAppearsTransparent:false];
View.layer.zPosition = 0;
}
[Window setIsExtended:enable];
HideOrShowTrafficLights();
UpdateStyle();
return S_OK;
}
virtual HRESULT SetExtendClientAreaHints (AvnExtendClientAreaChromeHints hints) override
{
_extendClientHints = hints;
SetExtendClientArea(_isClientAreaExtended);
return S_OK;
}
virtual HRESULT GetExtendTitleBarHeight (double*ret) override
{
if(ret == nullptr)
{
return E_POINTER;
}
*ret = [Window getExtendedTitleBarHeight];
return S_OK;
}
virtual HRESULT SetExtendTitleBarHeight (double value) override
{
[StandardContainer SetTitleBarHeightHint:value];
return S_OK; return S_OK;
} }
@ -802,8 +910,9 @@ private:
[Window setTitlebarAppearsTransparent:NO]; [Window setTitlebarAppearsTransparent:NO];
[Window setTitle:_lastTitle]; [Window setTitle:_lastTitle];
[Window setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable]; Window.styleMask = Window.styleMask | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable;
Window.styleMask = Window.styleMask & ~NSWindowStyleMaskFullSizeContentView;
[Window toggleFullScreen:nullptr]; [Window toggleFullScreen:nullptr];
} }
@ -951,19 +1060,120 @@ protected:
{ {
s |= NSWindowStyleMaskMiniaturizable; s |= NSWindowStyleMaskMiniaturizable;
} }
if(_isClientAreaExtended)
{
s |= NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskTexturedBackground;
}
return s; return s;
} }
}; };
NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
@implementation AutoFitContentVisualEffectView @implementation AutoFitContentView
{
NSVisualEffectView* _titleBarMaterial;
NSBox* _titleBarUnderline;
NSView* _content;
NSVisualEffectView* _blurBehind;
double _titleBarHeightHint;
bool _settingSize;
}
-(AutoFitContentView* _Nonnull) initWithContent:(NSView *)content
{
_titleBarHeightHint = -1;
_content = content;
_settingSize = false;
[self setAutoresizesSubviews:true];
[self setWantsLayer:true];
_titleBarMaterial = [NSVisualEffectView new];
[_titleBarMaterial setBlendingMode:NSVisualEffectBlendingModeWithinWindow];
[_titleBarMaterial setMaterial:NSVisualEffectMaterialTitlebar];
[_titleBarMaterial setWantsLayer:true];
_titleBarMaterial.hidden = true;
_titleBarUnderline = [NSBox new];
_titleBarUnderline.boxType = NSBoxSeparator;
_titleBarUnderline.fillColor = [NSColor underPageBackgroundColor];
_titleBarUnderline.hidden = true;
[self addSubview:_titleBarMaterial];
[self addSubview:_titleBarUnderline];
_blurBehind = [NSVisualEffectView new];
[_blurBehind setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
[_blurBehind setMaterial:NSVisualEffectMaterialLight];
[_blurBehind setWantsLayer:true];
_blurBehind.hidden = true;
[self addSubview:_blurBehind];
[self addSubview:_content];
[self setWantsLayer:true];
return self;
}
-(void) ShowBlur:(bool)show
{
_blurBehind.hidden = !show;
}
-(void) ShowTitleBar: (bool) show
{
_titleBarMaterial.hidden = !show;
_titleBarUnderline.hidden = !show;
}
-(void) SetTitleBarHeightHint: (double) height
{
_titleBarHeightHint = height;
[self setFrameSize:self.frame.size];
}
-(void)setFrameSize:(NSSize)newSize -(void)setFrameSize:(NSSize)newSize
{ {
[super setFrameSize:newSize]; if(_settingSize)
if([[self subviews] count] == 0) {
return; return;
[[self subviews][0] setFrameSize: newSize]; }
_settingSize = true;
[super setFrameSize:newSize];
[_blurBehind setFrameSize:newSize];
[_content setFrameSize:newSize];
auto window = objc_cast<AvnWindow>([self window]);
// TODO get actual titlebar size
double height = _titleBarHeightHint == -1 ? [window getExtendedTitleBarHeight] : _titleBarHeightHint;
NSRect tbar;
tbar.origin.x = 0;
tbar.origin.y = newSize.height - height;
tbar.size.width = newSize.width;
tbar.size.height = height;
[_titleBarMaterial setFrame:tbar];
tbar.size.height = height < 1 ? 0 : 1;
[_titleBarUnderline setFrame:tbar];
_settingSize = false;
}
-(void) SetContent: (NSView* _Nonnull) content
{
if(content != nullptr)
{
[content removeFromSuperview];
[self addSubview:content];
_content = content;
}
} }
@end @end
@ -1081,10 +1291,15 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_parent->UpdateCursor(); _parent->UpdateCursor();
auto fsize = [self convertSizeToBacking: [self frame].size]; auto fsize = [self convertSizeToBacking: [self frame].size];
_lastPixelSize.Width = (int)fsize.width;
_lastPixelSize.Height = (int)fsize.height; if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height)
[self updateRenderTarget]; {
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}); _lastPixelSize.Width = (int)fsize.width;
_lastPixelSize.Height = (int)fsize.height;
[self updateRenderTarget];
_parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
}
} }
- (void)updateLayer - (void)updateLayer
@ -1523,15 +1738,43 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
bool _canBecomeKeyAndMain; bool _canBecomeKeyAndMain;
bool _closed; bool _closed;
bool _isEnabled; bool _isEnabled;
bool _isExtended;
AvnMenu* _menu; AvnMenu* _menu;
double _lastScaling; double _lastScaling;
} }
-(void) setIsExtended:(bool)value;
{
_isExtended = value;
}
-(double) getScaling -(double) getScaling
{ {
return _lastScaling; return _lastScaling;
} }
-(double) getExtendedTitleBarHeight
{
if(_isExtended)
{
for (id subview in self.contentView.superview.subviews)
{
if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")])
{
NSView *titlebarView = [subview subviews][0];
return (double)titlebarView.frame.size.height;
}
}
return -1;
}
else
{
return 0;
}
}
+(void)closeAll +(void)closeAll
{ {
NSArray<NSWindow*>* windows = [NSArray arrayWithArray:[NSApp windows]]; NSArray<NSWindow*>* windows = [NSArray arrayWithArray:[NSApp windows]];
@ -1650,6 +1893,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
[self setOpaque:NO]; [self setOpaque:NO];
[self setBackgroundColor: [NSColor clearColor]]; [self setBackgroundColor: [NSColor clearColor]];
[self invalidateShadow]; [self invalidateShadow];
_isExtended = false;
return self; return self;
} }

2
nukebuild/Build.cs

@ -268,6 +268,8 @@ partial class Build : NukeBuild
.DependsOn(CreateIntermediateNugetPackages) .DependsOn(CreateIntermediateNugetPackages)
.Executes(() => .Executes(() =>
{ {
BuildTasksPatcher.PatchBuildTasksInPackage(Parameters.NugetIntermediateRoot / "Avalonia.Build.Tasks." +
Parameters.Version + ".nupkg");
var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config"); var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config");
EnsureCleanDirectory(Parameters.NugetRoot); EnsureCleanDirectory(Parameters.NugetRoot);
if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config, if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config,

76
nukebuild/BuildTasksPatcher.cs

@ -0,0 +1,76 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
using ILRepacking;
using Mono.Cecil;
public class BuildTasksPatcher
{
public static void PatchBuildTasksInPackage(string packagePath)
{
using (var archive = new ZipArchive(File.Open(packagePath, FileMode.Open, FileAccess.ReadWrite),
ZipArchiveMode.Update))
{
foreach (var entry in archive.Entries.ToList())
{
if (entry.Name == "Avalonia.Build.Tasks.dll")
{
var temp = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".dll");
var output = temp + ".output";
var patched = new MemoryStream();
try
{
entry.ExtractToFile(temp, true);
var repack = new ILRepacking.ILRepack(new RepackOptions()
{
Internalize = true,
InputAssemblies = new[]
{
temp, typeof(Mono.Cecil.AssemblyDefinition).Assembly.GetModules()[0]
.FullyQualifiedName
},
SearchDirectories = new string[0],
OutputFile = output
});
repack.Repack();
// 'hurr-durr assembly with the same name is already loaded' prevention
using (var asm = AssemblyDefinition.ReadAssembly(output,
new ReaderParameters { ReadWrite = true, InMemory = true, }))
{
asm.Name = new AssemblyNameDefinition(
"Avalonia.Build.Tasks."
+ Guid.NewGuid().ToString().Replace("-", ""),
new Version(0, 0, 0));
asm.Write(patched);
patched.Position = 0;
}
}
finally
{
try
{
if (File.Exists(temp))
File.Delete(temp);
if (File.Exists(output))
File.Delete(output);
}
catch
{
//ignore
}
}
var fn = entry.FullName;
entry.Delete();
var newEntry = archive.CreateEntry(fn, CompressionLevel.Optimal);
using (var s = newEntry.Open())
patched.CopyTo(s);
}
}
}
}
}

3
nukebuild/_build.csproj

@ -14,6 +14,9 @@
<PackageReference Include="xunit.runner.console" Version="2.3.1" /> <PackageReference Include="xunit.runner.console" Version="2.3.1" />
<PackageReference Include="JetBrains.dotMemoryUnit" Version="3.0.20171219.105559" /> <PackageReference Include="JetBrains.dotMemoryUnit" Version="3.0.20171219.105559" />
<PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " /> <PackageReference Include="vswhere" Version="2.6.7" Condition=" '$(OS)' == 'Windows_NT' " />
<PackageReference Include="ILRepack.NETStandard" Version="2.0.4" />
<!-- Keep in sync with Avalonia.Build.Tasks -->
<PackageReference Include="Avalonia.Unofficial.Cecil" Version="20190417.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

2
readme.md

@ -16,7 +16,7 @@ To see the status of some of our features, please see our [Roadmap](https://gith
## 🚀 Getting Started ## 🚀 Getting Started
The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starer guide see our [documentation](http://avaloniaui.net/docs/quickstart/create-new-project). The Avalonia [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) contains project and control templates that will help you get started, or you can use the .NET Core CLI. For a starter guide see our [documentation](http://avaloniaui.net/docs/quickstart/create-new-project).
Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/ Avalonia is delivered via <b>NuGet</b> package manager. You can find the packages here: https://www.nuget.org/packages/Avalonia/

5
samples/BindingDemo/MainWindow.xaml

@ -5,7 +5,8 @@
xmlns:local="clr-namespace:BindingDemo" xmlns:local="clr-namespace:BindingDemo"
Title="AvaloniaUI Bindings Test" Title="AvaloniaUI Bindings Test"
Width="800" Width="800"
Height="600"> Height="600"
x:DataType="vm:MainWindowViewModel">
<Window.Styles> <Window.Styles>
<Style Selector="TextBlock.h1"> <Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="18"/> <Setter Property="FontSize" Value="18"/>
@ -60,7 +61,7 @@
<TextBlock FontSize="16" Text="Scheduler"/> <TextBlock FontSize="16" Text="Scheduler"/>
<TextBox Watermark="Background Thread" Text="{Binding CurrentTime, Mode=OneWay}"/> <TextBox Watermark="Background Thread" Text="{Binding CurrentTime, Mode=OneWay}"/>
<TextBlock FontSize="16" Text="Stream Operator"/> <TextBlock FontSize="16" Text="Stream Operator"/>
<TextBox Watermark="StreamOperator" Text="{Binding CurrentTimeObservable^, Mode=OneWay}"/> <TextBox Watermark="StreamOperator" Text="{CompiledBinding CurrentTimeObservable^, Mode=OneWay}"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

4
samples/BindingDemo/ViewModels/MainWindowViewModel.cs

@ -53,7 +53,7 @@ namespace BindingDemo.ViewModels
}); });
CurrentTimeObservable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)) CurrentTimeObservable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1))
.Select(x => DateTimeOffset.Now.ToString()); .Select(x => DateTimeOffset.Now);
} }
public ObservableCollection<TestItem> Items { get; } public ObservableCollection<TestItem> Items { get; }
@ -90,7 +90,7 @@ namespace BindingDemo.ViewModels
private set { this.RaiseAndSetIfChanged(ref _currentTime, value); } private set { this.RaiseAndSetIfChanged(ref _currentTime, value); }
} }
public IObservable<string> CurrentTimeObservable { get; } public IObservable<DateTimeOffset> CurrentTimeObservable { get; }
public ReactiveCommand<object, Unit> StringValueCommand { get; } public ReactiveCommand<object, Unit> StringValueCommand { get; }
public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel(); public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel();

3
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

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

55
samples/ControlCatalog.NetCore/Program.cs

@ -3,9 +3,16 @@ using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Dialogs; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Headless;
using Avalonia.LogicalTree;
using Avalonia.Skia;
using Avalonia.ReactiveUI; using Avalonia.ReactiveUI;
using Avalonia.Threading;
using Avalonia.Dialogs;
namespace ControlCatalog.NetCore namespace ControlCatalog.NetCore
{ {
@ -40,6 +47,52 @@ namespace ControlCatalog.NetCore
SilenceConsole(); SilenceConsole();
return builder.StartLinuxFbDev(args, scaling: GetScaling()); return builder.StartLinuxFbDev(args, scaling: GetScaling());
} }
else if (args.Contains("--vnc"))
{
return builder.StartWithHeadlessVncPlatform(null, 5901, args, ShutdownMode.OnMainWindowClose);
}
else if (args.Contains("--full-headless"))
{
return builder
.UseHeadless(true)
.AfterSetup(_ =>
{
DispatcherTimer.RunOnce(async () =>
{
var window = ((IClassicDesktopStyleApplicationLifetime)Application.Current.ApplicationLifetime)
.MainWindow;
var tc = window.GetLogicalDescendants().OfType<TabControl>().First();
foreach (var page in tc.Items.Cast<TabItem>().ToList())
{
// Skip DatePicker because of some layout bug in grid
if (page.Header.ToString() == "DatePicker")
continue;
Console.WriteLine("Selecting " + page.Header);
tc.SelectedItem = page;
await Task.Delay(500);
}
Console.WriteLine("Selecting the first page");
tc.SelectedItem = tc.Items.OfType<object>().First();
await Task.Delay(500);
Console.WriteLine("Clicked through all pages, triggering GC");
for (var c = 0; c < 3; c++)
{
GC.Collect(2, GCCollectionMode.Forced);
await Task.Delay(500);
}
void FormatMem(string metric, long bytes)
{
Console.WriteLine(metric + ": " + bytes / 1024 / 1024 + "MB");
}
FormatMem("GC allocated bytes", GC.GetTotalMemory(true));
FormatMem("WorkingSet64", Process.GetCurrentProcess().WorkingSet64);
}, TimeSpan.FromSeconds(1));
})
.StartWithClassicDesktopLifetime(args);
}
else if (args.Contains("--drm")) else if (args.Contains("--drm"))
{ {
SilenceConsole(); SilenceConsole();

38
samples/ControlCatalog/App.xaml.cs

@ -11,37 +11,37 @@ namespace ControlCatalog
{ {
public static Styles FluentDark = new Styles public static Styles FluentDark = new Styles
{ {
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
{ {
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default") Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentDark.xaml")
}, },
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) };
public static Styles FluentLight = new Styles
{
new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
{ {
Source = new Uri("resm:Avalonia.Themes.Fluent.Accents.FluentDark.xaml?assembly=Avalonia.Themes.Fluent") Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentLight.xaml")
}, },
}; };
public static Styles FluentLight = new Styles public static Styles DefaultLight = new Styles
{ {
new StyleInclude(new Uri("resm:Styles?assembly=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")) new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{ {
Source = new Uri("resm:Avalonia.Themes.Fluent.Accents.FluentLight.xaml?assembly=Avalonia.Themes.Fluent") Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
}, },
};
public static Styles DefaultLight = new Styles
{
new StyleInclude(new Uri("resm:Styles?assembly=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.Default/Accents/BaseLight.xaml")
}, },
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) 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")
}, },
}; };
@ -49,11 +49,19 @@ namespace ControlCatalog
{ {
new StyleInclude(new Uri("resm:Styles?assembly=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")) 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")
}, },
}; };

6
samples/ControlCatalog/MainView.xaml

@ -2,7 +2,6 @@
xmlns:pages="clr-namespace:ControlCatalog.Pages" xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainView" x:Class="ControlCatalog.MainView"
Background="Transparent"
Foreground="{DynamicResource ThemeForegroundBrush}" Foreground="{DynamicResource ThemeForegroundBrush}"
FontSize="{DynamicResource FontSizeNormal}"> FontSize="{DynamicResource FontSizeNormal}">
<Grid> <Grid>
@ -14,6 +13,7 @@
</Style> </Style>
</Grid.Styles> </Grid.Styles>
<TabControl Classes="sidebar" Name="Sidebar"> <TabControl Classes="sidebar" Name="Sidebar">
<TabItem Header="Acrylic"><pages:AcrylicPage/></TabItem>
<TabItem Header="AutoCompleteBox"><pages:AutoCompleteBoxPage/></TabItem> <TabItem Header="AutoCompleteBox"><pages:AutoCompleteBoxPage/></TabItem>
<TabItem Header="Border"><pages:BorderPage/></TabItem> <TabItem Header="Border"><pages:BorderPage/></TabItem>
<TabItem Header="Button"><pages:ButtonPage/></TabItem> <TabItem Header="Button"><pages:ButtonPage/></TabItem>
@ -55,6 +55,7 @@
<TabItem Header="Pointers (Touch)"><pages:PointersPage/></TabItem> <TabItem Header="Pointers (Touch)"><pages:PointersPage/></TabItem>
<TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem> <TabItem Header="ProgressBar"><pages:ProgressBarPage/></TabItem>
<TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem> <TabItem Header="RadioButton"><pages:RadioButtonPage/></TabItem>
<TabItem Header="RelativePanel"><pages:RelativePanelPage/></TabItem>
<TabItem Header="ScrollViewer"><pages:ScrollViewerPage/></TabItem> <TabItem Header="ScrollViewer"><pages:ScrollViewerPage/></TabItem>
<TabItem Header="Slider"><pages:SliderPage/></TabItem> <TabItem Header="Slider"><pages:SliderPage/></TabItem>
<TabItem Header="SplitView"><pages:SplitViewPage/></TabItem> <TabItem Header="SplitView"><pages:SplitViewPage/></TabItem>
@ -66,6 +67,7 @@
<TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem> <TabItem Header="ToolTip"><pages:ToolTipPage/></TabItem>
<TabItem Header="TreeView"><pages:TreeViewPage/></TabItem> <TabItem Header="TreeView"><pages:TreeViewPage/></TabItem>
<TabItem Header="Viewbox"><pages:ViewboxPage/></TabItem> <TabItem Header="Viewbox"><pages:ViewboxPage/></TabItem>
<TabItem Header="Window Customizations"><pages:WindowCustomizationsPage/></TabItem>
<TabControl.Tag> <TabControl.Tag>
<StackPanel Width="115" Spacing="4" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="8"> <StackPanel Width="115" Spacing="4" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="8">
<ComboBox x:Name="Decorations" SelectedIndex="0"> <ComboBox x:Name="Decorations" SelectedIndex="0">
@ -79,7 +81,7 @@
<ComboBoxItem>Simple - Light</ComboBoxItem> <ComboBoxItem>Simple - Light</ComboBoxItem>
<ComboBoxItem>Simple - Dark</ComboBoxItem> <ComboBoxItem>Simple - Dark</ComboBoxItem>
</ComboBox> </ComboBox>
<ComboBox x:Name="TransparencyLevels" SelectedIndex="0"> <ComboBox x:Name="TransparencyLevels" SelectedIndex="{Binding TransparencyLevel}">
<ComboBoxItem>None</ComboBoxItem> <ComboBoxItem>None</ComboBoxItem>
<ComboBoxItem>Transparent</ComboBoxItem> <ComboBoxItem>Transparent</ComboBoxItem>
<ComboBoxItem>Blur</ComboBoxItem> <ComboBoxItem>Blur</ComboBoxItem>

7
samples/ControlCatalog/MainView.xaml.cs

@ -58,13 +58,6 @@ namespace ControlCatalog
if (VisualRoot is Window window) if (VisualRoot is Window window)
window.SystemDecorations = (SystemDecorations)decorations.SelectedIndex; window.SystemDecorations = (SystemDecorations)decorations.SelectedIndex;
}; };
var transparencyLevels = this.Find<ComboBox>("TransparencyLevels");
transparencyLevels.SelectionChanged += (sender, e) =>
{
if (VisualRoot is Window window)
window.TransparencyLevelHint = (WindowTransparencyLevel)transparencyLevels.SelectedIndex;
};
} }
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)

41
samples/ControlCatalog/MainWindow.xaml

@ -7,7 +7,12 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ControlCatalog.ViewModels" xmlns:vm="clr-namespace:ControlCatalog.ViewModels"
xmlns:v="clr-namespace:ControlCatalog.Views" xmlns:v="clr-namespace:ControlCatalog.Views"
x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}" Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}"> ExtendClientAreaToDecorationsHint="{Binding ExtendClientAreaEnabled}"
ExtendClientAreaChromeHints="{Binding ChromeHints}"
ExtendClientAreaTitleBarHeightHint="{Binding TitleBarHeight}"
TransparencyLevelHint="{Binding TransparencyLevel}"
x:Name="MainWindow"
x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}">
<NativeMenu.Menu> <NativeMenu.Menu>
<NativeMenu> <NativeMenu>
<NativeMenuItem Header="File"> <NativeMenuItem Header="File">
@ -56,20 +61,30 @@
</NativeMenu> </NativeMenu>
</NativeMenu.Menu> </NativeMenu.Menu>
<Window.DataTemplates> <Window.DataTemplates>
<DataTemplate DataType="vm:NotificationViewModel"> <DataTemplate DataType="vm:NotificationViewModel">
<v:CustomNotificationView /> <v:CustomNotificationView />
</DataTemplate> </DataTemplate>
</Window.DataTemplates> </Window.DataTemplates>
<DockPanel LastChildFill="True"> <Panel>
<Menu Name="MainMenu" DockPanel.Dock="Top"> <Panel Margin="{Binding #MainWindow.OffScreenMargin}">
<MenuItem Header="File"> <DockPanel LastChildFill="True" Margin="{Binding #MainWindow.WindowDecorationMargin}">
<MenuItem Header="Exit" Command="{Binding ExitCommand}" /> <Menu Name="MainMenu" DockPanel.Dock="Top">
</MenuItem> <MenuItem Header="File">
<MenuItem Header="Help"> <MenuItem Header="Exit" Command="{Binding ExitCommand}" />
<MenuItem Header="About" Command="{Binding AboutCommand}" /> </MenuItem>
</MenuItem> <MenuItem Header="Help">
</Menu> <MenuItem Header="About" Command="{Binding AboutCommand}" />
<local:MainView /> </MenuItem>
</DockPanel> </Menu>
<local:MainView />
</DockPanel>
</Panel>
<Border IsVisible="{Binding ExtendClientAreaEnabled}" BorderThickness="1 1 1 0" CornerRadius="4 4 0 0" BorderBrush="#55000000" Height="22" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="250 8 0 0">
<Border.Background>
<SolidColorBrush Color="White" Opacity="0.7" />
</Border.Background>
<TextBlock Margin="5 5 5 0" Text="Content In Title Bar" />
</Border>
</Panel>
</Window> </Window>

139
samples/ControlCatalog/Pages/AcrylicPage.xaml

@ -0,0 +1,139 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.AcrylicPage">
<Border Padding="20" HorizontalAlignment="Center">
<StackPanel Spacing="20">
<ExperimentalAcrylicBorder Width="660" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="White"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
<StackPanel Spacing="5" Margin="40 10">
<StackPanel Orientation="Horizontal">
<TextBlock Text="TintOpacity" Foreground="Black" />
<Slider Name="TintOpacitySlider" Minimum="0" Maximum="1" Value="0.9" Width="400" />
<TextBlock Text="{Binding #TintOpacitySlider.Value}" Foreground="Black" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MaterialOpacity" Foreground="Black" />
<Slider Name="MaterialOpacitySlider" Minimum="0" Maximum="1" Value="0.8" Width="400" />
<TextBlock Text="{Binding #MaterialOpacitySlider.Value}" Foreground="Black" />
</StackPanel>
</StackPanel>
</ExperimentalAcrylicBorder>
<StackPanel Orientation="Horizontal" Spacing="20">
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="#FF0000"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="#00FF00"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="#000000"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="20">
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="#0000FF"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="#FFFF00"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="#000000"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="20">
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="White"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="#3c3c3c"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<ExperimentalAcrylicBorder Height="200" Width="200" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="White"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
</StackPanel>
<ExperimentalAcrylicBorder Height="200" Width="660" CornerRadius="5">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
TintColor="Red"
TintOpacity="{Binding #TintOpacitySlider.Value}"
MaterialOpacity="{Binding #MaterialOpacitySlider.Value}"
BackgroundSource="Digger" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
</StackPanel>
</Border>
</UserControl>

19
samples/ControlCatalog/Pages/AcrylicPage.xaml.cs

@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class AcrylicPage : UserControl
{
public AcrylicPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

12
samples/ControlCatalog/Pages/BorderPage.xaml

@ -9,27 +9,27 @@
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16"> <Border BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="2" Padding="16">
<TextBlock>Border</TextBlock> <TextBlock>Border</TextBlock>
</Border> </Border>
<Border Background="{DynamicResource ThemeAccentBrush2}" <Border Background="{DynamicResource SystemAccentColorDark1}"
BorderBrush="{DynamicResource ThemeAccentBrush}" BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="4" BorderThickness="4"
Padding="16"> Padding="16">
<TextBlock>Border and Background</TextBlock> <TextBlock>Border and Background</TextBlock>
</Border> </Border>
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" <Border BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="4" BorderThickness="4"
CornerRadius="8" CornerRadius="8"
Padding="16"> Padding="16">
<TextBlock>Rounded Corners</TextBlock> <TextBlock>Rounded Corners</TextBlock>
</Border> </Border>
<Border Background="{DynamicResource ThemeAccentBrush2}" <Border Background="{DynamicResource SystemAccentColor}"
CornerRadius="8" CornerRadius="8"
Padding="16"> Padding="16">
<TextBlock>Rounded Corners</TextBlock> <TextBlock>Rounded Corners</TextBlock>
</Border> </Border>
<Border BorderBrush="{DynamicResource ThemeAccentBrush2}" Width="100" Height="100" <Border Width="100" Height="100"
BorderThickness="0" BorderThickness="0"
Background="White" Background="White"
CornerRadius="100" ClipToBounds="True"> CornerRadius="100" ClipToBounds="True">

14
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -9,10 +9,10 @@
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<StackPanel Orientation="Vertical" Spacing="8" Width="150"> <StackPanel Orientation="Vertical" Spacing="8" Width="200">
<Button>Button</Button> <Button>Standard XAML Button</Button>
<Button Foreground="White">Foreground</Button> <Button Foreground="White">Foreground</Button>
<Button Background="{DynamicResource ThemeAccentBrush}">Background</Button> <Button Background="{DynamicResource SystemAccentColor}">Background</Button>
<Button IsEnabled="False">Disabled</Button> <Button IsEnabled="False">Disabled</Button>
<Button Content="Re-themed"> <Button Content="Re-themed">
<Button.Styles> <Button.Styles>
@ -32,10 +32,10 @@
<StackPanel Orientation="Vertical" Spacing="8" Width="150"> <StackPanel Orientation="Vertical" Spacing="8" Width="150">
<Button BorderThickness="0">No Border</Button> <Button BorderThickness="0">No Border</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}">Border Color</Button> <Button BorderBrush="{DynamicResource SystemAccentColor}">Border Color</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="4">Thick Border</Button> <Button BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="4">Thick Border</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="4" IsEnabled="False">Disabled</Button> <Button BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="4" IsEnabled="False">Disabled</Button>
<Button BorderBrush="{DynamicResource ThemeAccentBrush}" KeyboardNavigation.IsTabStop="False">IsTabStop=False</Button> <Button BorderBrush="{DynamicResource SystemAccentColor}" KeyboardNavigation.IsTabStop="False">IsTabStop=False</Button>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>

2
samples/ControlCatalog/Pages/ComboBoxPage.xaml

@ -16,7 +16,7 @@
<ComboBox SelectedIndex="0"> <ComboBox SelectedIndex="0">
<ComboBoxItem> <ComboBoxItem>
<Panel> <Panel>
<Rectangle Fill="{DynamicResource ThemeAccentBrush}"/> <Rectangle Fill="{DynamicResource SystemAccentColor}"/>
<TextBlock Margin="8">Control Items</TextBlock> <TextBlock Margin="8">Control Items</TextBlock>
</Panel> </Panel>
</ComboBoxItem> </ComboBoxItem>

4
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -9,7 +9,7 @@
Margin="0,16,0,0" Margin="0,16,0,0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<Border Background="{DynamicResource ThemeAccentBrush}" <Border Background="{DynamicResource SystemAccentColor}"
Margin="16" Margin="16"
Padding="48,48,48,48"> Padding="48,48,48,48">
<Border.ContextMenu> <Border.ContextMenu>
@ -34,7 +34,7 @@
</Border.ContextMenu> </Border.ContextMenu>
<TextBlock Text="Defined in XAML"/> <TextBlock Text="Defined in XAML"/>
</Border> </Border>
<Border Background="{DynamicResource ThemeAccentBrush}" <Border Background="{DynamicResource SystemAccentColor}"
Margin="16" Margin="16"
Padding="48,48,48,48"> Padding="48,48,48,48">
<Border.ContextMenu> <Border.ContextMenu>

6
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -10,15 +10,15 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<StackPanel> <StackPanel>
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMeText"> <Border BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="2" Padding="16" Name="DragMeText">
<TextBlock Name="DragStateText">Drag Me</TextBlock> <TextBlock Name="DragStateText">Drag Me</TextBlock>
</Border> </Border>
<Border BorderBrush="{DynamicResource ThemeAccentBrush}" BorderThickness="2" Padding="16" Name="DragMeCustom"> <Border BorderBrush="{DynamicResource SystemAccentColor}" BorderThickness="2" Padding="16" Name="DragMeCustom">
<TextBlock Name="DragStateCustom">Drag Me (custom)</TextBlock> <TextBlock Name="DragStateCustom">Drag Me (custom)</TextBlock>
</Border> </Border>
</StackPanel> </StackPanel>
<Border Background="{DynamicResource ThemeAccentBrush2}" Padding="16" <Border Background="{DynamicResource SystemAccentColorDark1}" Padding="16"
DragDrop.AllowDrop="True"> DragDrop.AllowDrop="True">
<TextBlock Name="DropState">Drop some text or files here</TextBlock> <TextBlock Name="DropState">Drop some text or files here</TextBlock>
</Border> </Border>

36
samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml

@ -1,6 +1,30 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ItemsRepeaterPage"> 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> <DockPanel>
<StackPanel DockPanel.Dock="Top" Spacing="4" Margin="0 0 0 16"> <StackPanel DockPanel.Dock="Top" Spacing="4" Margin="0 0 0 16">
<TextBlock Classes="h1">ItemsRepeater</TextBlock> <TextBlock Classes="h1">ItemsRepeater</TextBlock>
@ -23,16 +47,8 @@
<ScrollViewer Name="scroller" <ScrollViewer Name="scroller"
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<ItemsRepeater Name="repeater" Background="Transparent" Items="{Binding Items}"> <ItemsRepeater Name="repeater" Background="Transparent" Items="{Binding Items}"
<ItemsRepeater.ItemTemplate> ItemTemplate="{StaticResource elementFactory}"/>
<DataTemplate>
<TextBlock Focusable="True"
Background="{Binding Background}"
Height="{Binding Height}"
Text="{Binding Text}"/>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</ScrollViewer> </ScrollViewer>
</Border> </Border>
</DockPanel> </DockPanel>

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

@ -38,6 +38,12 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this); 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) private void LayoutChanged(object sender, SelectionChangedEventArgs e)
{ {
if (_repeater == null) if (_repeater == null)
@ -79,6 +85,26 @@ namespace ControlCatalog.Pages
MinItemHeight = 200, MinItemHeight = 200,
}; };
break; break;
case 4:
_scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
_scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
_repeater.Layout = new WrapLayout
{
Orientation = Orientation.Vertical,
HorizontalSpacing = 20,
VerticalSpacing = 20
};
break;
case 5:
_scroller.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
_scroller.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
_repeater.Layout = new WrapLayout
{
Orientation = Orientation.Horizontal,
HorizontalSpacing = 20,
VerticalSpacing = 20
};
break;
} }
} }

8
samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml

@ -11,10 +11,10 @@
RowDefinitions="24,Auto,24" RowDefinitions="24,Auto,24"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center"> VerticalAlignment="Center">
<Border Background="{DynamicResource ThemeAccentBrush}" Grid.Column="1" Grid.Row="0"/> <Border Background="{DynamicResource SystemAccentColor}" Grid.Column="1" Grid.Row="0"/>
<Border Background="{DynamicResource ThemeAccentBrush}" Grid.Column="0" Grid.Row="1"/> <Border Background="{DynamicResource SystemAccentColor}" Grid.Column="0" Grid.Row="1"/>
<Border Background="{DynamicResource ThemeAccentBrush}" Grid.Column="2" Grid.Row="1"/> <Border Background="{DynamicResource SystemAccentColor}" Grid.Column="2" Grid.Row="1"/>
<Border Background="{DynamicResource ThemeAccentBrush}" Grid.Column="1" Grid.Row="2"/> <Border Background="{DynamicResource SystemAccentColor}" Grid.Column="1" Grid.Row="2"/>
<LayoutTransformControl Name="layoutTransform" Grid.Column="1" Grid.Row="1"> <LayoutTransformControl Name="layoutTransform" Grid.Column="1" Grid.Row="1">
<LayoutTransformControl.LayoutTransform> <LayoutTransformControl.LayoutTransform>

8
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -10,7 +10,13 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<StackPanel Orientation="Vertical" Spacing="8"> <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> <Button Command="{Binding AddItemCommand}">Add</Button>

60
samples/ControlCatalog/Pages/RelativePanelPage.axaml

@ -0,0 +1,60 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.RelativePanelPage">
<RelativePanel Width="620" Height="700" Margin="32">
<Border Name="Rect1" Background="Red" Height="50" Width="50">
<TextBlock Text="Rect1" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect2" Background="Blue" Height="50" Width="50" RelativePanel.AlignHorizontalCenterWithPanel="True">
<TextBlock Text="Rect2" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect3" Background="Green" Height="50" Width="50" RelativePanel.AlignRightWithPanel="True">
<TextBlock Text="Rect3" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect4" Background="Red" Height="50" Width="50" RelativePanel.AlignBottomWithPanel="True">
<TextBlock Text="Rect4" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect5" Background="Blue" Height="50" Width="50" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignHorizontalCenterWithPanel="True">
<TextBlock Text="Rect5" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect6" Background="Green" Height="50" Width="50" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignRightWithPanel="True">
<TextBlock Text="Rect6" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect7" Background="Blue" Height="50" RelativePanel.RightOf="{Binding ElementName=Rect1}">
<TextBlock Text="Rect7 (RightOf Rect1)" Padding="10,0" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect8" Background="Green" Height="50" RelativePanel.Below="{Binding ElementName=Rect7}">
<TextBlock Text="Rect8 (Below Rect7)" Padding="10,0" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect9" Background="Blue" Height="140" Width="460" RelativePanel.AlignHorizontalCenterWithPanel="True" RelativePanel.AlignVerticalCenterWithPanel="True">
<TextBlock Text="Rect9" Padding="10" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Top"/>
</Border>
<Border Name="Rect10" Background="Red" Width="50" RelativePanel.RightOf="{Binding ElementName=Rect9}" RelativePanel.AlignVerticalCenterWith="{Binding ElementName=Rect9}">
<TextBlock Text="Rect14 (RightOf Rect9, AlignVerticalCenterWith Rect9)" Padding="10,0" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-90"/>
</TransformGroup>
</TextBlock.RenderTransform>
</TextBlock>
</Border>
<Border Name="Rect11" Background="Red" Height="50" RelativePanel.AlignBottomWith="{Binding ElementName=Rect9}" RelativePanel.AlignHorizontalCenterWith="{Binding ElementName=Rect9}">
<TextBlock Text="Rect11 (AlignBottomWith Rect9, AlignHorizontalCenterWith Rect9)" Padding="10,0" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect12" Background="Red" Height="50" RelativePanel.Below="{Binding ElementName=Rect8}" RelativePanel.AlignLeftWith="{Binding ElementName=Rect7}">
<TextBlock Text="Rect12 (Below Rect8, AlignLeftWith Rect7)" Padding="10,0" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect13" Background="Blue" Height="50" RelativePanel.Below="{Binding ElementName=Rect12}" RelativePanel.AlignRightWith="{Binding ElementName=Rect12}">
<TextBlock Text="Rect13 (Below Rect12, AlignRightWith Rect12)" Padding="10,0" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect14" Background="Green" Height="50" RelativePanel.Above="{Binding ElementName=Rect9}" RelativePanel.AlignRightWith="{Binding ElementName=Rect9}">
<TextBlock Text="Rect14 (Above Rect9, AlignRightWith Rect9)" Padding="10,0" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border Name="Rect15" Background="Red" Height="50" RelativePanel.LeftOf="{Binding ElementName=Rect2}" RelativePanel.AlignTopWith="{Binding ElementName=Rect9}">
<TextBlock Text="Rect15 (LeftOf Rect2, AlignTopWith Rect9)" Padding="10,0" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</RelativePanel>
</UserControl>

19
samples/ControlCatalog/Pages/RelativePanelPage.axaml.cs

@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class RelativePanelPage : UserControl
{
public RelativePanelPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

69
samples/ControlCatalog/Pages/TextBlockPage.xaml

@ -64,51 +64,42 @@
<TextDecorationCollection> <TextDecorationCollection>
<TextDecoration <TextDecoration
Location="Overline" Location="Overline"
PenThicknessUnit="Pixel"> StrokeThicknessUnit="Pixel"
<TextDecoration.Pen> StrokeThickness="2">
<Pen Thickness="2"> <TextDecoration.Stroke>
<Pen.Brush> <LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%"> <LinearGradientBrush.GradientStops>
<LinearGradientBrush.GradientStops> <GradientStop Offset="0" Color="Red"/>
<GradientStop Offset="0" Color="Red"/> <GradientStop Offset="1" Color="Green"/>
<GradientStop Offset="1" Color="Green"/> </LinearGradientBrush.GradientStops>
</LinearGradientBrush.GradientStops> </LinearGradientBrush>
</LinearGradientBrush> </TextDecoration.Stroke>
</Pen.Brush>
</Pen>
</TextDecoration.Pen>
</TextDecoration> </TextDecoration>
<TextDecoration <TextDecoration
Location="Strikethrough" Location="Strikethrough"
PenThicknessUnit="Pixel"> StrokeThicknessUnit="Pixel"
<TextDecoration.Pen> StrokeThickness="1">
<Pen Thickness="1"> <TextDecoration.Stroke>
<Pen.Brush> <LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%"> <LinearGradientBrush.GradientStops>
<LinearGradientBrush.GradientStops> <GradientStop Offset="0" Color="Green"/>
<GradientStop Offset="0" Color="Green"/> <GradientStop Offset="1" Color="Blue"/>
<GradientStop Offset="1" Color="Blue"/> </LinearGradientBrush.GradientStops>
</LinearGradientBrush.GradientStops> </LinearGradientBrush>
</LinearGradientBrush> </TextDecoration.Stroke>
</Pen.Brush>
</Pen>
</TextDecoration.Pen>
</TextDecoration> </TextDecoration>
<TextDecoration <TextDecoration
Location="Underline" Location="Underline"
PenThicknessUnit="Pixel"> StrokeThicknessUnit="Pixel"
<TextDecoration.Pen> StrokeThickness="2">
<Pen Thickness="2"> <TextDecoration.Stroke>
<Pen.Brush> <LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%"> <LinearGradientBrush.GradientStops>
<LinearGradientBrush.GradientStops> <GradientStop Offset="0" Color="Blue"/>
<GradientStop Offset="0" Color="Blue"/> <GradientStop Offset="1" Color="Red"/>
<GradientStop Offset="1" Color="Red"/> </LinearGradientBrush.GradientStops>
</LinearGradientBrush.GradientStops> </LinearGradientBrush>
</LinearGradientBrush> </TextDecoration.Stroke>
</Pen.Brush>
</Pen>
</TextDecoration.Pen>
</TextDecoration> </TextDecoration>
</TextDecorationCollection> </TextDecorationCollection>
</TextBlock.TextDecorations> </TextBlock.TextDecorations>

4
samples/ControlCatalog/Pages/ToolTipPage.xaml

@ -12,7 +12,7 @@
HorizontalAlignment="Center"> HorizontalAlignment="Center">
<Border Grid.Column="0" <Border Grid.Column="0"
Grid.Row="1" Grid.Row="1"
Background="{DynamicResource ThemeAccentBrush}" Background="{DynamicResource SystemAccentColor}"
Margin="5" Margin="5"
Padding="50" Padding="50"
ToolTip.Tip="This is a ToolTip"> ToolTip.Tip="This is a ToolTip">
@ -26,7 +26,7 @@
<Border Name="Border" <Border Name="Border"
Grid.Column="1" Grid.Column="1"
Grid.Row="1" Grid.Row="1"
Background="{DynamicResource ThemeAccentBrush}" Background="{DynamicResource SystemAccentColor}"
Margin="5" Margin="5"
Padding="50" Padding="50"
ToolTip.Placement="Bottom"> ToolTip.Placement="Bottom">

19
samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml

@ -0,0 +1,19 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.WindowCustomizationsPage">
<StackPanel Spacing="10" Margin="25">
<CheckBox Content="Extend Client Area to Decorations" IsChecked="{Binding ExtendClientAreaEnabled}" />
<CheckBox Content="Title Bar" IsChecked="{Binding SystemTitleBarEnabled}" />
<CheckBox Content="Prefer System Chrome" IsChecked="{Binding PreferSystemChromeEnabled}" />
<Slider Minimum="-1" Maximum="200" Value="{Binding TitleBarHeight}" />
<ComboBox x:Name="TransparencyLevels" SelectedIndex="{Binding TransparencyLevel}">
<ComboBoxItem>None</ComboBoxItem>
<ComboBoxItem>Transparent</ComboBoxItem>
<ComboBoxItem>Blur</ComboBoxItem>
<ComboBoxItem>AcrylicBlur</ComboBoxItem>
</ComboBox>
</StackPanel>
</UserControl>

19
samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml.cs

@ -0,0 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class WindowCustomizationsPage : UserControl
{
public WindowCustomizationsPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

6
samples/ControlCatalog/SideBar.xaml

@ -12,7 +12,7 @@
<Style Selector="TabControl.sidebar"> <Style Selector="TabControl.sidebar">
<Setter Property="TabStripPlacement" Value="Left"/> <Setter Property="TabStripPlacement" Value="Left"/>
<Setter Property="Padding" Value="8 0 0 0"/> <Setter Property="Padding" Value="8 0 0 0"/>
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush}"/> <Setter Property="Background" Value="{DynamicResource SystemAccentColor}"/>
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Border <Border
@ -74,12 +74,12 @@
<Setter Property="Opacity" Value="1"/> <Setter Property="Opacity" Value="1"/>
</Style> </Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover"> <Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="{DynamicResource SystemAccentColorLight2}"/>
</Style> </Style>
<Style Selector="TabControl.sidebar > TabItem:selected"> <Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/> <Setter Property="Opacity" Value="1"/>
</Style> </Style>
<Style Selector="TabControl.sidebar > TabItem:selected"> <Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/> <Setter Property="Background" Value="{DynamicResource SystemAccentColorLight1}"/>
</Style> </Style>
</Styles> </Styles>

10
samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs

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

65
samples/ControlCatalog/ViewModels/MainWindowViewModel.cs

@ -3,6 +3,8 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Notifications; using Avalonia.Controls.Notifications;
using Avalonia.Dialogs; using Avalonia.Dialogs;
using Avalonia.Platform;
using System;
using ReactiveUI; using ReactiveUI;
namespace ControlCatalog.ViewModels namespace ControlCatalog.ViewModels
@ -14,6 +16,12 @@ namespace ControlCatalog.ViewModels
private bool _isMenuItemChecked = true; private bool _isMenuItemChecked = true;
private WindowState _windowState; private WindowState _windowState;
private WindowState[] _windowStates; private WindowState[] _windowStates;
private int _transparencyLevel;
private ExtendClientAreaChromeHints _chromeHints;
private bool _extendClientAreaEnabled;
private bool _systemTitleBarEnabled;
private bool _preferSystemChromeEnabled;
private double _titleBarHeight;
public MainWindowViewModel(IManagedNotificationManager notificationManager) public MainWindowViewModel(IManagedNotificationManager notificationManager)
{ {
@ -62,6 +70,63 @@ namespace ControlCatalog.ViewModels
WindowState.Maximized, WindowState.Maximized,
WindowState.FullScreen, WindowState.FullScreen,
}; };
this.WhenAnyValue(x => x.SystemTitleBarEnabled, x=>x.PreferSystemChromeEnabled)
.Subscribe(x =>
{
var hints = ExtendClientAreaChromeHints.NoChrome | ExtendClientAreaChromeHints.OSXThickTitleBar;
if(x.Item1)
{
hints |= ExtendClientAreaChromeHints.SystemChrome;
}
if(x.Item2)
{
hints |= ExtendClientAreaChromeHints.PreferSystemChrome;
}
ChromeHints = hints;
});
SystemTitleBarEnabled = true;
TitleBarHeight = -1;
}
public int TransparencyLevel
{
get { return _transparencyLevel; }
set { this.RaiseAndSetIfChanged(ref _transparencyLevel, value); }
}
public ExtendClientAreaChromeHints ChromeHints
{
get { return _chromeHints; }
set { this.RaiseAndSetIfChanged(ref _chromeHints, value); }
}
public bool ExtendClientAreaEnabled
{
get { return _extendClientAreaEnabled; }
set { this.RaiseAndSetIfChanged(ref _extendClientAreaEnabled, value); }
}
public bool SystemTitleBarEnabled
{
get { return _systemTitleBarEnabled; }
set { this.RaiseAndSetIfChanged(ref _systemTitleBarEnabled, value); }
}
public bool PreferSystemChromeEnabled
{
get { return _preferSystemChromeEnabled; }
set { this.RaiseAndSetIfChanged(ref _preferSystemChromeEnabled, value); }
}
public double TitleBarHeight
{
get { return _titleBarHeight; }
set { this.RaiseAndSetIfChanged(ref _titleBarHeight, value); }
} }
public WindowState WindowState public WindowState WindowState

5
samples/RenderDemo/App.xaml

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

4
samples/RenderDemo/Pages/ClippingPage.xaml

@ -43,7 +43,7 @@
C 72.078834 28.113269 74.047517 25.960974 74.931641 23.777344 C 72.078834 28.113269 74.047517 25.960974 74.931641 23.777344
C 78.93827 14.586564 73.049722 2.8815081 63.248047 0.67382812 C 78.93827 14.586564 73.049722 2.8815081 63.248047 0.67382812
C 61.721916 0.22817968 60.165597 0.038541919 58.625 0.07421875 z "> C 61.721916 0.22817968 60.165597 0.038541919 58.625 0.07421875 z ">
<Border Name="clipChild" Background="{DynamicResource ThemeAccentBrush}" Margin="4"> <Border Name="clipChild" Background="Red" Margin="4">
<!-- Setting opacity puts the TextBox on a new layer --> <!-- Setting opacity puts the TextBox on a new layer -->
<TextBox Text="Avalonia" Opacity="0.9" VerticalAlignment="Center"/> <TextBox Text="Avalonia" Opacity="0.9" VerticalAlignment="Center"/>
<Border.RenderTransform> <Border.RenderTransform>
@ -53,4 +53,4 @@
</Border> </Border>
<CheckBox Name="useMask" IsChecked="True" Grid.Row="1">Apply Geometry Clip</CheckBox> <CheckBox Name="useMask" IsChecked="True" Grid.Row="1">Apply Geometry Clip</CheckBox>
</Grid> </Grid>
</UserControl> </UserControl>

125
samples/RenderDemo/SideBar.xaml

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

1
samples/interop/NativeEmbedSample/NativeEmbedSample.csproj

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

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

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

8
src/Avalonia.Base/AvaloniaProperty.cs

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia namespace Avalonia
@ -9,7 +10,7 @@ namespace Avalonia
/// <summary> /// <summary>
/// Base class for avalonia properties. /// Base class for avalonia properties.
/// </summary> /// </summary>
public abstract class AvaloniaProperty : IEquatable<AvaloniaProperty> public abstract class AvaloniaProperty : IEquatable<AvaloniaProperty>, IPropertyInfo
{ {
/// <summary> /// <summary>
/// Represents an unset property value. /// Represents an unset property value.
@ -582,6 +583,11 @@ namespace Avalonia
return _defaultMetadata; return _defaultMetadata;
} }
bool IPropertyInfo.CanGet => true;
bool IPropertyInfo.CanSet => true;
object IPropertyInfo.Get(object target) => ((AvaloniaObject)target).GetValue(this);
void IPropertyInfo.Set(object target, object value) => ((AvaloniaObject)target).SetValue(this, value);
} }
/// <summary> /// <summary>

73
src/Avalonia.Base/Data/Core/ClrPropertyInfo.cs

@ -0,0 +1,73 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Avalonia.Data.Core
{
public class ClrPropertyInfo : IPropertyInfo
{
private readonly Func<object, object> _getter;
private readonly Action<object, object> _setter;
public ClrPropertyInfo(string name, Func<object, object> getter, Action<object, object> setter, Type propertyType)
{
_getter = getter;
_setter = setter;
PropertyType = propertyType;
Name = name;
}
public string Name { get; }
public Type PropertyType { get; }
public object Get(object target)
{
if (_getter == null)
throw new NotSupportedException("Property " + Name + " doesn't have a getter");
return _getter(target);
}
public void Set(object target, object value)
{
if (_setter == null)
throw new NotSupportedException("Property " + Name + " doesn't have a setter");
_setter(target, value);
}
public bool CanSet => _setter != null;
public bool CanGet => _getter != null;
}
public class ReflectionClrPropertyInfo : ClrPropertyInfo
{
static Action<object, object> CreateSetter(PropertyInfo info)
{
if (info.SetMethod == null)
return null;
var target = Expression.Parameter(typeof(object), "target");
var value = Expression.Parameter(typeof(object), "value");
return Expression.Lambda<Action<object, object>>(
Expression.Call(Expression.Convert(target, info.DeclaringType), info.SetMethod,
Expression.Convert(value, info.SetMethod.GetParameters()[0].ParameterType)),
target, value)
.Compile();
}
static Func<object, object> CreateGetter(PropertyInfo info)
{
if (info.GetMethod == null)
return null;
var target = Expression.Parameter(typeof(object), "target");
return Expression.Lambda<Func<object, object>>(
Expression.Convert(Expression.Call(Expression.Convert(target, info.DeclaringType), info.GetMethod),
typeof(object)))
.Compile();
}
public ReflectionClrPropertyInfo(PropertyInfo info) : base(info.Name,
CreateGetter(info), CreateSetter(info), info.PropertyType)
{
}
}
}

32
src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@ -54,6 +54,7 @@ namespace Avalonia.Data.Core
private object _root; private object _root;
private IDisposable _rootSubscription; private IDisposable _rootSubscription;
private WeakReference<object> _value; private WeakReference<object> _value;
private IReadOnlyList<ITransformNode> _transformNodes;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class. /// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
@ -188,6 +189,24 @@ namespace Avalonia.Data.Core
description ?? expression.ToString()); description ?? expression.ToString());
} }
private IReadOnlyList<ITransformNode> GetTransformNodesFromChain()
{
LinkedList<ITransformNode> transforms = new LinkedList<ITransformNode>();
var node = _node;
while (node != null)
{
if (node is ITransformNode transform)
{
transforms.AddFirst(transform);
}
node = node.Next;
}
return new List<ITransformNode>(transforms);
}
private IReadOnlyList<ITransformNode> TransformNodes => (_transformNodes ?? (_transformNodes = GetTransformNodesFromChain()));
/// <summary> /// <summary>
/// Attempts to set the value of a property expression. /// Attempts to set the value of a property expression.
/// </summary> /// </summary>
@ -203,18 +222,13 @@ namespace Avalonia.Data.Core
{ {
if (Leaf is SettableNode settable) if (Leaf is SettableNode settable)
{ {
var node = _node; foreach (var transform in TransformNodes)
while (node != null)
{ {
if (node is ITransformNode transform) value = transform.Transform(value);
if (value is BindingNotification)
{ {
value = transform.Transform(value); return false;
if (value is BindingNotification)
{
return false;
}
} }
node = node.Next;
} }
return settable.SetTargetValue(value, priority); return settable.SetTargetValue(value, priority);
} }

14
src/Avalonia.Base/Data/Core/IPropertyInfo.cs

@ -0,0 +1,14 @@
using System;
namespace Avalonia.Data.Core
{
public interface IPropertyInfo
{
string Name { get; }
object Get(object target);
void Set(object target, object value);
bool CanSet { get; }
bool CanGet { get; }
Type PropertyType { get; }
}
}

2
src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs

@ -59,7 +59,7 @@ namespace Avalonia.Data.Core.Plugins
return Observable.Empty<object>(); return Observable.Empty<object>();
} }
protected IObservable<object> HandleCompleted(Task task) private IObservable<object> HandleCompleted(Task task)
{ {
var resultProperty = task.GetType().GetRuntimeProperty("Result"); var resultProperty = task.GetType().GetRuntimeProperty("Result");

32
src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs

@ -7,6 +7,7 @@ namespace Avalonia.Data.Core
public class PropertyAccessorNode : SettableNode public class PropertyAccessorNode : SettableNode
{ {
private readonly bool _enableValidation; private readonly bool _enableValidation;
private IPropertyAccessorPlugin _customPlugin;
private IPropertyAccessor _accessor; private IPropertyAccessor _accessor;
public PropertyAccessorNode(string propertyName, bool enableValidation) public PropertyAccessorNode(string propertyName, bool enableValidation)
@ -15,6 +16,13 @@ namespace Avalonia.Data.Core
_enableValidation = enableValidation; _enableValidation = enableValidation;
} }
public PropertyAccessorNode(string propertyName, bool enableValidation, IPropertyAccessorPlugin customPlugin)
{
PropertyName = propertyName;
_enableValidation = enableValidation;
_customPlugin = customPlugin;
}
public override string Description => PropertyName; public override string Description => PropertyName;
public string PropertyName { get; } public string PropertyName { get; }
public override Type PropertyType => _accessor?.PropertyType; public override Type PropertyType => _accessor?.PropertyType;
@ -37,17 +45,7 @@ namespace Avalonia.Data.Core
{ {
reference.TryGetTarget(out object target); reference.TryGetTarget(out object target);
IPropertyAccessorPlugin plugin = null; var plugin = _customPlugin ?? GetPropertyAccessorPluginForObject(target);
foreach (IPropertyAccessorPlugin x in ExpressionObserver.PropertyAccessors)
{
if (x.Match(target, PropertyName))
{
plugin = x;
break;
}
}
var accessor = plugin?.Start(reference, PropertyName); var accessor = plugin?.Start(reference, PropertyName);
// We need to handle accessor fallback before handling validation. Validators do not support null accessors. // We need to handle accessor fallback before handling validation. Validators do not support null accessors.
@ -82,6 +80,18 @@ namespace Avalonia.Data.Core
accessor.Subscribe(ValueChanged); accessor.Subscribe(ValueChanged);
} }
private IPropertyAccessorPlugin GetPropertyAccessorPluginForObject(object target)
{
foreach (IPropertyAccessorPlugin x in ExpressionObserver.PropertyAccessors)
{
if (x.Match(target, PropertyName))
{
return x;
}
}
return null;
}
protected override void StopListeningCore() protected override void StopListeningCore()
{ {
_accessor.Dispose(); _accessor.Dispose();

91
src/Avalonia.Base/Data/Core/PropertyPath.cs

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Avalonia.Data.Core
{
public class PropertyPath
{
public IReadOnlyList<IPropertyPathElement> Elements { get; }
public PropertyPath(IEnumerable<IPropertyPathElement> elements)
{
Elements = elements.ToList();
}
}
public class PropertyPathBuilder
{
readonly List<IPropertyPathElement> _elements = new List<IPropertyPathElement>();
public PropertyPathBuilder Property(IPropertyInfo property)
{
_elements.Add(new PropertyPropertyPathElement(property));
return this;
}
public PropertyPathBuilder ChildTraversal()
{
_elements.Add(new ChildTraversalPropertyPathElement());
return this;
}
public PropertyPathBuilder EnsureType(Type type)
{
_elements.Add(new EnsureTypePropertyPathElement(type));
return this;
}
public PropertyPathBuilder Cast(Type type)
{
_elements.Add(new CastTypePropertyPathElement(type));
return this;
}
public PropertyPath Build()
{
return new PropertyPath(_elements);
}
}
public interface IPropertyPathElement
{
}
public class PropertyPropertyPathElement : IPropertyPathElement
{
public IPropertyInfo Property { get; }
public PropertyPropertyPathElement(IPropertyInfo property)
{
Property = property;
}
}
public class ChildTraversalPropertyPathElement : IPropertyPathElement
{
}
public class EnsureTypePropertyPathElement : IPropertyPathElement
{
public Type Type { get; }
public EnsureTypePropertyPathElement(Type type)
{
Type = type;
}
}
public class CastTypePropertyPathElement : IPropertyPathElement
{
public CastTypePropertyPathElement(Type type)
{
Type = type;
}
public Type Type { get; }
}
}

37
src/Avalonia.Base/Data/Core/StreamNode.cs

@ -1,35 +1,54 @@
using System; using System;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Data.Core.Plugins;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
public class StreamNode : ExpressionNode public class StreamNode : ExpressionNode
{ {
private IStreamPlugin _customPlugin = null;
private IDisposable _subscription; private IDisposable _subscription;
public override string Description => "^"; public override string Description => "^";
public StreamNode() { }
public StreamNode(IStreamPlugin customPlugin)
{
_customPlugin = customPlugin;
}
protected override void StartListeningCore(WeakReference<object> reference) protected override void StartListeningCore(WeakReference<object> reference)
{ {
GetPlugin(reference)?.Start(reference).Subscribe(ValueChanged);
}
protected override void StopListeningCore()
{
_subscription?.Dispose();
_subscription = null;
}
private IStreamPlugin GetPlugin(WeakReference<object> reference)
{
if (_customPlugin != null)
{
return _customPlugin;
}
foreach (var plugin in ExpressionObserver.StreamHandlers) foreach (var plugin in ExpressionObserver.StreamHandlers)
{ {
if (plugin.Match(reference)) if (plugin.Match(reference))
{ {
_subscription = plugin.Start(reference).Subscribe(ValueChanged); return plugin;
return;
} }
} }
// TODO: Improve error. // TODO: Improve error
ValueChanged(new BindingNotification( ValueChanged(new BindingNotification(
new MarkupBindingChainException("Stream operator applied to unsupported type", Description), new MarkupBindingChainException("Stream operator applied to unsupported type", Description),
BindingErrorType.Error)); BindingErrorType.Error));
} return null;
protected override void StopListeningCore()
{
_subscription?.Dispose();
_subscription = null;
} }
} }
} }

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

@ -1,3 +1,5 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Threading; using System.Threading;
namespace Avalonia.Threading namespace Avalonia.Threading
@ -7,6 +9,20 @@ namespace Avalonia.Threading
/// </summary> /// </summary>
public class AvaloniaSynchronizationContext : SynchronizationContext 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> /// <summary>
/// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer. /// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer.
/// </summary> /// </summary>
@ -22,7 +38,8 @@ namespace Avalonia.Threading
return; return;
} }
SetSynchronizationContext(new AvaloniaSynchronizationContext()); SetSynchronizationContext(new AvaloniaSynchronizationContext(AvaloniaLocator.Current
.GetService<INonPumpingPlatformWaitProvider>()));
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -39,5 +56,13 @@ namespace Avalonia.Threading
else else
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait(); 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);
}
} }
} }

20
src/Avalonia.Base/Utilities/CharacterReader.cs

@ -79,5 +79,25 @@ namespace Avalonia.Utilities
Position += len; Position += len;
return span; return span;
} }
public ReadOnlySpan<char> TryPeek(int count)
{
if (_s.Length < count)
return ReadOnlySpan<char>.Empty;
return _s.Slice(0, count);
}
public ReadOnlySpan<char> PeekWhitespace()
{
var trimmed = _s.TrimStart();
return _s.Slice(0, _s.Length - trimmed.Length);
}
public void Skip(int count)
{
if (_s.Length < count)
throw new IndexOutOfRangeException();
_s = _s.Slice(count);
}
} }
} }

46
src/Avalonia.Base/Utilities/KeywordParser.cs

@ -0,0 +1,46 @@
using System;
namespace Avalonia.Utilities
{
#if !BUILDTASK
public
#endif
static class KeywordParser
{
public static bool CheckKeyword(this ref CharacterReader r, string keyword)
{
return (CheckKeywordInternal(ref r, keyword) >= 0);
}
static int CheckKeywordInternal(this ref CharacterReader r, string keyword)
{
var ws = r.PeekWhitespace();
var chars = r.TryPeek(ws.Length + keyword.Length);
if (chars.IsEmpty)
return -1;
if (SpanEquals(chars.Slice(ws.Length), keyword.AsSpan()))
return chars.Length;
return -1;
}
static bool SpanEquals(ReadOnlySpan<char> left, ReadOnlySpan<char> right)
{
if (left.Length != right.Length)
return false;
for(var c=0; c<left.Length;c++)
if (left[c] != right[c])
return false;
return true;
}
public static bool TakeIfKeyword(this ref CharacterReader r, string keyword)
{
var l = CheckKeywordInternal(ref r, keyword);
if (l < 0)
return false;
r.Skip(l);
return true;
}
}
}

46
src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj

@ -5,7 +5,7 @@
<OutputType>exe</OutputType> <OutputType>exe</OutputType>
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
<BuildOutputTargetFolder>tools</BuildOutputTargetFolder> <BuildOutputTargetFolder>tools</BuildOutputTargetFolder>
<DefineConstants>$(DefineConstants);BUILDTASK;XAMLIL_CECIL_INTERNAL;XAMLIL_INTERNAL</DefineConstants> <DefineConstants>$(DefineConstants);BUILDTASK;XAMLX_CECIL_INTERNAL;XAMLX_INTERNAL</DefineConstants>
<CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies Condition="$(TargetFramework) == 'netstandard2.0'">true</CopyLocalLockFileAssemblies>
<NoWarn>NU1605</NoWarn> <NoWarn>NU1605</NoWarn>
</PropertyGroup> </PropertyGroup>
@ -17,19 +17,22 @@
<Compile Include="../Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs"> <Compile Include="../Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaResourceXamlInfo.cs">
<Link>Shared/AvaloniaResourceXamlInfo.cs</Link> <Link>Shared/AvaloniaResourceXamlInfo.cs</Link>
</Compile> </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> <Link>XamlIlExtensions/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Remove="external/cecil/**/*.*" /> <Compile Remove="external/cecil/**/*.*" />
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlIl\**\*.cs"> <Compile Include="../Markup/Avalonia.Markup.Xaml.Loader/xamlil.github\src\XamlX\**\*.cs">
<Link>XamlIl/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>XamlIl/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlIl.Cecil\**\*.cs"> <Compile Include="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX.IL.Cecil\**\*.cs">
<Link>XamlIl.Cecil/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>XamlIl.Cecil/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="../Markup/Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs"> <Compile Include="../Markup/Avalonia.Markup\Markup\Parsers\SelectorGrammar.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="../Markup/Avalonia.Markup\Markup\Parsers\PropertyPathGrammar.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs"> <Compile Include="../Markup/Avalonia.Markup.Xaml/Parsers/PropertyParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
@ -38,36 +41,25 @@
</Compile> </Compile>
<Compile Include="../Avalonia.Base/Utilities/CharacterReader.cs"> <Compile Include="../Avalonia.Base/Utilities/CharacterReader.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="../Avalonia.Base/Utilities/IdentifierParser.cs"> <Compile Include="../Avalonia.Base/Utilities/IdentifierParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Include="..\Markup\Avalonia.Markup\Markup\Parsers\ArgumentListParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Utilities/KeywordParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="..\Markup\Avalonia.Markup\Markup\Parsers\BindingExpressionGrammar.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile>
<Compile Include="../Avalonia.Base/Utilities/StyleClassParser.cs"> <Compile Include="../Avalonia.Base/Utilities/StyleClassParser.cs">
<Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link> <Link>Markup/%(RecursiveDir)%(FileName)%(Extension)</Link>
</Compile> </Compile>
<Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\**\obj\**\*.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\**\obj\**\*.cs" /> <Compile Remove="../Markup/Avalonia.Markup.Xaml.Loader\xamlil.github\src\XamlX\IL\SreTypeSystem.cs" />
<Compile Remove="../Markup/Avalonia.Markup.Xaml/XamlIl\xamlil.github\src\XamlIl\TypeSystem\SreTypeSystem.cs" />
<PackageReference Include="Avalonia.Unofficial.Cecil" Version="20190417.2.0" PrivateAssets="All" /> <PackageReference Include="Avalonia.Unofficial.Cecil" Version="20190417.2.0" PrivateAssets="All" />
<PackageReference Condition="$(TargetFramework) == 'netstandard2.0'" Include="ILRepack.MSBuild.Task" Version="2.0.13" PrivateAssets="All" />
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" PrivateAssets="All" /> <PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<Target Name="ILRepack" AfterTargets="Build" Condition="$(TargetFramework) == 'netstandard2.0'">
<PropertyGroup>
<WorkingDirectory>$(MSBuildThisFileDirectory)bin\$(Configuration)\$(TargetFramework)</WorkingDirectory>
</PropertyGroup>
<ItemGroup>
<InputAssemblies Include="Mono.Cecil.dll" />
</ItemGroup>
<ILRepack OutputType="$(OutputType)" MainAssembly="$(AssemblyName).dll" OutputAssembly="$(AssemblyName).dll" InputAssemblies="@(InputAssemblies)" WorkingDirectory="$(WorkingDirectory)" />
<ItemGroup>
<DeleteNonNeededResults Include="$(WorkingDirectory)\*.dll" />
<DeleteNonNeededResults Remove="$(WorkingDirectory)\Avalonia.Build.Tasks.dll" />
</ItemGroup>
<Delete Files="@(DeleteNonNeededResults)" />
</Target>
</Project> </Project>

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

@ -5,7 +5,7 @@ using Avalonia.Utilities;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
using Mono.Collections.Generic; using Mono.Collections.Generic;
using XamlIl.TypeSystem; using XamlX.TypeSystem;
namespace Avalonia.Build.Tasks namespace Avalonia.Build.Tasks
{ {

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

@ -7,17 +7,18 @@ using System.Text;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions; using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
using Microsoft.Build.Framework; using Microsoft.Build.Framework;
using Mono.Cecil; using Mono.Cecil;
using XamlIl.TypeSystem;
using Avalonia.Utilities; using Avalonia.Utilities;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
using Mono.Cecil.Rocks; using Mono.Cecil.Rocks;
using XamlIl; using XamlX;
using XamlIl.Ast; using XamlX.Ast;
using XamlIl.Parsers; using XamlX.Parsers;
using XamlIl.Transform; using XamlX.Transform;
using XamlX.TypeSystem;
using FieldAttributes = Mono.Cecil.FieldAttributes; using FieldAttributes = Mono.Cecil.FieldAttributes;
using MethodAttributes = Mono.Cecil.MethodAttributes; using MethodAttributes = Mono.Cecil.MethodAttributes;
using TypeAttributes = Mono.Cecil.TypeAttributes; using TypeAttributes = Mono.Cecil.TypeAttributes;
using XamlX.IL;
namespace Avalonia.Build.Tasks namespace Avalonia.Build.Tasks
{ {
@ -50,23 +51,32 @@ namespace Avalonia.Build.Tasks
if (avares.Resources.Count(CheckXamlName) == 0 && emres.Resources.Count(CheckXamlName) == 0) if (avares.Resources.Count(CheckXamlName) == 0 && emres.Resources.Count(CheckXamlName) == 0)
// Nothing to do // Nothing to do
return new CompileResult(true); return new CompileResult(true);
var xamlLanguage = AvaloniaXamlIlLanguage.Configure(typeSystem); var clrPropertiesDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlHelpers",
var compilerConfig = new XamlIlTransformerConfiguration(typeSystem, TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(clrPropertiesDef);
var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(indexerAccessorClosure);
var (xamlLanguage , emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem);
var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
typeSystem.TargetAssembly, typeSystem.TargetAssembly,
xamlLanguage, xamlLanguage,
XamlIlXmlnsMappings.Resolve(typeSystem, xamlLanguage), XamlXmlnsMappings.Resolve(typeSystem, xamlLanguage),
AvaloniaXamlIlLanguage.CustomValueConverter); AvaloniaXamlIlLanguage.CustomValueConverter,
new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)),
new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)));
var contextDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlContext", var contextDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlContext",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object); TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(contextDef); asm.MainModule.Types.Add(contextDef);
var contextClass = XamlIlContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem, var contextClass = XamlILContextDefinition.GenerateContextClass(typeSystem.CreateTypeBuilder(contextDef), typeSystem,
xamlLanguage); xamlLanguage, emitConfig);
var compiler = new AvaloniaXamlIlCompiler(compilerConfig, contextClass) { EnableIlVerification = verifyIl }; var compiler = new AvaloniaXamlIlCompiler(compilerConfig, emitConfig, contextClass) { EnableIlVerification = verifyIl };
var editorBrowsableAttribute = typeSystem var editorBrowsableAttribute = typeSystem
.GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute")) .GetTypeReference(typeSystem.FindType("System.ComponentModel.EditorBrowsableAttribute"))
@ -126,35 +136,35 @@ namespace Avalonia.Build.Tasks
// StreamReader is needed here to handle BOM // StreamReader is needed here to handle BOM
var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd(); var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd();
var parsed = XDocumentXamlIlParser.Parse(xaml); var parsed = XDocumentXamlParser.Parse(xaml);
var initialRoot = (XamlIlAstObjectNode)parsed.Root; var initialRoot = (XamlAstObjectNode)parsed.Root;
var precompileDirective = initialRoot.Children.OfType<XamlIlAstXmlDirective>() var precompileDirective = initialRoot.Children.OfType<XamlAstXmlDirective>()
.FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Precompile"); .FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Precompile");
if (precompileDirective != null) if (precompileDirective != null)
{ {
var precompileText = (precompileDirective.Values[0] as XamlIlAstTextNode)?.Text.Trim() var precompileText = (precompileDirective.Values[0] as XamlAstTextNode)?.Text.Trim()
.ToLowerInvariant(); .ToLowerInvariant();
if (precompileText == "false") if (precompileText == "false")
continue; continue;
if (precompileText != "true") if (precompileText != "true")
throw new XamlIlParseException("Invalid value for x:Precompile", precompileDirective); throw new XamlParseException("Invalid value for x:Precompile", precompileDirective);
} }
var classDirective = initialRoot.Children.OfType<XamlIlAstXmlDirective>() var classDirective = initialRoot.Children.OfType<XamlAstXmlDirective>()
.FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class"); .FirstOrDefault(d => d.Namespace == XamlNamespaces.Xaml2006 && d.Name == "Class");
IXamlIlType classType = null; IXamlType classType = null;
if (classDirective != null) if (classDirective != null)
{ {
if (classDirective.Values.Count != 1 || !(classDirective.Values[0] is XamlIlAstTextNode tn)) if (classDirective.Values.Count != 1 || !(classDirective.Values[0] is XamlAstTextNode tn))
throw new XamlIlParseException("x:Class should have a string value", classDirective); throw new XamlParseException("x:Class should have a string value", classDirective);
classType = typeSystem.TargetAssembly.FindType(tn.Text); classType = typeSystem.TargetAssembly.FindType(tn.Text);
if (classType == null) if (classType == null)
throw new XamlIlParseException($"Unable to find type `{tn.Text}`", classDirective); throw new XamlParseException($"Unable to find type `{tn.Text}`", classDirective);
compiler.OverrideRootType(parsed, compiler.OverrideRootType(parsed,
new XamlIlAstClrTypeReference(classDirective, classType, false)); new XamlAstClrTypeReference(classDirective, classType, false));
initialRoot.Children.Remove(classDirective); initialRoot.Children.Remove(classDirective);
} }
@ -323,7 +333,7 @@ namespace Avalonia.Build.Tasks
catch (Exception e) catch (Exception e)
{ {
int lineNumber = 0, linePosition = 0; int lineNumber = 0, linePosition = 0;
if (e is XamlIlParseException xe) if (e is XamlParseException xe)
{ {
lineNumber = xe.LineNumber; lineNumber = xe.LineNumber;
linePosition = xe.LinePosition; linePosition = xe.LinePosition;

8
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -3920,6 +3920,14 @@ namespace Avalonia.Controls
dataGridColumn: CurrentColumn, dataGridColumn: CurrentColumn,
dataGridRow: EditingRow, dataGridRow: EditingRow,
dataGridCell: editingCell); 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 // We're done, so raise the CellEditEnded event

5
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@ -767,6 +767,11 @@ namespace Avalonia.Controls
} }
} }
internal void InvalidateDesiredHeight()
{
_cellsElement?.InvalidateDesiredHeight();
}
internal void ResetGridLine() internal void ResetGridLine()
{ {
_bottomGridLine = null; _bottomGridLine = null;

5
src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs

@ -299,6 +299,11 @@ namespace Avalonia.Controls.Primitives
DesiredHeight = 0; DesiredHeight = 0;
} }
internal void InvalidateDesiredHeight()
{
DesiredHeight = 0;
}
private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge) private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge)
{ {
if (!column.IsVisible) if (!column.IsVisible)

2
src/Avalonia.Controls.DataGrid/Themes/Default.xaml

@ -188,7 +188,7 @@
</Style> </Style>
<Style Selector="DataGrid"> <Style Selector="DataGrid">
<Setter Property="RowBackground" Value="{DynamicResource ThemeAccentBrush4}" /> <Setter Property="RowBackground" Value="{DynamicResource SystemAccentColorDark2}" />
<Setter Property="AlternatingRowBackground" Value="#00FFFFFF" /> <Setter Property="AlternatingRowBackground" Value="#00FFFFFF" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" /> <Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="HeadersVisibility" Value="Column" /> <Setter Property="HeadersVisibility" Value="Column" />

23
src/Avalonia.Controls/AcrylicPlatformCompensationLevels.cs

@ -0,0 +1,23 @@
namespace Avalonia.Controls
{
/// <summary>
/// Defines compensation levels for the platform depending on the transparency level.
/// It controls the base opacity level of the 'tracing paper' layer that compensates
/// for low blur radius.
/// </summary>
public struct AcrylicPlatformCompensationLevels
{
public AcrylicPlatformCompensationLevels(double transparent, double blurred, double acrylic)
{
TransparentLevel = transparent;
BlurLevel = blurred;
AcrylicBlurLevel = acrylic;
}
public double TransparentLevel { get; }
public double BlurLevel { get; }
public double AcrylicBlurLevel { get; }
}
}

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

@ -0,0 +1,84 @@
using System;
using System.Reactive.Disposables;
using Avalonia.Controls.Primitives;
#nullable enable
namespace Avalonia.Controls.Chrome
{
/// <summary>
/// Draws window minimize / maximize / close buttons in a <see cref="TitleBar"/> when managed client decorations are enabled.
/// </summary>
public class CaptionButtons : TemplatedControl
{
private CompositeDisposable? _disposables;
private Window? _hostWindow;
public void Attach(Window hostWindow)
{
if (_disposables == null)
{
_hostWindow = hostWindow;
_disposables = new CompositeDisposable
{
_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);
})
};
}
}
public void Detach()
{
if (_disposables != null)
{
_disposables.Dispose();
_disposables = null;
_hostWindow = null;
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
var closeButton = e.NameScope.Get<Panel>("PART_CloseButton");
var restoreButton = e.NameScope.Get<Panel>("PART_RestoreButton");
var minimiseButton = e.NameScope.Get<Panel>("PART_MinimiseButton");
var fullScreenButton = e.NameScope.Get<Panel>("PART_FullScreenButton");
closeButton.PointerReleased += (sender, e) => _hostWindow?.Close();
restoreButton.PointerReleased += (sender, e) =>
{
if (_hostWindow != null)
{
_hostWindow.WindowState = _hostWindow.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
}
};
minimiseButton.PointerReleased += (sender, e) =>
{
if (_hostWindow != null)
{
_hostWindow.WindowState = WindowState.Minimized;
}
};
fullScreenButton.PointerReleased += (sender, e) =>
{
if (_hostWindow != null)
{
_hostWindow.WindowState = _hostWindow.WindowState == WindowState.FullScreen ? WindowState.Normal : WindowState.FullScreen;
}
};
}
}
}

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

@ -0,0 +1,97 @@
using System;
using System.Reactive.Disposables;
using Avalonia.Controls.Primitives;
#nullable enable
namespace Avalonia.Controls.Chrome
{
/// <summary>
/// Draws a titlebar when managed client decorations are enabled.
/// </summary>
public class TitleBar : TemplatedControl
{
private CompositeDisposable? _disposables;
private CaptionButtons? _captionButtons;
private void UpdateSize(Window window)
{
if (window != null)
{
Margin = new Thickness(
window.OffScreenMargin.Left,
window.OffScreenMargin.Top,
window.OffScreenMargin.Right,
window.OffScreenMargin.Bottom);
if (window.WindowState != WindowState.FullScreen)
{
Height = window.WindowDecorationMargin.Top;
if (_captionButtons != null)
{
_captionButtons.Height = Height;
}
}
IsVisible = window.PlatformImpl.NeedsManagedDecorations;
}
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_captionButtons?.Detach();
_captionButtons = e.NameScope.Get<CaptionButtons>("PART_CaptionButtons");
if (VisualRoot is Window window)
{
_captionButtons?.Attach(window);
UpdateSize(window);
}
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (VisualRoot is Window window)
{
_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);
_disposables?.Dispose();
_captionButtons?.Detach();
_captionButtons = null;
}
}
}

31
src/Avalonia.Controls/ComboBox.cs

@ -7,6 +7,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -63,6 +64,18 @@ namespace Avalonia.Controls
public static readonly StyledProperty<IBrush> PlaceholderForegroundProperty = public static readonly StyledProperty<IBrush> PlaceholderForegroundProperty =
AvaloniaProperty.Register<ComboBox, IBrush>(nameof(PlaceholderForeground)); AvaloniaProperty.Register<ComboBox, IBrush>(nameof(PlaceholderForeground));
/// <summary>
/// Defines the <see cref="HorizontalContentAlignment"/> property.
/// </summary>
public static readonly StyledProperty<HorizontalAlignment> HorizontalContentAlignmentProperty =
ContentControl.HorizontalContentAlignmentProperty.AddOwner<ComboBox>();
/// <summary>
/// Defines the <see cref="VerticalContentAlignment"/> property.
/// </summary>
public static readonly StyledProperty<VerticalAlignment> VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner<ComboBox>();
private bool _isDropDownOpen; private bool _isDropDownOpen;
private Popup _popup; private Popup _popup;
private object _selectionBoxItem; private object _selectionBoxItem;
@ -133,6 +146,24 @@ namespace Avalonia.Controls
set { SetValue(VirtualizationModeProperty, value); } set { SetValue(VirtualizationModeProperty, value); }
} }
/// <summary>
/// Gets or sets the horizontal alignment of the content within the control.
/// </summary>
public HorizontalAlignment HorizontalContentAlignment
{
get { return GetValue(HorizontalContentAlignmentProperty); }
set { SetValue(HorizontalContentAlignmentProperty, value); }
}
/// <summary>
/// Gets or sets the vertical alignment of the content within the control.
/// </summary>
public VerticalAlignment VerticalContentAlignment
{
get { return GetValue(VerticalContentAlignmentProperty); }
set { SetValue(VerticalContentAlignmentProperty, value); }
}
/// <inheritdoc/> /// <inheritdoc/>
protected override IItemContainerGenerator CreateItemContainerGenerator() protected override IItemContainerGenerator CreateItemContainerGenerator()
{ {

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

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

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

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

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

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

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

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

5
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; } get { return _scaling; }
set set
@ -52,6 +52,9 @@ namespace Avalonia.Controls.Embedding.Offscreen
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; } public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
/// <inheritdoc/>
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 1, 1);
public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot; public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot;
public virtual Point PointToClient(PixelPoint point) => point.ToPoint(1); public virtual Point PointToClient(PixelPoint point) => point.ToPoint(1);

117
src/Avalonia.Controls/ExperimentalAcrylicBorder.cs

@ -0,0 +1,117 @@
using Avalonia.Controls.Utils;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Platform;
using System;
namespace Avalonia.Controls
{
public class ExperimentalAcrylicBorder : Decorator
{
public static readonly StyledProperty<CornerRadius> CornerRadiusProperty =
Border.CornerRadiusProperty.AddOwner<ExperimentalAcrylicBorder>();
public static readonly StyledProperty<ExperimentalAcrylicMaterial> MaterialProperty =
AvaloniaProperty.Register<ExperimentalAcrylicBorder, ExperimentalAcrylicMaterial>(nameof(Material));
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
private IDisposable _subscription;
static ExperimentalAcrylicBorder()
{
AffectsRender<ExperimentalAcrylicBorder>(
MaterialProperty,
CornerRadiusProperty);
}
/// <summary>
/// Gets or sets the radius of the border rounded corners.
/// </summary>
public CornerRadius CornerRadius
{
get { return GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
public ExperimentalAcrylicMaterial Material
{
get => GetValue(MaterialProperty);
set => SetValue(MaterialProperty, value);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
var tl = (e.Root as TopLevel);
_subscription = tl.GetObservable(TopLevel.ActualTransparencyLevelProperty)
.Subscribe(x =>
{
switch (x)
{
case WindowTransparencyLevel.Transparent:
case WindowTransparencyLevel.None:
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.TransparentLevel;
break;
case WindowTransparencyLevel.Blur:
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.BlurLevel;
break;
case WindowTransparencyLevel.AcrylicBlur:
Material.PlatformTransparencyCompensationLevel = tl.PlatformImpl.AcrylicCompensationLevels.AcrylicBlurLevel;
break;
}
});
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
_subscription?.Dispose();
}
public override void Render(DrawingContext context)
{
if (context.PlatformImpl is IDrawingContextWithAcrylicLikeSupport idc)
{
var cornerRadius = CornerRadius;
idc.DrawRectangle(
Material,
new RoundedRect(
new Rect(Bounds.Size),
cornerRadius.TopLeft, cornerRadius.TopRight,
cornerRadius.BottomRight, cornerRadius.BottomLeft));
}
else
{
_borderRenderHelper.Render(context, Bounds.Size, new Thickness(), CornerRadius, new SolidColorBrush(Material.FallbackColor), null, default);
}
}
/// <summary>
/// Measures the control.
/// </summary>
/// <param name="availableSize">The available size.</param>
/// <returns>The desired size of the control.</returns>
protected override Size MeasureOverride(Size availableSize)
{
return LayoutHelper.MeasureChild(Child, availableSize, Padding);
}
/// <summary>
/// Arranges the control's child.
/// </summary>
/// <param name="finalSize">The size allocated to the control.</param>
/// <returns>The space taken.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
return LayoutHelper.ArrangeChild(Child, finalSize, Padding);
}
}
}

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

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

38
src/Avalonia.Controls/Platform/ExtendClientAreaChromeHints.cs

@ -0,0 +1,38 @@
using System;
namespace Avalonia.Platform
{
/// <summary>
/// Hint for Window Chrome when ClientArea is Extended.
/// </summary>
[Flags]
public enum ExtendClientAreaChromeHints
{
/// <summary>
/// The will be no chrome at all.
/// </summary>
NoChrome,
/// <summary>
/// The default for the platform.
/// </summary>
Default = SystemChrome,
/// <summary>
/// Use SystemChrome
/// </summary>
SystemChrome = 0x01,
/// <summary>
/// Use system chrome where possible. OSX system chrome is used, Windows managed chrome is used.
/// This is because Windows Chrome can not be shown ontop of user content.
/// </summary>
PreferSystemChrome = 0x02,
/// <summary>
/// On OSX the titlebar is the thicker toolbar kind. Causes traffic lights to be positioned
/// slightly lower than normal.
/// </summary>
OSXThickTitleBar = 0x08,
}
}

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

@ -23,10 +23,10 @@ namespace Avalonia.Platform
Size ClientSize { get; } Size ClientSize { get; }
/// <summary> /// <summary>
/// Gets the scaling factor for the toplevel. /// Gets the scaling factor for the toplevel. This is used for rendering.
/// </summary> /// </summary>
double Scaling { get; } double RenderScaling { get; }
/// <summary> /// <summary>
/// The list of native platform's surfaces that can be consumed by rendering subsystems. /// The list of native platform's surfaces that can be consumed by rendering subsystems.
/// </summary> /// </summary>
@ -127,5 +127,10 @@ namespace Avalonia.Platform
/// Gets the current <see cref="WindowTransparencyLevel"/> of the TopLevel. /// Gets the current <see cref="WindowTransparencyLevel"/> of the TopLevel.
/// </summary> /// </summary>
WindowTransparencyLevel TransparencyLevel { get; } WindowTransparencyLevel TransparencyLevel { get; }
/// <summary>
/// Gets the <see cref="AcrylicPlatformCompensationLevels"/> for the platform.
/// </summary>
AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
} }
} }

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

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

46
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -68,6 +68,34 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
Func<bool> Closing { get; set; } Func<bool> Closing { get; set; }
/// <summary>
/// Gets a value to indicate if the platform was able to extend client area to non-client area.
/// </summary>
bool IsClientAreaExtendedToDecorations { get; }
/// <summary>
/// Gets or Sets an action that is called whenever one of the extend client area properties changed.
/// </summary>
Action<bool> ExtendClientAreaToDecorationsChanged { get; set; }
/// <summary>
/// Gets a flag that indicates if Managed decorations i.e. caption buttons are required.
/// This property is used when <see cref="IsClientAreaExtendedToDecorations"/> is set.
/// </summary>
bool NeedsManagedDecorations { get; }
/// <summary>
/// Gets a thickness that describes the amount each side of the non-client area extends into the client area.
/// It includes the titlebar.
/// </summary>
Thickness ExtendedMargins { get; }
/// <summary>
/// Gets a thickness that describes the margin around the window that is offscreen.
/// This may happen when a window is maximized and <see cref="IsClientAreaExtendedToDecorations"/> is set.
/// </summary>
Thickness OffScreenMargin { get; }
/// <summary> /// <summary>
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler. /// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
/// </summary> /// </summary>
@ -94,5 +122,23 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
/// ///
void SetMinMaxSize(Size minSize, Size maxSize); void SetMinMaxSize(Size minSize, Size maxSize);
/// <summary>
/// Sets if the ClientArea is extended into the non-client area.
/// </summary>
/// <param name="extendIntoClientAreaHint">true to enable, false to disable</param>
void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint);
/// <summary>
/// Sets hints that configure how the client area extends.
/// </summary>
/// <param name="hints"></param>
void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints);
/// <summary>
/// Sets how big the non-client titlebar area should be.
/// </summary>
/// <param name="titleBarHeight">-1 for platform default, otherwise the height in DIPs.</param>
void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight);
} }
} }

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> /// <summary>
/// Moves to the selected page, animating if a <see cref="PageTransition"/> is set. /// Moves to the selected page, animating if a <see cref="PageTransition"/> is set.
/// </summary> /// </summary>

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

@ -86,7 +86,7 @@ namespace Avalonia.Controls.Presenters
private IControl _child; private IControl _child;
private bool _createdChild; private bool _createdChild;
private IDataTemplate _dataTemplate; private IRecyclingDataTemplate _recyclingDataTemplate;
private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper(); private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper();
/// <summary> /// <summary>
@ -95,6 +95,7 @@ namespace Avalonia.Controls.Presenters
static ContentPresenter() static ContentPresenter()
{ {
AffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty); AffectsRender<ContentPresenter>(BackgroundProperty, BorderBrushProperty, BorderThicknessProperty, CornerRadiusProperty);
AffectsArrange<ContentPresenter>(HorizontalContentAlignmentProperty, VerticalContentAlignmentProperty);
AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty); AffectsMeasure<ContentPresenter>(BorderThicknessProperty, PaddingProperty);
ContentProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.ContentChanged(e)); ContentProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.ContentChanged(e));
ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.ContentChanged(e)); ContentTemplateProperty.Changed.AddClassHandler<ContentPresenter>((x, e) => x.ContentChanged(e));
@ -280,7 +281,7 @@ namespace Avalonia.Controls.Presenters
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{ {
base.OnAttachedToLogicalTree(e); base.OnAttachedToLogicalTree(e);
_dataTemplate = null; _recyclingDataTemplate = null;
_createdChild = false; _createdChild = false;
InvalidateMeasure(); InvalidateMeasure();
} }
@ -306,22 +307,21 @@ namespace Avalonia.Controls.Presenters
{ {
var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default; 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 if (dataTemplate is IRecyclingDataTemplate rdt)
// as the old data template, try to recycle the existing child control to display
// the new data.
if (dataTemplate == _dataTemplate && dataTemplate.SupportsRecycling)
{ {
newChild = oldChild; var toRecycle = rdt == _recyclingDataTemplate ? oldChild : null;
newChild = rdt.Build(content, toRecycle);
_recyclingDataTemplate = rdt;
} }
else else
{ {
_dataTemplate = dataTemplate; newChild = dataTemplate.Build(content);
newChild = _dataTemplate.Build(content); _recyclingDataTemplate = null;
} }
} }
else else
{ {
_dataTemplate = null; _recyclingDataTemplate = null;
} }
return newChild; return newChild;
@ -421,7 +421,7 @@ namespace Avalonia.Controls.Presenters
LogicalChildren.Remove(Child); LogicalChildren.Remove(Child);
((ISetInheritanceParent)Child).SetParent(Child.Parent); ((ISetInheritanceParent)Child).SetParent(Child.Parent);
Child = null; Child = null;
_dataTemplate = null; _recyclingDataTemplate = null;
} }
InvalidateMeasure(); InvalidateMeasure();

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

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

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

@ -273,7 +273,7 @@ namespace Avalonia.Controls.Presenters
return new FormattedText return new FormattedText
{ {
Constraint = constraint, Constraint = constraint,
Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily, FontWeight, FontStyle), Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily, FontStyle, FontWeight),
FontSize = FontSize, FontSize = FontSize,
Text = text ?? string.Empty, Text = text ?? string.Empty,
TextAlignment = TextAlignment, TextAlignment = TextAlignment,
@ -490,7 +490,7 @@ namespace Avalonia.Controls.Presenters
return new FormattedText return new FormattedText
{ {
Text = "X", Text = "X",
Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily, FontWeight, FontStyle), Typeface = FontManager.Current?.GetOrAddTypeface(FontFamily, FontStyle, FontWeight),
FontSize = FontSize, FontSize = FontSize,
TextAlignment = TextAlignment, TextAlignment = TextAlignment,
Constraint = availableSize, Constraint = availableSize,

21
src/Avalonia.Controls/Primitives/AccessText.cs

@ -97,9 +97,7 @@ namespace Avalonia.Controls.Primitives
{ {
var lastLine = TextLayout.TextLines[TextLayout.TextLines.Count - 1]; var lastLine = TextLayout.TextLines[TextLayout.TextLines.Count - 1];
var offsetX = lastLine.LineMetrics.BaselineOrigin.X; var lineX = lastLine.LineMetrics.Size.Width;
var lineX = offsetX + lastLine.LineMetrics.Size.Width;
var lineY = Bounds.Height - lastLine.LineMetrics.Size.Height; var lineY = Bounds.Height - lastLine.LineMetrics.Size.Height;
@ -110,32 +108,33 @@ namespace Avalonia.Controls.Primitives
foreach (var textLine in TextLayout.TextLines) foreach (var textLine in TextLayout.TextLines)
{ {
if (textLine.Text.End < textPosition) if (textLine.TextRange.End < textPosition)
{ {
currentY += textLine.LineMetrics.Size.Height; currentY += textLine.LineMetrics.Size.Height;
continue; continue;
} }
var currentX = textLine.LineMetrics.BaselineOrigin.X; var currentX = 0.0;
foreach (var textRun in textLine.TextRuns) foreach (var textRun in textLine.TextRuns)
{ {
if (!(textRun is ShapedTextRun shapedRun)) if (!(textRun is ShapedTextCharacters shapedTextCharacters))
{ {
continue; continue;
} }
if (shapedRun.GlyphRun.Characters.End < textPosition) if (shapedTextCharacters.GlyphRun.Characters.End < textPosition)
{ {
currentX += shapedRun.GlyphRun.Bounds.Width; currentX += shapedTextCharacters.GlyphRun.Bounds.Width;
continue; continue;
} }
var characterHit = shapedRun.GlyphRun.FindNearestCharacterHit(textPosition, out var width); var characterHit =
shapedTextCharacters.GlyphRun.FindNearestCharacterHit(textPosition, out var width);
var distance = shapedRun.GlyphRun.GetDistanceFromCharacterHit(characterHit); var distance = shapedTextCharacters.GlyphRun.GetDistanceFromCharacterHit(characterHit);
currentX += distance - width; currentX += distance - width;
@ -144,7 +143,7 @@ namespace Avalonia.Controls.Primitives
width = 0.0; width = 0.0;
} }
return new Rect(currentX, currentY, width, shapedRun.GlyphRun.Bounds.Height); return new Rect(currentX, currentY, width, shapedTextCharacters.GlyphRun.Bounds.Height);
} }
} }

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

@ -0,0 +1,34 @@
using System.Linq;
using Avalonia.Rendering;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Controls.Primitives
{
public class ChromeOverlayLayer : Panel, ICustomSimpleHitTest
{
public static Panel? GetOverlayLayer(IVisual visual)
{
foreach (var v in visual.GetVisualAncestors())
if (v is VisualLayerManager vlm)
if (vlm.OverlayLayer != null)
return vlm.ChromeOverlayLayer;
if (visual is TopLevel tl)
{
var layers = tl.GetVisualDescendants().OfType<VisualLayerManager>().FirstOrDefault();
return layers?.ChromeOverlayLayer;
}
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) 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; var newSelectedIndex = SelectedIndex;

9
src/Avalonia.Controls/Primitives/Track.cs

@ -353,6 +353,15 @@ namespace Avalonia.Controls.Primitives
var trackLength = isVertical ? arrangeSize.Height : arrangeSize.Width; var trackLength = isVertical ? arrangeSize.Height : arrangeSize.Width;
double thumbMinLength = 10; double thumbMinLength = 10;
StyledProperty<double> minLengthProperty = isVertical ? MinHeightProperty : MinWidthProperty;
var thumb = Thumb;
if (thumb != null && thumb.IsSet(minLengthProperty))
{
thumbMinLength = thumb.GetValue(minLengthProperty);
}
thumbLength = trackLength * viewportSize / extent; thumbLength = trackLength * viewportSize / extent;
CoerceLength(ref thumbLength, trackLength); CoerceLength(ref thumbLength, trackLength);
thumbLength = Math.Max(thumbMinLength, thumbLength); thumbLength = Math.Max(thumbMinLength, thumbLength);

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

@ -1,18 +1,23 @@
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Metadata;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
{ {
public class VisualLayerManager : Decorator public class VisualLayerManager : Decorator
{ {
private const int AdornerZIndex = int.MaxValue - 100; private const int AdornerZIndex = int.MaxValue - 100;
private const int OverlayZIndex = int.MaxValue - 99; private const int ChromeZIndex = int.MaxValue - 99;
private const int OverlayZIndex = int.MaxValue - 98;
private ILogicalRoot _logicalRoot; private ILogicalRoot _logicalRoot;
private readonly List<Control> _layers = new List<Control>(); 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 bool IsPopup { get; set; }
public AdornerLayer AdornerLayer public AdornerLayer AdornerLayer
{ {
get get
@ -24,6 +29,26 @@ namespace Avalonia.Controls.Primitives
} }
} }
public ChromeOverlayLayer ChromeOverlayLayer
{
get
{
var current = GetValue(ChromeOverlayLayerProperty);
if (current is null)
{
var chromeOverlayLayer = new ChromeOverlayLayer();
AddLayer(chromeOverlayLayer, ChromeZIndex);
SetValue(ChromeOverlayLayerProperty, chromeOverlayLayer);
current = chromeOverlayLayer;
}
return current;
}
}
public OverlayLayer OverlayLayer public OverlayLayer OverlayLayer
{ {
get get
@ -31,7 +56,7 @@ namespace Avalonia.Controls.Primitives
if (IsPopup) if (IsPopup)
return null; return null;
var rv = FindLayer<OverlayLayer>(); var rv = FindLayer<OverlayLayer>();
if(rv == null) if (rv == null)
AddLayer(rv = new OverlayLayer(), OverlayZIndex); AddLayer(rv = new OverlayLayer(), OverlayZIndex);
return rv; return rv;
} }
@ -52,7 +77,8 @@ namespace Avalonia.Controls.Primitives
layer.ZIndex = zindex; layer.ZIndex = zindex;
VisualChildren.Add(layer); VisualChildren.Add(layer);
if (((ILogical)this).IsAttachedToLogicalTree) if (((ILogical)this).IsAttachedToLogicalTree)
((ILogical)layer).NotifyAttachedToLogicalTree(new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this)); ((ILogical)layer).NotifyAttachedToLogicalTree(
new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this));
InvalidateArrange(); InvalidateArrange();
} }

1
src/Avalonia.Controls/Properties/AssemblyInfo.cs

@ -13,3 +13,4 @@ using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Shapes")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Shapes")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Templates")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Templates")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Notifications")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Notifications")]
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Chrome")]

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

@ -0,0 +1,546 @@
using Avalonia.Layout;
#nullable enable
namespace Avalonia.Controls
{
public partial class RelativePanel
{
private static void OnAlignPropertiesChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e)
{
if (d is Layoutable layoutable && layoutable.Parent is Layoutable layoutableParent)
{
layoutableParent.InvalidateArrange();
}
}
static RelativePanel()
{
ClipToBoundsProperty.OverrideDefaultValue<RelativePanel>(true);
AboveProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignBottomWithPanelProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignBottomWithProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignHorizontalCenterWithPanelProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignHorizontalCenterWithProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignLeftWithPanelProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignLeftWithProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignRightWithPanelProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignRightWithProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignTopWithPanelProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignTopWithProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignVerticalCenterWithPanelProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
AlignVerticalCenterWithProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
BelowProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
LeftOfProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
RightOfProperty.Changed.AddClassHandler<Layoutable>(OnAlignPropertiesChanged);
}
/// <summary>
/// Gets the value of the RelativePanel.Above XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.Above XAML attached property value of the specified object.
/// (The element to position this element above.)
/// </returns>
[ResolveByName]
public static object GetAbove(AvaloniaObject obj)
{
return (object)obj.GetValue(AboveProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to position this element above.)</param>
public static void SetAbove(AvaloniaObject obj, object value)
{
obj.SetValue(AboveProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AboveProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> AboveProperty =
AvaloniaProperty.RegisterAttached<Layoutable, object>("Above", typeof(RelativePanel));
/// <summary>
/// Gets the value of the RelativePanel.AlignBottomWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignBottomWithPanel XAML attached property value of the specified
/// object. (true to align this element's bottom edge with the panel's bottom edge;
/// otherwise, false.)
/// </returns>
public static bool GetAlignBottomWithPanel(AvaloniaObject obj)
{
return (bool)obj.GetValue(AlignBottomWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to align this element's bottom edge with the panel's
/// bottom edge; otherwise, false.)
/// </param>
public static void SetAlignBottomWithPanel(AvaloniaObject obj, bool value)
{
obj.SetValue(AlignBottomWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignBottomWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<bool> AlignBottomWithPanelProperty =
AvaloniaProperty.RegisterAttached<Layoutable, bool>("AlignBottomWithPanel", typeof(RelativePanel));
/// <summary>
/// Gets the value of the RelativePanel.AlignBottomWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignBottomWith XAML attached property value of the specified object.
/// (The element to align this element's bottom edge with.)
/// </returns>
[ResolveByName]
public static object GetAlignBottomWith(AvaloniaObject obj)
{
return (object)obj.GetValue(AlignBottomWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's bottom edge with.)</param>
public static void SetAlignBottomWith(AvaloniaObject obj, object value)
{
obj.SetValue(AlignBottomWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignBottomWithProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> AlignBottomWithProperty =
AvaloniaProperty.RegisterAttached<Layoutable, object>("AlignBottomWith", typeof(RelativePanel));
/// <summary>
/// Gets the value of the RelativePanel.AlignHorizontalCenterWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignHorizontalCenterWithPanel XAML attached property value
/// of the specified object. (true to horizontally center this element in the panel;
/// otherwise, false.)
/// </returns>
public static bool GetAlignHorizontalCenterWithPanel(AvaloniaObject obj)
{
return (bool)obj.GetValue(AlignHorizontalCenterWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to horizontally center this element in the panel; otherwise,
/// false.)
/// </param>
public static void SetAlignHorizontalCenterWithPanel(AvaloniaObject obj, bool value)
{
obj.SetValue(AlignHorizontalCenterWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignHorizontalCenterWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<bool> AlignHorizontalCenterWithPanelProperty =
AvaloniaProperty.RegisterAttached<Layoutable, bool>("AlignHorizontalCenterWithPanel", typeof(RelativePanel), false);
/// <summary>
/// Gets the value of the RelativePanel.AlignHorizontalCenterWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignHorizontalCenterWith XAML attached property value of the
/// specified object. (The element to align this element's horizontal center with.)
/// </returns>
[ResolveByName]
public static object GetAlignHorizontalCenterWith(AvaloniaObject obj)
{
return (object)obj.GetValue(AlignHorizontalCenterWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's horizontal center with.)</param>
public static void SetAlignHorizontalCenterWith(AvaloniaObject obj, object value)
{
obj.SetValue(AlignHorizontalCenterWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignHorizontalCenterWithProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> AlignHorizontalCenterWithProperty =
AvaloniaProperty.RegisterAttached<Layoutable, object>("AlignHorizontalCenterWith", typeof(object), typeof(RelativePanel));
/// <summary>
/// Gets the value of the RelativePanel.AlignLeftWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignLeftWithPanel XAML attached property value of the specified
/// object. (true to align this element's left edge with the panel's left edge; otherwise,
/// false.)
/// </returns>
public static bool GetAlignLeftWithPanel(AvaloniaObject obj)
{
return (bool)obj.GetValue(AlignLeftWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to align this element's left edge with the panel's left
/// edge; otherwise, false.)
/// </param>
public static void SetAlignLeftWithPanel(AvaloniaObject obj, bool value)
{
obj.SetValue(AlignLeftWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignLeftWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<bool> AlignLeftWithPanelProperty =
AvaloniaProperty.RegisterAttached<Layoutable, bool>("AlignLeftWithPanel", typeof(RelativePanel), false);
/// <summary>
/// Gets the value of the RelativePanel.AlignLeftWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignLeftWith XAML attached property value of the specified
/// object. (The element to align this element's left edge with.)
/// </returns>
[ResolveByName]
public static object GetAlignLeftWith(AvaloniaObject obj)
{
return (object)obj.GetValue(AlignLeftWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's left edge with.)</param>
public static void SetAlignLeftWith(AvaloniaObject obj, object value)
{
obj.SetValue(AlignLeftWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignLeftWithProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> AlignLeftWithProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, object>("AlignLeftWith");
/// <summary>
/// Gets the value of the RelativePanel.AlignRightWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignRightWithPanel XAML attached property value of the specified
/// object. (true to align this element's right edge with the panel's right edge;
/// otherwise, false.)
/// </returns>
public static bool GetAlignRightWithPanel(AvaloniaObject obj)
{
return (bool)obj.GetValue(AlignRightWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to align this element's right edge with the panel's right
/// edge; otherwise, false.)
/// </param>
public static void SetAlignRightWithPanel(AvaloniaObject obj, bool value)
{
obj.SetValue(AlignRightWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignRightWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<bool> AlignRightWithPanelProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, bool>("AlignRightWithPanel", false);
/// <summary>
/// Gets the value of the RelativePanel.AlignRightWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignRightWith XAML attached property value of the specified
/// object. (The element to align this element's right edge with.)
/// </returns>
[ResolveByName]
public static object GetAlignRightWith(AvaloniaObject obj)
{
return (object)obj.GetValue(AlignRightWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignRightWith XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's right edge with.)</param>
public static void SetAlignRightWith(AvaloniaObject obj, object value)
{
obj.SetValue(AlignRightWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignRightWithProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> AlignRightWithProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, object>("AlignRightWith");
/// <summary>
/// Gets the value of the RelativePanel.AlignTopWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignTopWithPanel XAML attached property value of the specified
/// object. (true to align this element's top edge with the panel's top edge; otherwise,
/// false.)
/// </returns>
public static bool GetAlignTopWithPanel(AvaloniaObject obj)
{
return (bool)obj.GetValue(AlignTopWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignTopWithPanel XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to align this element's top edge with the panel's top
/// edge; otherwise, false.)
/// </param>
public static void SetAlignTopWithPanel(AvaloniaObject obj, bool value)
{
obj.SetValue(AlignTopWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignTopWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<bool> AlignTopWithPanelProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, bool>("AlignTopWithPanel", false);
/// <summary>
/// Gets the value of the RelativePanel.AlignTopWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>The value to set. (The element to align this element's top edge with.)</returns>
[ResolveByName]
public static object GetAlignTopWith(AvaloniaObject obj)
{
return (object)obj.GetValue(AlignTopWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignTopWith XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's top edge with.)</param>
public static void SetAlignTopWith(AvaloniaObject obj, object value)
{
obj.SetValue(AlignTopWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignTopWithProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> AlignTopWithProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, object>("AlignTopWith");
/// <summary>
/// Gets the value of the RelativePanel.AlignVerticalCenterWithPanel XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.AlignVerticalCenterWithPanel XAML attached property value of
/// the specified object. (true to vertically center this element in the panel; otherwise,
/// false.)
/// </returns>
public static bool GetAlignVerticalCenterWithPanel(AvaloniaObject obj)
{
return (bool)obj.GetValue(AlignVerticalCenterWithPanelProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignVerticalCenterWithPanel XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">
/// The value to set. (true to vertically center this element in the panel; otherwise,
/// false.)
/// </param>
public static void SetAlignVerticalCenterWithPanel(AvaloniaObject obj, bool value)
{
obj.SetValue(AlignVerticalCenterWithPanelProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignVerticalCenterWithPanelProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<bool> AlignVerticalCenterWithPanelProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, bool>("AlignVerticalCenterWithPanel", false);
/// <summary>
/// Gets the value of the RelativePanel.AlignVerticalCenterWith XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>The value to set. (The element to align this element's vertical center with.)</returns>
[ResolveByName]
public static object GetAlignVerticalCenterWith(AvaloniaObject obj)
{
return (object)obj.GetValue(AlignVerticalCenterWithProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.AlignVerticalCenterWith XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to align this element's horizontal center with.)</param>
public static void SetAlignVerticalCenterWith(AvaloniaObject obj, object value)
{
obj.SetValue(AlignVerticalCenterWithProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.AlignVerticalCenterWithProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> AlignVerticalCenterWithProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, object>("AlignVerticalCenterWith");
/// <summary>
/// Gets the value of the RelativePanel.Below XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.Below XAML attached property value of the specified object.
/// (The element to position this element below.)
/// </returns>
[ResolveByName]
public static object GetBelow(AvaloniaObject obj)
{
return (object)obj.GetValue(BelowProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.Above XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to position this element below.)</param>
public static void SetBelow(AvaloniaObject obj, object value)
{
obj.SetValue(BelowProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.BelowProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> BelowProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, object>("Below");
/// <summary>
/// Gets the value of the RelativePanel.LeftOf XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.LeftOf XAML attached property value of the specified object.
/// (The element to position this element to the left of.)
/// </returns>
[ResolveByName]
public static object GetLeftOf(AvaloniaObject obj)
{
return (object)obj.GetValue(LeftOfProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.LeftOf XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to position this element to the left of.)</param>
public static void SetLeftOf(AvaloniaObject obj, object value)
{
obj.SetValue(LeftOfProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.LeftOfProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> LeftOfProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, object>("LeftOf");
/// <summary>
/// Gets the value of the RelativePanel.RightOf XAML attached property for the target element.
/// </summary>
/// <param name="obj">The object from which the property value is read.</param>
/// <returns>
/// The RelativePanel.RightOf XAML attached property value of the specified object.
/// (The element to position this element to the right of.)
/// </returns>
[ResolveByName]
public static object GetRightOf(AvaloniaObject obj)
{
return (object)obj.GetValue(RightOfProperty);
}
/// <summary>
/// Sets the value of the RelativePanel.RightOf XAML attached property for a target element.
/// </summary>
/// <param name="obj">The object to which the property value is written.</param>
/// <param name="value">The value to set. (The element to position this element to the right of.)</param>
public static void SetRightOf(AvaloniaObject obj, object value)
{
obj.SetValue(RightOfProperty, value);
}
/// <summary>
/// Identifies the <see cref="RelativePanel.RightOfProperty"/> XAML attached property.
/// </summary>
public static readonly AttachedProperty<object> RightOfProperty =
AvaloniaProperty.RegisterAttached<RelativePanel, Layoutable, object>("RightOf");
}
}

536
src/Avalonia.Controls/RelativePanel.cs

@ -0,0 +1,536 @@
// 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;
using Avalonia.Layout;
#nullable enable
namespace Avalonia.Controls
{
public partial class RelativePanel : Panel
{
private readonly Graph _childGraph;
public RelativePanel() => _childGraph = new Graph();
private Layoutable? GetDependencyElement(AvaloniaProperty property, AvaloniaObject child)
{
var dependency = child.GetValue(property);
if (dependency is Layoutable layoutable)
{
if (Children.Contains((ILayoutable)layoutable))
return layoutable;
throw new ArgumentException($"RelativePanel error: Element does not exist in the current context: {property.Name}");
}
return null;
}
protected override Size MeasureOverride(Size availableSize)
{
_childGraph.Clear();
foreach (Layoutable child in Children)
{
if (child == null)
continue;
var node = _childGraph.AddNode(child);
node.AlignLeftWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignLeftWithProperty, child));
node.AlignTopWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignTopWithProperty, child));
node.AlignRightWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignRightWithProperty, child));
node.AlignBottomWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignBottomWithProperty, child));
node.LeftOfNode = _childGraph.AddLink(node, GetDependencyElement(LeftOfProperty, child));
node.AboveNode = _childGraph.AddLink(node, GetDependencyElement(AboveProperty, child));
node.RightOfNode = _childGraph.AddLink(node, GetDependencyElement(RightOfProperty, child));
node.BelowNode = _childGraph.AddLink(node, GetDependencyElement(BelowProperty, child));
node.AlignHorizontalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignHorizontalCenterWithProperty, child));
node.AlignVerticalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignVerticalCenterWithProperty, child));
}
_childGraph.Measure(availableSize);
_childGraph.Reset(false);
var boundingSize = _childGraph.GetBoundingSize(Width.IsNaN(), Height.IsNaN());
_childGraph.Reset();
_childGraph.Measure(boundingSize);
return boundingSize;
}
protected override Size ArrangeOverride(Size arrangeSize)
{
_childGraph.GetNodes().Do(node => node.Arrange(arrangeSize));
return arrangeSize;
}
private class GraphNode
{
public bool Measured { get; set; }
public Layoutable Element { get; }
private bool HorizontalOffsetFlag { get; set; }
private bool VerticalOffsetFlag { get; set; }
private Size BoundingSize { get; set; }
public Size OriginDesiredSize { get; set; }
public double Left { get; set; } = double.NaN;
public double Top { get; set; } = double.NaN;
public double Right { get; set; } = double.NaN;
public double Bottom { get; set; } = double.NaN;
public HashSet<GraphNode> OutgoingNodes { get; }
public GraphNode? AlignLeftWithNode { get; set; }
public GraphNode? AlignTopWithNode { get; set; }
public GraphNode? AlignRightWithNode { get; set; }
public GraphNode? AlignBottomWithNode { get; set; }
public GraphNode? LeftOfNode { get; set; }
public GraphNode? AboveNode { get; set; }
public GraphNode? RightOfNode { get; set; }
public GraphNode? BelowNode { get; set; }
public GraphNode? AlignHorizontalCenterWith { get; set; }
public GraphNode? AlignVerticalCenterWith { get; set; }
public GraphNode(Layoutable element)
{
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 AvailableSize { get; set; }
public Graph() => _nodeDic = new Dictionary<AvaloniaObject, GraphNode>();
public IEnumerable<GraphNode> GetNodes() => _nodeDic.Values;
public void Clear()
{
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)
return null;
GraphNode nodeTo;
if (_nodeDic.ContainsKey(to))
{
nodeTo = _nodeDic[to];
}
else
{
nodeTo = new GraphNode(to);
_nodeDic[to] = nodeTo;
}
from.OutgoingNodes.Add(nodeTo);
return nodeTo;
}
public GraphNode AddNode(Layoutable value)
{
if (!_nodeDic.ContainsKey(value))
{
var node = new GraphNode(value);
_nodeDic.Add(value, node);
return node;
}
return _nodeDic[value];
}
public void Measure(Size availableSize)
{
AvailableSize = availableSize;
Measure(_nodeDic.Values, null);
}
private void Measure(IEnumerable<GraphNode> nodes, HashSet<AvaloniaObject>? set)
{
set ??= new HashSet<AvaloniaObject>();
foreach (var node in nodes)
{
if (!node.Measured && !node.OutgoingNodes.Any())
{
MeasureChild(node);
continue;
}
if (node.OutgoingNodes.All(item => item.Measured))
{
MeasureChild(node);
continue;
}
if (!set.Add(node.Element))
throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete.");
Measure(node.OutgoingNodes, set);
if (!node.Measured)
{
MeasureChild(node);
}
}
}
private void MeasureChild(GraphNode node)
{
var child = node.Element;
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
node.OriginDesiredSize = child.DesiredSize;
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)
{
node.Left = node.Left.IsNaN() ? node.AlignLeftWithNode.Left : node.AlignLeftWithNode.Left * 0.5;
}
if (node.AlignTopWithNode != null)
{
node.Top = node.Top.IsNaN() ? node.AlignTopWithNode.Top : node.AlignTopWithNode.Top * 0.5;
}
if (node.AlignRightWithNode != null)
{
node.Right = node.Right.IsNaN()
? node.AlignRightWithNode.Right
: node.AlignRightWithNode.Right * 0.5;
}
if (node.AlignBottomWithNode != null)
{
node.Bottom = node.Bottom.IsNaN()
? node.AlignBottomWithNode.Bottom
: node.AlignBottomWithNode.Bottom * 0.5;
}
var availableHeight = AvailableSize.Height - node.Top - node.Bottom;
if (availableHeight.IsNaN())
{
availableHeight = AvailableSize.Height;
if (!node.Top.IsNaN() && node.Bottom.IsNaN())
{
availableHeight -= node.Top;
}
else if (node.Top.IsNaN() && !node.Bottom.IsNaN())
{
availableHeight -= node.Bottom;
}
}
var availableWidth = AvailableSize.Width - node.Left - node.Right;
if (availableWidth.IsNaN())
{
availableWidth = AvailableSize.Width;
if (!node.Left.IsNaN() && node.Right.IsNaN())
{
availableWidth -= node.Left;
}
else if (node.Left.IsNaN() && !node.Right.IsNaN())
{
availableWidth -= node.Right;
}
}
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())
{
node.Right = node.RightOfNode.Right - childSize.Width;
}
if (node.Left.IsNaN())
{
node.Left = AvailableSize.Width - node.RightOfNode.Right;
}
}
if (node.BelowNode != null)
{
if (node.Bottom.IsNaN())
{
node.Bottom = node.BelowNode.Bottom - childSize.Height;
}
if (node.Top.IsNaN())
{
node.Top = AvailableSize.Height - node.BelowNode.Bottom;
}
}
if (node.AlignHorizontalCenterWith != null)
{
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
node.Left = (node.Left + halfWidthLeft) * 0.5;
if (node.Right.IsNaN())
node.Right = halfWidthRight;
else
node.Right = (node.Right + halfWidthRight) * 0.5;
}
if (node.AlignVerticalCenterWith != null)
{
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
node.Top = (node.Top + halfHeightTop) * 0.5;
if (node.Bottom.IsNaN())
node.Bottom = halfHeightBottom;
else
node.Bottom = (node.Bottom + halfHeightBottom) * 0.5;
}
if (GetAlignHorizontalCenterWithPanel(child))
{
var halfSubWidth = (AvailableSize.Width - childSize.Width) * 0.5;
if (node.Left.IsNaN())
node.Left = halfSubWidth;
else
node.Left = (node.Left + halfSubWidth) * 0.5;
if (node.Right.IsNaN())
node.Right = halfSubWidth;
else
node.Right = (node.Right + halfSubWidth) * 0.5;
}
if (GetAlignVerticalCenterWithPanel(child))
{
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
{
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
{
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);
}
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))); GetAvaloniaInputModifiers(pressed.Modifiers)));
}, DispatcherPriority.Input); }, DispatcherPriority.Input);
} }
if (obj is PointerPressedEventMessage released) if (obj is PointerReleasedEventMessage released)
{ {
Dispatcher.UIThread.Post(() => 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 namespace Avalonia.Controls
{ {
internal class ItemTemplateWrapper internal class ItemTemplateWrapper : IElementFactory
{ {
private readonly IDataTemplate _dataTemplate; private readonly IDataTemplate _dataTemplate;
public ItemTemplateWrapper(IDataTemplate dataTemplate) => _dataTemplate = 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 selectedTemplate = _dataTemplate;
var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate); var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);
@ -37,7 +50,7 @@ namespace Avalonia.Controls
return element; return element;
} }
public void RecycleElement(IControl parent, IControl element) private void RecycleElement(IControl parent, IControl element)
{ {
var selectedTemplate = _dataTemplate; var selectedTemplate = _dataTemplate;
var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate); var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);

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

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

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

@ -11,10 +11,13 @@ using Avalonia.Controls.Templates;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
internal class RecyclePool public class RecyclePool
{ {
public static readonly AttachedProperty<IDataTemplate> OriginTemplateProperty = internal static readonly AttachedProperty<IDataTemplate> OriginTemplateProperty =
AvaloniaProperty.RegisterAttached<Control, IDataTemplate>("OriginTemplate", typeof(RecyclePool)); 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 static ConditionalWeakTable<IDataTemplate, RecyclePool> s_pools = new ConditionalWeakTable<IDataTemplate, RecyclePool>();
private readonly Dictionary<string, List<ElementInfo>> _elements = new Dictionary<string, List<ElementInfo>>(); private readonly Dictionary<string, List<ElementInfo>> _elements = new Dictionary<string, List<ElementInfo>>();
@ -77,6 +80,9 @@ namespace Avalonia.Controls
return null; 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) private IPanel EnsureOwnerIsPanelOrNull(IControl owner)
{ {
if (owner is IPanel panel) if (owner is IPanel panel)

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

Loading…
Cancel
Save