diff --git a/.gitmodules b/.gitmodules
index 9dbc50ef61..2d11fdfa9e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,5 +2,5 @@
path = nukebuild/Numerge
url = https://github.com/kekekeks/Numerge.git
[submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"]
- path = src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github
+ path = src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
url = https://github.com/kekekeks/XamlX.git
diff --git a/Avalonia.sln b/Avalonia.sln
index 4ab647a25e..ffd7b4d4f4 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -123,10 +123,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
+ build\AndroidWorkarounds.props = build\AndroidWorkarounds.props
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
- build\BuildTargets.targets = build\BuildTargets.targets
+ build\CoreLibraries.props = build\CoreLibraries.props
+ build\EmbedXaml.props = build\EmbedXaml.props
build\HarfBuzzSharp.props = build\HarfBuzzSharp.props
+ build\iOSWorkarounds.props = build\iOSWorkarounds.props
build\JetBrains.Annotations.props = build\JetBrains.Annotations.props
build\JetBrains.dotMemoryUnit.props = build\JetBrains.dotMemoryUnit.props
build\Magick.NET-Q16-AnyCPU.props = build\Magick.NET-Q16-AnyCPU.props
@@ -136,17 +139,25 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\NetCore.props = build\NetCore.props
build\NetFX.props = build\NetFX.props
build\ReactiveUI.props = build\ReactiveUI.props
+ build\ReferenceCoreLibraries.props = build\ReferenceCoreLibraries.props
build\Rx.props = build\Rx.props
build\SampleApp.props = build\SampleApp.props
+ build\SharedVersion.props = build\SharedVersion.props
build\SharpDX.props = build\SharpDX.props
build\SkiaSharp.props = build\SkiaSharp.props
+ build\SourceLink.props = build\SourceLink.props
+ build\System.Drawing.Common.props = build\System.Drawing.Common.props
build\System.Memory.props = build\System.Memory.props
+ build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
build\XUnit.props = build\XUnit.props
+ build\ApiDiff.props = build\ApiDiff.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"
ProjectSection(SolutionItems) = preProject
build\UnitTests.NetCore.targets = build\UnitTests.NetCore.targets
+ build\BuildTargets.targets = build\BuildTargets.targets
+ build\LegacyProject.targets = build\LegacyProject.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}"
@@ -211,6 +222,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless", "src\Av
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.Vnc", "src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj", "{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Markup.Xaml.Loader", "src\Markup\Avalonia.Markup.Xaml.Loader\Avalonia.Markup.Xaml.Loader.csproj", "{909A8CBD-7D0E-42FD-B841-022AD8925820}"
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@@ -1998,6 +2011,30 @@ Global
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhone.Build.0 = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|Any CPU.Build.0 = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhone.Build.0 = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {909A8CBD-7D0E-42FD-B841-022AD8925820}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2056,6 +2093,7 @@ Global
{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{3C84E04B-36CF-4D0D-B965-C26DD649D1F3} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9}
+ {909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}
diff --git a/Avalonia.v3.ncrunchsolution b/Avalonia.v3.ncrunchsolution
index a2208a9a91..bef7e45524 100644
--- a/Avalonia.v3.ncrunchsolution
+++ b/Avalonia.v3.ncrunchsolution
@@ -3,6 +3,7 @@
tests\TestFiles\**.*
src\Avalonia.Build.Tasks\bin\Debug\netstandard2.0\Avalonia.Build.Tasks.dll
+ src\Avalonia.Build.Tasks\bin\Debug\netstandard2.0\Mono.Cecil.dll
True
.ncrunch
diff --git a/Directory.Build.props b/Directory.Build.props
index 1f26df9bbc..b41f8c488e 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,5 +1,6 @@
$(MSBuildThisFileDirectory)build-intermediate/nuget
+ $(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll
diff --git a/NuGet.Config b/NuGet.Config
index 2a1e0af74d..3abd236d42 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -1,7 +1,9 @@
+
+
diff --git a/build/ApiDiff.props b/build/ApiDiff.props
new file mode 100644
index 0000000000..1573dc2dd6
--- /dev/null
+++ b/build/ApiDiff.props
@@ -0,0 +1,12 @@
+
+
+ 0.10.0-preview2
+ $(PackageId)
+ Avalonia
+
+
+
+
+
+
+
diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props
index a8d9332c57..bbef48050e 100644
--- a/build/SkiaSharp.props
+++ b/build/SkiaSharp.props
@@ -1,6 +1,6 @@
-
+
diff --git a/dirs.proj b/dirs.proj
index 4b3b1183f0..26c8f54b23 100644
--- a/dirs.proj
+++ b/dirs.proj
@@ -6,7 +6,7 @@
-
+
diff --git a/native/Avalonia.Native/src/OSX/Screens.mm b/native/Avalonia.Native/src/OSX/Screens.mm
index 278daf9a18..455cfa2e41 100644
--- a/native/Avalonia.Native/src/OSX/Screens.mm
+++ b/native/Avalonia.Native/src/OSX/Screens.mm
@@ -4,6 +4,14 @@ class Screens : public ComSingleObject
{
public:
FORWARD_IUNKNOWN()
+
+ private:
+ CGFloat PrimaryDisplayHeight()
+ {
+ return NSMaxY([[[NSScreen screens] firstObject] frame]);
+ }
+
+public:
virtual HRESULT GetScreenCount (int* ret) override
{
@autoreleasepool
@@ -25,15 +33,15 @@ class Screens : public ComSingleObject
auto screen = [[NSScreen screens] objectAtIndex:index];
- ret->Bounds.X = [screen frame].origin.x;
- ret->Bounds.Y = [screen frame].origin.y;
ret->Bounds.Height = [screen frame].size.height;
ret->Bounds.Width = [screen frame].size.width;
+ ret->Bounds.X = [screen frame].origin.x;
+ ret->Bounds.Y = PrimaryDisplayHeight() - [screen frame].origin.y - ret->Bounds.Height;
- ret->WorkingArea.X = [screen visibleFrame].origin.x;
- ret->WorkingArea.Y = [screen visibleFrame].origin.y;
ret->WorkingArea.Height = [screen visibleFrame].size.height;
ret->WorkingArea.Width = [screen visibleFrame].size.width;
+ ret->WorkingArea.X = [screen visibleFrame].origin.x;
+ ret->WorkingArea.Y = ret->Bounds.Height - [screen visibleFrame].origin.y - ret->WorkingArea.Height;
ret->PixelDensity = [screen backingScaleFactor];
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index 872269bb26..2d0ffbe4f0 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -1291,10 +1291,15 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
_parent->UpdateCursor();
auto fsize = [self convertSizeToBacking: [self frame].size];
- _lastPixelSize.Width = (int)fsize.width;
- _lastPixelSize.Height = (int)fsize.height;
- [self updateRenderTarget];
- _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
+
+ if(_lastPixelSize.Width != (int)fsize.width || _lastPixelSize.Height != (int)fsize.height)
+ {
+ _lastPixelSize.Width = (int)fsize.width;
+ _lastPixelSize.Height = (int)fsize.height;
+ [self updateRenderTarget];
+
+ _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
+ }
}
- (void)updateLayer
diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs
index fe877dc49c..fbfbf47e1b 100644
--- a/nukebuild/Build.cs
+++ b/nukebuild/Build.cs
@@ -128,7 +128,9 @@ partial class Build : NukeBuild
{
var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp";
- NpmTasks.NpmInstall(c => c.SetWorkingDirectory(webappDir));
+ NpmTasks.NpmInstall(c => c
+ .SetWorkingDirectory(webappDir)
+ .SetArgumentConfigurator(a => a.Add("--silent")));
NpmTasks.NpmRun(c => c
.SetWorkingDirectory(webappDir)
.SetCommand("dist"));
diff --git a/readme.md b/readme.md
index 19a9a8420d..c1bf5bbfde 100644
--- a/readme.md
+++ b/readme.md
@@ -1,4 +1,4 @@
-[](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) [](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [](#backers) [](#sponsors) 
+[](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) []( https://aka.ms/dotnet-discord) [](https://dev.azure.com/AvaloniaUI/AvaloniaUI/_build/latest?definitionId=4) [](#backers) [](#sponsors) 
[](https://www.nuget.org/packages/Avalonia) [](https://www.nuget.org/packages/Avalonia) [](https://www.myget.org/gallery/avalonia-ci) 
diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs
index 0baa6589c6..3f1ec289d1 100644
--- a/samples/ControlCatalog/App.xaml.cs
+++ b/samples/ControlCatalog/App.xaml.cs
@@ -29,11 +29,19 @@ namespace ControlCatalog
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
- Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default")
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
- Source = new Uri("resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default")
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
+ },
+ new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml")
+ },
+ new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
},
};
@@ -41,11 +49,19 @@ namespace ControlCatalog
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
- Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default")
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
+ },
+ new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
+ },
+ new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
+ {
+ Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseDark.xaml")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
- Source = new Uri("resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default")
+ Source = new Uri("avares://Avalonia.Themes.Default/DefaultTheme.xaml")
},
};
diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index fa4fd7dd07..af95e3c356 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -2,7 +2,6 @@
xmlns:pages="clr-namespace:ControlCatalog.Pages"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.MainView"
- Background="Black"
Foreground="{DynamicResource ThemeForegroundBrush}"
FontSize="{DynamicResource FontSizeNormal}">
diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
index 08bb1584a9..304782dbf9 100644
--- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
+++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml
@@ -1,6 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ItemsRepeater
@@ -12,8 +36,6 @@
Stack - Horizontal
UniformGrid - Vertical
UniformGrid - Horizontal
- WrapLayout - Horizontal
- WrapLayout - Veritcal
@@ -25,20 +47,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
index a4003cadc9..9e898c4536 100644
--- a/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
+++ b/samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
@@ -38,6 +38,12 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this);
}
+ public void OnSelectTemplateKey(object sender, SelectTemplateEventArgs e)
+ {
+ var item = (ItemsRepeaterPageViewModel.Item)e.DataContext;
+ e.TemplateKey = (item.Index % 2 == 0) ? "even" : "odd";
+ }
+
private void LayoutChanged(object sender, SelectionChangedEventArgs e)
{
if (_repeater == null)
diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml
index 47b4ce7151..f4d81418ac 100644
--- a/samples/ControlCatalog/Pages/ListBoxPage.xaml
+++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml
@@ -10,7 +10,13 @@
HorizontalAlignment="Center"
Spacing="16">
-
+
diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml
index de9ea34e80..e9d2301e89 100644
--- a/samples/ControlCatalog/Pages/MenuPage.xaml
+++ b/samples/ControlCatalog/Pages/MenuPage.xaml
@@ -20,7 +20,9 @@
@@ -52,6 +54,33 @@
+
+
+
+ Mixed
+
+
diff --git a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
index 73aaeff994..f893a6e28e 100644
--- a/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
+++ b/samples/ControlCatalog/ViewModels/ItemsRepeaterPageViewModel.cs
@@ -62,13 +62,9 @@ namespace ControlCatalog.ViewModels
public class Item : ReactiveObject
{
private double _height = double.NaN;
- private int _index;
-
- public Item(int index)
- {
- _index = index;
- }
+ public Item(int index) => Index = index;
+ public int Index { get; }
public string Text { get; set; }
public double Height
@@ -76,8 +72,6 @@ namespace ControlCatalog.ViewModels
get => _height;
set => this.RaiseAndSetIfChanged(ref _height, value);
}
-
- public IBrush Background => ((_index % 2) == 0) ? Brushes.Yellow : Brushes.Wheat;
}
}
}
diff --git a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
index dc9c4a8f49..9e7ae8b716 100644
--- a/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
+++ b/samples/ControlCatalog/ViewModels/MenuPageViewModel.cs
@@ -17,6 +17,23 @@ namespace ControlCatalog.ViewModels
SaveCommand = ReactiveCommand.Create(Save, Observable.Return(false));
OpenRecentCommand = ReactiveCommand.Create(OpenRecent);
+ var recentItems = new[]
+ {
+ new MenuItemViewModel
+ {
+ Header = "File1.txt",
+ Command = OpenRecentCommand,
+ CommandParameter = @"c:\foo\File1.txt"
+ },
+ new MenuItemViewModel
+ {
+ Header = "File2.txt",
+ Command = OpenRecentCommand,
+ CommandParameter = @"c:\foo\File2.txt"
+ },
+ };
+
+ RecentItems = recentItems;
MenuItems = new[]
{
new MenuItemViewModel
@@ -24,27 +41,13 @@ namespace ControlCatalog.ViewModels
Header = "_File",
Items = new[]
{
- new MenuItemViewModel { Header = "_Open...", Command = OpenCommand },
+ new MenuItemViewModel { Header = "O_pen...", Command = OpenCommand },
new MenuItemViewModel { Header = "Save", Command = SaveCommand },
new MenuItemViewModel { Header = "-" },
new MenuItemViewModel
{
Header = "Recent",
- Items = new[]
- {
- new MenuItemViewModel
- {
- Header = "File1.txt",
- Command = OpenRecentCommand,
- CommandParameter = @"c:\foo\File1.txt"
- },
- new MenuItemViewModel
- {
- Header = "File2.txt",
- Command = OpenRecentCommand,
- CommandParameter = @"c:\foo\File2.txt"
- },
- }
+ Items = recentItems
},
}
},
@@ -61,6 +64,7 @@ namespace ControlCatalog.ViewModels
}
public IReadOnlyList MenuItems { get; set; }
+ public IReadOnlyList RecentItems { get; set; }
public ReactiveCommand OpenCommand { get; }
public ReactiveCommand SaveCommand { get; }
public ReactiveCommand OpenRecentCommand { get; }
diff --git a/samples/RenderDemo/App.xaml b/samples/RenderDemo/App.xaml
index ccc3f54cc0..61e4d2385b 100644
--- a/samples/RenderDemo/App.xaml
+++ b/samples/RenderDemo/App.xaml
@@ -3,8 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="RenderDemo.App">
-
-
+
-
\ No newline at end of file
+
diff --git a/samples/RenderDemo/SideBar.xaml b/samples/RenderDemo/SideBar.xaml
index 07fdb91a16..fd23067f61 100644
--- a/samples/RenderDemo/SideBar.xaml
+++ b/samples/RenderDemo/SideBar.xaml
@@ -1,65 +1,68 @@
-
+
-
-
-
-
-
+
+
+
+
+
+
diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
index 69fd2a9f13..71dce93ce7 100644
--- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
+++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
@@ -126,7 +126,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Visibility = ViewStates.Visible;
}
- public double Scaling => 1;
+ public double RenderScaling => 1;
void Draw()
{
diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj
index 73c619942e..ee91acf45e 100644
--- a/src/Avalonia.Animation/Avalonia.Animation.csproj
+++ b/src/Avalonia.Animation/Avalonia.Animation.csproj
@@ -1,9 +1,10 @@
- netstandard2.0
+ netstandard2.0
+
diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index cbcf7a7386..9bfd387ca9 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -3,7 +3,7 @@
netstandard2.0
Avalonia.Base
Avalonia
- True
+ True
@@ -13,4 +13,5 @@
+
diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs
index 0bfc713ba0..397826df53 100644
--- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs
+++ b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs
@@ -9,6 +9,16 @@ namespace Avalonia.Threading
///
public class AvaloniaScheduler : LocalScheduler
{
+ ///
+ /// Users can schedule actions on the dispatcher thread while being on the correct thread already.
+ /// We are optimizing this case by invoking user callback immediately which can lead to stack overflows in certain cases.
+ /// To prevent this we are limiting amount of reentrant calls to before we will
+ /// schedule on a dispatcher anyway.
+ ///
+ private const int MaxReentrantSchedules = 32;
+
+ private int _reentrancyGuard;
+
///
/// The instance of the .
///
@@ -24,31 +34,58 @@ namespace Avalonia.Threading
///
public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action)
{
- var composite = new CompositeDisposable(2);
+ IDisposable PostOnDispatcher()
+ {
+ var composite = new CompositeDisposable(2);
+
+ var cancellation = new CancellationDisposable();
+
+ Dispatcher.UIThread.Post(() =>
+ {
+ if (!cancellation.Token.IsCancellationRequested)
+ {
+ composite.Add(action(this, state));
+ }
+ }, DispatcherPriority.DataBind);
+
+ composite.Add(cancellation);
+
+ return composite;
+ }
+
if (dueTime == TimeSpan.Zero)
{
if (!Dispatcher.UIThread.CheckAccess())
{
- var cancellation = new CancellationDisposable();
- Dispatcher.UIThread.Post(() =>
- {
- if (!cancellation.Token.IsCancellationRequested)
- {
- composite.Add(action(this, state));
- }
- }, DispatcherPriority.DataBind);
- composite.Add(cancellation);
+ return PostOnDispatcher();
}
else
{
- return action(this, state);
+ if (_reentrancyGuard >= MaxReentrantSchedules)
+ {
+ return PostOnDispatcher();
+ }
+
+ try
+ {
+ _reentrancyGuard++;
+
+ return action(this, state);
+ }
+ finally
+ {
+ _reentrancyGuard--;
+ }
}
}
else
{
+ var composite = new CompositeDisposable(2);
+
composite.Add(DispatcherTimer.RunOnce(() => composite.Add(action(this, state)), dueTime));
+
+ return composite;
}
- return composite;
}
}
}
diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
index 38a23f918f..40cf81358f 100644
--- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
+++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
@@ -1,3 +1,5 @@
+using System;
+using System.Runtime.ConstrainedExecution;
using System.Threading;
namespace Avalonia.Threading
@@ -7,6 +9,20 @@ namespace Avalonia.Threading
///
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();
+ }
+
///
/// Controls if SynchronizationContext should be installed in InstallIfNeeded. Used by Designer.
///
@@ -22,7 +38,8 @@ namespace Avalonia.Threading
return;
}
- SetSynchronizationContext(new AvaloniaSynchronizationContext());
+ SetSynchronizationContext(new AvaloniaSynchronizationContext(AvaloniaLocator.Current
+ .GetService()));
}
///
@@ -39,5 +56,13 @@ namespace Avalonia.Threading
else
Dispatcher.UIThread.InvokeAsync(() => d(state), DispatcherPriority.Send).Wait();
}
+
+ [PrePrepareMethod]
+ public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
+ {
+ if (_waitProvider != null)
+ return _waitProvider.Wait(waitHandles, waitAll, millisecondsTimeout);
+ return base.Wait(waitHandles, waitAll, millisecondsTimeout);
+ }
}
}
diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
index 7117fd51a2..490364c0d8 100644
--- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
+++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
@@ -17,14 +17,14 @@
Shared/AvaloniaResourceXamlInfo.cs
-
+
XamlIlExtensions/%(RecursiveDir)%(FileName)%(Extension)
-
+
XamlIl/%(RecursiveDir)%(FileName)%(Extension)
-
+
XamlIl.Cecil/%(RecursiveDir)%(FileName)%(Extension)
@@ -57,8 +57,8 @@
Markup/%(RecursiveDir)%(FileName)%(Extension)
-
-
+
+
diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
index c1cc0e7bf0..c9c9c562bd 100644
--- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
+++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
@@ -128,7 +128,7 @@ namespace Avalonia.Build.Tasks
asm.MainModule.Types.Add(typeDef);
var builder = typeSystem.CreateTypeBuilder(typeDef);
- foreach (var res in group.Resources.Where(CheckXamlName))
+ foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x=>x.FilePath.ToLowerInvariant()))
{
try
{
diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
index 27853a1540..6975642db2 100644
--- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
+++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
@@ -22,4 +22,5 @@
+
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index fe9656a00b..7e921944ea 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -3922,6 +3922,12 @@ namespace Avalonia.Controls
dataGridCell: editingCell);
EditingRow.InvalidateDesiredHeight();
+ var column = editingCell.OwningColumn;
+ if (column.Width.IsSizeToCells || column.Width.IsAuto)
+ {// Invalidate desired width and force recalculation
+ column.SetWidthDesiredValue(0);
+ EditingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width);
+ }
}
// We're done, so raise the CellEditEnded event
diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj
index 32331d29ab..480dcfcb85 100644
--- a/src/Avalonia.Controls/Avalonia.Controls.csproj
+++ b/src/Avalonia.Controls/Avalonia.Controls.csproj
@@ -1,6 +1,6 @@
- netstandard2.0
+ netstandard2.0
@@ -14,4 +14,5 @@
+
diff --git a/src/Avalonia.Controls/Chrome/CaptionButtons.cs b/src/Avalonia.Controls/Chrome/CaptionButtons.cs
index 75d6c366b8..a86cbc271b 100644
--- a/src/Avalonia.Controls/Chrome/CaptionButtons.cs
+++ b/src/Avalonia.Controls/Chrome/CaptionButtons.cs
@@ -38,12 +38,10 @@ namespace Avalonia.Controls.Chrome
{
if (_disposables != null)
{
- var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow);
-
- layer?.Children.Remove(this);
-
_disposables.Dispose();
_disposables = null;
+
+ _hostWindow = null;
}
}
diff --git a/src/Avalonia.Controls/Chrome/TitleBar.cs b/src/Avalonia.Controls/Chrome/TitleBar.cs
index 78b49d2a03..c0c8076dd8 100644
--- a/src/Avalonia.Controls/Chrome/TitleBar.cs
+++ b/src/Avalonia.Controls/Chrome/TitleBar.cs
@@ -12,106 +12,86 @@ namespace Avalonia.Controls.Chrome
public class TitleBar : TemplatedControl
{
private CompositeDisposable? _disposables;
- private readonly Window? _hostWindow;
private CaptionButtons? _captionButtons;
- public TitleBar(Window hostWindow)
+ private void UpdateSize(Window window)
{
- _hostWindow = hostWindow;
- }
-
- public TitleBar()
- {
-
- }
-
- public void Attach()
- {
- if (_disposables == null)
- {
- var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow);
-
- layer?.Children.Add(this);
-
- if (_hostWindow != null)
- {
- _disposables = new CompositeDisposable
- {
- _hostWindow.GetObservable(Window.WindowDecorationMarginProperty)
- .Subscribe(x => UpdateSize()),
-
- _hostWindow.GetObservable(Window.ExtendClientAreaTitleBarHeightHintProperty)
- .Subscribe(x => UpdateSize()),
-
- _hostWindow.GetObservable(Window.OffScreenMarginProperty)
- .Subscribe(x => UpdateSize()),
-
- _hostWindow.GetObservable(Window.WindowStateProperty)
- .Subscribe(x =>
- {
- PseudoClasses.Set(":minimized", x == WindowState.Minimized);
- PseudoClasses.Set(":normal", x == WindowState.Normal);
- PseudoClasses.Set(":maximized", x == WindowState.Maximized);
- PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
- })
- };
-
- _captionButtons?.Attach(_hostWindow);
- }
-
- UpdateSize();
- }
- }
-
- private void UpdateSize()
- {
- if (_hostWindow != null)
+ if (window != null)
{
Margin = new Thickness(
- _hostWindow.OffScreenMargin.Left,
- _hostWindow.OffScreenMargin.Top,
- _hostWindow.OffScreenMargin.Right,
- _hostWindow.OffScreenMargin.Bottom);
+ window.OffScreenMargin.Left,
+ window.OffScreenMargin.Top,
+ window.OffScreenMargin.Right,
+ window.OffScreenMargin.Bottom);
- if (_hostWindow.WindowState != WindowState.FullScreen)
+ if (window.WindowState != WindowState.FullScreen)
{
- Height = _hostWindow.WindowDecorationMargin.Top;
+ Height = window.WindowDecorationMargin.Top;
if (_captionButtons != null)
{
_captionButtons.Height = Height;
}
}
+
+ IsVisible = window.PlatformImpl.NeedsManagedDecorations;
}
}
- public void Detach()
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
- if (_disposables != null)
- {
- var layer = ChromeOverlayLayer.GetOverlayLayer(_hostWindow);
-
- layer?.Children.Remove(this);
+ base.OnApplyTemplate(e);
- _disposables.Dispose();
- _disposables = null;
+ _captionButtons?.Detach();
+
+ _captionButtons = e.NameScope.Get("PART_CaptionButtons");
- _captionButtons?.Detach();
+ if (VisualRoot is Window window)
+ {
+ _captionButtons?.Attach(window);
+
+ UpdateSize(window);
}
}
- protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
- base.OnApplyTemplate(e);
-
- _captionButtons = e.NameScope.Get("PART_CaptionButtons");
+ base.OnAttachedToVisualTree(e);
- if (_hostWindow != null)
+ if (VisualRoot is Window window)
{
- _captionButtons.Attach(_hostWindow);
+ _disposables = new CompositeDisposable
+ {
+ window.GetObservable(Window.WindowDecorationMarginProperty)
+ .Subscribe(x => UpdateSize(window)),
+ window.GetObservable(Window.ExtendClientAreaTitleBarHeightHintProperty)
+ .Subscribe(x => UpdateSize(window)),
+ window.GetObservable(Window.OffScreenMarginProperty)
+ .Subscribe(x => UpdateSize(window)),
+ window.GetObservable(Window.ExtendClientAreaChromeHintsProperty)
+ .Subscribe(x => UpdateSize(window)),
+ window.GetObservable(Window.WindowStateProperty)
+ .Subscribe(x =>
+ {
+ PseudoClasses.Set(":minimized", x == WindowState.Minimized);
+ PseudoClasses.Set(":normal", x == WindowState.Normal);
+ PseudoClasses.Set(":maximized", x == WindowState.Maximized);
+ PseudoClasses.Set(":fullscreen", x == WindowState.FullScreen);
+ }),
+ window.GetObservable(Window.IsExtendedIntoWindowDecorationsProperty)
+ .Subscribe(x => UpdateSize(window))
+ };
}
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
- UpdateSize();
+ _disposables?.Dispose();
+
+ _captionButtons?.Detach();
+ _captionButtons = null;
}
}
}
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
index 5d3311e8c6..a41c159980 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePicker.cs
@@ -88,7 +88,7 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect(nameof(SelectedDate),
x => x.SelectedDate, (x, v) => x.SelectedDate = v);
- //Template Items
+ // Template Items
private Button _flyoutButton;
private TextBlock _dayText;
private TextBlock _monthText;
@@ -359,10 +359,14 @@ namespace Avalonia.Controls
}
}
- Grid.SetColumn(_spacer1, 1);
- Grid.SetColumn(_spacer2, 3);
- _spacer1.IsVisible = columnIndex > 1;
- _spacer2.IsVisible = columnIndex > 2;
+ var isSpacer1Visible = columnIndex > 1;
+ var isSpacer2Visible = columnIndex > 2;
+ // ternary conditional operator is used to make sure grid cells will be validated
+ Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
+ Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
+
+ _spacer1.IsVisible = isSpacer1Visible;
+ _spacer2.IsVisible = isSpacer2Visible;
}
private void SetSelectedDateText()
@@ -398,7 +402,7 @@ namespace Avalonia.Controls
var deltaY = _presenter.GetOffsetForPopup();
- //The extra 5 px I think is related to default popup placement behavior
+ // The extra 5 px I think is related to default popup placement behavior
_popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
diff --git a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
index 8b86e46e88..31527ccb16 100644
--- a/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
@@ -77,7 +77,7 @@ namespace Avalonia.Controls
DatePicker.YearVisibleProperty.AddOwner(x =>
x.YearVisible, (x, v) => x.YearVisible = v);
- //Template Items
+ // Template Items
private Grid _pickerContainer;
private Button _acceptButton;
private Button _dismissButton;
@@ -107,7 +107,7 @@ namespace Avalonia.Controls
private bool _yearVisible = true;
private DateTimeOffset _syncDate;
- private GregorianCalendar _calendar;
+ private readonly GregorianCalendar _calendar;
private bool _suppressUpdateSelection;
public DatePickerPresenter()
@@ -234,7 +234,7 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
- //These are requirements, so throw if not found
+ // These are requirements, so throw if not found
_pickerContainer = e.NameScope.Get("PickerContainer");
_monthHost = e.NameScope.Get("MonthHost");
_dayHost = e.NameScope.Get("DayHost");
@@ -326,7 +326,7 @@ namespace Avalonia.Controls
///
private void InitPicker()
{
- //OnApplyTemplate must've been called before we can init here...
+ // OnApplyTemplate must've been called before we can init here...
if (_pickerContainer == null)
return;
@@ -344,12 +344,11 @@ namespace Avalonia.Controls
SetGrid();
- //Date should've been set when we reach this point
+ // Date should've been set when we reach this point
var dt = Date;
if (DayVisible)
{
- GregorianCalendar gc = new GregorianCalendar();
- var maxDays = gc.GetDaysInMonth(dt.Year, dt.Month);
+ var maxDays = _calendar.GetDaysInMonth(dt.Year, dt.Month);
_daySelector.MaximumValue = maxDays;
_daySelector.MinimumValue = 1;
_daySelector.SelectedValue = dt.Day;
@@ -407,10 +406,14 @@ namespace Avalonia.Controls
}
}
- Grid.SetColumn(_spacer1, 1);
- Grid.SetColumn(_spacer2, 3);
- _spacer1.IsVisible = columnIndex > 1;
- _spacer2.IsVisible = columnIndex > 2;
+ var isSpacer1Visible = columnIndex > 1;
+ var isSpacer2Visible = columnIndex > 2;
+ // ternary conditional operator is used to make sure grid cells will be validated
+ Grid.SetColumn(_spacer1, isSpacer1Visible ? 1 : 0);
+ Grid.SetColumn(_spacer2, isSpacer2Visible ? 3 : 0);
+
+ _spacer1.IsVisible = isSpacer1Visible;
+ _spacer2.IsVisible = isSpacer2Visible;
}
private void SetInitialFocus()
@@ -433,12 +436,12 @@ namespace Avalonia.Controls
}
}
- private void OnDismissButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private void OnDismissButtonClicked(object sender, RoutedEventArgs e)
{
OnDismiss();
}
- private void OnAcceptButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private void OnAcceptButtonClicked(object sender, RoutedEventArgs e)
{
Date = _syncDate;
OnConfirmed();
@@ -471,7 +474,7 @@ namespace Avalonia.Controls
_syncDate = newDate;
- //We don't need to update the days if not displaying day, not february
+ // We don't need to update the days if not displaying day, not february
if (!DayVisible || _syncDate.Month != 2)
return;
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
index 3fa426b1d3..e54da1fb3a 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePicker.cs
@@ -15,7 +15,7 @@ namespace Avalonia.Controls
/// Defines the property
///
public static readonly DirectProperty MinuteIncrementProperty =
- AvaloniaProperty.RegisterDirect(nameof(MinuteIncrement),
+ AvaloniaProperty.RegisterDirect(nameof(MinuteIncrement),
x => x.MinuteIncrement, (x, v) => x.MinuteIncrement = v);
///
@@ -34,17 +34,17 @@ namespace Avalonia.Controls
/// Defines the property
///
public static readonly DirectProperty ClockIdentifierProperty =
- AvaloniaProperty.RegisterDirect(nameof(ClockIdentifier),
+ AvaloniaProperty.RegisterDirect(nameof(ClockIdentifier),
x => x.ClockIdentifier, (x, v) => x.ClockIdentifier = v);
///
/// Defines the property
///
public static readonly DirectProperty SelectedTimeProperty =
- AvaloniaProperty.RegisterDirect(nameof(SelectedTime),
+ AvaloniaProperty.RegisterDirect(nameof(SelectedTime),
x => x.SelectedTime, (x, v) => x.SelectedTime = v);
- //Template Items
+ // Template Items
private TimePickerPresenter _presenter;
private Button _flyoutButton;
private Border _firstPickerHost;
@@ -52,7 +52,7 @@ namespace Avalonia.Controls
private Border _thirdPickerHost;
private TextBlock _hourText;
private TextBlock _minuteText;
- public TextBlock _periodText;
+ private TextBlock _periodText;
private Rectangle _firstSplitter;
private Rectangle _secondSplitter;
private Grid _contentGrid;
@@ -145,7 +145,7 @@ namespace Avalonia.Controls
if (_flyoutButton != null)
_flyoutButton.Click -= OnFlyoutButtonClicked;
- if(_presenter != null)
+ if (_presenter != null)
{
_presenter.Confirmed -= OnConfirmed;
_presenter.Dismissed -= OnDismissPicker;
@@ -170,7 +170,6 @@ namespace Avalonia.Controls
_popup = e.NameScope.Find("Popup");
_presenter = e.NameScope.Find("PickerPresenter");
-
if (_flyoutButton != null)
_flyoutButton.Click += OnFlyoutButtonClicked;
@@ -185,7 +184,6 @@ namespace Avalonia.Controls
_presenter[!TimePickerPresenter.MinuteIncrementProperty] = this[!MinuteIncrementProperty];
_presenter[!TimePickerPresenter.ClockIdentifierProperty] = this[!ClockIdentifierProperty];
}
-
}
private void SetGrid()
@@ -195,30 +193,19 @@ namespace Avalonia.Controls
bool use24HourClock = ClockIdentifier == "24HourClock";
- if (!use24HourClock)
- {
- _contentGrid.ColumnDefinitions = new ColumnDefinitions("*,Auto,*,Auto,*");
- _thirdPickerHost.IsVisible = true;
- _secondSplitter.IsVisible = true;
+ var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
+ _contentGrid.ColumnDefinitions = new ColumnDefinitions(columnsD);
- Grid.SetColumn(_firstPickerHost, 0);
- Grid.SetColumn(_secondPickerHost, 2);
- Grid.SetColumn(_thirdPickerHost, 4);
+ _thirdPickerHost.IsVisible = !use24HourClock;
+ _secondSplitter.IsVisible = !use24HourClock;
- Grid.SetColumn(_firstSplitter, 1);
- Grid.SetColumn(_secondSplitter, 3);
- }
- else
- {
- _contentGrid.ColumnDefinitions = new ColumnDefinitions("*,Auto,*");
- _thirdPickerHost.IsVisible = false;
- _secondSplitter.IsVisible = false;
+ Grid.SetColumn(_firstPickerHost, 0);
+ Grid.SetColumn(_secondPickerHost, 2);
- Grid.SetColumn(_firstPickerHost, 0);
- Grid.SetColumn(_secondPickerHost, 2);
+ Grid.SetColumn(_thirdPickerHost, use24HourClock ? 0 : 4);
- Grid.SetColumn(_firstSplitter, 1);
- }
+ Grid.SetColumn(_firstSplitter, 1);
+ Grid.SetColumn(_secondSplitter, use24HourClock ? 0 : 3);
}
private void SetSelectedTimeText()
@@ -237,14 +224,13 @@ namespace Avalonia.Controls
hr = hr > 12 ? hr - 12 : hr == 0 ? 12 : hr;
newTime = new TimeSpan(hr, newTime.Minutes, 0);
}
- _hourText.Text = newTime.ToString("%h");
+ _hourText.Text = newTime.ToString("%h");
_minuteText.Text = newTime.ToString("mm");
PseudoClasses.Set(":hasnotime", false);
_periodText.Text = time.Value.Hours >= 12 ? CultureInfo.CurrentCulture.DateTimeFormat.PMDesignator :
CultureInfo.CurrentCulture.DateTimeFormat.AMDesignator;
-
}
else
{
@@ -262,7 +248,7 @@ namespace Avalonia.Controls
SelectedTimeChanged?.Invoke(this, new TimePickerSelectedValueChangedEventArgs(oldTime, newTime));
}
- private void OnFlyoutButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private void OnFlyoutButtonClicked(object sender, Interactivity.RoutedEventArgs e)
{
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
@@ -270,7 +256,7 @@ namespace Avalonia.Controls
var deltaY = _presenter.GetOffsetForPopup();
- //The extra 5 px I think is related to default popup placement behavior
+ // The extra 5 px I think is related to default popup placement behavior
_popup.Host.ConfigurePosition(_popup.PlacementTarget, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
@@ -287,6 +273,5 @@ namespace Avalonia.Controls
_popup.Close();
SelectedTime = _presenter.Time;
}
-
}
}
diff --git a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
index 5ab5b92e16..920eeeb406 100644
--- a/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
+++ b/src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
@@ -39,7 +39,7 @@ namespace Avalonia.Controls
KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Cycle);
}
- //TemplateItems
+ // TemplateItems
private Grid _pickerContainer;
private Button _acceptButton;
private Button _dismissButton;
@@ -55,8 +55,8 @@ namespace Avalonia.Controls
private Button _minuteDownButton;
private Button _periodDownButton;
- //Backing Fields
- private TimeSpan _Time;
+ // Backing Fields
+ private TimeSpan _time;
private int _minuteIncrement = 1;
private string _clockIdentifier = "12HourClock";
@@ -83,7 +83,7 @@ namespace Avalonia.Controls
get => _clockIdentifier;
set
{
- if (string.IsNullOrEmpty(value) || value == "" || !(value == "12HourClock" || value == "24HourClock"))
+ if (string.IsNullOrEmpty(value) || !(value == "12HourClock" || value == "24HourClock"))
throw new ArgumentException("Invalid ClockIdentifier");
SetAndRaise(ClockIdentifierProperty, ref _clockIdentifier, value);
InitPicker();
@@ -95,10 +95,10 @@ namespace Avalonia.Controls
///
public TimeSpan Time
{
- get => _Time;
+ get => _time;
set
{
- SetAndRaise(TimeProperty, ref _Time, value);
+ SetAndRaise(TimeProperty, ref _time, value);
InitPicker();
}
}
@@ -213,26 +213,24 @@ namespace Avalonia.Controls
private void SetGrid()
{
- if (ClockIdentifier == "12HourClock")
- {
- _pickerContainer.ColumnDefinitions = new ColumnDefinitions("*,Auto,*,Auto,*");
- _spacer2.IsVisible = true;
- _periodHost.IsVisible = true;
- }
- else
- {
- _pickerContainer.ColumnDefinitions = new ColumnDefinitions("*,Auto,*");
- _spacer2.IsVisible = false;
- _periodHost.IsVisible = false;
- }
+ bool use24HourClock = ClockIdentifier == "24HourClock";
+
+ var columnsD = use24HourClock ? "*, Auto, *" : "*, Auto, *, Auto, *";
+ _pickerContainer.ColumnDefinitions = new ColumnDefinitions(columnsD);
+
+ _spacer2.IsVisible = !use24HourClock;
+ _periodHost.IsVisible = !use24HourClock;
+
+ Grid.SetColumn(_spacer2, use24HourClock ? 0 : 3);
+ Grid.SetColumn(_periodHost, use24HourClock ? 0 : 4);
}
- private void OnDismissButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private void OnDismissButtonClicked(object sender, RoutedEventArgs e)
{
OnDismiss();
}
- private void OnAcceptButtonClicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
+ private void OnAcceptButtonClicked(object sender, RoutedEventArgs e)
{
OnConfirmed();
}
diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
index f8bd2878d9..522103c7bd 100644
--- a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Controls.Embedding.Offscreen
}
}
- public double Scaling
+ public double RenderScaling
{
get { return _scaling; }
set
diff --git a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
index cd1ce3deae..9e65ef5f81 100644
--- a/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
@@ -142,7 +142,6 @@ namespace Avalonia.Controls.Generators
private readonly IDataTemplate _inner;
public WrapperTreeDataTemplate(IDataTemplate inner) => _inner = inner;
public IControl Build(object param) => _inner.Build(param);
- public bool SupportsRecycling => _inner.SupportsRecycling;
public bool Match(object data) => _inner.Match(data);
public InstancedBinding ItemsSelector(object item) => null;
}
diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs
index aab099911b..e7570a87c1 100644
--- a/src/Avalonia.Controls/MenuItem.cs
+++ b/src/Avalonia.Controls/MenuItem.cs
@@ -98,8 +98,7 @@ namespace Avalonia.Controls
private ICommand? _command;
private bool _commandCanExecute = true;
- private Popup? _popup;
- private IDisposable? _gridHack;
+ private Popup _popup;
///
/// Initializes static members of the class.
@@ -325,9 +324,6 @@ namespace Avalonia.Controls
{
Command.CanExecuteChanged -= CanExecuteChanged;
}
-
- _gridHack?.Dispose();
- _gridHack = null;
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
@@ -347,9 +343,7 @@ namespace Avalonia.Controls
// the WPF codebase:
//
// https://github.com/dotnet/wpf/blob/89537909bdf36bc918e88b37751add46a8980bb0/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/MenuItem.cs#L2126-L2141
- _gridHack = Bind(
- DefinitionBase.PrivateSharedSizeScopeProperty,
- parent.GetBindingObservable(DefinitionBase.PrivateSharedSizeScopeProperty));
+ SetValue(DefinitionBase.PrivateSharedSizeScopeProperty, parent.GetValue(DefinitionBase.PrivateSharedSizeScopeProperty));
}
}
diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
index 0d77cbf802..7514f214aa 100644
--- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
+++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs
@@ -23,10 +23,10 @@ namespace Avalonia.Platform
Size ClientSize { get; }
///
- /// Gets the scaling factor for the toplevel.
+ /// Gets the scaling factor for the toplevel. This is used for rendering.
///
- double Scaling { get; }
-
+ double RenderScaling { get; }
+
///
/// The list of native platform's surfaces that can be consumed by rendering subsystems.
///
diff --git a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
index b190c4f2e7..ecaf87d1ed 100644
--- a/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
+++ b/src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
@@ -13,6 +13,11 @@ namespace Avalonia.Platform
/// Hides the window.
///
void Hide();
+
+ ///
+ /// Gets the scaling factor for Window positioning and sizing.
+ ///
+ double DesktopScaling { get; }
///
/// Gets the position of the window in device pixels.
diff --git a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs b/src/Avalonia.Controls/Presenters/CarouselPresenter.cs
index 70a7583daf..7888249bdd 100644
--- a/src/Avalonia.Controls/Presenters/CarouselPresenter.cs
+++ b/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));
+ }
+
///
/// Moves to the selected page, animating if a is set.
///
diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
index c4571505ba..8837901816 100644
--- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs
@@ -86,7 +86,7 @@ namespace Avalonia.Controls.Presenters
private IControl _child;
private bool _createdChild;
- private IDataTemplate _dataTemplate;
+ private IRecyclingDataTemplate _recyclingDataTemplate;
private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper();
///
@@ -281,7 +281,7 @@ namespace Avalonia.Controls.Presenters
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
- _dataTemplate = null;
+ _recyclingDataTemplate = null;
_createdChild = false;
InvalidateMeasure();
}
@@ -307,22 +307,21 @@ namespace Avalonia.Controls.Presenters
{
var dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default;
- // We have content and it isn't a control, so if the new data template is the same
- // as the old data template, try to recycle the existing child control to display
- // the new data.
- if (dataTemplate == _dataTemplate && dataTemplate.SupportsRecycling)
+ if (dataTemplate is IRecyclingDataTemplate rdt)
{
- newChild = oldChild;
+ var toRecycle = rdt == _recyclingDataTemplate ? oldChild : null;
+ newChild = rdt.Build(content, toRecycle);
+ _recyclingDataTemplate = rdt;
}
else
{
- _dataTemplate = dataTemplate;
- newChild = _dataTemplate.Build(content);
+ newChild = dataTemplate.Build(content);
+ _recyclingDataTemplate = null;
}
}
else
{
- _dataTemplate = null;
+ _recyclingDataTemplate = null;
}
return newChild;
@@ -422,7 +421,7 @@ namespace Avalonia.Controls.Presenters
LogicalChildren.Remove(Child);
((ISetInheritanceParent)Child).SetParent(Child.Parent);
Child = null;
- _dataTemplate = null;
+ _recyclingDataTemplate = null;
}
InvalidateMeasure();
diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
index 23846bcd2e..52f173fc71 100644
--- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
+++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
@@ -229,8 +229,6 @@ namespace Avalonia.Controls.Presenters
}
PanelCreated(Panel);
-
- ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
///
diff --git a/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs b/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
index ba0fdfd535..7171ecc302 100644
--- a/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
+++ b/src/Avalonia.Controls/Primitives/ChromeOverlayLayer.cs
@@ -8,7 +8,7 @@ namespace Avalonia.Controls.Primitives
{
public class ChromeOverlayLayer : Panel, ICustomSimpleHitTest
{
- public static ChromeOverlayLayer? GetOverlayLayer(IVisual visual)
+ public static Panel? GetOverlayLayer(IVisual visual)
{
foreach (var v in visual.GetVisualAncestors())
if (v is VisualLayerManager vlm)
@@ -24,6 +24,11 @@ namespace Avalonia.Controls.Primitives
return null;
}
+ public void Add(Control c)
+ {
+ base.Children.Add(c);
+ }
+
public bool HitTest(Point point) => Children.HitTestCustom(point);
}
}
diff --git a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
index b0e3d1ab08..91ed5d975d 100644
--- a/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
+++ b/src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositionerPopupImplHelper.cs
@@ -40,9 +40,9 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
public void MoveAndResize(Point devicePoint, Size virtualSize)
{
- _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.Scaling);
+ _moveResize(new PixelPoint((int)devicePoint.X, (int)devicePoint.Y), virtualSize, _parent.RenderScaling);
}
- public virtual double Scaling => _parent.Scaling;
+ public virtual double Scaling => _parent.DesktopScaling;
}
}
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index c915dc70b6..59b7777b1b 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/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;
diff --git a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
index a4e230e2f4..d8d3450c6f 100644
--- a/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
+++ b/src/Avalonia.Controls/Primitives/VisualLayerManager.cs
@@ -13,10 +13,12 @@ namespace Avalonia.Controls.Primitives
private ILogicalRoot _logicalRoot;
private readonly List _layers = new List();
-
+
+ public static readonly StyledProperty ChromeOverlayLayerProperty =
+ AvaloniaProperty.Register(nameof(ChromeOverlayLayer));
public bool IsPopup { get; set; }
-
+
public AdornerLayer AdornerLayer
{
get
@@ -32,10 +34,19 @@ namespace Avalonia.Controls.Primitives
{
get
{
- var rv = FindLayer();
- if (rv == null)
- AddLayer(rv = new ChromeOverlayLayer(), ChromeZIndex);
- return rv;
+ var current = GetValue(ChromeOverlayLayerProperty);
+
+ if (current is null)
+ {
+ var chromeOverlayLayer = new ChromeOverlayLayer();
+ AddLayer(chromeOverlayLayer, ChromeZIndex);
+
+ SetValue(ChromeOverlayLayerProperty, chromeOverlayLayer);
+
+ current = chromeOverlayLayer;
+ }
+
+ return current;
}
}
@@ -46,7 +57,7 @@ namespace Avalonia.Controls.Primitives
if (IsPopup)
return null;
var rv = FindLayer();
- if(rv == null)
+ if (rv == null)
AddLayer(rv = new OverlayLayer(), OverlayZIndex);
return rv;
}
@@ -88,7 +99,8 @@ namespace Avalonia.Controls.Primitives
layer.ZIndex = zindex;
VisualChildren.Add(layer);
if (((ILogical)this).IsAttachedToLogicalTree)
- ((ILogical)layer).NotifyAttachedToLogicalTree(new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this));
+ ((ILogical)layer).NotifyAttachedToLogicalTree(
+ new LogicalTreeAttachmentEventArgs(_logicalRoot, layer, this));
InvalidateArrange();
}
diff --git a/src/Avalonia.Controls/ProgressBar.cs b/src/Avalonia.Controls/ProgressBar.cs
index e904957429..a92f24a050 100644
--- a/src/Avalonia.Controls/ProgressBar.cs
+++ b/src/Avalonia.Controls/ProgressBar.cs
@@ -215,7 +215,7 @@ namespace Avalonia.Controls
TemplateProperties.Container2AnimationEndPosition = barIndicatorWidth2 * 1.66; // Position at 166%
// Remove these properties when we switch to fluent as default and removed the old one.
- IndeterminateStartingOffset = -(dim / 5d);
+ IndeterminateStartingOffset = -dim;
IndeterminateEndingOffset = dim;
var padding = Padding;
diff --git a/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs b/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
index f93de5ca15..f64c26682b 100644
--- a/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
+++ b/src/Avalonia.Controls/RelativePanel.AttachedProperties.cs
@@ -33,7 +33,7 @@ namespace Avalonia.Controls
AlignVerticalCenterWithProperty.Changed.AddClassHandler(OnAlignPropertiesChanged);
BelowProperty.Changed.AddClassHandler(OnAlignPropertiesChanged);
LeftOfProperty.Changed.AddClassHandler(OnAlignPropertiesChanged);
- LeftOfProperty.Changed.AddClassHandler(OnAlignPropertiesChanged);
+ RightOfProperty.Changed.AddClassHandler(OnAlignPropertiesChanged);
}
///
diff --git a/src/Avalonia.Controls/RelativePanel.cs b/src/Avalonia.Controls/RelativePanel.cs
index 033a5559f5..27f13a3f57 100644
--- a/src/Avalonia.Controls/RelativePanel.cs
+++ b/src/Avalonia.Controls/RelativePanel.cs
@@ -1,4 +1,4 @@
-/// Ported from https://github.com/HandyOrg/HandyControl/blob/master/src/Shared/HandyControl_Shared/Controls/Panel/RelativePanel.cs
+// Ported from https://github.com/HandyOrg/HandyControl/blob/master/src/Shared/HandyControl_Shared/Controls/Panel/RelativePanel.cs
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,25 +14,28 @@ namespace Avalonia.Controls
public RelativePanel() => _childGraph = new Graph();
- protected override Size MeasureOverride(Size availableSize)
+ private Layoutable? GetDependencyElement(AvaloniaProperty property, AvaloniaObject child)
{
- foreach (var child in Children)
+ var dependency = child.GetValue(property);
+
+ if (dependency is Layoutable layoutable)
{
- child?.Measure(availableSize);
+ if (Children.Contains((ILayoutable)layoutable))
+ return layoutable;
+
+ throw new ArgumentException($"RelativePanel error: Element does not exist in the current context: {property.Name}");
}
- return availableSize;
+ return null;
}
- protected override Size ArrangeOverride(Size arrangeSize)
+ protected override Size MeasureOverride(Size availableSize)
{
- _childGraph.Reset(arrangeSize);
-
- foreach (var child in Children.OfType())
+ _childGraph.Clear();
+ foreach (Layoutable child in Children)
{
if (child == null)
continue;
-
var node = _childGraph.AddNode(child);
node.AlignLeftWithNode = _childGraph.AddLink(node, GetDependencyElement(AlignLeftWithProperty, child));
@@ -47,63 +50,44 @@ namespace Avalonia.Controls
node.AlignHorizontalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignHorizontalCenterWithProperty, child));
node.AlignVerticalCenterWith = _childGraph.AddLink(node, GetDependencyElement(AlignVerticalCenterWithProperty, child));
- }
- if (_childGraph.CheckCyclic())
- {
- throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete.");
}
+ _childGraph.Measure(availableSize);
- var size = new Size();
-
- foreach (var child in Children)
- {
- if (child.Bounds.Bottom > size.Height)
- {
- size = size.WithHeight(child.Bounds.Bottom);
- }
+ _childGraph.Reset(false);
+ var boundingSize = _childGraph.GetBoundingSize(Width.IsNaN(), Height.IsNaN());
+ _childGraph.Reset();
+ _childGraph.Measure(boundingSize);
+ return boundingSize;
+ }
- if (child.Bounds.Right > size.Width)
- {
- size = size.WithWidth(child.Bounds.Right);
- }
- }
+ protected override Size ArrangeOverride(Size arrangeSize)
+ {
+ _childGraph.GetNodes().Do(node => node.Arrange(arrangeSize));
+ return arrangeSize;
+ }
- if (VerticalAlignment == VerticalAlignment.Stretch)
- {
- size = size.WithHeight(arrangeSize.Height);
- }
+ private class GraphNode
+ {
+ public bool Measured { get; set; }
- if (HorizontalAlignment == HorizontalAlignment.Stretch)
- {
- size = size.WithWidth(arrangeSize.Width);
- }
+ public Layoutable Element { get; }
- return size;
- }
+ private bool HorizontalOffsetFlag { get; set; }
- private Layoutable? GetDependencyElement(AvaloniaProperty property, AvaloniaObject child)
- {
- var dependency = child.GetValue(property);
+ private bool VerticalOffsetFlag { get; set; }
- if (dependency is Layoutable layoutable)
- {
- if (Children.Contains((ILayoutable)layoutable))
- return layoutable;
+ private Size BoundingSize { get; set; }
- throw new ArgumentException($"RelativePanel error: Element does not exist in the current context: {property.Name}");
- }
+ public Size OriginDesiredSize { get; set; }
- return null;
- }
+ public double Left { get; set; } = double.NaN;
- private class GraphNode
- {
- public Point Position { get; set; }
+ public double Top { get; set; } = double.NaN;
- public bool Arranged { get; set; }
+ public double Right { get; set; } = double.NaN;
- public Layoutable Element { get; }
+ public double Bottom { get; set; } = double.NaN;
public HashSet OutgoingNodes { get; }
@@ -132,19 +116,106 @@ namespace Avalonia.Controls
OutgoingNodes = new HashSet();
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 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 _nodeDic;
- private Size _arrangeSize;
+ private Size AvailableSize { get; set; }
+
+ public Graph() => _nodeDic = new Dictionary();
+
+ public IEnumerable GetNodes() => _nodeDic.Values;
- public Graph()
+ public void Clear()
{
- _nodeDic = new Dictionary();
+ AvailableSize = new Size();
+ _nodeDic.Clear();
}
+ public void Reset(bool clearPos = true) => _nodeDic.Values.Do(node => node.Reset(clearPos));
+
public GraphNode? AddLink(GraphNode from, Layoutable? to)
{
if (to == null)
@@ -177,177 +248,289 @@ namespace Avalonia.Controls
return _nodeDic[value];
}
- public void Reset(Size arrangeSize)
+ public void Measure(Size availableSize)
{
- _arrangeSize = arrangeSize;
- _nodeDic.Clear();
+ AvailableSize = availableSize;
+ Measure(_nodeDic.Values, null);
}
- public bool CheckCyclic() => CheckCyclic(_nodeDic.Values, null);
-
- private bool CheckCyclic(IEnumerable nodes, HashSet? set)
+ private void Measure(IEnumerable nodes, HashSet? set)
{
- set ??= new HashSet();
+ set ??= new HashSet();
foreach (var node in nodes)
{
- if (!node.Arranged && node.OutgoingNodes.Count == 0)
+ if (!node.Measured && !node.OutgoingNodes.Any())
{
- ArrangeChild(node, true);
+ MeasureChild(node);
continue;
}
-
- if (node.OutgoingNodes.All(item => item.Arranged))
+
+ if (node.OutgoingNodes.All(item => item.Measured))
{
- ArrangeChild(node);
+ MeasureChild(node);
continue;
}
-
+
if (!set.Add(node.Element))
- return true;
+ throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete.");
+
+ Measure(node.OutgoingNodes, set);
- return CheckCyclic(node.OutgoingNodes, set);
+ if (!node.Measured)
+ {
+ MeasureChild(node);
+ }
}
-
- return false;
}
- private void ArrangeChild(GraphNode node, bool ignoneSibling = false)
+ private void MeasureChild(GraphNode node)
{
var child = node.Element;
- var childSize = child.DesiredSize;
- var childPos = new Point();
+ child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
+ node.OriginDesiredSize = child.DesiredSize;
- if (GetAlignHorizontalCenterWithPanel(child))
+ var alignLeftWithPanel = GetAlignLeftWithPanel(child);
+ var alignTopWithPanel = GetAlignTopWithPanel(child);
+ var alignRightWithPanel = GetAlignRightWithPanel(child);
+ var alignBottomWithPanel = GetAlignBottomWithPanel(child);
+
+ if (alignLeftWithPanel)
+ node.Left = 0;
+ if (alignTopWithPanel)
+ node.Top = 0;
+ if (alignRightWithPanel)
+ node.Right = 0;
+ if (alignBottomWithPanel)
+ node.Bottom = 0;
+
+ if (node.AlignLeftWithNode != null)
{
- childPos = childPos.WithX((_arrangeSize.Width - childSize.Width) / 2);
+ node.Left = node.Left.IsNaN() ? node.AlignLeftWithNode.Left : node.AlignLeftWithNode.Left * 0.5;
}
- if (GetAlignVerticalCenterWithPanel(child))
+ if (node.AlignTopWithNode != null)
{
- childPos = childPos.WithY((_arrangeSize.Height - childSize.Height) / 2);
+ node.Top = node.Top.IsNaN() ? node.AlignTopWithNode.Top : node.AlignTopWithNode.Top * 0.5;
}
- var alignLeftWithPanel = GetAlignLeftWithPanel(child);
- var alignTopWithPanel = GetAlignTopWithPanel(child);
- var alignRightWithPanel = GetAlignRightWithPanel(child);
- var alignBottomWithPanel = GetAlignBottomWithPanel(child);
+ if (node.AlignRightWithNode != null)
+ {
+ node.Right = node.Right.IsNaN()
+ ? node.AlignRightWithNode.Right
+ : node.AlignRightWithNode.Right * 0.5;
+ }
- if (!ignoneSibling)
+ if (node.AlignBottomWithNode != null)
{
- if (node.LeftOfNode != null)
- {
- childPos = childPos.WithX(node.LeftOfNode.Position.X - childSize.Width);
- }
+ node.Bottom = node.Bottom.IsNaN()
+ ? node.AlignBottomWithNode.Bottom
+ : node.AlignBottomWithNode.Bottom * 0.5;
+ }
- if (node.AboveNode != null)
- {
- childPos = childPos.WithY(node.AboveNode.Position.Y - childSize.Height);
- }
+ var availableHeight = AvailableSize.Height - node.Top - node.Bottom;
+ if (availableHeight.IsNaN())
+ {
+ availableHeight = AvailableSize.Height;
- if (node.RightOfNode != null)
+ if (!node.Top.IsNaN() && node.Bottom.IsNaN())
{
- childPos = childPos.WithX(node.RightOfNode.Position.X + node.RightOfNode.Element.DesiredSize.Width);
+ availableHeight -= node.Top;
}
-
- if (node.BelowNode != null)
+ else if (node.Top.IsNaN() && !node.Bottom.IsNaN())
{
- childPos = childPos.WithY(node.BelowNode.Position.Y + node.BelowNode.Element.DesiredSize.Height);
+ availableHeight -= node.Bottom;
}
+ }
+
+ var availableWidth = AvailableSize.Width - node.Left - node.Right;
+ if (availableWidth.IsNaN())
+ {
+ availableWidth = AvailableSize.Width;
- if (node.AlignHorizontalCenterWith != null)
+ if (!node.Left.IsNaN() && node.Right.IsNaN())
{
- childPos = childPos.WithX(node.AlignHorizontalCenterWith.Position.X +
- (node.AlignHorizontalCenterWith.Element.DesiredSize.Width - childSize.Width) / 2);
+ availableWidth -= node.Left;
}
-
- if (node.AlignVerticalCenterWith != null)
+ else if (node.Left.IsNaN() && !node.Right.IsNaN())
{
- childPos = childPos.WithY(node.AlignVerticalCenterWith.Position.Y +
- (node.AlignVerticalCenterWith.Element.DesiredSize.Height - childSize.Height) / 2);
+ availableWidth -= node.Right;
}
+ }
- if (node.AlignLeftWithNode != null)
+ child.Measure(new Size(Math.Max(availableWidth, 0), Math.Max(availableHeight, 0)));
+ var childSize = child.DesiredSize;
+
+ if (node.LeftOfNode != null && node.Left.IsNaN())
+ {
+ node.Left = node.LeftOfNode.Left - childSize.Width;
+ }
+
+ if (node.AboveNode != null && node.Top.IsNaN())
+ {
+ node.Top = node.AboveNode.Top - childSize.Height;
+ }
+
+ if (node.RightOfNode != null)
+ {
+ if (node.Right.IsNaN())
{
- childPos = childPos.WithX(node.AlignLeftWithNode.Position.X);
+ node.Right = node.RightOfNode.Right - childSize.Width;
}
- if (node.AlignTopWithNode != null)
+ if (node.Left.IsNaN())
{
- childPos = childPos.WithY(node.AlignTopWithNode.Position.Y);
+ node.Left = AvailableSize.Width - node.RightOfNode.Right;
}
+ }
- if (node.AlignRightWithNode != null)
+ if (node.BelowNode != null)
+ {
+ if (node.Bottom.IsNaN())
{
- childPos = childPos.WithX(node.AlignRightWithNode.Element.DesiredSize.Width + node.AlignRightWithNode.Position.X - childSize.Width);
+ node.Bottom = node.BelowNode.Bottom - childSize.Height;
}
- if (node.AlignBottomWithNode != null)
+ if (node.Top.IsNaN())
{
- childPos = childPos.WithY(node.AlignBottomWithNode.Element.DesiredSize.Height + node.AlignBottomWithNode.Position.Y - childSize.Height);
+ node.Top = AvailableSize.Height - node.BelowNode.Bottom;
}
}
- if (alignLeftWithPanel)
+ if (node.AlignHorizontalCenterWith != null)
{
- if (node.AlignRightWithNode != null)
- {
- childPos = childPos.WithX((node.AlignRightWithNode.Element.DesiredSize.Width + node.AlignRightWithNode.Position.X - childSize.Width) / 2);
- }
+ var halfWidthLeft = (AvailableSize.Width + node.AlignHorizontalCenterWith.Left - node.AlignHorizontalCenterWith.Right - childSize.Width) * 0.5;
+ var halfWidthRight = (AvailableSize.Width - node.AlignHorizontalCenterWith.Left + node.AlignHorizontalCenterWith.Right - childSize.Width) * 0.5;
+
+ if (node.Left.IsNaN())
+ node.Left = halfWidthLeft;
else
- {
- childPos = childPos.WithX(0);
- }
+ node.Left = (node.Left + halfWidthLeft) * 0.5;
+
+ if (node.Right.IsNaN())
+ node.Right = halfWidthRight;
+ else
+ node.Right = (node.Right + halfWidthRight) * 0.5;
}
- if (alignTopWithPanel)
+ if (node.AlignVerticalCenterWith != null)
{
- if (node.AlignBottomWithNode != null)
- {
- childPos = childPos.WithY((node.AlignBottomWithNode.Element.DesiredSize.Height + node.AlignBottomWithNode.Position.Y - childSize.Height) / 2);
- }
+ var halfHeightTop = (AvailableSize.Height + node.AlignVerticalCenterWith.Top - node.AlignVerticalCenterWith.Bottom - childSize.Height) * 0.5;
+ var halfHeightBottom = (AvailableSize.Height - node.AlignVerticalCenterWith.Top + node.AlignVerticalCenterWith.Bottom - childSize.Height) * 0.5;
+
+ if (node.Top.IsNaN())
+ node.Top = halfHeightTop;
else
- {
- childPos = childPos.WithY(0);
- }
+ node.Top = (node.Top + halfHeightTop) * 0.5;
+
+ if (node.Bottom.IsNaN())
+ node.Bottom = halfHeightBottom;
+ else
+ node.Bottom = (node.Bottom + halfHeightBottom) * 0.5;
}
- if (alignRightWithPanel)
+ if (GetAlignHorizontalCenterWithPanel(child))
{
- if (alignLeftWithPanel)
- {
- childPos = childPos.WithX((_arrangeSize.Width - childSize.Width) / 2);
- }
- else if (node.AlignLeftWithNode == null)
- {
- childPos = childPos.WithX(_arrangeSize.Width - childSize.Width);
- }
+ var halfSubWidth = (AvailableSize.Width - childSize.Width) * 0.5;
+
+ if (node.Left.IsNaN())
+ node.Left = halfSubWidth;
else
- {
- childPos = childPos.WithX((_arrangeSize.Width + node.AlignLeftWithNode.Position.X - childSize.Width) / 2);
- }
+ node.Left = (node.Left + halfSubWidth) * 0.5;
+
+ if (node.Right.IsNaN())
+ node.Right = halfSubWidth;
+ else
+ node.Right = (node.Right + halfSubWidth) * 0.5;
}
- if (alignBottomWithPanel)
+ if (GetAlignVerticalCenterWithPanel(child))
{
- if (alignTopWithPanel)
- {
- childPos = childPos.WithY((_arrangeSize.Height - childSize.Height) / 2);
- }
- else if (node.AlignTopWithNode == null)
+ var halfSubHeight = (AvailableSize.Height - childSize.Height) * 0.5;
+
+ if (node.Top.IsNaN())
+ node.Top = halfSubHeight;
+ else
+ node.Top = (node.Top + halfSubHeight) * 0.5;
+
+ if (node.Bottom.IsNaN())
+ node.Bottom = halfSubHeight;
+ else
+ node.Bottom = (node.Bottom + halfSubHeight) * 0.5;
+ }
+
+ if (node.Left.IsNaN())
+ {
+ if (!node.Right.IsNaN())
+ node.Left = AvailableSize.Width - node.Right - childSize.Width;
+ else
{
- childPos = childPos.WithY(_arrangeSize.Height - childSize.Height);
+ node.Left = 0;
+ node.Right = AvailableSize.Width - childSize.Width;
}
+ }
+ else if (!node.Left.IsNaN() && node.Right.IsNaN())
+ {
+ node.Right = AvailableSize.Width - node.Left - childSize.Width;
+ }
+
+ if (node.Top.IsNaN())
+ {
+ if (!node.Bottom.IsNaN())
+ node.Top = AvailableSize.Height - node.Bottom - childSize.Height;
else
{
- childPos = childPos.WithY((_arrangeSize.Height + node.AlignTopWithNode.Position.Y - childSize.Height) / 2);
+ node.Top = 0;
+ node.Bottom = AvailableSize.Height - childSize.Height;
}
}
+ else if (!node.Top.IsNaN() && node.Bottom.IsNaN())
+ {
+ node.Bottom = AvailableSize.Height - node.Top - childSize.Height;
+ }
+
+ node.Measured = true;
+ }
+
+ public Size GetBoundingSize(bool calcWidth, bool calcHeight)
+ {
+ var boundingSize = new Size();
+
+ foreach (var node in _nodeDic.Values)
+ {
+ var size = node.GetBoundingSize();
+ boundingSize = boundingSize.WithWidth(Math.Max(boundingSize.Width, size.Width));
+ boundingSize = boundingSize.WithHeight(Math.Max(boundingSize.Height, size.Height));
+ }
+
+ boundingSize = boundingSize.WithWidth(calcWidth ? boundingSize.Width : AvailableSize.Width);
+ boundingSize = boundingSize.WithHeight(calcHeight ? boundingSize.Height : AvailableSize.Height);
+ return boundingSize;
+ }
+ }
+ }
+
+ internal static partial class Extensions
+ {
+ ///
+ /// Returns a value that indicates whether the specified value is not a number ().
+ ///
+ /// A double-precision floating-point number.
+ /// true if evaluates to ; otherwise, false.
+ public static bool IsNaN(this double d)
+ {
+ return double.IsNaN(d);
+ }
- child.Arrange(new Rect(childPos.X, childPos.Y, childSize.Width, childSize.Height));
- node.Position = childPos;
- node.Arranged = true;
+ public static IEnumerable Do(this IEnumerable source, Action predicate)
+ {
+ var enumerable = source as IList ?? source.ToList();
+ foreach (var item in enumerable)
+ {
+ predicate.Invoke(item);
}
+
+ return enumerable;
}
}
}
diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
index f5c673d5f9..931c27c575 100644
--- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
+++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
@@ -192,7 +192,7 @@ namespace Avalonia.Controls.Remote.Server
GetAvaloniaInputModifiers(pressed.Modifiers)));
}, DispatcherPriority.Input);
}
- if (obj is PointerPressedEventMessage released)
+ if (obj is PointerReleasedEventMessage released)
{
Dispatcher.UIThread.Post(() =>
{
diff --git a/src/Avalonia.Controls/Repeater/ElementFactory.cs b/src/Avalonia.Controls/Repeater/ElementFactory.cs
new file mode 100644
index 0000000000..644e077221
--- /dev/null
+++ b/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);
+ }
+}
diff --git a/src/Avalonia.Controls/Repeater/IElementFactory.cs b/src/Avalonia.Controls/Repeater/IElementFactory.cs
new file mode 100644
index 0000000000..6a899a6f26
--- /dev/null
+++ b/src/Avalonia.Controls/Repeater/IElementFactory.cs
@@ -0,0 +1,66 @@
+using Avalonia.Controls.Templates;
+
+namespace Avalonia.Controls
+{
+ ///
+ /// Represents the optional arguments to use when calling an implementation of the
+ /// 's method.
+ ///
+ public class ElementFactoryGetArgs
+ {
+ ///
+ /// Gets or sets the data item for which an appropriate element tree should be realized
+ /// when calling .
+ ///
+ public object Data { get; set; }
+
+ ///
+ /// Gets or sets the that is expected to be the parent of the
+ /// realized element from .
+ ///
+ public IControl Parent { get; set; }
+
+ ///
+ /// Gets or sets the index of the item that should be realized.
+ ///
+ public int Index { get; set; }
+ }
+
+ ///
+ /// Represents the optional arguments to use when calling an implementation of the
+ /// 's method.
+ ///
+ public class ElementFactoryRecycleArgs
+ {
+ ///
+ /// Gets or sets the to recycle when calling
+ /// .
+ ///
+ public IControl Element { get; set; }
+
+ ///
+ /// Gets or sets the that is expected to be the parent of the
+ /// realized element from .
+ ///
+ public IControl Parent { get; set; }
+ }
+
+ ///
+ /// A data template that supports creating and recyling elements for an .
+ ///
+ public interface IElementFactory : IDataTemplate
+ {
+ ///
+ /// Gets an .
+ ///
+ /// The element args.
+ public IControl GetElement(ElementFactoryGetArgs args);
+
+ ///
+ /// Recycles an that was previously retrieved using
+ /// .
+ ///
+ /// The recycle args.
+ public void RecycleElement(ElementFactoryRecycleArgs args);
+ }
+}
diff --git a/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs b/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
index 04d859c742..dd97cde218 100644
--- a/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
+++ b/src/Avalonia.Controls/Repeater/ItemTemplateWrapper.cs
@@ -7,13 +7,26 @@ using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
- internal class ItemTemplateWrapper
+ internal class ItemTemplateWrapper : IElementFactory
{
private readonly IDataTemplate _dataTemplate;
public ItemTemplateWrapper(IDataTemplate dataTemplate) => _dataTemplate = dataTemplate;
- public IControl GetElement(IControl parent, object data)
+ public IControl Build(object param) => GetElement(null, param);
+ public bool Match(object data) => _dataTemplate.Match(data);
+
+ public IControl GetElement(ElementFactoryGetArgs args)
+ {
+ return GetElement(args.Parent, args.Data);
+ }
+
+ public void RecycleElement(ElementFactoryRecycleArgs args)
+ {
+ RecycleElement(args.Parent, args.Element);
+ }
+
+ private IControl GetElement(IControl parent, object data)
{
var selectedTemplate = _dataTemplate;
var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);
@@ -37,7 +50,7 @@ namespace Avalonia.Controls
return element;
}
- public void RecycleElement(IControl parent, IControl element)
+ private void RecycleElement(IControl parent, IControl element)
{
var selectedTemplate = _dataTemplate;
var recyclePool = RecyclePool.GetPoolInstance(selectedTemplate);
diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
index 87f4760156..8bc356bdec 100644
--- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
+++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs
@@ -141,7 +141,7 @@ namespace Avalonia.Controls
///
public ItemsSourceView ItemsSourceView { get; private set; }
- internal ItemTemplateWrapper ItemTemplateShim { get; set; }
+ internal IElementFactory ItemTemplateShim { get; set; }
internal Point LayoutOrigin { get; set; }
internal object LayoutState { get; set; }
internal IControl MadeAnchor => _viewportManager.MadeAnchor;
@@ -664,7 +664,7 @@ namespace Avalonia.Controls
}
}
- ItemTemplateShim = new ItemTemplateWrapper(newValue);
+ ItemTemplateShim = newValue as IElementFactory ?? new ItemTemplateWrapper(newValue);
InvalidateMeasure();
}
diff --git a/src/Avalonia.Controls/Repeater/RecyclePool.cs b/src/Avalonia.Controls/Repeater/RecyclePool.cs
index 4e5950bdc5..28f299043c 100644
--- a/src/Avalonia.Controls/Repeater/RecyclePool.cs
+++ b/src/Avalonia.Controls/Repeater/RecyclePool.cs
@@ -11,10 +11,13 @@ using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
- internal class RecyclePool
+ public class RecyclePool
{
- public static readonly AttachedProperty OriginTemplateProperty =
- AvaloniaProperty.RegisterAttached("OriginTemplate", typeof(RecyclePool));
+ internal static readonly AttachedProperty OriginTemplateProperty =
+ AvaloniaProperty.RegisterAttached("OriginTemplate");
+
+ internal static readonly AttachedProperty ReuseKeyProperty =
+ AvaloniaProperty.RegisterAttached("ReuseKey", string.Empty);
private static ConditionalWeakTable s_pools = new ConditionalWeakTable();
private readonly Dictionary> _elements = new Dictionary>();
@@ -77,6 +80,9 @@ namespace Avalonia.Controls
return null;
}
+ internal string GetReuseKey(IControl element) => element.GetValue(ReuseKeyProperty);
+ internal void SetReuseKey(IControl element, string value) => element.SetValue(ReuseKeyProperty, value);
+
private IPanel EnsureOwnerIsPanelOrNull(IControl owner)
{
if (owner is IPanel panel)
diff --git a/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs b/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs
new file mode 100644
index 0000000000..9503239e34
--- /dev/null
+++ b/src/Avalonia.Controls/Repeater/RecyclingElementFactory.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Controls.Templates;
+
+#nullable enable
+
+namespace Avalonia.Controls
+{
+ public class SelectTemplateEventArgs : EventArgs
+ {
+ public string? TemplateKey { get; set; }
+ public object? DataContext { get; internal set; }
+ public IControl? Owner { get; internal set; }
+ }
+
+ public class RecyclingElementFactory : ElementFactory
+ {
+ private RecyclePool? _recyclePool;
+ private IDictionary? _templates;
+ private SelectTemplateEventArgs? _args;
+
+ public RecyclingElementFactory()
+ {
+ Templates = new Dictionary();
+ }
+
+ public RecyclePool RecyclePool
+ {
+ get => _recyclePool ??= new RecyclePool();
+ set => _recyclePool = value ?? throw new ArgumentNullException(nameof(value));
+ }
+
+ public IDictionary Templates
+ {
+ get => _templates ??= new Dictionary();
+ set => _templates = value ?? throw new ArgumentNullException(nameof(value));
+ }
+
+ public event EventHandler? SelectTemplateKey;
+
+ protected override IControl GetElementCore(ElementFactoryGetArgs args)
+ {
+ if (_templates == null || _templates.Count == 0)
+ {
+ throw new InvalidOperationException("Templates cannot be empty.");
+ }
+
+ var templateKey = Templates.Count == 1 ?
+ Templates.First().Key :
+ OnSelectTemplateKeyCore(args.Data, args.Parent);
+
+ if (string.IsNullOrEmpty(templateKey))
+ {
+ // Note: We could allow null/whitespace, which would work as long as
+ // the recycle pool is not shared. in order to make this work in all cases
+ // currently we validate that a valid template key is provided.
+ throw new InvalidOperationException("Template key cannot be null or empty.");
+ }
+
+ // Get an element from the Recycle Pool or create one
+ var element = RecyclePool.TryGetElement(templateKey, args.Parent);
+
+ if (element is null)
+ {
+ // No need to call HasKey if there is only one template.
+ if (Templates.Count > 1 && !Templates.ContainsKey(templateKey))
+ {
+ var message = $"No templates of key '{templateKey}' were found in the templates collection.";
+ throw new InvalidOperationException(message);
+ }
+
+ var dataTemplate = Templates[templateKey];
+ element = dataTemplate.Build(args.Data);
+
+ // Associate ReuseKey with element
+ RecyclePool.SetReuseKey(element, templateKey);
+ }
+
+ return element;
+ }
+
+ protected override void RecycleElementCore(ElementFactoryRecycleArgs args)
+ {
+ var element = args.Element;
+ var key = RecyclePool.GetReuseKey(element);
+ RecyclePool.PutElement(element, key, args.Parent);
+ }
+
+ protected virtual string OnSelectTemplateKeyCore(object dataContext, IControl owner)
+ {
+ if (SelectTemplateKey is object)
+ {
+ _args ??= new SelectTemplateEventArgs();
+ _args.TemplateKey = null;
+ _args.DataContext = dataContext;
+ _args.Owner = owner;
+
+ try
+ {
+ SelectTemplateKey(this, _args);
+ }
+ finally
+ {
+ _args.DataContext = null;
+ _args.Owner = null;
+ }
+ }
+
+ if (string.IsNullOrEmpty(_args?.TemplateKey))
+ {
+ throw new InvalidOperationException(
+ "Please provide a valid template identifier in the handler for the SelectTemplateKey event.");
+ }
+
+ return _args!.TemplateKey!;
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Repeater/ViewManager.cs b/src/Avalonia.Controls/Repeater/ViewManager.cs
index eff51804b9..416b1e2824 100644
--- a/src/Avalonia.Controls/Repeater/ViewManager.cs
+++ b/src/Avalonia.Controls/Repeater/ViewManager.cs
@@ -6,11 +6,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
-using System.Linq;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
-using Avalonia.Layout;
using Avalonia.Logging;
using Avalonia.VisualTree;
@@ -26,6 +24,8 @@ namespace Avalonia.Controls
private readonly UniqueIdElementPool _resetPool;
private IControl _lastFocusedElement;
private bool _isDataSourceStableResetPending;
+ private ElementFactoryGetArgs _elementFactoryGetArgs;
+ private ElementFactoryRecycleArgs _elementFactoryRecycleArgs;
private int _firstRealizedElementIndexHeldByLayout = FirstRealizedElementIndexDefault;
private int _lastRealizedElementIndexHeldByLayout = LastRealizedElementIndexDefault;
private bool _eventsSubscribed;
@@ -134,7 +134,14 @@ namespace Avalonia.Controls
if (_owner.ItemTemplateShim != null)
{
- _owner.ItemTemplateShim.RecycleElement(_owner, element);
+ var context = _elementFactoryRecycleArgs ??= new ElementFactoryRecycleArgs();
+ context.Element = element;
+ context.Parent = _owner;
+
+ _owner.ItemTemplateShim.RecycleElement(context);
+
+ context.Element = null;
+ context.Parent = null;
}
else
{
@@ -579,7 +586,7 @@ namespace Avalonia.Controls
var data = _owner.ItemsSourceView.GetAt(index);
var providedElementFactory = _owner.ItemTemplateShim;
- ItemTemplateWrapper GetElementFactory()
+ IElementFactory GetElementFactory()
{
if (providedElementFactory == null)
{
@@ -602,7 +609,20 @@ namespace Avalonia.Controls
}
var elementFactory = GetElementFactory();
- return elementFactory.GetElement(_owner, data);
+ var args = _elementFactoryGetArgs ??= new ElementFactoryGetArgs();
+
+ try
+ {
+ args.Data = data;
+ args.Parent = _owner;
+ args.Index = index;
+ return elementFactory.GetElement(args);
+ }
+ finally
+ {
+ args.Data = null;
+ args.Parent = null;
+ }
}
var element = GetElement();
@@ -732,6 +752,7 @@ namespace Avalonia.Controls
{
_owner.GotFocus += OnFocusChanged;
_owner.LostFocus += OnFocusChanged;
+ _eventsSubscribed = true;
}
}
diff --git a/src/Avalonia.Controls/Templates/FuncDataTemplate.cs b/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
index d454a29021..1afd86a11e 100644
--- a/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
+++ b/src/Avalonia.Controls/Templates/FuncDataTemplate.cs
@@ -6,7 +6,7 @@ namespace Avalonia.Controls.Templates
///
/// Builds a control for a piece of data.
///
- public class FuncDataTemplate : FuncTemplate
+
+
diff --git a/src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj b/src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj
index c0e46416c1..805aae2d1a 100644
--- a/src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj
+++ b/src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj
@@ -17,4 +17,5 @@
+
diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
index 5f49a46839..db8684747d 100644
--- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
+++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
@@ -2,6 +2,7 @@
netstandard2.0
Avalonia
+ Avalonia.Diagnostics
@@ -28,4 +29,5 @@
+
diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
index c06fbec801..be3564e781 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/ViewLocator.cs
@@ -7,8 +7,6 @@ namespace Avalonia.Diagnostics
{
internal class ViewLocator : IDataTemplate
{
- public bool SupportsRecycling => false;
-
public IControl Build(object data)
{
var name = data.GetType().FullName.Replace("ViewModel", "View");
diff --git a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
index f54ddf32be..dd476e2559 100644
--- a/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
+++ b/src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
@@ -15,4 +15,6 @@
+
+
diff --git a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
index d9fd3b78a2..260d7350d2 100644
--- a/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
+++ b/src/Avalonia.FreeDesktop/Avalonia.FreeDesktop.csproj
@@ -1,7 +1,7 @@
- netstandard2.0
+ netstandard2.0
@@ -10,6 +10,5 @@
-
-
+
diff --git a/src/Avalonia.FreeDesktop/DBusHelper.cs b/src/Avalonia.FreeDesktop/DBusHelper.cs
index b445f86613..91c4c28995 100644
--- a/src/Avalonia.FreeDesktop/DBusHelper.cs
+++ b/src/Avalonia.FreeDesktop/DBusHelper.cs
@@ -43,7 +43,7 @@ namespace Avalonia.FreeDesktop
public void Initialized()
{
lock (_lock)
- _ctx = new AvaloniaSynchronizationContext();
+ _ctx = new AvaloniaSynchronizationContext(null);
}
}
public static Connection Connection { get; private set; }
diff --git a/src/Avalonia.Headless/Avalonia.Headless.csproj b/src/Avalonia.Headless/Avalonia.Headless.csproj
index b871316a2c..ac591b68a8 100644
--- a/src/Avalonia.Headless/Avalonia.Headless.csproj
+++ b/src/Avalonia.Headless/Avalonia.Headless.csproj
@@ -1,8 +1,9 @@
netstandard2.0
+
-
+
diff --git a/src/Avalonia.Headless/HeadlessWindowImpl.cs b/src/Avalonia.Headless/HeadlessWindowImpl.cs
index 5bd46b6714..8f4fa5e304 100644
--- a/src/Avalonia.Headless/HeadlessWindowImpl.cs
+++ b/src/Avalonia.Headless/HeadlessWindowImpl.cs
@@ -41,7 +41,8 @@ namespace Avalonia.Headless
}
public Size ClientSize { get; set; }
- public double Scaling { get; } = 1;
+ public double RenderScaling { get; } = 1;
+ public double DesktopScaling => RenderScaling;
public IEnumerable Surfaces { get; }
public Action Input { get; set; }
public Action Paint { get; set; }
@@ -62,9 +63,9 @@ namespace Avalonia.Headless
public IInputRoot InputRoot { get; set; }
- public Point PointToClient(PixelPoint point) => point.ToPoint(Scaling);
+ public Point PointToClient(PixelPoint point) => point.ToPoint(RenderScaling);
- public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, Scaling);
+ public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
public void SetCursor(IPlatformHandle cursor)
{
@@ -201,7 +202,7 @@ namespace Avalonia.Headless
public ILockedFramebuffer Lock()
{
- var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, Scaling), new Vector(96, 96) * Scaling);
+ var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, RenderScaling), new Vector(96, 96) * RenderScaling);
var fb = bmp.Lock();
return new FramebufferProxy(fb, () =>
{
diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj
index ea560ce2ea..2204778afe 100644
--- a/src/Avalonia.Input/Avalonia.Input.csproj
+++ b/src/Avalonia.Input/Avalonia.Input.csproj
@@ -10,4 +10,5 @@
+
diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
index 730ca2bd6e..2da23410d6 100644
--- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
+++ b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj
@@ -11,4 +11,5 @@
+
\ No newline at end of file
diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj
index 410a0791b1..076ec56cd6 100644
--- a/src/Avalonia.Layout/Avalonia.Layout.csproj
+++ b/src/Avalonia.Layout/Avalonia.Layout.csproj
@@ -8,4 +8,5 @@
+
diff --git a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
index 0f2551ffeb..6d1b95b997 100644
--- a/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
+++ b/src/Avalonia.Native/AvaloniaNativeMenuExporter.cs
@@ -11,7 +11,7 @@ namespace Avalonia.Native
class AvaloniaNativeMenuExporter : ITopLevelNativeMenuExporter
{
private IAvaloniaNativeFactory _factory;
- private bool _resetQueued;
+ private bool _resetQueued = true;
private bool _exported = false;
private IAvnWindow _nativeWindow;
private NativeMenu _menu;
@@ -39,8 +39,7 @@ namespace Avalonia.Native
public void SetNativeMenu(NativeMenu menu)
{
_menu = menu == null ? new NativeMenu() : menu;
-
- DoLayoutReset();
+ DoLayoutReset(true);
}
internal void UpdateIfNeeded()
@@ -74,31 +73,34 @@ namespace Avalonia.Native
return result;
}
- void DoLayoutReset()
+ private void DoLayoutReset(bool forceUpdate = false)
{
- _resetQueued = false;
-
- if (_nativeWindow is null)
+ if (_resetQueued || forceUpdate)
{
- var appMenu = NativeMenu.GetMenu(Application.Current);
+ _resetQueued = false;
- if (appMenu == null)
+ if (_nativeWindow is null)
{
- appMenu = CreateDefaultAppMenu();
- NativeMenu.SetMenu(Application.Current, appMenu);
- }
+ var appMenu = NativeMenu.GetMenu(Application.Current);
- SetMenu(appMenu);
- }
- else
- {
- if (_menu != null)
+ if (appMenu == null)
+ {
+ appMenu = CreateDefaultAppMenu();
+ NativeMenu.SetMenu(Application.Current, appMenu);
+ }
+
+ SetMenu(appMenu);
+ }
+ else
{
- SetMenu(_nativeWindow, _menu);
+ if (_menu != null)
+ {
+ SetMenu(_nativeWindow, _menu);
+ }
}
- }
- _exported = true;
+ _exported = true;
+ }
}
internal void QueueReset()
@@ -106,7 +108,7 @@ namespace Avalonia.Native
if (_resetQueued)
return;
_resetQueued = true;
- Dispatcher.UIThread.Post(DoLayoutReset, DispatcherPriority.Background);
+ Dispatcher.UIThread.Post(() => DoLayoutReset(), DispatcherPriority.Background);
}
private void SetMenu(NativeMenu menu)
diff --git a/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs b/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs
deleted file mode 100644
index 8aa9b1a122..0000000000
--- a/src/Avalonia.Native/OsxManagedPopupPositionerPopupImplHelper.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Avalonia.Controls.Primitives.PopupPositioning;
-using Avalonia.Platform;
-
-namespace Avalonia.Native
-{
- class OsxManagedPopupPositionerPopupImplHelper : ManagedPopupPositionerPopupImplHelper
- {
- public OsxManagedPopupPositionerPopupImplHelper(IWindowBaseImpl parent, MoveResizeDelegate moveResize) : base(parent, moveResize)
- {
-
- }
-
- public override double Scaling => 1;
- }
-}
diff --git a/src/Avalonia.Native/OsxUnicodeKeys.cs b/src/Avalonia.Native/OsxUnicodeKeys.cs
index b4056c8cd8..fd9acfa714 100644
--- a/src/Avalonia.Native/OsxUnicodeKeys.cs
+++ b/src/Avalonia.Native/OsxUnicodeKeys.cs
@@ -140,6 +140,11 @@ namespace Avalonia.Native.Interop
}
else
{
+ if (key >= Key.D0 && key <= Key.D9)
+ {
+ return key.ToString().Replace("D", "");
+ }
+
return key.ToString().ToLower();
}
}
diff --git a/src/Avalonia.Native/PopupImpl.cs b/src/Avalonia.Native/PopupImpl.cs
index b0da5fdc43..2d246e08d2 100644
--- a/src/Avalonia.Native/PopupImpl.cs
+++ b/src/Avalonia.Native/PopupImpl.cs
@@ -26,7 +26,7 @@ namespace Avalonia.Native
var context = _opts.UseGpu ? glFeature?.DeferredContext : null;
Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context);
}
- PopupPositioner = new ManagedPopupPositioner(new OsxManagedPopupPositionerPopupImplHelper(parent, MoveResize));
+ PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
}
private void MoveResize(PixelPoint position, Size size, double scaling)
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index f916e95d7c..4b13666edd 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -81,7 +81,7 @@ namespace Avalonia.Native
_glSurface = new GlPlatformSurface(window, _glContext);
Screen = new ScreenImpl(screens);
_savedLogicalSize = ClientSize;
- _savedScaling = Scaling;
+ _savedScaling = RenderScaling;
_nativeControlHost = new NativeControlHostImpl(_native.CreateNativeControlHost());
var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
@@ -300,7 +300,15 @@ namespace Avalonia.Native
public IRenderer CreateRenderer(IRenderRoot root)
{
if (_deferredRendering)
- return new DeferredRenderer(root, AvaloniaLocator.Current.GetService());
+ {
+ var loop = AvaloniaLocator.Current.GetService();
+ var customRendererFactory = AvaloniaLocator.Current.GetService();
+
+ if (customRendererFactory != null)
+ return customRendererFactory.Create(root, loop);
+ return new DeferredRenderer(root, loop);
+ }
+
return new ImmediateRenderer(root);
}
@@ -369,7 +377,9 @@ namespace Avalonia.Native
_native.SetTopMost(value);
}
- public double Scaling => _native?.GetScaling() ?? 1;
+ public double RenderScaling => _native?.GetScaling() ?? 1;
+
+ public double DesktopScaling => 1;
public Action Deactivated { get; set; }
public Action Activated { get; set; }
@@ -432,7 +442,7 @@ namespace Avalonia.Native
TransparencyLevel = transparencyLevel;
- _native.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur);
+ _native?.SetBlurEnabled(TransparencyLevel >= WindowTransparencyLevel.Blur);
TransparencyLevelChanged?.Invoke(TransparencyLevel);
}
}
diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
index 72132eed93..d761e60c07 100644
--- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
+++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj
@@ -9,6 +9,5 @@
-
-
+
diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
index 64145b9c3c..d01a495108 100644
--- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
+++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
@@ -8,4 +8,5 @@
+
diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
index 6684772bfe..6c241bbc75 100644
--- a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
+++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj
@@ -4,8 +4,10 @@
AVALONIA_REMOTE_PROTOCOL;$(DefineConstants)
true
Key.snk
+ Avalonia.Remote.Protocol
+
\ No newline at end of file
diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj
index b4f6c2c942..d9f0cef3da 100644
--- a/src/Avalonia.Styling/Avalonia.Styling.csproj
+++ b/src/Avalonia.Styling/Avalonia.Styling.csproj
@@ -8,4 +8,5 @@
+
diff --git a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
index c44cc358e8..153e21761e 100644
--- a/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
+++ b/src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj
@@ -19,4 +19,5 @@
+
diff --git a/src/Avalonia.Themes.Default/ComboBox.xaml b/src/Avalonia.Themes.Default/ComboBox.xaml
index 411253941a..cced76e850 100644
--- a/src/Avalonia.Themes.Default/ComboBox.xaml
+++ b/src/Avalonia.Themes.Default/ComboBox.xaml
@@ -1,4 +1,5 @@
-
+
@@ -24,6 +25,7 @@
+
+
+
diff --git a/src/Avalonia.Themes.Default/ProgressBar.xaml b/src/Avalonia.Themes.Default/ProgressBar.xaml
index 43a0752bc8..d9c407245b 100644
--- a/src/Avalonia.Themes.Default/ProgressBar.xaml
+++ b/src/Avalonia.Themes.Default/ProgressBar.xaml
@@ -13,19 +13,14 @@
-
-
+
+
+
+
+
-
-
+
+
@@ -54,36 +49,31 @@
-
-
diff --git a/src/Avalonia.Themes.Default/TextBox.xaml b/src/Avalonia.Themes.Default/TextBox.xaml
index 4fb3653e89..6a746dda30 100644
--- a/src/Avalonia.Themes.Default/TextBox.xaml
+++ b/src/Avalonia.Themes.Default/TextBox.xaml
@@ -40,6 +40,8 @@
-
-
-
-
-
-
-
@@ -144,6 +148,8 @@
@@ -181,10 +187,15 @@
-
+
+
+
@@ -210,14 +221,15 @@
-
-
-
diff --git a/src/Avalonia.Themes.Fluent/NumericUpDown.xaml b/src/Avalonia.Themes.Fluent/NumericUpDown.xaml
index d1610b343c..cc5e2234f8 100644
--- a/src/Avalonia.Themes.Fluent/NumericUpDown.xaml
+++ b/src/Avalonia.Themes.Fluent/NumericUpDown.xaml
@@ -43,7 +43,7 @@
-
-
-
-
diff --git a/src/Avalonia.Themes.Fluent/TitleBar.xaml b/src/Avalonia.Themes.Fluent/TitleBar.xaml
index 45798d3fa1..4dba5b4ba4 100644
--- a/src/Avalonia.Themes.Fluent/TitleBar.xaml
+++ b/src/Avalonia.Themes.Fluent/TitleBar.xaml
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Avalonia.Themes.Fluent/ToggleButton.xaml b/src/Avalonia.Themes.Fluent/ToggleButton.xaml
index f2f07d3e2a..49e2280a6d 100644
--- a/src/Avalonia.Themes.Fluent/ToggleButton.xaml
+++ b/src/Avalonia.Themes.Fluent/ToggleButton.xaml
@@ -39,67 +39,67 @@
-
-
-
-
-
-
-
-
-
-
- ";
- var loader = new AvaloniaXamlLoader();
- var styles = (Styles)loader.Load(xaml);
+ var styles = (Styles)AvaloniaRuntimeXamlLoader.Load(xaml);
var brush = (SolidColorBrush)styles.Resources["brush"];
Assert.Equal(0xff506070, brush.Color.ToUint32());
@@ -350,8 +338,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
";
- var loader = new AvaloniaXamlLoader();
- var window = (Window)loader.Load(xaml);
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = window.FindControl