diff --git a/.gitignore b/.gitignore index d965a09574..640725fa26 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,11 @@ csx # Windows Store app package directory AppPackages/ +# NCrunch +_NCrunch_*/ +*.ncrunchsolution.user +nCrunchTemp_*/ + # Others sql/ *.Cache @@ -157,7 +162,8 @@ $RECYCLE.BIN/ ################# ## Cake ################# -tools/ +tools/* +!tools/packages.config .nuget artifacts/ nuget diff --git a/.gitmodules b/.gitmodules index 0b2349ad42..6e9b10efa4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,9 +5,6 @@ path = src/Avalonia.HtmlRenderer/external url = https://github.com/AvaloniaUI/HTML-Renderer.git branch = perspex-pcl -[submodule "src/Markup/Avalonia.Markup.Xaml/OmniXAML"] - path = src/Markup/Avalonia.Markup.Xaml/OmniXAML - url = https://github.com/AvaloniaUI/OmniXAML.git -[submodule "src/Markup/Avalonia.Markup.Xaml/glass"] - path = src/Markup/Avalonia.Markup.Xaml/glass - url = https://github.com/SuperJMN/glass +[submodule "src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github"] + path = src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github + url = https://github.com/AvaloniaUI/Portable.Xaml.git diff --git a/.ncrunch/Avalonia.Android.v3.ncrunchproject b/.ncrunch/Avalonia.Android.v3.ncrunchproject new file mode 100644 index 0000000000..a4953e14f9 --- /dev/null +++ b/.ncrunch/Avalonia.Android.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.AndroidTestApplication.v3.ncrunchproject b/.ncrunch/Avalonia.AndroidTestApplication.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.AndroidTestApplication.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Animation.v3.ncrunchproject b/.ncrunch/Avalonia.Animation.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Animation.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Base.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Base.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Base.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..13f78c1d4c --- /dev/null +++ b/.ncrunch/Avalonia.Base.UnitTests.v3.ncrunchproject @@ -0,0 +1,8 @@ + + + + LongTestTimesWithoutParallelExecution + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Base.v3.ncrunchproject b/.ncrunch/Avalonia.Base.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Base.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Benchmarks.v3.ncrunchproject b/.ncrunch/Avalonia.Benchmarks.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Benchmarks.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Cairo.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Cairo.RenderTests.v3.ncrunchproject new file mode 100644 index 0000000000..101c806e63 --- /dev/null +++ b/.ncrunch/Avalonia.Cairo.RenderTests.v3.ncrunchproject @@ -0,0 +1,8 @@ + + + + AbnormalReferenceResolution + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Cairo.v3.ncrunchproject b/.ncrunch/Avalonia.Cairo.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Cairo.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Controls.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..13f78c1d4c --- /dev/null +++ b/.ncrunch/Avalonia.Controls.UnitTests.v3.ncrunchproject @@ -0,0 +1,8 @@ + + + + LongTestTimesWithoutParallelExecution + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Controls.v3.ncrunchproject b/.ncrunch/Avalonia.Controls.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Controls.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Designer.v3.ncrunchproject b/.ncrunch/Avalonia.Designer.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Designer.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.DesignerSupport.TestApp.v3.ncrunchproject b/.ncrunch/Avalonia.DesignerSupport.TestApp.v3.ncrunchproject new file mode 100644 index 0000000000..a4953e14f9 --- /dev/null +++ b/.ncrunch/Avalonia.DesignerSupport.TestApp.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.DesignerSupport.Tests.v3.ncrunchproject b/.ncrunch/Avalonia.DesignerSupport.Tests.v3.ncrunchproject new file mode 100644 index 0000000000..a4953e14f9 --- /dev/null +++ b/.ncrunch/Avalonia.DesignerSupport.Tests.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.DesignerSupport.v3.ncrunchproject b/.ncrunch/Avalonia.DesignerSupport.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.DesignerSupport.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Diagnostics.v3.ncrunchproject b/.ncrunch/Avalonia.Diagnostics.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Diagnostics.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject new file mode 100644 index 0000000000..235da29767 --- /dev/null +++ b/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + 1000 + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Direct2D1.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Direct2D1.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Direct2D1.UnitTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Direct2D1.v3.ncrunchproject b/.ncrunch/Avalonia.Direct2D1.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Direct2D1.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.DotNetCoreRuntime.v3.ncrunchproject b/.ncrunch/Avalonia.DotNetCoreRuntime.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.DotNetCoreRuntime.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.DotNetFrameworkRuntime.v3.ncrunchproject b/.ncrunch/Avalonia.DotNetFrameworkRuntime.v3.ncrunchproject new file mode 100644 index 0000000000..3cd9a982c9 --- /dev/null +++ b/.ncrunch/Avalonia.DotNetFrameworkRuntime.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + False + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Gtk.v3.ncrunchproject b/.ncrunch/Avalonia.Gtk.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Gtk.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Gtk3.v3.ncrunchproject b/.ncrunch/Avalonia.Gtk3.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Gtk3.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.HtmlRenderer.v3.ncrunchproject b/.ncrunch/Avalonia.HtmlRenderer.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.HtmlRenderer.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Input.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Input.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Input.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Input.UnitTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Input.v3.ncrunchproject b/.ncrunch/Avalonia.Input.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Input.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Interactivity.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Interactivity.UnitTests.netcoreapp1.1.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Interactivity.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Interactivity.UnitTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Interactivity.v3.ncrunchproject b/.ncrunch/Avalonia.Interactivity.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Interactivity.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Layout.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Layout.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Layout.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Layout.UnitTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Layout.v3.ncrunchproject b/.ncrunch/Avalonia.Layout.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Layout.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.LeakTests.v3.ncrunchproject b/.ncrunch/Avalonia.LeakTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.LeakTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject b/.ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.LinuxFramebuffer.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Logging.Serilog.v3.ncrunchproject b/.ncrunch/Avalonia.Logging.Serilog.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Logging.Serilog.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Markup.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Markup.UnitTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.Xaml.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Markup.Xaml.UnitTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.Xaml.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.Xaml.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Markup.Xaml.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Markup.v3.ncrunchproject b/.ncrunch/Avalonia.Markup.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Markup.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.ReactiveUI.v3.ncrunchproject b/.ncrunch/Avalonia.ReactiveUI.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.ReactiveUI.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.RenderTests.v3.ncrunchproject new file mode 100644 index 0000000000..95a483b433 --- /dev/null +++ b/.ncrunch/Avalonia.RenderTests.v3.ncrunchproject @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.Android.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.Android.v3.ncrunchproject new file mode 100644 index 0000000000..a4953e14f9 --- /dev/null +++ b/.ncrunch/Avalonia.Skia.Android.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.Desktop.NetStandard.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.Desktop.NetStandard.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Skia.Desktop.NetStandard.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.Desktop.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.Desktop.v3.ncrunchproject new file mode 100644 index 0000000000..ae3801f32b --- /dev/null +++ b/.ncrunch/Avalonia.Skia.Desktop.v3.ncrunchproject @@ -0,0 +1,8 @@ + + + True + False + True + x86 + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject new file mode 100644 index 0000000000..235da29767 --- /dev/null +++ b/.ncrunch/Avalonia.Skia.RenderTests.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + 1000 + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.iOS.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.iOS.v3.ncrunchproject new file mode 100644 index 0000000000..a4953e14f9 --- /dev/null +++ b/.ncrunch/Avalonia.Skia.iOS.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Skia.v3.ncrunchproject b/.ncrunch/Avalonia.Skia.v3.ncrunchproject new file mode 100644 index 0000000000..95a483b433 --- /dev/null +++ b/.ncrunch/Avalonia.Skia.v3.ncrunchproject @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Styling.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Styling.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Styling.UnitTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Styling.v3.ncrunchproject b/.ncrunch/Avalonia.Styling.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Styling.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Themes.Default.v3.ncrunchproject b/.ncrunch/Avalonia.Themes.Default.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Themes.Default.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.UnitTests.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject b/.ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Visuals.UnitTests.net461.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Visuals.UnitTests.v3.ncrunchproject b/.ncrunch/Avalonia.Visuals.UnitTests.v3.ncrunchproject new file mode 100644 index 0000000000..ee655751a0 --- /dev/null +++ b/.ncrunch/Avalonia.Visuals.UnitTests.v3.ncrunchproject @@ -0,0 +1,9 @@ + + + + AbnormalReferenceResolution + LongTestTimesWithoutParallelExecution + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Visuals.v3.ncrunchproject b/.ncrunch/Avalonia.Visuals.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Visuals.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Win32.Interop.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.NetStandard.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.NetStandard.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.Win32.NetStandard.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.Shared.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.Shared.v3.ncrunchproject new file mode 100644 index 0000000000..95a483b433 --- /dev/null +++ b/.ncrunch/Avalonia.Win32.Shared.v3.ncrunchproject @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Win32.v3.ncrunchproject b/.ncrunch/Avalonia.Win32.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Avalonia.Win32.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.iOS.v3.ncrunchproject b/.ncrunch/Avalonia.iOS.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.iOS.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.iOSTestApplication.v3.ncrunchproject b/.ncrunch/Avalonia.iOSTestApplication.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/Avalonia.iOSTestApplication.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/BindingTest.v3.ncrunchproject b/.ncrunch/BindingTest.v3.ncrunchproject new file mode 100644 index 0000000000..a4953e14f9 --- /dev/null +++ b/.ncrunch/BindingTest.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/.ncrunch/ControlCatalog.Android.v3.ncrunchproject b/.ncrunch/ControlCatalog.Android.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/ControlCatalog.Android.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/ControlCatalog.Desktop.v3.ncrunchproject b/.ncrunch/ControlCatalog.Desktop.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/ControlCatalog.Desktop.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/ControlCatalog.iOS.v3.ncrunchproject b/.ncrunch/ControlCatalog.iOS.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/ControlCatalog.iOS.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/ControlCatalog.v3.ncrunchproject b/.ncrunch/ControlCatalog.v3.ncrunchproject new file mode 100644 index 0000000000..a4953e14f9 --- /dev/null +++ b/.ncrunch/ControlCatalog.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/.ncrunch/Direct3DInteropSample.v3.ncrunchproject b/.ncrunch/Direct3DInteropSample.v3.ncrunchproject new file mode 100644 index 0000000000..6800b4a3fe --- /dev/null +++ b/.ncrunch/Direct3DInteropSample.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/GtkInteropDemo.v3.ncrunchproject b/.ncrunch/GtkInteropDemo.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/GtkInteropDemo.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/PlatformSupport.v3.ncrunchproject b/.ncrunch/PlatformSupport.v3.ncrunchproject new file mode 100644 index 0000000000..95a483b433 --- /dev/null +++ b/.ncrunch/PlatformSupport.v3.ncrunchproject @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.ncrunch/RenderHelpers.v3.ncrunchproject b/.ncrunch/RenderHelpers.v3.ncrunchproject new file mode 100644 index 0000000000..95a483b433 --- /dev/null +++ b/.ncrunch/RenderHelpers.v3.ncrunchproject @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.ncrunch/RenderTest.v3.ncrunchproject b/.ncrunch/RenderTest.v3.ncrunchproject new file mode 100644 index 0000000000..e12537d535 --- /dev/null +++ b/.ncrunch/RenderTest.v3.ncrunchproject @@ -0,0 +1,8 @@ + + + + MissingOrIgnoredProjectReference + + True + + \ No newline at end of file diff --git a/.ncrunch/VirtualizationTest.v3.ncrunchproject b/.ncrunch/VirtualizationTest.v3.ncrunchproject new file mode 100644 index 0000000000..a4953e14f9 --- /dev/null +++ b/.ncrunch/VirtualizationTest.v3.ncrunchproject @@ -0,0 +1,6 @@ + + + True + True + + \ No newline at end of file diff --git a/.ncrunch/WindowsInteropTest.v3.ncrunchproject b/.ncrunch/WindowsInteropTest.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/WindowsInteropTest.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index af7d5dbebf..a3c02eea58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,14 @@ language: csharp os: - linux - osx +dist: trusty +env: + global: + - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 + - DOTNET_CLI_TELEMETRY_OPTOUT=1 mono: - latest +dotnet: 1.0.1 script: - ./build.sh --target "Travis" --platform "Mono" --configuration "Release" notifications: diff --git a/Avalonia.sln b/Avalonia.sln index ad14483b40..afaee6a907 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.4 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" EndProject @@ -15,9 +15,6 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Direct2D1", "src\Windows\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj", "{3E908F67-5543-4879-A1DC-08EACE79B3CD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Designer", "src\Windows\Avalonia.Designer\Avalonia.Designer.csproj", "{EC42600F-049B-43FF-AED1-8314D61B2749}" - ProjectSection(ProjectDependencies) = postProject - {2B888490-D14A-4BCA-AB4B-48676FA93C9B} = {2B888490-D14A-4BCA-AB4B-48676FA93C9B} - EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Input", "src\Avalonia.Input\Avalonia.Input.csproj", "{62024B2D-53EB-4638-B26B-85EEAA54866E}" EndProject @@ -85,9 +82,6 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Markup.UnitTests", "tests\Avalonia.Markup.UnitTests\Avalonia.Markup.UnitTests.csproj", "{8EF392D5-1416-45AA-9956-7CBBC3229E8A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BindingTest", "samples\BindingTest\BindingTest.csproj", "{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}" - ProjectSection(ProjectDependencies) = postProject - {B61B66A3-B82D-4875-8001-89D3394FE0C9} = {B61B66A3-B82D-4875-8001-89D3394FE0C9} - EndProjectSection EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "RenderHelpers", "src\Shared\RenderHelpers\RenderHelpers.shproj", "{3C4C0CB4-0C0F-4450-A37B-148C84FF905F}" EndProject @@ -126,9 +120,6 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog", "samples\ControlCatalog\ControlCatalog.csproj", "{D0A739B9-3C68-4BA6-A328-41606954B6BD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}" - ProjectSection(ProjectDependencies) = postProject - {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} = {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} - EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}" EndProject @@ -164,6 +155,44 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Skia.Desktop.NetSt EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Gtk3", "src\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj", "{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.NetCore", "samples\ControlCatalog.NetCore\ControlCatalog.NetCore.csproj", "{39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{74487168-7D91-487E-BF93-055F2251461E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}" + ProjectSection(SolutionItems) = preProject + 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 + build\Markup.props = build\Markup.props + build\Microsoft.CSharp.props = build\Microsoft.CSharp.props + build\Microsoft.Reactive.Testing.props = build\Microsoft.Reactive.Testing.props + build\Moq.props = build\Moq.props + build\NetCore.props = build\NetCore.props + build\Rx.props = build\Rx.props + build\Serilog.props = build\Serilog.props + build\Serilog.Sinks.Trace.props = build\Serilog.Sinks.Trace.props + build\SharpDX.props = build\SharpDX.props + build\SkiaSharp.Desktop.props = build\SkiaSharp.Desktop.props + build\SkiaSharp.props = build\SkiaSharp.props + build\Splat.props = build\Splat.props + build\Sprache.props = build\Sprache.props + build\XUnit.props = build\XUnit.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 + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linux", "Linux", "{86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.LinuxFramebuffer", "src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj", "{854568D5-13D1-4B4F-B50D-534DC7EFD3C9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Direct3DInteropSample", "samples\interop\Direct3DInteropSample\Direct3DInteropSample.csproj", "{638580B0-7910-40EF-B674-DCB34DA308CD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "src\Windows\Avalonia.Win32.Interop\Avalonia.Win32.Interop.csproj", "{CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13 @@ -2442,6 +2471,166 @@ Global {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Mono.Build.0 = Release|Any CPU {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|x86.ActiveCfg = Release|Any CPU {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|x86.Build.0 = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhone.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|Mono.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|Mono.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|x86.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.AppStore|x86.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhone.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Mono.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|Mono.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Debug|x86.Build.0 = Debug|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Any CPU.Build.0 = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhone.ActiveCfg = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhone.Build.0 = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Mono.ActiveCfg = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|Mono.Build.0 = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|x86.ActiveCfg = Release|Any CPU + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3}.Release|x86.Build.0 = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhone.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Mono.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|Mono.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|x86.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.AppStore|x86.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhone.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Mono.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|Mono.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|x86.ActiveCfg = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Debug|x86.Build.0 = Debug|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Any CPU.Build.0 = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhone.ActiveCfg = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhone.Build.0 = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Mono.ActiveCfg = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|Mono.Build.0 = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|x86.ActiveCfg = Release|Any CPU + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9}.Release|x86.Build.0 = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhone.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Mono.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|Mono.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|x86.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.AppStore|x86.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhone.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Mono.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|Mono.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|x86.ActiveCfg = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Debug|x86.Build.0 = Debug|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Any CPU.Build.0 = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhone.ActiveCfg = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhone.Build.0 = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Mono.ActiveCfg = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|Mono.Build.0 = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|x86.ActiveCfg = Release|Any CPU + {638580B0-7910-40EF-B674-DCB34DA308CD}.Release|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Ad-Hoc|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.AppStore|x86.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhone.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Mono.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|Mono.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|x86.ActiveCfg = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Debug|x86.Build.0 = Debug|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Any CPU.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhone.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Mono.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|Mono.Build.0 = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|x86.ActiveCfg = Release|Any CPU + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2497,5 +2686,11 @@ Global {40759A76-D0F2-464E-8000-6FF0F5C4BD7C} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {7D2D3083-71DD-4CC9-8907-39A0D86FB322} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E} {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} = {B9894058-278A-46B5-B6ED-AD613FCC03B3} + {39D7B147-1A5B-47C2-9D01-21FB7C47C4B3} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {F3AC8BC1-27F5-4255-9AFC-04ABFD11683A} = {74487168-7D91-487E-BF93-055F2251461E} + {4D6FAF79-58B4-482F-9122-0668C346364C} = {74487168-7D91-487E-BF93-055F2251461E} + {854568D5-13D1-4B4F-B50D-534DC7EFD3C9} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B} + {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} + {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} EndGlobalSection EndGlobal diff --git a/Avalonia.sln.DotSettings b/Avalonia.sln.DotSettings index ab21d6e50b..1fd6f8d092 100644 --- a/Avalonia.sln.DotSettings +++ b/Avalonia.sln.DotSettings @@ -1,4 +1,5 @@  + ExplicitlyExcluded ExplicitlyExcluded ExplicitlyExcluded HINT diff --git a/Avalonia.v2.ncrunchsolution b/Avalonia.v2.ncrunchsolution deleted file mode 100644 index fe4feb2cb6..0000000000 Binary files a/Avalonia.v2.ncrunchsolution and /dev/null differ diff --git a/Avalonia.v3.ncrunchsolution b/Avalonia.v3.ncrunchsolution new file mode 100644 index 0000000000..1b5b0c8930 --- /dev/null +++ b/Avalonia.v3.ncrunchsolution @@ -0,0 +1,10 @@ + + + + tests\TestFiles\**.* + + True + .ncrunch + True + + \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index db7d129dd1..7457a1d5bb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,26 +1,31 @@ -os: Visual Studio 2015 +os: Visual Studio 2017 platform: - Any CPU +skip_branch_with_pr: true configuration: - Release environment: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_CLI_TELEMETRY_OPTOUT: 1 NUGET_API_KEY: secure: Xv89dlP2MSBZKhl1nrWSxqcDgCXB0HRhOd4SWQ+jRJ7QoLxQel5mLTipXM++J3G5 NUGET_API_URL: https://www.nuget.org/api/v2/package MYGET_API_KEY: secure: OtVfyN3ErqQrDTnWH2HDfJDlCiu/i4/X4wFmK3ZXXP7HmCiXYPSbTjMPwwdOxRaK MYGET_API_URL: https://www.myget.org/F/avalonia-ci/api/v2/package +init: +- ps: if (Test-Path env:nuget_address) {[System.IO.File]::AppendAllText("C:\Windows\System32\drivers\etc\hosts", "`n$($env:nuget_address)`tapi.nuget.org")} install: - if not exist gtk-sharp-2.12.26.msi appveyor DownloadFile http://download.xamarin.com/GTKforWindows/Windows/gtk-sharp-2.12.26.msi - - msiexec /i gtk-sharp-2.12.26.msi /qn /norestart + - if not exist dotnet-1.0.1.exe appveyor DownloadFile https://go.microsoft.com/fwlink/?linkid=843448 -FileName "dotnet-1.0.1.exe" + - ps: Start-Process -FilePath "msiexec" -ArgumentList "/i gtk-sharp-2.12.26.msi /quiet /qn /norestart" -Wait + - ps: Start-Process -FilePath "dotnet-1.0.1.exe" -ArgumentList "/quiet" -Wait - cmd: set PATH=%programfiles(x86)%\GtkSharp\2.12\bin\;%PATH% before_build: - git submodule update --init build_script: - ps: .\build.ps1 -Target "AppVeyor" -Platform "$env:platform" -Configuration "$env:configuration" after_build: -- .\packages\JetBrains.dotMemoryUnit.2.1.20150828.125449\tools\dotMemoryUnit.exe -targetExecutable="%xunit20%\xunit.console.x86.exe" -returnTargetExitCode --"tests\Avalonia.LeakTests\bin\Release\Avalonia.LeakTests.dll" - - "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%" - pip install codecov - codecov -f "./artifacts/coverage.xml" @@ -29,5 +34,7 @@ test: off artifacts: - path: artifacts\nuget\*.nupkg - path: artifacts\zip\*.zip + - path: artifacts\inspectcode.xml cache: - gtk-sharp-2.12.26.msi + - dotnet-1.0.1.exe diff --git a/build.cake b/build.cake index ee68c74bb3..6c2e554df4 100644 --- a/build.cake +++ b/build.cake @@ -4,12 +4,14 @@ #addin "nuget:?package=Polly&version=4.2.0" #addin "nuget:?package=NuGet.Core&version=2.12.0" - +#tool "nuget:?package=xunit.runner.console&version=2.2.0" +#tool "nuget:https://dotnet.myget.org/F/nuget-build/?package=NuGet.CommandLine&version=4.3.0-preview1-3980&prerelease" +#tool "JetBrains.ReSharper.CommandLineTools" /////////////////////////////////////////////////////////////////////////////// // TOOLS /////////////////////////////////////////////////////////////////////////////// -#tool "nuget:?package=xunit.runner.console&version=2.1.0" +#tool "nuget:?package=xunit.runner.console&version=2.2.0" #tool "nuget:?package=OpenCover" /////////////////////////////////////////////////////////////////////////////// @@ -96,50 +98,13 @@ Task("Clean") CleanDirectory(parameters.TestsRoot); }); -Task("Prepare-XBuild-Solution") - .Does(() => -{ - var blacklistedProjects = new[] - { - "Avalonia.Win32.NetStandard", - "Avalonia.DotNetCoreRuntime", - "Avalonia.Skia.Desktop.NetStandard", - "Avalonia.Gtk3" - }; - var blacklistedGuids = System.IO.File.ReadAllLines(parameters.MSBuildSolution) - .Where(l=>l.StartsWith("Project") && blacklistedProjects.Any(p=>l.Contains(p))) - .Select(l => l.Split(',').Select(part => part.Trim()).FirstOrDefault(part => part.StartsWith("\"{"))) - .Where(g=>g!=null) - .Select(l=>l.Trim(new[]{'"', '}', '{'}).ToLower()).ToArray(); - - Console.WriteLine("Blacklisted guids are: " + string.Join(",", blacklistedGuids)); - var removeUntilEndProject = false; - - System.IO.File.WriteAllLines(parameters.XBuildSolution, System.IO.File.ReadAllLines(parameters.MSBuildSolution) - .Where(l => - { - if(removeUntilEndProject) - { - if(l.StartsWith("EndProject")) - removeUntilEndProject = false; - return false; - } - - var blacklist = blacklistedGuids.Any(g => l.ToLower().Contains(g)); - if(blacklist && l.StartsWith("Project")) - removeUntilEndProject = true; - - return !blacklist; - })); -}); - Task("Restore-NuGet-Packages") .IsDependentOn("Clean") - .IsDependentOn("Prepare-XBuild-Solution") + .WithCriteria(parameters.IsRunningOnWindows) .Does(() => { var maxRetryCount = 5; - var toolTimeout = 1d; + var toolTimeout = 2d; Policy .Handle() .Retry(maxRetryCount, (exception, retryCount, context) => { @@ -153,21 +118,22 @@ Task("Restore-NuGet-Packages") toolTimeout+=0.5; }}) .Execute(()=> { - if(parameters.IsRunningOnWindows) - { NuGetRestore(parameters.MSBuildSolution, new NuGetRestoreSettings { ToolTimeout = TimeSpan.FromMinutes(toolTimeout) }); - } - else - { - NuGetRestore(parameters.XBuildSolution, new NuGetRestoreSettings { - ToolTimeout = TimeSpan.FromMinutes(toolTimeout) - }); - } }); }); +void DotNetCoreBuild() +{ + DotNetCoreRestore("samples\\ControlCatalog.NetCore"); + DotNetBuild("samples\\ControlCatalog.NetCore"); +} + +Task("DotNetCoreBuild") + .IsDependentOn("Clean") + .Does(() => DotNetCoreBuild()); + Task("Build") .IsDependentOn("Restore-NuGet-Packages") .Does(() => @@ -177,40 +143,69 @@ Task("Build") MSBuild(parameters.MSBuildSolution, settings => { settings.SetConfiguration(parameters.Configuration); settings.WithProperty("Platform", "\"" + parameters.Platform + "\""); + settings.WithProperty("UseRoslynPathHack", "true"); settings.SetVerbosity(Verbosity.Minimal); settings.WithProperty("Windows", "True"); - settings.UseToolVersion(MSBuildToolVersion.VS2015); + settings.UseToolVersion(MSBuildToolVersion.VS2017); settings.SetNodeReuse(false); }); } else { - XBuild(parameters.XBuildSolution, settings => { - settings.SetConfiguration(parameters.Configuration); - settings.WithProperty("Platform", "\"" + parameters.Platform + "\""); - settings.SetVerbosity(Verbosity.Minimal); - }); + DotNetCoreBuild(); } }); +void RunCoreTest(string dir, Parameters parameters, bool net461Only) +{ + Information("Running tests from " + dir); + DotNetCoreRestore(dir); + var frameworks = new List(){"netcoreapp1.1"}; + if(parameters.IsRunningOnWindows) + frameworks.Add("net461"); + foreach(var fw in frameworks) + { + if(fw != "net461" && net461Only) + continue; + Information("Running for " + fw); + DotNetCoreTest(System.IO.Path.Combine(dir, System.IO.Path.GetFileName(dir)+".csproj"), + new DotNetCoreTestSettings { + Configuration = parameters.Configuration, + Framework = fw + }); + } +} + +Task("Run-Net-Core-Unit-Tests") + .IsDependentOn("Clean") + .Does(() => { + RunCoreTest("./tests/Avalonia.Base.UnitTests", parameters, false); + RunCoreTest("./tests/Avalonia.Controls.UnitTests", parameters, false); + RunCoreTest("./tests/Avalonia.Input.UnitTests", parameters, false); + RunCoreTest("./tests/Avalonia.Interactivity.UnitTests", parameters, false); + RunCoreTest("./tests/Avalonia.Layout.UnitTests", parameters, false); + RunCoreTest("./tests/Avalonia.Markup.UnitTests", parameters, false); + RunCoreTest("./tests/Avalonia.Markup.Xaml.UnitTests", parameters, false); + RunCoreTest("./tests/Avalonia.Styling.UnitTests", parameters, false); + RunCoreTest("./tests/Avalonia.Visuals.UnitTests", parameters, false); + }); + Task("Run-Unit-Tests") + .IsDependentOn("Run-Net-Core-Unit-Tests") .IsDependentOn("Build") + //.IsDependentOn("Run-Leak-Tests") .WithCriteria(() => !parameters.SkipTests) .Does(() => { + if(!parameters.IsRunningOnWindows) + return; + var unitTests = GetDirectories("./tests/Avalonia.*.UnitTests") .Select(dir => System.IO.Path.GetFileName(dir.FullPath)) .Where(name => parameters.IsRunningOnWindows ? true : !(name.IndexOf("Direct2D", StringComparison.OrdinalIgnoreCase) >= 0)) .Select(name => MakeAbsolute(File("./tests/" + name + "/bin/" + parameters.DirSuffix + "/" + name + ".dll"))) .ToList(); - if (parameters.IsRunningOnWindows) - { - var leakTests = GetFiles("./tests/Avalonia.LeakTests/bin/" + parameters.DirSuffix + "/*.LeakTests.dll"); - - unitTests.AddRange(leakTests); - } - var toolPath = (parameters.IsPlatformAnyCPU || parameters.IsPlatformX86) ? "./tools/xunit.runner.console/tools/xunit.console.x86.exe" : "./tools/xunit.runner.console/tools/xunit.console.exe"; @@ -236,7 +231,11 @@ Task("Run-Unit-Tests") { CopyDirectory(test.GetDirectory(), parameters.TestsRoot); } - + + CopyFile(System.IO.Path.Combine(packages.NugetPackagesDir, "SkiaSharp", packages.SkiaSharpVersion, + "runtimes", "win7-x86", "native", "libSkiaSharp.dll"), + System.IO.Path.Combine(parameters.TestsRoot.ToString(), "libSkiaSharp.dll")); + var testsInDirectoryToRun = new List(); if(parameters.IsRunningOnWindows) { @@ -275,11 +274,15 @@ Task("Zip-Files") Zip(parameters.ZipSourceControlCatalogDesktopDirs, parameters.ZipTargetControlCatalogDesktopDirs, GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dll") + + GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.config") + + GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.so") + + GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.dylib") + GetFiles(parameters.ZipSourceControlCatalogDesktopDirs.FullPath + "/*.exe")); }); Task("Create-NuGet-Packages") .IsDependentOn("Run-Unit-Tests") + .IsDependentOn("Inspect") .Does(() => { foreach(var nuspec in packages.NuspecNuGetSettings) @@ -327,7 +330,6 @@ Task("Publish-NuGet") .WithCriteria(() => !parameters.IsLocalBuild) .WithCriteria(() => !parameters.IsPullRequest) .WithCriteria(() => parameters.IsMainRepo) - .WithCriteria(() => parameters.IsMasterBranch) .WithCriteria(() => parameters.IsNuGetRelease) .Does(() => { @@ -356,6 +358,78 @@ Task("Publish-NuGet") Information("Publish-NuGet Task failed, but continuing with next Task..."); }); +Task("Run-Leak-Tests") + .WithCriteria(parameters.IsRunningOnWindows) + .IsDependentOn("Build") + .Does(() => + { + DotNetCoreRestore("tests\\Avalonia.LeakTests\\toolproject\\tool.csproj"); + DotNetBuild("tests\\Avalonia.LeakTests\\toolproject\\tool.csproj", settings => settings.SetConfiguration("Release")); + var report = "tests\\Avalonia.LeakTests\\bin\\Release\\report.xml"; + if(System.IO.File.Exists(report)) + System.IO.File.Delete(report); + var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName="tests\\Avalonia.LeakTests\\toolproject\\bin\\dotMemoryUnit.exe", + Arguments="-targetExecutable=\"tools\\xunit.runner.console\\tools\\xunit.console.x86.exe\" -returnTargetExitCode -- tests\\Avalonia.LeakTests\\bin\\Release\\Avalonia.LeakTests.dll -xml tests\\Avalonia.LeakTests\\bin\\Release\\report.xml ", + UseShellExecute = false, + }); + var st = System.Diagnostics.Stopwatch.StartNew(); + while(!proc.HasExited && !System.IO.File.Exists(report)) + { + if(st.Elapsed.TotalSeconds>60) + { + Error("Timed out, probably a bug in dotMemoryUnit"); + proc.Kill(); + throw new Exception("dotMemory issue"); + } + proc.WaitForExit(100); + } + try{ + proc.Kill(); + }catch{} + var doc = System.Xml.Linq.XDocument.Load(report); + if(doc.Root.Descendants("assembly").Any(x=>x.Attribute("failed").Value.ToString() != "0")) + { + throw new Exception("Tests failed"); + } + + }); + +Task("Inspect") + .WithCriteria(parameters.IsRunningOnWindows) + .IsDependentOn("Restore-NuGet-Packages") + .Does(() => + { + var badIssues = new []{"PossibleNullReferenceException"}; + var whitelist = new []{"tests", "src\\android", "src\\ios", + "src\\windows\\avalonia.designer", "src\\avalonia.htmlrenderer\\external", + "src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"}; + Information("Running code inspections"); + + + StartProcess("tools\\JetBrains.ReSharper.CommandLineTools\\tools\\inspectcode.exe", + new ProcessSettings{ Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln" }); + Information("Analyzing report"); + var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml")); + var failBuild = false; + foreach(var xml in doc.Descendants("Issue")) + { + var typeId = xml.Attribute("TypeId").Value.ToString(); + if(badIssues.Contains(typeId)) + { + var file = xml.Attribute("File").Value.ToString().ToLower(); + if(whitelist.Any(wh => file.StartsWith(wh))) + continue; + var line = xml.Attribute("Line").Value.ToString(); + Error(typeId + " - " + file + " on line " + line); + failBuild = true; + } + } + if(failBuild) + throw new Exception("Issues found"); + }); + /////////////////////////////////////////////////////////////////////////////// // TARGETS /////////////////////////////////////////////////////////////////////////////// @@ -363,16 +437,20 @@ Task("Publish-NuGet") Task("Package") .IsDependentOn("Create-NuGet-Packages"); -Task("Default") - .IsDependentOn("Package"); - +Task("Default").Does(() => +{ + if(parameters.IsRunningOnWindows) + RunTarget("Package"); + else + RunTarget("Run-Net-Core-Unit-Tests"); +}); Task("AppVeyor") .IsDependentOn("Zip-Files") .IsDependentOn("Publish-MyGet") .IsDependentOn("Publish-NuGet"); Task("Travis") - .IsDependentOn("Run-Unit-Tests"); + .IsDependentOn("Run-Net-Core-Unit-Tests"); /////////////////////////////////////////////////////////////////////////////// // EXECUTE diff --git a/build/Base.props b/build/Base.props new file mode 100644 index 0000000000..6689465338 --- /dev/null +++ b/build/Base.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/JetBrains.Annotations.props b/build/JetBrains.Annotations.props new file mode 100644 index 0000000000..7bc12cbd84 --- /dev/null +++ b/build/JetBrains.Annotations.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/JetBrains.dotMemoryUnit.props b/build/JetBrains.dotMemoryUnit.props new file mode 100644 index 0000000000..b4cf4def5b --- /dev/null +++ b/build/JetBrains.dotMemoryUnit.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/Magick.NET-Q16-AnyCPU.props b/build/Magick.NET-Q16-AnyCPU.props new file mode 100644 index 0000000000..ec747a17d8 --- /dev/null +++ b/build/Magick.NET-Q16-AnyCPU.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/Markup.props b/build/Markup.props new file mode 100644 index 0000000000..5be0cfbf0c --- /dev/null +++ b/build/Markup.props @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/build/Microsoft.CSharp.props b/build/Microsoft.CSharp.props new file mode 100644 index 0000000000..d0fa63bc3e --- /dev/null +++ b/build/Microsoft.CSharp.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/Microsoft.Reactive.Testing.props b/build/Microsoft.Reactive.Testing.props new file mode 100644 index 0000000000..5ee6df708e --- /dev/null +++ b/build/Microsoft.Reactive.Testing.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/Moq.props b/build/Moq.props new file mode 100644 index 0000000000..55242d922e --- /dev/null +++ b/build/Moq.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/NetCore.props b/build/NetCore.props new file mode 100644 index 0000000000..cebb02c7f5 --- /dev/null +++ b/build/NetCore.props @@ -0,0 +1,6 @@ + + + + + + diff --git a/build/Rx.props b/build/Rx.props new file mode 100644 index 0000000000..5d74a6b214 --- /dev/null +++ b/build/Rx.props @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/build/Serilog.Sinks.Trace.props b/build/Serilog.Sinks.Trace.props new file mode 100644 index 0000000000..ef79fd7669 --- /dev/null +++ b/build/Serilog.Sinks.Trace.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/Serilog.props b/build/Serilog.props new file mode 100644 index 0000000000..e9bd39f6af --- /dev/null +++ b/build/Serilog.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/SharpDX.props b/build/SharpDX.props new file mode 100644 index 0000000000..69aa817a01 --- /dev/null +++ b/build/SharpDX.props @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/build/SkiaSharp.props b/build/SkiaSharp.props new file mode 100644 index 0000000000..04e8a3ad4f --- /dev/null +++ b/build/SkiaSharp.props @@ -0,0 +1,6 @@ + + + + + + diff --git a/build/Splat.props b/build/Splat.props new file mode 100644 index 0000000000..bc3367be01 --- /dev/null +++ b/build/Splat.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/Sprache.props b/build/Sprache.props new file mode 100644 index 0000000000..8ea84de163 --- /dev/null +++ b/build/Sprache.props @@ -0,0 +1,5 @@ + + + + + diff --git a/build/UnitTests.NetCore.targets b/build/UnitTests.NetCore.targets new file mode 100644 index 0000000000..13bb4ed230 --- /dev/null +++ b/build/UnitTests.NetCore.targets @@ -0,0 +1,28 @@ + + + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + MinimumRecommendedRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + MinimumRecommendedRules.ruleset + + + + + \ No newline at end of file diff --git a/build/XUnit.props b/build/XUnit.props new file mode 100644 index 0000000000..d59cee6536 --- /dev/null +++ b/build/XUnit.props @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + true + + + + + true + + + diff --git a/build/readme.md b/build/readme.md new file mode 100644 index 0000000000..aed508a508 --- /dev/null +++ b/build/readme.md @@ -0,0 +1,27 @@ +### MSBuild Props + +```XML + + + + + + + + + + + + + + + + + +``` + +### MSBuild Targets + +```XML + +``` \ No newline at end of file diff --git a/docs/guidelines/build.md b/docs/guidelines/build.md index 12047ff357..822496814b 100644 --- a/docs/guidelines/build.md +++ b/docs/guidelines/build.md @@ -2,7 +2,7 @@ ## Windows -Avalonia requires at least Visual Studio 2015 to build on Windows. +Avalonia requires at least Visual Studio 2017 to build on Windows. ### Install GTK Sharp diff --git a/docs/index.md b/docs/index.md index 99424e3f75..37d95b203d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,7 +10,7 @@ What does alpha mean? Well, it means that it's now at a stage where you can have ## How do I try it out -The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/a4542e8a-b56c-4295-8df1-7e220178b873). +The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio). This will add a Avalonia project template and a Window template to the standard Visual Studo "Add" dialog (yes, icons still to come :) ): diff --git a/docs/tutorial/from-wpf.md b/docs/tutorial/from-wpf.md index aa7a9bc13a..0eb6e80327 100644 --- a/docs/tutorial/from-wpf.md +++ b/docs/tutorial/from-wpf.md @@ -33,7 +33,7 @@ placed in a `DataTemplates` collection on each control (and on `Application`): - + @@ -161,7 +161,7 @@ the same way that event class listeners are added](../spec/working-with-properti ## RenderTransforms and RenderTransformOrigin -RenderTransformOrigins are different in WPF and Avalonia: If you apply a `RenderTransform`, keep in mind that our default value for the RenderTransformOrigin is `RelativePoint.Middle`. In WPF the default value is `RelativePoint.TopLeft` (0, 0). In controls like Viewbox (currently being developed) the same code will lead to a different rendering behavior: +RenderTransformOrigins are different in WPF and Avalonia: If you apply a `RenderTransform`, keep in mind that our default value for the RenderTransformOrigin is `RelativePoint.Center`. In WPF the default value is `RelativePoint.TopLeft` (0, 0). In controls like Viewbox (currently being developed) the same code will lead to a different rendering behavior: In WPF: ![WPF](https://files.gitter.im/AvaloniaUI/Avalonia/cDrM/image.png) diff --git a/docs/tutorial/gettingstarted.md b/docs/tutorial/gettingstarted.md index 0972d26c48..5b7fe436a7 100644 --- a/docs/tutorial/gettingstarted.md +++ b/docs/tutorial/gettingstarted.md @@ -4,7 +4,7 @@ ![](images/add-dialogs.png) -The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://visualstudiogallery.msdn.microsoft.com/e1c6ae1f-6fd9-467d-8f62-1e28b4225213). +The easiest way to try out Avalonia is to install the [Visual Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio). This will add a Avalonia project template and a Window template to the standard Visual Studo “Add” dialog (yes, icons still to come :) ): diff --git a/packages.cake b/packages.cake index 67483c4579..0824dc6098 100644 --- a/packages.cake +++ b/packages.cake @@ -1,9 +1,49 @@ +using System.Xml.Linq; + public class Packages { public List NuspecNuGetSettings { get; private set; } public FilePath[] NugetPackages { get; private set; } public FilePath[] BinFiles { get; private set; } - + public string NugetPackagesDir {get; private set;} + public string SkiaSharpVersion {get; private set; } + public string SkiaSharpLinuxVersion {get; private set; } + public Dictionary>> PackageVersions{get; private set;} + + + + class DependencyBuilder : List + { + Packages _parent; + public DependencyBuilder(Packages parent) + { + _parent = parent; + } + + string GetVersion(string name) + { + return _parent.PackageVersions[name].First().Item1; + } + + + public DependencyBuilder Dep(string name, params string[] fws) + { + if(fws.Length == 0) + Add(new NuSpecDependency() { Id = name, Version = GetVersion(name) }); + foreach(var fw in fws) + Add(new NuSpecDependency() { Id = name, TargetFramework = fw, Version = GetVersion(name) }); + return this; + } + public DependencyBuilder Deps(string[] fws, params string[] deps) + { + foreach(var fw in fws) + foreach(var name in deps) + Add(new NuSpecDependency() { Id = name, TargetFramework = fw, Version = GetVersion(name) }); + return this; + } + } + + //new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" }, public Packages(ICakeContext context, Parameters parameters) { // NUGET NUSPECS @@ -20,24 +60,30 @@ public class Packages path => path.Replace(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar).ToUpperInvariant()); // Key: Package Id - // Value is Tuple where Item1: Package Version, Item2: The packages.config file path. + // Value is Tuple where Item1: Package Version, Item2: The *.csproj/*.props file path. var packageVersions = new Dictionary>>(); - - System.IO.Directory.EnumerateFiles(((DirectoryPath)context.Directory("./src")).FullPath, "packages.config", SearchOption.AllDirectories).ToList().ForEach(fileName => + PackageVersions = packageVersions; + System.IO.Directory.EnumerateFiles(((DirectoryPath)context.Directory("./build")).FullPath, + "*.props", SearchOption.AllDirectories).ToList().ForEach(fileName => { if (!ignoredSubModulesPaths.Any(i => normalizePath(fileName).Contains(normalizePath(i)))) { - var file = new PackageReferenceFile(fileName); - foreach (PackageReference packageReference in file.GetPackageReferences()) + var xdoc = XDocument.Load(fileName); + foreach (var reference in xdoc.Descendants().Where(x => x.Name.LocalName == "PackageReference")) { + var name = reference.Attribute("Include").Value; + var versionAttribute = reference.Attribute("Version"); + var version = versionAttribute != null + ? versionAttribute.Value + : reference.Elements().First(x=>x.Name.LocalName == "Version").Value; IList> versions; - packageVersions.TryGetValue(packageReference.Id, out versions); + packageVersions.TryGetValue(name, out versions); if (versions == null) { versions = new List>(); - packageVersions[packageReference.Id] = versions; + packageVersions[name] = versions; } - versions.Add(Tuple.Create(packageReference.Version.ToString(), fileName)); + versions.Add(Tuple.Create(version, fileName)); } } }); @@ -65,22 +111,33 @@ public class Packages var SplatVersion = packageVersions["Splat"].FirstOrDefault().Item1; var SpracheVersion = packageVersions["Sprache"].FirstOrDefault().Item1; var SystemReactiveVersion = packageVersions["System.Reactive"].FirstOrDefault().Item1; - var SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1; + var SystemValueTupleVersion = packageVersions["System.ValueTuple"].FirstOrDefault().Item1; + SkiaSharpVersion = packageVersions["SkiaSharp"].FirstOrDefault().Item1; + SkiaSharpLinuxVersion = packageVersions["Avalonia.Skia.Linux.Natives"].FirstOrDefault().Item1; var SharpDXVersion = packageVersions["SharpDX"].FirstOrDefault().Item1; var SharpDXDirect2D1Version = packageVersions["SharpDX.Direct2D1"].FirstOrDefault().Item1; var SharpDXDirect3D11Version = packageVersions["SharpDX.Direct3D11"].FirstOrDefault().Item1; + var SharpDXDirect3D9Version = packageVersions["SharpDX.Direct3D9"].FirstOrDefault().Item1; var SharpDXDXGIVersion = packageVersions["SharpDX.DXGI"].FirstOrDefault().Item1; context.Information("Package: Serilog, version: {0}", SerilogVersion); context.Information("Package: Splat, version: {0}", SplatVersion); context.Information("Package: Sprache, version: {0}", SpracheVersion); context.Information("Package: System.Reactive, version: {0}", SystemReactiveVersion); + context.Information("Package: System.ValueTuple, version: {0}", SystemValueTupleVersion); context.Information("Package: SkiaSharp, version: {0}", SkiaSharpVersion); + context.Information("Package: Avalonia.Skia.Linux.Natives, version: {0}", SkiaSharpLinuxVersion); context.Information("Package: SharpDX, version: {0}", SharpDXVersion); context.Information("Package: SharpDX.Direct2D1, version: {0}", SharpDXDirect2D1Version); context.Information("Package: SharpDX.Direct3D11, version: {0}", SharpDXDirect3D11Version); + context.Information("Package: SharpDX.Direct3D9, version: {0}", SharpDXDirect3D9Version); context.Information("Package: SharpDX.DXGI, version: {0}", SharpDXDXGIVersion); + var nugetPackagesDir = System.Environment.GetEnvironmentVariable("NUGET_HOME") + ?? System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("USERPROFILE") ?? System.Environment.GetEnvironmentVariable("HOME"), ".nuget"); + + NugetPackagesDir = System.IO.Path.Combine(nugetPackagesDir, "packages"); + var SetNuGetNuspecCommonProperties = new Action ((nuspec) => { nuspec.Version = parameters.Version; nuspec.Authors = new [] { "Avalonia Team" }; @@ -129,12 +186,12 @@ public class Packages }; var coreLibrariesFiles = coreLibraries.Select((lib) => { - return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/" + lib[1] + lib[2]); + return (FilePath)context.File(lib[0] + lib[1] + "/bin/" + parameters.DirSuffix + "/netstandard1.3/" + lib[1] + lib[2]); }).ToList(); var coreLibrariesNuSpecContent = coreLibrariesFiles.Select((file) => { return new NuSpecContent { - Source = file.FullPath, Target = "lib/portable-windows8+net45" + Source = file.FullPath, Target = "lib/netstandard1.3" }; }); @@ -161,7 +218,7 @@ public class Packages var netCoreRuntimePlatformExtensions = new [] {".xml", ".dll"}; var netCoreRuntimePlatform = netCoreRuntimePlatformExtensions.Select(libSuffix => { return new NuSpecContent { - Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath, + Source = ((FilePath)context.File("./src/Avalonia.DotNetCoreRuntime/bin/" + parameters.DirSuffix + "/netcoreapp1.0/Avalonia.DotNetCoreRuntime" + libSuffix)).FullPath, Target = "lib/netcoreapp1.0" }; }); @@ -174,21 +231,25 @@ public class Packages new NuGetPackSettings() { Id = "Avalonia", - Dependencies = new [] + Dependencies = new DependencyBuilder(this) { new NuSpecDependency() { Id = "Serilog", Version = SerilogVersion }, new NuSpecDependency() { Id = "Splat", Version = SplatVersion }, new NuSpecDependency() { Id = "Sprache", Version = SpracheVersion }, new NuSpecDependency() { Id = "System.Reactive", Version = SystemReactiveVersion }, - new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" }, //.NET Core + new NuSpecDependency() { Id = "System.Threading.ThreadPool", TargetFramework = "netcoreapp1.0", Version = "4.3.0" }, + new NuSpecDependency() { Id = "Microsoft.Extensions.DependencyModel", TargetFramework = "netcoreapp1.0", Version = "1.1.0" }, new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp1.0", Version = "1.6.0" }, - new NuSpecDependency() { Id = "Microsoft.NETCore.Portable.Compatibility", TargetFramework = "netcoreapp1.0", Version = "1.0.1" }, - new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp1.0", Version = "2.0.0" }, - new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = "2.3.0" }, + new NuSpecDependency() { Id = "Splat", TargetFramework = "netcoreapp1.0", Version = SplatVersion }, + new NuSpecDependency() { Id = "Serilog", TargetFramework = "netcoreapp1.0", Version = SerilogVersion }, new NuSpecDependency() { Id = "Sprache", TargetFramework = "netcoreapp1.0", Version = SpracheVersion }, - new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion } - }, + new NuSpecDependency() { Id = "System.Reactive", TargetFramework = "netcoreapp1.0", Version = SystemReactiveVersion }, + } + .Deps(new string[]{null, "netcoreapp1.0"}, + "System.ValueTuple", "System.ComponentModel.TypeConverter", "System.ComponentModel.Primitives", + "System.Runtime.Serialization.Primitives", "System.Xml.XmlDocument", "System.Xml.ReaderWriter") + .ToArray(), Files = coreLibrariesNuSpecContent .Concat(win32CoreLibrariesNuSpecContent).Concat(net45RuntimePlatform) .Concat(netcoreappCoreLibrariesNuSpecContent).Concat(netCoreRuntimePlatform) @@ -208,9 +269,9 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/portable-windows8+net45" } + new NuSpecContent { Source = "Avalonia.HtmlRenderer.dll", Target = "lib/netstandard1.3" } }, - BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix), + BasePath = context.Directory("./src/Avalonia.HtmlRenderer/bin/" + parameters.DirSuffix + "/netstandard1.3"), OutputDirectory = parameters.NugetRoot } }; @@ -324,7 +385,7 @@ public class Packages Files = new [] { new NuSpecContent { Source = "Avalonia.Win32/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/net45" }, - new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/Avalonia.Win32.dll", Target = "lib/netstandard1.1" } + new NuSpecContent { Source = "Avalonia.Win32.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.Win32.dll", Target = "lib/netstandard1.3" } }, BasePath = context.Directory("./src/Windows"), OutputDirectory = parameters.NugetRoot @@ -345,9 +406,9 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/net45" } + new NuSpecContent { Source = "Avalonia.Direct2D1.dll", Target = "lib/netstandard1.3" } }, - BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix), + BasePath = context.Directory("./src/Windows/Avalonia.Direct2D1/bin/" + parameters.DirSuffix + "/netstandard1.3"), OutputDirectory = parameters.NugetRoot }, /////////////////////////////////////////////////////////////////////////////// @@ -379,9 +440,9 @@ public class Packages }, Files = new [] { - new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.1" } + new NuSpecContent { Source = "Avalonia.Gtk3.dll", Target = "lib/netstandard1.3" } }, - BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix), + BasePath = context.Directory("./src/Gtk/Avalonia.Gtk3/bin/" + parameters.DirSuffix + "/netstandard1.3"), OutputDirectory = parameters.NugetRoot }, /////////////////////////////////////////////////////////////////////////////// @@ -411,16 +472,12 @@ public class Packages { new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }, new NuSpecDependency() { Id = "SkiaSharp", Version = SkiaSharpVersion }, - //.NET Core - new NuSpecDependency() { Id = "Avalonia", TargetFramework = "netcoreapp1.0", Version = parameters.Version }, - new NuSpecDependency() { Id = "SkiaSharp", TargetFramework = "netcoreapp1.0", Version = SkiaSharpVersion }, - new NuSpecDependency() { Id = "NETStandard.Library", TargetFramework = "netcoreapp1.0", Version = "1.6.0" }, - new NuSpecDependency() { Id = "Microsoft.NETCore.Portable.Compatibility", TargetFramework = "netcoreapp1.0", Version = "1.0.1" } + new NuSpecDependency() { Id = "Avalonia.Skia.Linux.Natives", Version = SkiaSharpLinuxVersion } }, Files = new [] { new NuSpecContent { Source = "Avalonia.Skia.Desktop/bin/" + parameters.DirSuffixSkia + "/Avalonia.Skia.Desktop.dll", Target = "lib/net45" }, - new NuSpecContent { Source = "Avalonia.Skia.Desktop.NetStandard/bin/" + parameters.DirSuffix + "/Avalonia.Skia.Desktop.dll", Target = "lib/netcoreapp1.0" } + new NuSpecContent { Source = "Avalonia.Skia.Desktop.NetStandard/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.Skia.Desktop.dll", Target = "lib/netstandard1.3" } }, BasePath = context.Directory("./src/Skia/"), OutputDirectory = parameters.NugetRoot @@ -433,11 +490,17 @@ public class Packages Id = "Avalonia.Desktop", Dependencies = new [] { - new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Gtk", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Cairo", Version = parameters.Version }, - new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", Version = parameters.Version } + //Full .NET + new NuSpecDependency() { Id = "Avalonia.Direct2D1", TargetFramework="net45", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Gtk", TargetFramework="net45", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Cairo", TargetFramework="net45", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="net45", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="net45", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="net45", Version = parameters.Version }, + //.NET Core + new NuSpecDependency() { Id = "Avalonia.Win32", TargetFramework="netcoreapp1.0", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", TargetFramework="netcoreapp1.0", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Gtk3", TargetFramework="netcoreapp1.0", Version = parameters.Version } }, Files = new NuSpecContent[] { @@ -445,6 +508,40 @@ public class Packages }, BasePath = context.Directory("./"), OutputDirectory = parameters.NugetRoot + }, + new NuGetPackSettings() + { + Id = "Avalonia.Win32.Interoperability", + Dependencies = new [] + { + new NuSpecDependency() { Id = "Avalonia.Win32", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Direct2D1", Version = parameters.Version }, + new NuSpecDependency() { Id = "SharpDX.Direct3D9", Version = SharpDXDirect3D9Version }, + }, + Files = new [] + { + new NuSpecContent { Source = "Avalonia.Win32.Interop/bin/" + parameters.DirSuffix + "/Avalonia.Win32.Interop.dll", Target = "lib/net45" } + }, + BasePath = context.Directory("./src/Windows"), + OutputDirectory = parameters.NugetRoot + }, + /////////////////////////////////////////////////////////////////////////////// + // Avalonia.LinuxFramebuffer + /////////////////////////////////////////////////////////////////////////////// + new NuGetPackSettings() + { + Id = "Avalonia.LinuxFramebuffer", + Dependencies = new [] + { + new NuSpecDependency() { Id = "Avalonia", Version = parameters.Version }, + new NuSpecDependency() { Id = "Avalonia.Skia.Desktop", Version = parameters.Version } + }, + Files = new [] + { + new NuSpecContent { Source = "Avalonia.LinuxFramebuffer/bin/" + parameters.DirSuffix + "/netstandard1.3/Avalonia.LinuxFramebuffer.dll", Target = "lib/netstandard1.3" } + }, + BasePath = context.Directory("./src/Linux/"), + OutputDirectory = parameters.NugetRoot } }; diff --git a/parameters.cake b/parameters.cake index 4a7270011c..12f448303e 100644 --- a/parameters.cake +++ b/parameters.cake @@ -75,17 +75,25 @@ public class Parameters IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleasePlatform, Platform) && StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration); IsMyGetRelease = !IsTagged && IsReleasable; - IsNuGetRelease = IsTagged && IsReleasable; + // VERSION Version = context.Argument("force-nuget-version", context.ParseAssemblyInfo(AssemblyInfoPath).AssemblyVersion); if (IsRunningOnAppVeyor) { + string tagVersion = null; if (IsTagged) { - // Use Tag Name as version - Version = buildSystem.AppVeyor.Environment.Repository.Tag.Name; + var tag = buildSystem.AppVeyor.Environment.Repository.Tag.Name; + var nugetReleasePrefix = "nuget-release-"; + IsNuGetRelease = IsTagged && IsReleasable && tag.StartsWith(nugetReleasePrefix); + if(IsNuGetRelease) + tagVersion = tag.Substring(nugetReleasePrefix.Length); + } + if(tagVersion != null) + { + Version = tagVersion; } else { diff --git a/readme.md b/readme.md index 0cc2b05e9d..621037e89e 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@ | Gitter Chat | Windows Build Status | Linux/Mac Build Status | Code Coverage | |---|---|---|---| -| [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build status](https://ci.appveyor.com/api/projects/status/hubk3k0w9idyibfg/branch/master?svg=true)](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [![Build Status](https://travis-ci.org/AvaloniaUI/Avalonia.svg?branch=master)](https://travis-ci.org/AvaloniaUI/Avalonia) | [![codecov](https://codecov.io/gh/AvaloniaUI/Avalonia/branch/master/graph/badge.svg)](https://codecov.io/gh/AvaloniaUI/Avalonia) | +| [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/AvaloniaUI/Avalonia?utm_campaign=pr-badge&utm_content=badge&utm_medium=badge&utm_source=badge) | [![Build status](https://ci.appveyor.com/api/projects/status/hubk3k0w9idyibfg/branch/master?svg=true)](https://ci.appveyor.com/project/AvaloniaUI/Avalonia/branch/master) | [![Build Status](https://travis-ci.org/AvaloniaUI/Avalonia.svg?branch=master)](https://travis-ci.org/AvaloniaUI/Avalonia) | [![codecov](https://codecov.io/gh/AvaloniaUI/Avalonia/branch/master/graph/badge.svg)](https://codecov.io/gh/AvaloniaUI/Avalonia) | A multi-platform .NET UI framework. It can run on Windows, Linux, Mac OS X, iOS and Android. @@ -42,7 +42,7 @@ using Direct2D and other operating systems using Gtk & Cairo. Avalonia is now in alpha. What does "alpha" mean? Well, it means that it's now at a stage where you can have a play and hopefully create simple applications. There's now a [Visual -Studio Extension](https://visualstudiogallery.msdn.microsoft.com/e1c6ae1f-6fd9-467d-8f62-1e28b4225213) +Studio Extension](https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.AvaloniaforVisualStudio) containing project and item templates that will help you get started, and there's an initial complement of controls. There's still a lot missing, and you *will* find bugs, and the API *will* change, but this represents the first time diff --git a/samples/BindingTest/App.config b/samples/BindingTest/App.config index 98b10dfce2..373b3a13f8 100644 --- a/samples/BindingTest/App.config +++ b/samples/BindingTest/App.config @@ -1,21 +1,21 @@ - + - + - - + + - - + + - - + + diff --git a/samples/BindingTest/BindingTest.csproj b/samples/BindingTest/BindingTest.csproj index 2dc0cd445b..8e78b0c2cd 100644 --- a/samples/BindingTest/BindingTest.csproj +++ b/samples/BindingTest/BindingTest.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,10 +9,11 @@ Properties BindingTest BindingTest - v4.5 + v4.6.1 512 true + PackageReference AnyCPU @@ -37,29 +38,9 @@ - - ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.dll - True - - - ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll - True - - - ..\..\packages\Splat.1.6.2\lib\Net45\Splat.dll - True - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\net45\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\net45\System.Reactive.Interfaces.dll - True - @@ -92,7 +73,6 @@ Designer - @@ -178,11 +158,8 @@ - + + + + \ No newline at end of file diff --git a/samples/BindingTest/BindingTest.v2.ncrunchproject b/samples/BindingTest/BindingTest.v2.ncrunchproject deleted file mode 100644 index f744eecae0..0000000000 --- a/samples/BindingTest/BindingTest.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/samples/BindingTest/MainWindow.xaml b/samples/BindingTest/MainWindow.xaml index 95f671fd84..7d633bcd0c 100644 --- a/samples/BindingTest/MainWindow.xaml +++ b/samples/BindingTest/MainWindow.xaml @@ -44,6 +44,8 @@ + + diff --git a/samples/BindingTest/ViewModels/MainWindowViewModel.cs b/samples/BindingTest/ViewModels/MainWindowViewModel.cs index 4b58bf2279..2ec052c258 100644 --- a/samples/BindingTest/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingTest/ViewModels/MainWindowViewModel.cs @@ -49,6 +49,9 @@ namespace BindingTest.ViewModels Thread.Sleep(1000); } }); + + CurrentTimeObservable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)) + .Select(x => DateTimeOffset.Now.ToString()); } public ObservableCollection Items { get; } @@ -85,6 +88,7 @@ namespace BindingTest.ViewModels private set { this.RaiseAndSetIfChanged(ref _currentTime, value); } } + public IObservable CurrentTimeObservable { get; } public ReactiveCommand StringValueCommand { get; } public DataAnnotationsErrorViewModel DataAnnotationsValidation { get; } = new DataAnnotationsErrorViewModel(); diff --git a/samples/BindingTest/packages.config b/samples/BindingTest/packages.config deleted file mode 100644 index 3c5f85de81..0000000000 --- a/samples/BindingTest/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj index 41d6329d26..71e3c4169c 100644 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.csproj +++ b/samples/ControlCatalog.Android/ControlCatalog.Android.csproj @@ -73,7 +73,6 @@ - @@ -157,12 +156,6 @@ ControlCatalog + - \ No newline at end of file diff --git a/samples/ControlCatalog.Android/ControlCatalog.Android.v2.ncrunchproject b/samples/ControlCatalog.Android/ControlCatalog.Android.v2.ncrunchproject deleted file mode 100644 index e1b4d7cf28..0000000000 --- a/samples/ControlCatalog.Android/ControlCatalog.Android.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - false - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/samples/ControlCatalog.Android/GettingStarted.Xamarin b/samples/ControlCatalog.Android/GettingStarted.Xamarin deleted file mode 100644 index e9d4f6a411..0000000000 --- a/samples/ControlCatalog.Android/GettingStarted.Xamarin +++ /dev/null @@ -1,4 +0,0 @@ - - GS\Android\CS\AndroidApp\GettingStarted.html - false - \ No newline at end of file diff --git a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs index 96f0e76fd8..cee3331ba8 100644 --- a/samples/ControlCatalog.Android/Resources/Resource.Designer.cs +++ b/samples/ControlCatalog.Android/Resources/Resource.Designer.cs @@ -28,6 +28,8 @@ namespace ControlCatalog.Android { global::Avalonia.Android.Resource.String.ApplicationName = global::ControlCatalog.Android.Resource.String.ApplicationName; global::Avalonia.Android.Resource.String.Hello = global::ControlCatalog.Android.Resource.String.Hello; + global::Avalonia.Android.Resource.String.library_name = global::ControlCatalog.Android.Resource.String.library_name; + global::Splat.Resource.String.library_name = global::ControlCatalog.Android.Resource.String.library_name; } public partial class Attribute @@ -94,11 +96,14 @@ namespace ControlCatalog.Android public partial class String { + // aapt resource value: 0x7f040002 + public const int ApplicationName = 2130968578; + // aapt resource value: 0x7f040001 - public const int ApplicationName = 2130968577; + public const int Hello = 2130968577; // aapt resource value: 0x7f040000 - public const int Hello = 2130968576; + public const int library_name = 2130968576; static String() { diff --git a/samples/ControlCatalog.Desktop/App.config b/samples/ControlCatalog.Desktop/App.config index 2231c71e63..68403e421f 100644 --- a/samples/ControlCatalog.Desktop/App.config +++ b/samples/ControlCatalog.Desktop/App.config @@ -1,13 +1,21 @@ - + - + - - + + + + + + + + + + diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index ca5743dd5c..a3940a3a7d 100644 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj +++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,10 +9,11 @@ Properties ControlCatalog.Desktop ControlCatalog.Desktop - v4.5 + v4.6.1 512 true + PackageReference AnyCPU @@ -25,7 +26,7 @@ 4 - AnyCPU + x86 pdbonly true bin\Release\ @@ -37,14 +38,6 @@ - - ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.dll - True - - - ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll - True - @@ -57,7 +50,6 @@ - @@ -149,11 +141,7 @@ - + + + \ No newline at end of file diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.v2.ncrunchproject b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.v2.ncrunchproject deleted file mode 100644 index e1b4d7cf28..0000000000 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - false - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/samples/ControlCatalog.Desktop/packages.config b/samples/ControlCatalog.Desktop/packages.config deleted file mode 100644 index b3abe6f487..0000000000 --- a/samples/ControlCatalog.Desktop/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj new file mode 100644 index 0000000000..e0e848b91b --- /dev/null +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp1.1 + + + + + + + + + \ No newline at end of file diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs new file mode 100644 index 0000000000..7c67a239e6 --- /dev/null +++ b/samples/ControlCatalog.NetCore/Program.cs @@ -0,0 +1,29 @@ +using System; +using System.Linq; +using Avalonia; + +namespace ControlCatalog.NetCore +{ + class Program + { + static void Main(string[] args) + { + if (args.Contains("--fbdev")) AppBuilder.Configure().InitializeWithLinuxFramebuffer(tl => + { + tl.Content = new MainView(); + System.Threading.ThreadPool.QueueUserWorkItem(_ => ConsoleSilencer()); + }); + else + AppBuilder.Configure() + .UsePlatformDetect() + .Start(); + } + + static void ConsoleSilencer() + { + Console.CursorVisible = false; + while (true) + Console.ReadKey(true); + } + } +} \ No newline at end of file diff --git a/samples/ControlCatalog.iOS/ControlCatalog.iOS.v2.ncrunchproject b/samples/ControlCatalog.iOS/ControlCatalog.iOS.v2.ncrunchproject deleted file mode 100644 index e1b4d7cf28..0000000000 --- a/samples/ControlCatalog.iOS/ControlCatalog.iOS.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - false - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index f4a0915dd1..a75de098e8 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,20 +1,8 @@ - - - + - 10.0 - Debug - AnyCPU - {D0A739B9-3C68-4BA6-A328-41606954B6BD} - Library - Properties - ControlCatalog - ControlCatalog - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 + netstandard1.3 + False + false true @@ -33,6 +21,9 @@ prompt 4 + + + @@ -56,6 +47,7 @@ Designer + Designer @@ -106,6 +98,9 @@ CarouselPage.xaml + + ContextMenuPage.xaml + CheckBoxPage.xaml @@ -129,7 +124,7 @@ SliderPage.xaml - + TreeViewPage.xaml @@ -153,62 +148,20 @@ - - {3E53A01A-B331-47F3-B828-4A5717E77A24} - Avalonia.Markup.Xaml - - - {6417E941-21BC-467B-A771-0DE389353CE6} - Avalonia.Markup - - - {D211E587-D8BC-45B9-95A4-F297C8FA5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {D2221C82-4A25-4583-9B43-D791E3F6820C} - Avalonia.Controls - - - {7062AE20-5DCC-4442-9645-8195BDECE63E} - Avalonia.Diagnostics - - - {5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D} - Avalonia.HtmlRenderer - - - {62024B2D-53EB-4638-B26B-85EEAA54866E} - Avalonia.Input - - - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} - Avalonia.Interactivity - - - {42472427-4774-4C81-8AFF-9F27B8E31721} - Avalonia.Layout - - - {6417B24E-49C2-4985-8DB2-3AB9D898EC91} - Avalonia.ReactiveUI - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - - - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} - Avalonia.Styling - - - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F} - Avalonia.Themes.Default - + + + + + + + + + + + + + + @@ -225,12 +178,5 @@ - - + \ No newline at end of file diff --git a/samples/ControlCatalog/ControlCatalog.v2.ncrunchproject b/samples/ControlCatalog/ControlCatalog.v2.ncrunchproject deleted file mode 100644 index f744eecae0..0000000000 --- a/samples/ControlCatalog/ControlCatalog.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 24f631a5f4..7dd026c124 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -10,6 +10,7 @@ + diff --git a/samples/ControlCatalog/MainWindow.xaml.cs b/samples/ControlCatalog/MainWindow.xaml.cs index c7c61e2b4e..413794dfa2 100644 --- a/samples/ControlCatalog/MainWindow.xaml.cs +++ b/samples/ControlCatalog/MainWindow.xaml.cs @@ -10,6 +10,7 @@ namespace ControlCatalog { this.InitializeComponent(); this.AttachDevTools(); + Renderer.DrawDirtyRects = Renderer.DrawFps = true; } private void InitializeComponent() diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml b/samples/ControlCatalog/Pages/ContextMenuPage.xaml new file mode 100644 index 0000000000..f029b1106f --- /dev/null +++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml @@ -0,0 +1,36 @@ + + + Context Menu + A right click menu that can be applied to any control. + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs new file mode 100644 index 0000000000..dc73bef07a --- /dev/null +++ b/samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs @@ -0,0 +1,18 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace ControlCatalog.Pages +{ + public class ContextMenuPage : UserControl + { + public ContextMenuPage() + { + this.InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/ControlCatalog/Pages/MenuPage.xaml b/samples/ControlCatalog/Pages/MenuPage.xaml index 98171f29d6..36adcf1b8f 100644 --- a/samples/ControlCatalog/Pages/MenuPage.xaml +++ b/samples/ControlCatalog/Pages/MenuPage.xaml @@ -3,33 +3,33 @@ Menu A window menu - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/samples/ControlCatalog/packages.config b/samples/ControlCatalog/packages.config deleted file mode 100644 index 44ff0f0628..0000000000 --- a/samples/ControlCatalog/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/samples/RenderTest/App.config b/samples/RenderTest/App.config index 2231c71e63..68403e421f 100644 --- a/samples/RenderTest/App.config +++ b/samples/RenderTest/App.config @@ -1,13 +1,21 @@ - + - + - - + + + + + + + + + + diff --git a/samples/RenderTest/App.xaml b/samples/RenderTest/App.xaml index d9630eef58..c119f54915 100644 --- a/samples/RenderTest/App.xaml +++ b/samples/RenderTest/App.xaml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/samples/RenderTest/MainWindow.xaml b/samples/RenderTest/MainWindow.xaml index ab233fd90b..1d3001f1b1 100644 --- a/samples/RenderTest/MainWindow.xaml +++ b/samples/RenderTest/MainWindow.xaml @@ -1,3 +1,32 @@ + Title="Avalonia Render Test" + xmlns:pages="clr-namespace:RenderTest.Pages;assembly=RenderTest"> + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/RenderTest/MainWindow.xaml.cs b/samples/RenderTest/MainWindow.xaml.cs index feb121186f..76a8e81aca 100644 --- a/samples/RenderTest/MainWindow.xaml.cs +++ b/samples/RenderTest/MainWindow.xaml.cs @@ -2,16 +2,11 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Reactive.Linq; using Avalonia; -using Avalonia.Animation; using Avalonia.Controls; -using Avalonia.Controls.Shapes; -using Avalonia.Data; -using Avalonia.Layout; using Avalonia.Markup.Xaml; -using Avalonia.Media; -using Avalonia.Rendering; +using RenderTest.ViewModels; +using ReactiveUI; namespace RenderTest { @@ -20,63 +15,17 @@ namespace RenderTest public MainWindow() { this.InitializeComponent(); - this.CreateAnimations(); this.AttachDevTools(); - RendererMixin.DrawFpsCounter = true; + + var vm = new MainWindowViewModel(); + vm.WhenAnyValue(x => x.DrawDirtyRects).Subscribe(x => Renderer.DrawDirtyRects = x); + vm.WhenAnyValue(x => x.DrawFps).Subscribe(x => Renderer.DrawFps = x); + this.DataContext = vm; } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); } - - private void CreateAnimations() - { - const int Count = 100; - var panel = new WrapPanel(); - - for (var i = 0; i < Count; ++i) - { - var element = new Panel - { - Children = - { - new Ellipse - { - Width = 100, - Height = 100, - Fill = Brushes.Blue, - }, - new Path - { - Data = StreamGeometry.Parse( - "F1 M 16.6309,18.6563C 17.1309,8.15625 29.8809,14.1563 29.8809,14.1563C 30.8809,11.1563 34.1308,11.4063 34.1308,11.4063C 33.5,12 34.6309,13.1563 34.6309,13.1563C 32.1309,13.1562 31.1309,14.9062 31.1309,14.9062C 41.1309,23.9062 32.6309,27.9063 32.6309,27.9062C 24.6309,24.9063 21.1309,22.1562 16.6309,18.6563 Z M 16.6309,19.9063C 21.6309,24.1563 25.1309,26.1562 31.6309,28.6562C 31.6309,28.6562 26.3809,39.1562 18.3809,36.1563C 18.3809,36.1563 18,38 16.3809,36.9063C 15,36 16.3809,34.9063 16.3809,34.9063C 16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z"), - Fill = Brushes.Green, - HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center, - RenderTransform = new ScaleTransform(2, 2), - } - }, - Margin = new Thickness(4), - RenderTransform = new ScaleTransform(), - }; - - var start = Animate.Stopwatch.Elapsed; - var index = i; - var degrees = Animate.Timer - .Select(x => (x - start).TotalSeconds) - .Where(x => (x % Count) >= index && (x % Count) < index + 1) - .Select(x => (x % 1) / 1); - - element.RenderTransform.Bind( - ScaleTransform.ScaleXProperty, - degrees, - BindingPriority.Animation); - - panel.Children.Add(element); - } - - Content = panel; - } } } diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml b/samples/RenderTest/Pages/AnimationsPage.xaml new file mode 100644 index 0000000000..6eb7067245 --- /dev/null +++ b/samples/RenderTest/Pages/AnimationsPage.xaml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/samples/RenderTest/Pages/AnimationsPage.xaml.cs b/samples/RenderTest/Pages/AnimationsPage.xaml.cs new file mode 100644 index 0000000000..991896c3b5 --- /dev/null +++ b/samples/RenderTest/Pages/AnimationsPage.xaml.cs @@ -0,0 +1,90 @@ +using System.Reactive.Linq; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Controls.Shapes; +using Avalonia.Data; +using Avalonia.Input; +using Avalonia.Markup.Xaml; +using Avalonia.Media; + +namespace RenderTest.Pages +{ + public class AnimationsPage : UserControl + { + public AnimationsPage() + { + this.InitializeComponent(); + this.CreateAnimations(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void CreateAnimations() + { + const int Count = 100; + var panel = new WrapPanel(); + + for (var i = 0; i < Count; ++i) + { + Ellipse ellipse; + var element = new Panel + { + Children = + { + (ellipse = new Ellipse + { + Name = $"ellipse{i}", + Width = 100, + Height = 100, + Fill = Brushes.Blue, + }), + new Path + { + Data = StreamGeometry.Parse( + "F1 M 16.6309,18.6563C 17.1309,8.15625 29.8809,14.1563 29.8809,14.1563C 30.8809,11.1563 34.1308,11.4063 34.1308,11.4063C 33.5,12 34.6309,13.1563 34.6309,13.1563C 32.1309,13.1562 31.1309,14.9062 31.1309,14.9062C 41.1309,23.9062 32.6309,27.9063 32.6309,27.9062C 24.6309,24.9063 21.1309,22.1562 16.6309,18.6563 Z M 16.6309,19.9063C 21.6309,24.1563 25.1309,26.1562 31.6309,28.6562C 31.6309,28.6562 26.3809,39.1562 18.3809,36.1563C 18.3809,36.1563 18,38 16.3809,36.9063C 15,36 16.3809,34.9063 16.3809,34.9063C 16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z"), + Fill = Brushes.Green, + HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, + RenderTransform = new ScaleTransform(2, 2), + } + }, + Margin = new Thickness(4), + RenderTransform = new ScaleTransform(), + }; + + var start = Animate.Stopwatch.Elapsed; + var index = i % (Count / 2); + var degrees = Animate.Timer + .Select(x => (x - start).TotalSeconds) + .Where(x => (x % Count) >= index && (x % Count) < index + 1) + .Select(x => (x % 1) / 1); + + element.RenderTransform.Bind( + ScaleTransform.ScaleXProperty, + degrees, + BindingPriority.Animation); + + ellipse.PointerEnter += Ellipse_PointerEnter; + ellipse.PointerLeave += Ellipse_PointerLeave; + + panel.Children.Add(element); + } + + Content = panel; + } + + private void Ellipse_PointerEnter(object sender, PointerEventArgs e) + { + ((Ellipse)sender).Fill = Brushes.Red; + } + + private void Ellipse_PointerLeave(object sender, PointerEventArgs e) + { + ((Ellipse)sender).Fill = Brushes.Blue; + } + } +} diff --git a/samples/RenderTest/Pages/ClippingPage.xaml b/samples/RenderTest/Pages/ClippingPage.xaml new file mode 100644 index 0000000000..47e9d51e3c --- /dev/null +++ b/samples/RenderTest/Pages/ClippingPage.xaml @@ -0,0 +1,15 @@ + + + + + + + + + Apply Geometry Clip + + \ No newline at end of file diff --git a/samples/RenderTest/Pages/ClippingPage.xaml.cs b/samples/RenderTest/Pages/ClippingPage.xaml.cs new file mode 100644 index 0000000000..b416f12943 --- /dev/null +++ b/samples/RenderTest/Pages/ClippingPage.xaml.cs @@ -0,0 +1,48 @@ +using System; +using System.Reactive.Linq; +using Avalonia; +using Avalonia.Animation; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Markup.Xaml; +using Avalonia.Media; + +namespace RenderTest.Pages +{ + public class ClippingPage : UserControl + { + private Geometry _clip; + + public ClippingPage() + { + InitializeComponent(); + CreateAnimations(); + WireUpCheckbox(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + private void CreateAnimations() + { + var clipped = this.FindControl("clipChild"); + var degrees = Animate.Timer.Select(x => x.TotalMilliseconds / 5); + clipped.RenderTransform = new RotateTransform(); + clipped.RenderTransform.Bind(RotateTransform.AngleProperty, degrees, BindingPriority.Animation); + clipped.Bind( + Border.BackgroundProperty, + clipped.GetObservable(Control.IsPointerOverProperty) + .Select(x => x ? Brushes.Crimson : AvaloniaProperty.UnsetValue)); + } + + private void WireUpCheckbox() + { + var useMask = this.FindControl("useMask"); + var clipped = this.FindControl("clipped"); + _clip = clipped.Clip; + useMask.Click += (s, e) => clipped.Clip = clipped.Clip == null ? _clip : null; + } + } +} diff --git a/samples/RenderTest/RenderTest.csproj b/samples/RenderTest/RenderTest.csproj index f117c76342..95f1479b6b 100644 --- a/samples/RenderTest/RenderTest.csproj +++ b/samples/RenderTest/RenderTest.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,7 +9,7 @@ Properties RenderTest RenderTest - v4.5 + v4.6.1 512 true @@ -37,36 +37,8 @@ - - ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.dll - True - - - ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll - True - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\net45\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\net45\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\net45\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\net45\System.Reactive.PlatformServices.dll - True - - - ..\..\packages\System.Reactive.Windows.Threading.3.0.0\lib\net45\System.Reactive.Windows.Threading.dll - True - @@ -79,15 +51,21 @@ App.xaml + + ClippingPage.xaml + + + AnimationsPage.xaml + MainWindow.xaml + - @@ -167,6 +145,10 @@ {6417e941-21bc-467b-a771-0de389353ce6} Avalonia.Markup + + {925dd807-b651-475f-9f7c-cbeb974ce43d} + Avalonia.Skia.Desktop + {3e908f67-5543-4879-a1dc-08eace79b3cd} Avalonia.Direct2D1 @@ -181,12 +163,24 @@ Designer + + + Designer + + + + + Designer + + + + + Designer + + - + + + + \ No newline at end of file diff --git a/samples/RenderTest/RenderTest.v2.ncrunchproject b/samples/RenderTest/RenderTest.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/samples/RenderTest/RenderTest.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/samples/RenderTest/SideBar.xaml b/samples/RenderTest/SideBar.xaml new file mode 100644 index 0000000000..950de71e2b --- /dev/null +++ b/samples/RenderTest/SideBar.xaml @@ -0,0 +1,40 @@ + + + + + + + diff --git a/samples/RenderTest/ViewModels/MainWindowViewModel.cs b/samples/RenderTest/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000000..bd68752f66 --- /dev/null +++ b/samples/RenderTest/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,34 @@ +using System; +using ReactiveUI; + +namespace RenderTest.ViewModels +{ + public class MainWindowViewModel : ReactiveObject + { + private bool drawDirtyRects = true; + private bool drawFps = true; + + public MainWindowViewModel() + { + ToggleDrawDirtyRects = ReactiveCommand.Create(); + ToggleDrawDirtyRects.Subscribe(_ => DrawDirtyRects = !DrawDirtyRects); + ToggleDrawFps = ReactiveCommand.Create(); + ToggleDrawFps.Subscribe(_ => DrawFps = !DrawFps); + } + + public bool DrawDirtyRects + { + get { return drawDirtyRects; } + set { this.RaiseAndSetIfChanged(ref drawDirtyRects, value); } + } + + public bool DrawFps + { + get { return drawFps; } + set { this.RaiseAndSetIfChanged(ref drawFps, value); } + } + + public ReactiveCommand ToggleDrawDirtyRects { get; } + public ReactiveCommand ToggleDrawFps { get; } + } +} diff --git a/samples/RenderTest/packages.config b/samples/RenderTest/packages.config deleted file mode 100644 index 3c79dde8c3..0000000000 --- a/samples/RenderTest/packages.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/samples/VirtualizationTest/App.config b/samples/VirtualizationTest/App.config index 2231c71e63..68403e421f 100644 --- a/samples/VirtualizationTest/App.config +++ b/samples/VirtualizationTest/App.config @@ -1,13 +1,21 @@ - + - + - - + + + + + + + + + + diff --git a/samples/VirtualizationTest/VirtualizationTest.csproj b/samples/VirtualizationTest/VirtualizationTest.csproj index 9e80dfdc8f..327e659966 100644 --- a/samples/VirtualizationTest/VirtualizationTest.csproj +++ b/samples/VirtualizationTest/VirtualizationTest.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,7 +9,7 @@ Properties VirtualizationTest VirtualizationTest - v4.5 + v4.6.1 512 true @@ -37,28 +37,8 @@ - - ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.dll - True - - - ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.FullNetFx.dll - True - - - ..\..\packages\Splat.1.6.2\lib\Net45\Splat.dll - True - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\net45\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\net45\System.Reactive.Interfaces.dll - True - @@ -80,7 +60,6 @@ - @@ -175,11 +154,8 @@ - + + + + \ No newline at end of file diff --git a/samples/VirtualizationTest/VirtualizationTest.v2.ncrunchproject b/samples/VirtualizationTest/VirtualizationTest.v2.ncrunchproject deleted file mode 100644 index f744eecae0..0000000000 --- a/samples/VirtualizationTest/VirtualizationTest.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/samples/VirtualizationTest/packages.config b/samples/VirtualizationTest/packages.config deleted file mode 100644 index f2da2367fa..0000000000 --- a/samples/VirtualizationTest/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/samples/interop/Direct3DInteropSample/App.paml b/samples/interop/Direct3DInteropSample/App.paml new file mode 100644 index 0000000000..d9630eef58 --- /dev/null +++ b/samples/interop/Direct3DInteropSample/App.paml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/interop/Direct3DInteropSample/App.paml.cs b/samples/interop/Direct3DInteropSample/App.paml.cs new file mode 100644 index 0000000000..1b6d5fd39c --- /dev/null +++ b/samples/interop/Direct3DInteropSample/App.paml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Markup.Xaml; + +namespace Direct3DInteropSample +{ + public class App : Application + { + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj new file mode 100644 index 0000000000..da230d34c6 --- /dev/null +++ b/samples/interop/Direct3DInteropSample/Direct3DInteropSample.csproj @@ -0,0 +1,34 @@ + + + Exe + net461 + + + + + + %(Filename) + + + Designer + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs new file mode 100644 index 0000000000..ad40e81895 --- /dev/null +++ b/samples/interop/Direct3DInteropSample/MainWindow.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Direct2D1.Media; +using Avalonia.Markup.Xaml; +using Avalonia.Platform; +using Avalonia.Rendering; +using SharpDX; +using SharpDX.D3DCompiler; +using SharpDX.Direct2D1; +using SharpDX.Direct3D; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using SharpDX.WIC; +using SharpDX.Mathematics; +using AlphaMode = SharpDX.Direct2D1.AlphaMode; +using Buffer = SharpDX.Direct3D11.Buffer; +using DeviceContext = SharpDX.Direct3D11.DeviceContext; +using Factory1 = SharpDX.DXGI.Factory1; +using InputElement = SharpDX.Direct3D11.InputElement; +using Matrix = SharpDX.Matrix; +using PixelFormat = SharpDX.Direct2D1.PixelFormat; + +namespace Direct3DInteropSample +{ + class MainWindow : Window + { + private SharpDX.Direct3D11.Device _d3dDevice; + private SharpDX.DXGI.Device _dxgiDevice; + Texture2D backBuffer = null; + RenderTargetView renderView = null; + Texture2D depthBuffer = null; + DepthStencilView depthView = null; + private readonly SwapChain _swapChain; + private SwapChainDescription _desc; + private Matrix _proj = Matrix.Identity; + private Matrix _view; + private Buffer _contantBuffer; + private SharpDX.Direct2D1.Device _d2dDevice; + private SharpDX.Direct2D1.DeviceContext _d2dContext; + private RenderTarget _d2dRenderTarget; + private MainWindowViewModel _model; + + public MainWindow() + { + _dxgiDevice = AvaloniaLocator.Current.GetService(); + _d3dDevice = _dxgiDevice.QueryInterface(); + _d2dDevice = AvaloniaLocator.Current.GetService(); + DataContext = _model = new MainWindowViewModel(); + _desc = new SwapChainDescription() + { + BufferCount = 1, + ModeDescription = + new ModeDescription((int)ClientSize.Width, (int)ClientSize.Height, + new Rational(60, 1), Format.R8G8B8A8_UNorm), + IsWindowed = true, + OutputHandle = PlatformImpl?.Handle.Handle ?? IntPtr.Zero, + SampleDescription = new SampleDescription(1, 0), + SwapEffect = SwapEffect.Discard, + Usage = Usage.RenderTargetOutput + }; + + _swapChain = new SwapChain(new Factory1(), _d3dDevice, _desc); + + _d2dContext = new SharpDX.Direct2D1.DeviceContext(_d2dDevice, DeviceContextOptions.None) + { + DotsPerInch = new Size2F(96, 96) + }; + + CreateMesh(); + _view = Matrix.LookAtLH(new Vector3(0, 0, -5), new Vector3(0, 0, 0), Vector3.UnitY); + this.GetObservable(ClientSizeProperty).Subscribe(Resize); + Resize(ClientSize); + AvaloniaXamlLoader.Load(this); + Background = Avalonia.Media.Brushes.Transparent; + } + + + protected override void HandlePaint(Rect rect) + { + var viewProj = Matrix.Multiply(_view, _proj); + var context = _d3dDevice.ImmediateContext; + // Clear views + context.ClearDepthStencilView(depthView, DepthStencilClearFlags.Depth, 1.0f, 0); + context.ClearRenderTargetView(renderView, Color.White); + + var time = 50; + // Update WorldViewProj Matrix + var worldViewProj = Matrix.RotationX((float) _model.RotationX) * Matrix.RotationY((float) _model.RotationY) * + Matrix.RotationZ((float) _model.RotationZ) + * Matrix.Scaling((float) _model.Zoom) * viewProj; + worldViewProj.Transpose(); + context.UpdateSubresource(ref worldViewProj, _contantBuffer); + + // Draw the cube + context.Draw(36, 0); + base.HandlePaint(rect); + // Present! + _swapChain.Present(0, PresentFlags.None); + } + + + void CreateMesh() + { + var device = _d3dDevice; + // Compile Vertex and Pixel shaders + var vertexShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "VS", "vs_4_0"); + var vertexShader = new VertexShader(device, vertexShaderByteCode); + + var pixelShaderByteCode = ShaderBytecode.CompileFromFile("MiniCube.fx", "PS", "ps_4_0"); + var pixelShader = new PixelShader(device, pixelShaderByteCode); + + var signature = ShaderSignature.GetInputSignature(vertexShaderByteCode); + // Layout from VertexShader input signature + var layout = new InputLayout(device, signature, new[] + { + new InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0), + new InputElement("COLOR", 0, Format.R32G32B32A32_Float, 16, 0) + }); + + // Instantiate Vertex buiffer from vertex data + var vertices = Buffer.Create(device, BindFlags.VertexBuffer, new[] + { + new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Front + new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), + new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), + new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), + new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), + new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), + + new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // BACK + new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), + new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), + new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), + new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), + new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), + + new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Top + new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), + new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), + new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), + new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), + new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), + + new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Bottom + new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), + new Vector4(-1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), + new Vector4(-1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), + new Vector4( 1.0f,-1.0f, -1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), + new Vector4( 1.0f,-1.0f, 1.0f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), + + new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), // Left + new Vector4(-1.0f, -1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), + new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), + new Vector4(-1.0f, -1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), + new Vector4(-1.0f, 1.0f, 1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), + new Vector4(-1.0f, 1.0f, -1.0f, 1.0f), new Vector4(1.0f, 0.0f, 1.0f, 1.0f), + + new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), // Right + new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), + new Vector4( 1.0f, -1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), + new Vector4( 1.0f, -1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), + new Vector4( 1.0f, 1.0f, -1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), + new Vector4( 1.0f, 1.0f, 1.0f, 1.0f), new Vector4(0.0f, 1.0f, 1.0f, 1.0f), + }); + + // Create Constant Buffer + _contantBuffer = new Buffer(device, Utilities.SizeOf(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); + + var context = _d3dDevice.ImmediateContext; + + // Prepare All the stages + context.InputAssembler.InputLayout = layout; + context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; + context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertices, Utilities.SizeOf() * 2, 0)); + context.VertexShader.SetConstantBuffer(0, _contantBuffer); + context.VertexShader.Set(vertexShader); + context.PixelShader.Set(pixelShader); + } + + void Resize(Size size) + { + Utilities.Dispose(ref _d2dRenderTarget); + Utilities.Dispose(ref backBuffer); + Utilities.Dispose(ref renderView); + Utilities.Dispose(ref depthBuffer); + Utilities.Dispose(ref depthView); + var context = _d3dDevice.ImmediateContext; + // Resize the backbuffer + _swapChain.ResizeBuffers(_desc.BufferCount, (int)size.Width, (int)size.Height, Format.Unknown, SwapChainFlags.None); + + // Get the backbuffer from the swapchain + backBuffer = Texture2D.FromSwapChain(_swapChain, 0); + + // Renderview on the backbuffer + renderView = new RenderTargetView(_d3dDevice, backBuffer); + + // Create the depth buffer + depthBuffer = new Texture2D(_d3dDevice, new Texture2DDescription() + { + Format = Format.D32_Float_S8X24_UInt, + ArraySize = 1, + MipLevels = 1, + Width = (int)size.Width, + Height = (int)size.Height, + SampleDescription = new SampleDescription(1, 0), + Usage = ResourceUsage.Default, + BindFlags = BindFlags.DepthStencil, + CpuAccessFlags = CpuAccessFlags.None, + OptionFlags = ResourceOptionFlags.None + }); + + // Create the depth buffer view + depthView = new DepthStencilView(_d3dDevice, depthBuffer); + + // Setup targets and viewport for rendering + context.Rasterizer.SetViewport(new Viewport(0, 0, (int)size.Width, (int)size.Height, 0.0f, 1.0f)); + context.OutputMerger.SetTargets(depthView, renderView); + + // Setup new projection matrix with correct aspect ratio + _proj = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(size.Width / size.Height), 0.1f, 100.0f); + + using (var dxgiBackBuffer = _swapChain.GetBackBuffer(0)) + { + _d2dRenderTarget = new RenderTarget(AvaloniaLocator.Current.GetService() + , dxgiBackBuffer, new RenderTargetProperties + { + DpiX = 96, + DpiY = 96, + Type = RenderTargetType.Default, + PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied) + }); + } + + } + + class D3DRenderTarget: IRenderTarget + { + private readonly MainWindow _window; + + public D3DRenderTarget(MainWindow window) + { + _window = window; + } + public void Dispose() + { + + } + + public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) + { + return new DrawingContextImpl(visualBrushRenderer, _window._d2dRenderTarget, + AvaloniaLocator.Current.GetService()); + } + } + + + protected override IRenderTarget CreateRenderTarget() => new D3DRenderTarget(this); + } +} diff --git a/samples/interop/Direct3DInteropSample/MainWindow.paml b/samples/interop/Direct3DInteropSample/MainWindow.paml new file mode 100644 index 0000000000..37c6265836 --- /dev/null +++ b/samples/interop/Direct3DInteropSample/MainWindow.paml @@ -0,0 +1,14 @@ + + + + Rotation X + + Rotation Y + + Rotation Z + + Zoom + + + + \ No newline at end of file diff --git a/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs b/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs new file mode 100644 index 0000000000..d39a21cd07 --- /dev/null +++ b/samples/interop/Direct3DInteropSample/MainWindowViewModel.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ReactiveUI; + +namespace Direct3DInteropSample +{ + public class MainWindowViewModel : ReactiveObject + { + private double _rotationX; + + public double RotationX + { + get { return _rotationX; } + set { this.RaiseAndSetIfChanged(ref _rotationX, value); } + } + + private double _rotationY = 1; + + public double RotationY + { + get { return _rotationY; } + set { this.RaiseAndSetIfChanged(ref _rotationY, value); } + } + + private double _rotationZ = 2; + + public double RotationZ + { + get { return _rotationZ; } + set { this.RaiseAndSetIfChanged(ref _rotationZ, value); } + } + + + private double _zoom = 1; + + public double Zoom + { + get { return _zoom; } + set { this.RaiseAndSetIfChanged(ref _zoom, value); } + } + } +} diff --git a/samples/interop/Direct3DInteropSample/MiniCube.fx b/samples/interop/Direct3DInteropSample/MiniCube.fx new file mode 100644 index 0000000000..f246421f2d --- /dev/null +++ b/samples/interop/Direct3DInteropSample/MiniCube.fx @@ -0,0 +1,47 @@ +// Copyright (c) 2010-2013 SharpDX - Alexandre Mutel +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +struct VS_IN +{ + float4 pos : POSITION; + float4 col : COLOR; +}; + +struct PS_IN +{ + float4 pos : SV_POSITION; + float4 col : COLOR; +}; + +float4x4 worldViewProj; + +PS_IN VS( VS_IN input ) +{ + PS_IN output = (PS_IN)0; + + output.pos = mul(input.pos, worldViewProj); + output.col = input.col; + + return output; +} + +float4 PS( PS_IN input ) : SV_Target +{ + return input.col; +} \ No newline at end of file diff --git a/samples/interop/Direct3DInteropSample/Program.cs b/samples/interop/Direct3DInteropSample/Program.cs new file mode 100644 index 0000000000..d5de5ccb4e --- /dev/null +++ b/samples/interop/Direct3DInteropSample/Program.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia; + +namespace Direct3DInteropSample +{ + class Program + { + static void Main(string[] args) + { + AppBuilder.Configure().UseWin32(deferredRendering: false).UseDirect2D1().Start(); + } + } +} diff --git a/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj b/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj index 738a1e776c..56fa426f3c 100644 --- a/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj +++ b/samples/interop/GtkInteropDemo/GtkInteropDemo.csproj @@ -9,7 +9,7 @@ Properties GtkInteropDemo GtkInteropDemo - v4.6 + v4.6.1 512 true @@ -149,12 +149,6 @@ ControlCatalog + - \ No newline at end of file diff --git a/samples/interop/GtkInteropDemo/GtkInteropDemo.v2.ncrunchproject b/samples/interop/GtkInteropDemo/GtkInteropDemo.v2.ncrunchproject deleted file mode 100644 index e1b4d7cf28..0000000000 --- a/samples/interop/GtkInteropDemo/GtkInteropDemo.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - false - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/samples/interop/WindowsInteropTest/App.config b/samples/interop/WindowsInteropTest/App.config index 8324aa6ff1..4fe7131d2a 100644 --- a/samples/interop/WindowsInteropTest/App.config +++ b/samples/interop/WindowsInteropTest/App.config @@ -3,4 +3,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml index 1115cf5768..1d8dc32a69 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml @@ -1,10 +1,12 @@  @@ -14,8 +16,18 @@ + + + + + + + + + + - + diff --git a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs index e60c9ced0a..c7a23c22fc 100644 --- a/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs +++ b/samples/interop/WindowsInteropTest/EmbedToWpfDemo.xaml.cs @@ -11,7 +11,9 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using Avalonia; using Avalonia.Controls; +using Avalonia.VisualTree; using ControlCatalog; using Window = System.Windows.Window; @@ -25,7 +27,18 @@ namespace WindowsInteropTest public EmbedToWpfDemo() { InitializeComponent(); - Host.Content = new MainView(); + var view = new MainView(); + view.AttachedToVisualTree += delegate + { + ((TopLevel) view.GetVisualRoot()).AttachDevTools(); + }; + Host.Content = view; + var btn = (Avalonia.Controls.Button) RightBtn.Content; + btn.Click += delegate + { + btn.Content += "!"; + }; + } } } diff --git a/samples/interop/WindowsInteropTest/Program.cs b/samples/interop/WindowsInteropTest/Program.cs index 4770688ecf..fac06d74b0 100644 --- a/samples/interop/WindowsInteropTest/Program.cs +++ b/samples/interop/WindowsInteropTest/Program.cs @@ -15,7 +15,7 @@ namespace WindowsInteropTest { System.Windows.Forms.Application.EnableVisualStyles(); System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); - AppBuilder.Configure().UseWin32().UseSkia().SetupWithoutStarting(); + AppBuilder.Configure().UseWin32().UseDirect2D1().SetupWithoutStarting(); System.Windows.Forms.Application.Run(new SelectorForm()); } } diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj index 671ebd0020..2fa3ba30b9 100644 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj +++ b/samples/interop/WindowsInteropTest/WindowsInteropTest.csproj @@ -9,12 +9,12 @@ Properties WindowsInteropTest WindowsInteropTest - v4.6 + v4.6.1 512 true - AnyCPU + x86 true full false @@ -164,6 +164,10 @@ {3e908f67-5543-4879-a1dc-08eace79b3cd} Avalonia.Direct2D1 + + {cbc4ff2f-92d4-420b-be21-9fe0b930b04e} + Avalonia.Win32.Interop + {811a76cf-1cf6-440f-963b-bbe31bd72a82} Avalonia.Win32 @@ -179,12 +183,7 @@ MSBuild:Compile + - + \ No newline at end of file diff --git a/samples/interop/WindowsInteropTest/WindowsInteropTest.v2.ncrunchproject b/samples/interop/WindowsInteropTest/WindowsInteropTest.v2.ncrunchproject deleted file mode 100644 index e1b4d7cf28..0000000000 --- a/samples/interop/WindowsInteropTest/WindowsInteropTest.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - false - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/scripts/ReplaceNugetCache.ps1 b/scripts/ReplaceNugetCache.ps1 new file mode 100644 index 0000000000..5f678bedef --- /dev/null +++ b/scripts/ReplaceNugetCache.ps1 @@ -0,0 +1,5 @@ +copy ..\samples\ControlCatalog.NetCore\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp1.0\ +copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard1.3\ +copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard1.3\ +copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.skia.desktop\$args\lib\netstandard1.3\ +copy ..\samples\ControlCatalog.NetCore.\bin\Debug\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard1.3\ diff --git a/scripts/ReplaceNugetCache.sh b/scripts/ReplaceNugetCache.sh new file mode 100755 index 0000000000..2ce3e7648d --- /dev/null +++ b/scripts/ReplaceNugetCache.sh @@ -0,0 +1,7 @@ + #!/usr/bin/env bash + + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp1.0/ + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard1.1/ + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia.gtk3/$1/lib/netstandard1.1/ + cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp1.1/Avalonia**.dll ~/.nuget/packages/avalonia.skia.desktop/$1/lib/netstandard1.3/ + diff --git a/scripts/ReplaceNugetCacheRelease.ps1 b/scripts/ReplaceNugetCacheRelease.ps1 new file mode 100644 index 0000000000..f3c1a0a975 --- /dev/null +++ b/scripts/ReplaceNugetCacheRelease.ps1 @@ -0,0 +1,5 @@ +copy ..\samples\ControlCatalog.NetCore\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netcoreapp1.0\ +copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia\$args\lib\netstandard1.3\ +copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.gtk3\$args\lib\netstandard1.3\ +copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.skia.desktop\$args\lib\netstandard1.3\ +copy ..\samples\ControlCatalog.NetCore.\bin\Release\netcoreapp1.1\Avalonia**.dll ~\.nuget\packages\avalonia.win32\$args\lib\netstandard1.3\ diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs index 2089359c26..7134e7d1b4 100644 --- a/src/Android/Avalonia.Android/AndroidPlatform.cs +++ b/src/Android/Avalonia.Android/AndroidPlatform.cs @@ -51,14 +51,12 @@ namespace Avalonia.Android .Bind().ToTransient() .Bind().ToTransient() .Bind().ToSingleton() - .Bind().ToSingleton() .Bind().ToConstant(Instance) .Bind().ToConstant(new AndroidThreadingInterface()) .Bind().ToTransient() .Bind().ToConstant(Instance) .Bind().ToSingleton() .Bind().ToConstant(new DefaultRenderLoop(60)) - .Bind().ToConstant(new AssetLoader(app.GetType().Assembly)); SkiaPlatform.Initialize(); diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj index 3ee2f5af64..091fdcc4b5 100644 --- a/src/Android/Avalonia.Android/Avalonia.Android.csproj +++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -41,18 +41,6 @@ - - ..\..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.3\System.Reactive.Core.dll - True - - - ..\..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.3\System.Reactive.PlatformServices.dll - True - @@ -86,7 +74,6 @@ - @@ -145,11 +132,5 @@ - + \ No newline at end of file diff --git a/src/Android/Avalonia.Android/Avalonia.Android.v2.ncrunchproject b/src/Android/Avalonia.Android/Avalonia.Android.v2.ncrunchproject deleted file mode 100644 index f744eecae0..0000000000 --- a/src/Android/Avalonia.Android/Avalonia.Android.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs b/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs index 9ae79efb48..d52eeb15e4 100644 --- a/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs +++ b/src/Android/Avalonia.Android/Platform/Input/AndroidMouseDevice.cs @@ -4,6 +4,8 @@ namespace Avalonia.Android.Platform.Input { public class AndroidMouseDevice : MouseDevice { + public static AndroidMouseDevice Instance { get; } = new AndroidMouseDevice(); + public AndroidMouseDevice() { diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs index 55e729f5a2..051a058363 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs @@ -2,7 +2,7 @@ using System; using System.Runtime.InteropServices; using Android.Runtime; using Android.Views; -using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Platform; namespace Avalonia.Android.Platform.SkiaPlatform { @@ -44,7 +44,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform public int Width { get; } public int Height { get; } public int RowBytes { get; } - public Size Dpi { get; } = new Size(96, 96); + public Vector Dpi { get; } = new Vector(96, 96); public PixelFormat Format { get; } [DllImport("android")] diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 75772be171..6c8db17c71 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -10,8 +10,10 @@ using Avalonia.Platform; using System; using System.Collections.Generic; using System.Reactive.Disposables; +using Avalonia.Android.Platform.Input; using Avalonia.Controls; using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Rendering; namespace Avalonia.Android.Platform.SkiaPlatform { @@ -65,6 +67,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform } } + public IMouseDevice MouseDevice => AndroidMouseDevice.Instance; + public Action Closed { get; set; } public Action Input { get; set; } @@ -82,7 +86,12 @@ namespace Avalonia.Android.Platform.SkiaPlatform public IPlatformHandle Handle => _view; public IEnumerable Surfaces => new object[] {this}; - + + public IRenderer CreateRenderer(IRenderRoot root) + { + return new ImmediateRenderer(root); + } + public virtual void Hide() { _view.Visibility = ViewStates.Invisible; diff --git a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs index 702829b91c..0f90472bd0 100644 --- a/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs +++ b/src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidTouchEventsHelper.cs @@ -71,7 +71,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers if (x <= _point.X && r >= _point.X && y <= _point.Y && b >= _point.Y) { var inputRoot = _getInputRoot(); - var mouseDevice = MouseDevice.Instance; + var mouseDevice = Avalonia.Android.Platform.Input.AndroidMouseDevice.Instance; //in order the controls to work in a predictable way //we need to generate mouse move before first mouse down event diff --git a/src/Android/Avalonia.Android/Resources/Resource.Designer.cs b/src/Android/Avalonia.Android/Resources/Resource.Designer.cs index 80cbbc51ec..e66c2800d3 100644 --- a/src/Android/Avalonia.Android/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.Android/Resources/Resource.Designer.cs @@ -40,11 +40,14 @@ namespace Avalonia.Android public partial class String { + // aapt resource value: 0x7f020002 + public static int ApplicationName = 2130837506; + // aapt resource value: 0x7f020001 - public static int ApplicationName = 2130837505; + public static int Hello = 2130837505; // aapt resource value: 0x7f020000 - public static int Hello = 2130837504; + public static int library_name = 2130837504; static String() { diff --git a/src/Android/Avalonia.Android/packages.config b/src/Android/Avalonia.Android/packages.config deleted file mode 100644 index 057d0855b8..0000000000 --- a/src/Android/Avalonia.Android/packages.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj index d09f162150..87d1f67986 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj +++ b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -64,35 +64,8 @@ - - ..\..\..\packages\Serilog.1.5.14\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll - True - - - ..\..\..\packages\Splat.1.6.2\lib\monoandroid\Splat.dll - True - - - ..\..\..\packages\Sprache.2.1.0\lib\netstandard1.0\Sprache.dll - - - ..\..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.3\System.Reactive.Core.dll - True - - - ..\..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.3\System.Reactive.Linq.dll - True - - - ..\..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.3\System.Reactive.PlatformServices.dll - True - @@ -103,7 +76,6 @@ - @@ -181,11 +153,8 @@ - + + + + \ No newline at end of file diff --git a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.v2.ncrunchproject b/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.v2.ncrunchproject deleted file mode 100644 index e1b4d7cf28..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - true - false - false - false - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs index 3fa0707176..91327cf941 100644 --- a/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs +++ b/src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs @@ -28,6 +28,7 @@ namespace Avalonia.AndroidTestApplication { global::Avalonia.Android.Resource.String.ApplicationName = global::Avalonia.AndroidTestApplication.Resource.String.ApplicationName; global::Avalonia.Android.Resource.String.Hello = global::Avalonia.AndroidTestApplication.Resource.String.Hello; + global::Avalonia.Android.Resource.String.library_name = global::Avalonia.AndroidTestApplication.Resource.String.library_name; global::Splat.Resource.String.library_name = global::Avalonia.AndroidTestApplication.Resource.String.library_name; } diff --git a/src/Android/Avalonia.AndroidTestApplication/packages.config b/src/Android/Avalonia.AndroidTestApplication/packages.config deleted file mode 100644 index d353146747..0000000000 --- a/src/Android/Avalonia.AndroidTestApplication/packages.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Animation/Avalonia.Animation.csproj b/src/Avalonia.Animation/Avalonia.Animation.csproj index 46aac81b5a..8f832dabcf 100644 --- a/src/Avalonia.Animation/Avalonia.Animation.csproj +++ b/src/Avalonia.Animation/Avalonia.Animation.csproj @@ -1,20 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {D211E587-D8BC-45B9-95A4-F297C8FA5200} - Library - Properties - Avalonia.Animation - Avalonia.Animation - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 + netstandard1.3 + false true @@ -38,57 +25,13 @@ CS1591 true - - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - - + - - + \ No newline at end of file diff --git a/src/Avalonia.Animation/Avalonia.Animation.v2.ncrunchproject b/src/Avalonia.Animation/Avalonia.Animation.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Animation/Avalonia.Animation.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Animation/packages.config b/src/Avalonia.Animation/packages.config deleted file mode 100644 index 9f732f1bcb..0000000000 --- a/src/Avalonia.Animation/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index 90550a12f2..e0d12ce262 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -1,22 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Library - Properties - Avalonia - Avalonia.Base - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + false true @@ -44,118 +29,8 @@ Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\packages\JetBrains.Annotations.10.0.0\lib\portable-net4+sl5+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\JetBrains.Annotations.PCL328.dll - True - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - - - - - - - + + + \ No newline at end of file diff --git a/src/Avalonia.Base/Avalonia.Base.v2.ncrunchproject b/src/Avalonia.Base/Avalonia.Base.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Base/Avalonia.Base.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Base/AvaloniaDisposable.cs b/src/Avalonia.Base/AvaloniaDisposable.cs deleted file mode 100644 index 9e43a65f26..0000000000 --- a/src/Avalonia.Base/AvaloniaDisposable.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Platform; - -namespace Avalonia -{ - public abstract class AvaloniaDisposable : IDisposable - { -#if DEBUG_DISPOSE - public string DisposedAt { get; private set; } -#endif - - - public bool IsDisposed { get; private set; } - - public void Dispose() - { - IsDisposed = true; -#if DEBUG_DISPOSE - DisposedAt = AvaloniaLocator.Current.GetService().GetStackTrace(); -#endif - DoDispose(); - } - - protected void CheckDisposed() - { - if (IsDisposed) - throw new ObjectDisposedException(GetType().FullName -#if DEBUG_DISPOSE - , "Disposed at: \n" + DisposedAt -#endif - - ); - } - - protected abstract void DoDispose(); - } -} diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 409abfe8fa..1bdf1eb5e3 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -181,6 +181,7 @@ namespace Avalonia public void ClearValue(AvaloniaProperty property) { Contract.Requires(property != null); + VerifyAccess(); SetValue(property, AvaloniaProperty.UnsetValue); } @@ -193,6 +194,7 @@ namespace Avalonia public object GetValue(AvaloniaProperty property) { Contract.Requires(property != null); + VerifyAccess(); if (property.IsDirect) { @@ -234,7 +236,8 @@ namespace Avalonia public bool IsSet(AvaloniaProperty property) { Contract.Requires(property != null); - + VerifyAccess(); + PriorityValue value; if (_values.TryGetValue(property, out value)) @@ -575,13 +578,13 @@ namespace Avalonia if (notification == null) { - return TypeUtilities.CastOrDefault(value, type); + return TypeUtilities.ConvertImplicitOrDefault(value, type); } else { if (notification.HasValue) { - notification.SetValue(TypeUtilities.CastOrDefault(notification.Value, type)); + notification.SetValue(TypeUtilities.ConvertImplicitOrDefault(notification.Value, type)); } return notification; @@ -619,14 +622,9 @@ namespace Avalonia /// The default value. private object GetDefaultValue(AvaloniaProperty property) { - if (property.Inherits && _inheritanceParent != null) - { - return (_inheritanceParent as AvaloniaObject).GetValueInternal(property); - } - else - { - return ((IStyledPropertyAccessor)property).GetDefaultValue(GetType()); - } + if (property.Inherits && _inheritanceParent is AvaloniaObject aobj) + return aobj.GetValueInternal(property); + return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } /// @@ -732,7 +730,7 @@ namespace Avalonia ThrowNotRegistered(property); } - if (!TypeUtilities.TryCast(property.PropertyType, value, out value)) + if (!TypeUtilities.TryConvertImplicit(property.PropertyType, value, out value)) { throw new ArgumentException(string.Format( "Invalid value for Property '{0}': '{1}' ({2})", diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index 61006b1173..fb78e3b2a0 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -476,7 +476,7 @@ namespace Avalonia /// True if the value is valid, otherwise false. public bool IsValidValue(object value) { - return TypeUtilities.TryCast(PropertyType, value, out value); + return TypeUtilities.TryConvertImplicit(PropertyType, value, out value); } /// diff --git a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs index d63a5baf16..1aa239180c 100644 --- a/src/Avalonia.Base/Collections/AvaloniaDictionary.cs +++ b/src/Avalonia.Base/Collections/AvaloniaDictionary.cs @@ -103,11 +103,9 @@ namespace Avalonia.Collections _inner = new Dictionary(); - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[]")); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[]")); + if (CollectionChanged != null) { @@ -144,12 +142,9 @@ namespace Avalonia.Collections if (_inner.TryGetValue(key, out value)) { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]")); - } - + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); + if (CollectionChanged != null) { var e = new NotifyCollectionChangedEventArgs( @@ -199,11 +194,9 @@ namespace Avalonia.Collections private void NotifyAdd(TKey key, TValue value) { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs("Count")); - PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]")); - } + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Count")); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs($"Item[{key}]")); + if (CollectionChanged != null) { diff --git a/src/Avalonia.Base/Collections/AvaloniaList.cs b/src/Avalonia.Base/Collections/AvaloniaList.cs index ace898b55f..a3c3015a38 100644 --- a/src/Avalonia.Base/Collections/AvaloniaList.cs +++ b/src/Avalonia.Base/Collections/AvaloniaList.cs @@ -220,7 +220,7 @@ namespace Avalonia.Collections /// /// Removes all items from the collection. /// - public void Clear() + public virtual void Clear() { if (this.Count > 0) { diff --git a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs index ffb7199619..54cd132b95 100644 --- a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs +++ b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -59,7 +60,8 @@ namespace Avalonia.Collections /// the index in the collection and the item. /// /// - /// An action called when the collection is reset. + /// An action called when the collection is reset. This will be followed by calls to + /// for each item present in the collection after the reset. /// /// A disposable used to terminate the subscription. public static IDisposable ForEachItem( @@ -68,112 +70,38 @@ namespace Avalonia.Collections Action removed, Action reset) { - int index; - - NotifyCollectionChangedEventHandler handler = (_, e) => + void Add(int index, IList items) { - switch (e.Action) + foreach (T item in items) { - case NotifyCollectionChangedAction.Add: - index = e.NewStartingIndex; - - foreach (T item in e.NewItems) - { - added(index++, item); - } - - break; - - case NotifyCollectionChangedAction.Replace: - index = e.OldStartingIndex; - - foreach (T item in e.OldItems) - { - removed(index++, item); - } - - index = e.NewStartingIndex; - - foreach (T item in e.NewItems) - { - added(index++, item); - } - - break; - - case NotifyCollectionChangedAction.Remove: - index = e.OldStartingIndex; - - foreach (T item in e.OldItems) - { - removed(index++, item); - } - - break; - - case NotifyCollectionChangedAction.Reset: - if (reset == null) - { - throw new InvalidOperationException( - "Reset called on collection without reset handler."); - } - - reset(); - break; + added(index++, item); } - }; + } - index = 0; - foreach (T i in collection) + void Remove(int index, IList items) { - added(index++, i); + for (var i = items.Count - 1; i >= 0; --i) + { + removed(index + i, (T)items[i]); + } } - collection.CollectionChanged += handler; - - return Disposable.Create(() => collection.CollectionChanged -= handler); - } - - /// - /// Invokes an action for each item in a collection and subsequently each item added or - /// removed from the collection. - /// - /// The type of the collection items. - /// The collection. - /// - /// An action called initially with all items in the collection and subsequently with a - /// list of items added to the collection. The parameters passed are the index of the - /// first item added to the collection and the items added. - /// - /// - /// An action called with all items removed from the collection. The parameters passed - /// are the index of the first item removed from the collection and the items removed. - /// - /// - /// An action called when the collection is reset. - /// - /// A disposable used to terminate the subscription. - public static IDisposable ForEachItem( - this IAvaloniaReadOnlyList collection, - Action> added, - Action> removed, - Action reset) - { NotifyCollectionChangedEventHandler handler = (_, e) => { switch (e.Action) { case NotifyCollectionChangedAction.Add: - added(e.NewStartingIndex, e.NewItems.Cast()); + Add(e.NewStartingIndex, e.NewItems); break; + case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Replace: - removed(e.OldStartingIndex, e.OldItems.Cast()); - added(e.NewStartingIndex, e.NewItems.Cast()); + Remove(e.OldStartingIndex, e.OldItems); + Add(e.NewStartingIndex, e.NewItems); break; case NotifyCollectionChangedAction.Remove: - removed(e.OldStartingIndex, e.OldItems.Cast()); + Remove(e.OldStartingIndex, e.OldItems); break; case NotifyCollectionChangedAction.Reset: @@ -184,16 +112,31 @@ namespace Avalonia.Collections } reset(); + Add(0, (IList)collection); break; } }; - added(0, collection); + Add(0, (IList)collection); collection.CollectionChanged += handler; return Disposable.Create(() => collection.CollectionChanged -= handler); } + public static IAvaloniaReadOnlyList CreateDerivedList( + this IAvaloniaReadOnlyList collection, + Func select) + { + var result = new AvaloniaList(); + + collection.ForEachItem( + (i, item) => result.Insert(i, select(item)), + (i, item) => result.RemoveAt(i), + () => result.Clear()); + + return result; + } + /// /// Listens for property changed events from all items in a collection. /// diff --git a/src/Avalonia.Base/Data/BindingNotification.cs b/src/Avalonia.Base/Data/BindingNotification.cs index ecaf59e174..125c29b21b 100644 --- a/src/Avalonia.Base/Data/BindingNotification.cs +++ b/src/Avalonia.Base/Data/BindingNotification.cs @@ -44,11 +44,7 @@ namespace Avalonia.Data public static readonly BindingNotification UnsetValue = new BindingNotification(AvaloniaProperty.UnsetValue); - // Null cannot be held in WeakReference as it's indistinguishable from an expired value so - // use this value in its place. - private static readonly object NullValue = new object(); - - private WeakReference _value; + private object _value; /// /// Initializes a new instance of the class. @@ -56,7 +52,7 @@ namespace Avalonia.Data /// The binding value. public BindingNotification(object value) { - _value = new WeakReference(value ?? NullValue); + _value = value; } /// @@ -73,6 +69,7 @@ namespace Avalonia.Data Error = error; ErrorType = errorType; + _value = AvaloniaProperty.UnsetValue; } /// @@ -84,7 +81,7 @@ namespace Avalonia.Data public BindingNotification(Exception error, BindingErrorType errorType, object fallbackValue) : this(error, errorType) { - _value = new WeakReference(fallbackValue ?? NullValue); + _value = fallbackValue; } /// @@ -95,31 +92,12 @@ namespace Avalonia.Data /// If this property is read when is false then it will return /// . /// - public object Value - { - get - { - if (_value != null) - { - object result; - - if (_value.TryGetTarget(out result)) - { - return result == NullValue ? null : result; - } - } - - // There's the possibility of a race condition in that HasValue can return true, - // and then the value is GC'd before Value is read. We should be ok though as - // we return UnsetValue which should be a safe alternative. - return AvaloniaProperty.UnsetValue; - } - } + public object Value => _value; /// /// Gets a value indicating whether should be pushed to the target. /// - public bool HasValue => _value != null; + public bool HasValue => _value != AvaloniaProperty.UnsetValue; /// /// Gets the error that occurred on the source, if any. @@ -248,7 +226,7 @@ namespace Avalonia.Data /// public void ClearValue() { - _value = null; + _value = AvaloniaProperty.UnsetValue; } /// @@ -256,7 +234,7 @@ namespace Avalonia.Data /// public void SetValue(object value) { - _value = new WeakReference(value ?? NullValue); + _value = value; } /// diff --git a/src/Avalonia.Base/IPriorityValueOwner.cs b/src/Avalonia.Base/IPriorityValueOwner.cs index 57f98c0717..aeec720920 100644 --- a/src/Avalonia.Base/IPriorityValueOwner.cs +++ b/src/Avalonia.Base/IPriorityValueOwner.cs @@ -25,5 +25,10 @@ namespace Avalonia /// The source of the change. /// The notification. void BindingNotificationReceived(PriorityValue sender, BindingNotification notification); + + /// + /// Ensures that the current thread is the UI thread. + /// + void VerifyAccess(); } } diff --git a/src/Avalonia.Base/Metadata/AmbientAttribute.cs b/src/Avalonia.Base/Metadata/AmbientAttribute.cs new file mode 100644 index 0000000000..db36953300 --- /dev/null +++ b/src/Avalonia.Base/Metadata/AmbientAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; + +namespace Avalonia.Metadata +{ + /// + /// Defines the ambient class/property + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, Inherited = true)] + public class AmbientAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Base/Metadata/TemplateContent.cs b/src/Avalonia.Base/Metadata/TemplateContent.cs new file mode 100644 index 0000000000..ecc16b04a2 --- /dev/null +++ b/src/Avalonia.Base/Metadata/TemplateContent.cs @@ -0,0 +1,15 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; + +namespace Avalonia.Metadata +{ + /// + /// Defines the property that contains the object's content in markup. + /// + [AttributeUsage(AttributeTargets.Property)] + public class TemplateContentAttribute : Attribute + { + } +} diff --git a/src/Avalonia.Base/PriorityBindingEntry.cs b/src/Avalonia.Base/PriorityBindingEntry.cs index 25b7bede7e..eb7cf5414c 100644 --- a/src/Avalonia.Base/PriorityBindingEntry.cs +++ b/src/Avalonia.Base/PriorityBindingEntry.cs @@ -92,6 +92,8 @@ namespace Avalonia private void ValueChanged(object value) { + _owner.Owner.Owner?.VerifyAccess(); + var notification = value as BindingNotification; if (notification != null) diff --git a/src/Avalonia.Base/PriorityLevel.cs b/src/Avalonia.Base/PriorityLevel.cs index 122a6df821..b25247deaf 100644 --- a/src/Avalonia.Base/PriorityLevel.cs +++ b/src/Avalonia.Base/PriorityLevel.cs @@ -33,7 +33,6 @@ namespace Avalonia /// internal class PriorityLevel { - private PriorityValue _owner; private object _directValue; private int _nextIndex; @@ -48,13 +47,18 @@ namespace Avalonia { Contract.Requires(owner != null); - _owner = owner; + Owner = owner; Priority = priority; Value = _directValue = AvaloniaProperty.UnsetValue; ActiveBindingIndex = -1; Bindings = new LinkedList(); } + /// + /// Gets the owner of the level. + /// + public PriorityValue Owner { get; } + /// /// Gets the priority of this level. /// @@ -73,7 +77,7 @@ namespace Avalonia set { Value = _directValue = value; - _owner.LevelValueChanged(this); + Owner.LevelValueChanged(this); } } @@ -131,7 +135,7 @@ namespace Avalonia { Value = entry.Value; ActiveBindingIndex = entry.Index; - _owner.LevelValueChanged(this); + Owner.LevelValueChanged(this); } else { @@ -161,7 +165,7 @@ namespace Avalonia /// The error. public void Error(PriorityBindingEntry entry, BindingNotification error) { - _owner.LevelError(this, error); + Owner.LevelError(this, error); } /// @@ -175,14 +179,14 @@ namespace Avalonia { Value = binding.Value; ActiveBindingIndex = binding.Index; - _owner.LevelValueChanged(this); + Owner.LevelValueChanged(this); return; } } Value = DirectValue; ActiveBindingIndex = -1; - _owner.LevelValueChanged(this); + Owner.LevelValueChanged(this); } } } diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs index 3f4b405de9..c33d50ff0e 100644 --- a/src/Avalonia.Base/PriorityValue.cs +++ b/src/Avalonia.Base/PriorityValue.cs @@ -26,7 +26,6 @@ namespace Avalonia /// internal class PriorityValue { - private readonly IPriorityValueOwner _owner; private readonly Type _valueType; private readonly SingleOrDictionary _levels = new SingleOrDictionary(); private object _value; @@ -45,7 +44,7 @@ namespace Avalonia Type valueType, Func validate = null) { - _owner = owner; + Owner = owner; Property = property; _valueType = valueType; _value = AvaloniaProperty.UnsetValue; @@ -53,6 +52,11 @@ namespace Avalonia _validate = validate; } + /// + /// Gets the owner of the value. + /// + public IPriorityValueOwner Owner { get; } + /// /// Gets the property that the value represents. /// @@ -188,9 +192,9 @@ namespace Avalonia Logger.Log( LogEventLevel.Error, LogArea.Binding, - _owner, + Owner, "Error in binding to {Target}.{Property}: {Message}", - _owner, + Owner, Property, error.Error.Message); } @@ -245,7 +249,7 @@ namespace Avalonia value = (notification.HasValue) ? notification.Value : null; } - if (TypeUtilities.TryCast(_valueType, value, out castValue)) + if (TypeUtilities.TryConvertImplicit(_valueType, value, out castValue)) { var old = _value; @@ -264,24 +268,24 @@ namespace Avalonia if (notification == null || notification.HasValue) { - _owner?.Changed(this, old, _value); + Owner?.Changed(this, old, _value); } if (notification != null) { - _owner?.BindingNotificationReceived(this, notification); + Owner?.BindingNotificationReceived(this, notification); } } else { Logger.Error( LogArea.Binding, - _owner, + Owner, "Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})", Property.Name, _valueType, value, - value.GetType()); + value?.GetType()); } } } diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index f57afcdd70..2ee7378413 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -6,4 +6,5 @@ using System.Runtime.CompilerServices; [assembly: AssemblyTitle("Avalonia.Base")] [assembly: InternalsVisibleTo("Avalonia.Base.UnitTests")] +[assembly: InternalsVisibleTo("Avalonia.UnitTests")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index aee6a02e73..d46b7142f4 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -15,10 +15,10 @@ namespace Avalonia.Threading /// In Avalonia, there is usually only a single in the application - /// the one for the UI thread, retrieved via the property. /// - public class Dispatcher + public class Dispatcher : IDispatcher { - private readonly IPlatformThreadingInterface _platform; private readonly JobRunner _jobRunner; + private IPlatformThreadingInterface _platform; public static Dispatcher UIThread { get; } = new Dispatcher(AvaloniaLocator.Current.GetService()); @@ -26,22 +26,31 @@ namespace Avalonia.Threading public Dispatcher(IPlatformThreadingInterface platform) { _platform = platform; - if(_platform == null) - //TODO: Unit test mode, fix that somehow - return; _jobRunner = new JobRunner(platform); - _platform.Signaled += _jobRunner.RunJobs; + + if (_platform != null) + { + _platform.Signaled += _jobRunner.RunJobs; + } } + /// + /// Checks that the current thread is the UI thread. + /// public bool CheckAccess() => _platform?.CurrentThreadIsLoopThread ?? true; + /// + /// Checks that the current thread is the UI thread and throws if not. + /// + /// + /// The current thread is not the UI thread. + /// public void VerifyAccess() { if (!CheckAccess()) throw new InvalidOperationException("Call from invalid thread"); } - /// /// Runs the dispatcher's main loop. /// @@ -63,25 +72,35 @@ namespace Avalonia.Threading _jobRunner?.RunJobs(); } - /// - /// Invokes a method on the dispatcher thread. - /// - /// The method. - /// The priority with which to invoke the method. - /// A task that can be used to track the method's execution. + /// public Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) { return _jobRunner?.InvokeAsync(action, priority); } - /// - /// Post action that will be invoked on main thread - /// - /// The method. - /// The priority with which to invoke the method. + /// public void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal) { _jobRunner?.Post(action, priority); } + + /// + /// Allows unit tests to change the platform threading interface. + /// + internal void UpdateServices() + { + if (_platform != null) + { + _platform.Signaled -= _jobRunner.RunJobs; + } + + _platform = AvaloniaLocator.Current.GetService(); + _jobRunner.UpdateServices(); + + if (_platform != null) + { + _platform.Signaled += _jobRunner.RunJobs; + } + } } } \ No newline at end of file diff --git a/src/Avalonia.Base/Threading/IDispatcher.cs b/src/Avalonia.Base/Threading/IDispatcher.cs new file mode 100644 index 0000000000..6301015a9a --- /dev/null +++ b/src/Avalonia.Base/Threading/IDispatcher.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading.Tasks; + +namespace Avalonia.Threading +{ + /// + /// Dispatches jobs to a thread. + /// + public interface IDispatcher + { + /// + /// Determines whether the calling thread is the thread associated with this . + /// + /// True if he calling thread is the thread associated with the dispatched, otherwise false. + bool CheckAccess(); + + /// + /// Throws an exception if the calling thread is not the thread associated with this . + /// + void VerifyAccess(); + + /// + /// Invokes a method on the dispatcher thread. + /// + /// The method. + /// The priority with which to invoke the method. + /// A task that can be used to track the method's execution. + void InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal); + + /// + /// Post action that will be invoked on main thread + /// + /// The method. + /// The priority with which to invoke the method. + // TODO: The naming of this method is confusing: the Async suffix usually means return a task. + // Remove this and rename InvokeTaskAsync as InvokeAsync. See #816. + Task InvokeTaskAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal); + } +} \ No newline at end of file diff --git a/src/Avalonia.Base/Threading/JobRunner.cs b/src/Avalonia.Base/Threading/JobRunner.cs index 655ef34288..e212cd9a2e 100644 --- a/src/Avalonia.Base/Threading/JobRunner.cs +++ b/src/Avalonia.Base/Threading/JobRunner.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using Avalonia.Platform; @@ -14,8 +13,8 @@ namespace Avalonia.Threading /// internal class JobRunner { - private readonly IPlatformThreadingInterface _platform; private readonly Queue _queue = new Queue(); + private IPlatformThreadingInterface _platform; public JobRunner(IPlatformThreadingInterface platform) { @@ -82,6 +81,14 @@ namespace Avalonia.Threading AddJob(new Job(action, priority, true)); } + /// + /// Allows unit tests to change the platform threading interface. + /// + internal void UpdateServices() + { + _platform = AvaloniaLocator.Current.GetService(); + } + private void AddJob(Job job) { var needWake = false; @@ -91,7 +98,7 @@ namespace Avalonia.Threading _queue.Enqueue(job); } if (needWake) - _platform.Signal(); + _platform?.Signal(); } /// diff --git a/src/Avalonia.Base/Utilities/TypeUtilities.cs b/src/Avalonia.Base/Utilities/TypeUtilities.cs index 7295bfa7ab..dd93811498 100644 --- a/src/Avalonia.Base/Utilities/TypeUtilities.cs +++ b/src/Avalonia.Base/Utilities/TypeUtilities.cs @@ -14,17 +14,61 @@ namespace Avalonia.Utilities /// public static class TypeUtilities { - private static readonly Dictionary> Conversions = new Dictionary>() + private static int[] Conversions = { - { typeof(decimal), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char) } }, - { typeof(double), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } }, - { typeof(float), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(char), typeof(float) } }, - { typeof(ulong), new List { typeof(byte), typeof(ushort), typeof(uint), typeof(char) } }, - { typeof(long), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(char) } }, - { typeof(uint), new List { typeof(byte), typeof(ushort), typeof(char) } }, - { typeof(int), new List { typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(char) } }, - { typeof(ushort), new List { typeof(byte), typeof(char) } }, - { typeof(short), new List { typeof(byte) } } + 0b101111111111101, // Boolean + 0b100001111111110, // Char + 0b101111111111111, // SByte + 0b101111111111111, // Byte + 0b101111111111111, // Int16 + 0b101111111111111, // UInt16 + 0b101111111111111, // Int32 + 0b101111111111111, // UInt32 + 0b101111111111111, // Int64 + 0b101111111111111, // UInt64 + 0b101111111111101, // Single + 0b101111111111101, // Double + 0b101111111111101, // Decimal + 0b110000000000000, // DateTime + 0b111111111111111, // String + }; + + private static int[] ImplicitConversions = + { + 0b000000000000001, // Boolean + 0b001110111100010, // Char + 0b001110101010100, // SByte + 0b001111111111000, // Byte + 0b001110101010000, // Int16 + 0b001111111100000, // UInt16 + 0b001110101000000, // Int32 + 0b001111110000000, // UInt32 + 0b001110100000000, // Int64 + 0b001111000000000, // UInt64 + 0b000110000000000, // Single + 0b000100000000000, // Double + 0b001000000000000, // Decimal + 0b010000000000000, // DateTime + 0b100000000000000, // String + }; + + private static Type[] InbuiltTypes = + { + typeof(Boolean), + typeof(Char), + typeof(SByte), + typeof(Byte), + typeof(Int16), + typeof(UInt16), + typeof(Int32), + typeof(UInt32), + typeof(Int64), + typeof(UInt64), + typeof(Single), + typeof(Double), + typeof(Decimal), + typeof(DateTime), + typeof(String), }; private static readonly Type[] NumericTypes = new[] @@ -54,49 +98,104 @@ namespace Avalonia.Utilities } /// - /// Try to cast a value to a type, using implicit conversions if possible. + /// Try to convert a value to a type by any means possible. /// /// The type to cast to. /// The value to cast. + /// The culture to use. /// If sucessful, contains the cast value. /// True if the cast was sucessful, otherwise false. - public static bool TryCast(Type to, object value, out object result) + public static bool TryConvert(Type to, object value, CultureInfo culture, out object result) { - Contract.Requires(to != null); - if (value == null) { result = null; return AcceptsNull(to); } - var from = value.GetType(); - if (value == AvaloniaProperty.UnsetValue) { result = value; return true; } - else if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo())) + + var from = value.GetType(); + var fromTypeInfo = from.GetTypeInfo(); + var toTypeInfo = to.GetTypeInfo(); + + if (toTypeInfo.IsAssignableFrom(fromTypeInfo)) { result = value; return true; } - else if (Conversions.ContainsKey(to) && Conversions[to].Contains(from)) + + if (to == typeof(string)) { - result = Convert.ChangeType(value, to); + result = Convert.ToString(value); return true; } - else + + if (toTypeInfo.IsEnum && from == typeof(string)) + { + if (Enum.IsDefined(to, (string)value)) + { + result = Enum.Parse(to, (string)value); + return true; + } + } + + if (!fromTypeInfo.IsEnum && toTypeInfo.IsEnum) { - var cast = from.GetRuntimeMethods() - .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to); + result = null; + + if (TryConvert(Enum.GetUnderlyingType(to), value, culture, out object enumValue)) + { + result = Enum.ToObject(to, enumValue); + return true; + } + } - if (cast != null) + if (fromTypeInfo.IsEnum && IsNumeric(to)) + { + try { - result = cast.Invoke(null, new[] { value }); + result = Convert.ChangeType((int)value, to, culture); return true; } + catch + { + result = null; + return false; + } + } + + var convertableFrom = Array.IndexOf(InbuiltTypes, from); + var convertableTo = Array.IndexOf(InbuiltTypes, to); + + if (convertableFrom != -1 && convertableTo != -1) + { + if ((Conversions[convertableFrom] & 1 << convertableTo) != 0) + { + try + { + result = Convert.ChangeType(value, to, culture); + return true; + } + catch + { + result = null; + return false; + } + } + } + + var cast = from.GetRuntimeMethods() + .FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to); + + if (cast != null) + { + result = cast.Invoke(null, new[] { value }); + return true; } result = null; @@ -104,15 +203,14 @@ namespace Avalonia.Utilities } /// - /// Try to convert a value to a type, using if possible, - /// otherwise using . + /// Try to convert a value to a type using the implicit conversions allowed by the C# + /// language. /// /// The type to cast to. /// The value to cast. - /// The culture to use. /// If sucessful, contains the cast value. /// True if the cast was sucessful, otherwise false. - public static bool TryConvert(Type to, object value, CultureInfo culture, out object result) + public static bool TryConvertImplicit(Type to, object value, out object result) { if (value == null) { @@ -120,54 +218,44 @@ namespace Avalonia.Utilities return AcceptsNull(to); } - var from = value.GetType(); - if (value == AvaloniaProperty.UnsetValue) { result = value; return true; } - if (to.GetTypeInfo().IsAssignableFrom(from.GetTypeInfo())) - { - result = value; - return true; - } + var from = value.GetType(); + var fromTypeInfo = from.GetTypeInfo(); + var toTypeInfo = to.GetTypeInfo(); - if (to == typeof(string)) + if (toTypeInfo.IsAssignableFrom(fromTypeInfo)) { - result = Convert.ToString(value); + result = value; return true; } - if (to.GetTypeInfo().IsEnum && from == typeof(string)) - { - if (Enum.IsDefined(to, (string)value)) - { - result = Enum.Parse(to, (string)value); - return true; - } - } - - bool containsFrom = Conversions.ContainsKey(from); - bool containsTo = Conversions.ContainsKey(to); + var convertableFrom = Array.IndexOf(InbuiltTypes, from); + var convertableTo = Array.IndexOf(InbuiltTypes, to); - if ((containsFrom && containsTo) || (from == typeof(string) && containsTo)) + if (convertableFrom != -1 && convertableTo != -1) { - try - { - result = Convert.ChangeType(value, to, culture); - return true; - } - catch + if ((ImplicitConversions[convertableFrom] & 1 << convertableTo) != 0) { - result = null; - return false; + try + { + result = Convert.ChangeType(value, to, CultureInfo.InvariantCulture); + return true; + } + catch + { + result = null; + return false; + } } } var cast = from.GetRuntimeMethods() - .FirstOrDefault(m => (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.ReturnType == to); + .FirstOrDefault(m => m.Name == "op_Implicit" && m.ReturnType == to); if (cast != null) { @@ -180,29 +268,28 @@ namespace Avalonia.Utilities } /// - /// Casts a value to a type, returning the default for that type if the value could not be - /// cast. + /// Convert a value to a type by any means possible, returning the default for that type + /// if the value could not be converted. /// /// The value to cast. /// The type to cast to.. + /// The culture to use. /// A value of . - public static object CastOrDefault(object value, Type type) + public static object ConvertOrDefault(object value, Type type, CultureInfo culture) { - var typeInfo = type.GetTypeInfo(); - object result; + return TryConvert(type, value, culture, out object result) ? result : Default(type); + } - if (TypeUtilities.TryCast(type, value, out result)) - { - return result; - } - else if (typeInfo.IsValueType) - { - return Activator.CreateInstance(type); - } - else - { - return null; - } + /// + /// Convert a value to a type using the implicit conversions allowed by the C# language or + /// return the default for the type if the value could not be converted. + /// + /// The value to cast. + /// The type to cast to.. + /// A value of . + public static object ConvertImplicitOrDefault(object value, Type type) + { + return TryConvertImplicit(type, value, out object result) ? result : Default(type); } /// diff --git a/src/Avalonia.Base/Utilities/WeakObservable.cs b/src/Avalonia.Base/Utilities/WeakObservable.cs index c261cc0520..b1fe78c206 100644 --- a/src/Avalonia.Base/Utilities/WeakObservable.cs +++ b/src/Avalonia.Base/Utilities/WeakObservable.cs @@ -16,12 +16,13 @@ namespace Avalonia.Utilities /// Converts a .NET event conforming to the standard .NET event pattern into an observable /// sequence, subscribing weakly. /// + /// The type of target. /// The type of the event args. /// Object instance that exposes the event to convert. /// Name of the event to convert. /// - public static IObservable> FromEventPattern( - object target, + public static IObservable> FromEventPattern( + TTarget target, string eventName) where TEventArgs : EventArgs { diff --git a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs index 356be2ba31..f1e8fa6f9b 100644 --- a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs +++ b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs @@ -17,22 +17,23 @@ namespace Avalonia.Utilities /// /// Subscribes to an event on an object using a weak subscription. /// - /// The type of the event arguments. + /// The type of the target. + /// The type of the event arguments. /// The event source. /// The name of the event. /// The subscriber. - public static void Subscribe(object target, string eventName, IWeakSubscriber subscriber) - where T : EventArgs + public static void Subscribe(TTarget target, string eventName, IWeakSubscriber subscriber) + where TEventArgs : EventArgs { - var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); - Subscription sub; + var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); + Subscription sub; if (!dic.TryGetValue(eventName, out sub)) { - dic[eventName] = sub = new Subscription(dic, target, eventName); + dic[eventName] = sub = new Subscription(dic, typeof(TTarget), target, eventName); } - sub.Add(new WeakReference>(subscriber)); + sub.Add(new WeakReference>(subscriber)); } /// @@ -84,19 +85,18 @@ namespace Avalonia.Utilities private WeakReference>[] _data = new WeakReference>[16]; private int _count = 0; - public Subscription(SubscriptionDic sdic, object target, string eventName) + public Subscription(SubscriptionDic sdic, Type targetType, object target, string eventName) { _sdic = sdic; _target = target; _eventName = eventName; - var t = target.GetType(); Dictionary evDic; - if (!Accessors.TryGetValue(t, out evDic)) - Accessors[t] = evDic = new Dictionary(); + if (!Accessors.TryGetValue(targetType, out evDic)) + Accessors[targetType] = evDic = new Dictionary(); if (!evDic.TryGetValue(eventName, out _info)) { - var ev = t.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName); + var ev = targetType.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName); if (ev == null) { diff --git a/src/Avalonia.Base/packages.config b/src/Avalonia.Base/packages.config deleted file mode 100644 index c372c92f3f..0000000000 --- a/src/Avalonia.Base/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 97de093a59..1610d33f9b 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -55,8 +55,7 @@ namespace Avalonia.Controls public Action AfterSetupCallback { get; private set; } = builder => { }; /// - /// Gets or sets a method to call before is called on the - /// . + /// Gets or sets a method to call before Startis called on the . /// public Action BeforeStartCallback { get; private set; } = builder => { }; @@ -94,8 +93,7 @@ namespace Avalonia.Controls protected TAppBuilder Self => (TAppBuilder) this; /// - /// Registers a callback to call before is called on the - /// . + /// Registers a callback to call before Start is called on the . /// /// The callback. /// An instance. @@ -129,6 +127,24 @@ namespace Avalonia.Controls Instance.Run(window); } + /// + /// Starts the application with the provided instance of . + /// + /// The window type. + /// Instance of type TMainWindow to use when starting the app + /// A delegate that will be called to create a data context for the window (optional). + public void Start(TMainWindow mainWindow, Func dataContextProvider = null) + where TMainWindow : Window + { + Setup(); + BeforeStartCallback(Self); + + if (dataContextProvider != null) + mainWindow.DataContext = dataContextProvider(); + mainWindow.Show(); + Instance.Run(mainWindow); + } + /// /// Sets up the platform-specific services for the application, but does not run it. /// diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 6119103e6d..037546b186 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -1,22 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {D2221C82-4A25-4583-9B43-D791E3F6820C} - Library - Properties - Avalonia.Controls - Avalonia.Controls - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + false true @@ -44,239 +29,16 @@ Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\packages\JetBrains.Annotations.10.0.0\lib\portable-net4+sl5+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\JetBrains.Annotations.PCL328.dll - True - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - + + + + + + + - - - - - - {D211E587-D8BC-45B9-95A4-F297C8FA5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {62024B2D-53EB-4638-B26B-85EEAA54866E} - Avalonia.Input - - - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} - Avalonia.Interactivity - - - {42472427-4774-4C81-8AFF-9F27B8E31721} - Avalonia.Layout - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - - - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} - Avalonia.Styling - - - - - + + \ No newline at end of file diff --git a/src/Avalonia.Controls/Avalonia.Controls.v2.ncrunchproject b/src/Avalonia.Controls/Avalonia.Controls.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Controls/Avalonia.Controls.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 8f617466ed..78bbd836be 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -207,7 +207,11 @@ namespace Avalonia.Controls /// The event args. protected virtual void OnClick(RoutedEventArgs e) { - Command?.Execute(CommandParameter); + if (Command != null) + { + Command.Execute(CommandParameter); + e.Handled = true; + } } /// @@ -215,28 +219,34 @@ namespace Avalonia.Controls { base.OnPointerPressed(e); - PseudoClasses.Add(":pressed"); - e.Device.Capture(this); - e.Handled = true; - - if (ClickMode == ClickMode.Press) + if (e.MouseButton == MouseButton.Left) { - RaiseClickEvent(); + PseudoClasses.Add(":pressed"); + e.Device.Capture(this); + e.Handled = true; + + if (ClickMode == ClickMode.Press) + { + RaiseClickEvent(); + } } } /// - protected override void OnPointerReleased(PointerEventArgs e) + protected override void OnPointerReleased(PointerReleasedEventArgs e) { base.OnPointerReleased(e); - e.Device.Capture(null); - PseudoClasses.Remove(":pressed"); - e.Handled = true; - - if (ClickMode == ClickMode.Release && Classes.Contains(":pointerover")) + if (e.MouseButton == MouseButton.Left) { - RaiseClickEvent(); + e.Device.Capture(null); + PseudoClasses.Remove(":pressed"); + e.Handled = true; + + if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this))) + { + RaiseClickEvent(); + } } } @@ -275,7 +285,7 @@ namespace Avalonia.Controls { var button = e.Sender as Button; var isDefault = (bool)e.NewValue; - var inputRoot = button.VisualRoot as IInputElement; + var inputRoot = button?.VisualRoot as IInputElement; if (inputRoot != null) { diff --git a/src/Avalonia.Controls/Classes.cs b/src/Avalonia.Controls/Classes.cs index 02fd6e63bf..c897e86697 100644 --- a/src/Avalonia.Controls/Classes.cs +++ b/src/Avalonia.Controls/Classes.cs @@ -86,6 +86,20 @@ namespace Avalonia.Controls base.AddRange(c); } + /// + /// Remvoes all non-pseudoclasses from the collection. + /// + public override void Clear() + { + for (var i = Count - 1; i >= 0; --i) + { + if (!this[i].StartsWith(":")) + { + RemoveAt(i); + } + } + } + /// /// Inserts a style class into the collection. /// diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 0a69a5277f..fdb04f4ade 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -19,7 +19,7 @@ namespace Avalonia.Controls { ContextMenuProperty.Changed.Subscribe(ContextMenuChanged); - MenuItem.ClickEvent.AddClassHandler(x => x.OnContextMenuClick); + MenuItem.ClickEvent.AddClassHandler(x => x.OnContextMenuClick, handledEventsToo: true); } /// diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index ef253a28e2..eca5967a58 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -17,7 +17,9 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Logging; using Avalonia.LogicalTree; +using Avalonia.Rendering; using Avalonia.Styling; +using Avalonia.VisualTree; namespace Avalonia.Controls { @@ -33,7 +35,7 @@ namespace Avalonia.Controls /// - Implements to allow styling to work on the control. /// - Implements to form part of a logical tree. /// - public class Control : InputElement, IControl, INamed, ISetInheritanceParent, ISetLogicalParent, ISupportInitialize + public class Control : InputElement, IControl, INamed, ISetInheritanceParent, ISetLogicalParent, ISupportInitialize, IVisualBrushInitialize { /// /// Defines the property. @@ -116,6 +118,7 @@ namespace Avalonia.Controls public Control() { _nameScope = this as INameScope; + _isAttachedToLogicalTree = this is IStyleRoot; } /// @@ -352,21 +355,37 @@ namespace Avalonia.Controls if (--_initCount == 0 && _isAttachedToLogicalTree) { - if (!_styled) - { - RegisterWithNameScope(); - ApplyStyling(); - _styled = true; - } + InitializeStylesIfNeeded(); - if (!IsInitialized) - { - IsInitialized = true; - Initialized?.Invoke(this, EventArgs.Empty); - } + InitializeIfNeeded(); + } + } + + private void InitializeStylesIfNeeded(bool force = false) + { + if (_initCount == 0 && (!_styled || force)) + { + RegisterWithNameScope(); + ApplyStyling(); + _styled = true; + } + } + + private void InitializeIfNeeded() + { + if (_initCount == 0 && !IsInitialized) + { + IsInitialized = true; + Initialized?.Invoke(this, EventArgs.Empty); } } + /// + void ILogical.NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + this.OnAttachedToLogicalTreeCore(e); + } + /// void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { @@ -416,7 +435,7 @@ namespace Avalonia.Controls if (_isAttachedToLogicalTree) { - var oldRoot = FindStyleRoot(old); + var oldRoot = FindStyleRoot(old) ?? this as IStyleRoot; if (oldRoot == null) { @@ -434,7 +453,7 @@ namespace Avalonia.Controls _parent = (IControl)parent; - if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true) + if (_parent is IStyleRoot || _parent?.IsAttachedToLogicalTree == true || this is IStyleRoot) { var newRoot = FindStyleRoot(this); @@ -460,6 +479,38 @@ namespace Avalonia.Controls InheritanceParent = parent; } + /// + void IVisualBrushInitialize.EnsureInitialized() + { + if (VisualRoot == null) + { + if (!IsInitialized) + { + foreach (var i in this.GetSelfAndVisualDescendants()) + { + var c = i as IControl; + + if (c?.IsInitialized == false) + { + var init = c as ISupportInitialize; + + if (init != null) + { + init.BeginInit(); + init.EndInit(); + } + } + } + } + + if (!IsArrangeValid) + { + Measure(Size.Infinity); + Arrange(new Rect(DesiredSize)); + } + } + } + /// /// Adds a pseudo-class to be set when a property is true. /// @@ -539,11 +590,7 @@ namespace Avalonia.Controls { base.OnAttachedToVisualTreeCore(e); - if (!IsInitialized) - { - IsInitialized = true; - Initialized?.Invoke(this, EventArgs.Empty); - } + InitializeIfNeeded(); } /// @@ -611,7 +658,7 @@ namespace Avalonia.Controls if (_focusAdorner != null) { - var adornerLayer = _focusAdorner.Parent as Panel; + var adornerLayer = (IPanel)_focusAdorner.Parent; adornerLayer.Children.Remove(_focusAdorner); _focusAdorner = null; } @@ -711,12 +758,7 @@ namespace Avalonia.Controls { _isAttachedToLogicalTree = true; - if (_initCount == 0) - { - RegisterWithNameScope(); - ApplyStyling(); - _styled = true; - } + InitializeStylesIfNeeded(true); OnAttachedToLogicalTree(e); } diff --git a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs index 4688017187..179dccaf76 100644 --- a/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs +++ b/src/Avalonia.Controls/Embedding/EmbeddableControlRoot.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Styling; +using JetBrains.Annotations; namespace Avalonia.Controls.Embedding { @@ -18,8 +19,11 @@ namespace Avalonia.Controls.Embedding { } + [CanBeNull] public new IEmbeddableWindowImpl PlatformImpl => (IEmbeddableWindowImpl) base.PlatformImpl; + protected bool EnforceClientSize { get; set; } = true; + public void Prepare() { EnsureInitialized(); @@ -36,11 +40,12 @@ namespace Avalonia.Controls.Embedding init.EndInit(); } } - + protected override Size MeasureOverride(Size availableSize) { - base.MeasureOverride(PlatformImpl.ClientSize); - return PlatformImpl.ClientSize; + if (EnforceClientSize) + availableSize = PlatformImpl?.ClientSize ?? default(Size); + return base.MeasureOverride(availableSize); } private readonly NameScope _nameScope = new NameScope(); @@ -63,9 +68,6 @@ namespace Avalonia.Controls.Embedding public void Unregister(string name) => _nameScope.Unregister(name); Type IStyleable.StyleKey => typeof(EmbeddableControlRoot); - public void Dispose() - { - PlatformImpl.Dispose(); - } + public void Dispose() => PlatformImpl?.Dispose(); } } diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 43c8fbcbb4..f6f11aa9ad 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -63,10 +63,10 @@ namespace Avalonia.Controls Vector scale = Stretch.CalculateScaling(Bounds.Size, sourceSize); Size scaledSize = sourceSize * scale; Rect destRect = viewPort - .CenterIn(new Rect(scaledSize)) + .CenterRect(new Rect(scaledSize)) .Intersect(viewPort); Rect sourceRect = new Rect(sourceSize) - .CenterIn(new Rect(destRect.Size / scale)); + .CenterRect(new Rect(destRect.Size / scale)); context.DrawImage(source, 1, sourceRect, destRect); } diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 5d12c9963f..aa209e0462 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -354,7 +354,7 @@ namespace Avalonia.Controls } var collection = sender as ICollection; - PseudoClasses.Set(":empty", collection.Count == 0); + PseudoClasses.Set(":empty", collection == null || collection.Count == 0); } /// diff --git a/src/Avalonia.Controls/Menu.cs b/src/Avalonia.Controls/Menu.cs index e919275d4f..994af9dab8 100644 --- a/src/Avalonia.Controls/Menu.cs +++ b/src/Avalonia.Controls/Menu.cs @@ -47,7 +47,7 @@ namespace Avalonia.Controls static Menu() { ItemsPanelProperty.OverrideDefaultValue(typeof(Menu), DefaultPanel); - MenuItem.ClickEvent.AddClassHandler(x => x.OnMenuClick); + MenuItem.ClickEvent.AddClassHandler(x => x.OnMenuClick, handledEventsToo: true); MenuItem.SubmenuOpenedEvent.AddClassHandler(x => x.OnSubmenuOpened); } diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 3d15ed99e7..3d66fbc51b 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -102,6 +102,11 @@ namespace Avalonia.Controls AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler(x => x.AccessKeyPressed); } + public MenuItem() + { + + } + /// /// Occurs when a without a submenu is clicked. /// @@ -192,7 +197,11 @@ namespace Avalonia.Controls /// The click event args. protected virtual void OnClick(RoutedEventArgs e) { - Command?.Execute(CommandParameter); + if (Command != null) + { + Command.Execute(CommandParameter); + e.Handled = true; + } } /// diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs index 793399841c..3272d3779b 100644 --- a/src/Avalonia.Controls/Panel.cs +++ b/src/Avalonia.Controls/Panel.cs @@ -64,9 +64,12 @@ namespace Avalonia.Controls { Contract.Requires(value != null); - VisualChildren.Clear(); - _children.Clear(); - _children.AddRange(value); + if (_children != value) + { + VisualChildren.Clear(); + _children.Clear(); + _children.AddRange(value); + } } } diff --git a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs index 16f436fd45..60e25d2be6 100644 --- a/src/Avalonia.Controls/Platform/ITopLevelImpl.cs +++ b/src/Avalonia.Controls/Platform/ITopLevelImpl.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using Avalonia.Input; using Avalonia.Input.Raw; +using Avalonia.Rendering; +using JetBrains.Annotations; namespace Avalonia.Platform { @@ -59,6 +61,12 @@ namespace Avalonia.Platform /// Action ScalingChanged { get; set; } + /// + /// Creates a new renderer for the toplevel. + /// + /// The toplevel. + IRenderer CreateRenderer(IRenderRoot root); + /// /// Invalidates a rect on the toplevel. /// @@ -93,5 +101,11 @@ namespace Avalonia.Platform /// Gets or sets a method called when the underlying implementation is destroyed. /// Action Closed { get; set; } + + /// + /// Gets a mouse device associated with toplevel + /// + [CanBeNull] + IMouseDevice MouseDevice { get; } } } diff --git a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs index 84988e912f..4dc96a074d 100644 --- a/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs +++ b/src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Avalonia.Platform; namespace Avalonia.Controls.Platform.Surfaces { diff --git a/src/Avalonia.Controls/Platform/Surfaces/PixelFormat.cs b/src/Avalonia.Controls/Platform/Surfaces/PixelFormat.cs deleted file mode 100644 index c9f8eabe97..0000000000 --- a/src/Avalonia.Controls/Platform/Surfaces/PixelFormat.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Avalonia.Controls.Platform.Surfaces -{ - public enum PixelFormat - { - Rgb565, - Rgba8888, - Bgra8888 - } -} diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 40fc2f302c..c1adff402a 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -8,6 +8,7 @@ using Avalonia.Controls.Templates; using Avalonia.Layout; using Avalonia.LogicalTree; using Avalonia.Media; +using Avalonia.VisualTree; namespace Avalonia.Controls.Presenters { @@ -88,6 +89,7 @@ namespace Avalonia.Controls.Presenters static ContentPresenter() { ContentProperty.Changed.AddClassHandler(x => x.ContentChanged); + ContentTemplateProperty.Changed.AddClassHandler(x => x.ContentChanged); TemplatedParentProperty.Changed.AddClassHandler(x => x.TemplatedParentChanged); } @@ -313,27 +315,22 @@ namespace Avalonia.Controls.Presenters if (content != null && newChild == null) { - // We have content and it isn't a control, so first try to recycle the existing - // child control to display the new data by querying if the template that created - // the child can recycle items and that it also matches the new data. - if (oldChild != null && - _dataTemplate != null && - _dataTemplate.SupportsRecycling && - _dataTemplate.Match(content)) + 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) { newChild = oldChild; } else { - // We couldn't recycle an existing control so find a data template for the data - // and use it to create a control. - _dataTemplate = this.FindDataTemplate(content, ContentTemplate) ?? FuncDataTemplate.Default; + _dataTemplate = dataTemplate; newChild = _dataTemplate.Build(content); - // Try to give the new control its own name scope. - var controlResult = newChild as Control; - - if (controlResult != null) + // Give the new control its own name scope. + if (newChild is Control controlResult) { NameScope.SetNameScope(controlResult, new NameScope()); } @@ -424,6 +421,19 @@ namespace Avalonia.Controls.Presenters private void ContentChanged(AvaloniaPropertyChangedEventArgs e) { _createdChild = false; + + if (((ILogical)this).IsAttachedToLogicalTree) + { + UpdateChild(); + } + else if (Child != null) + { + VisualChildren.Remove(Child); + LogicalChildren.Remove(Child); + Child = null; + _dataTemplate = null; + } + InvalidateMeasure(); } diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 96190720ae..20602d5475 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -155,8 +155,7 @@ namespace Avalonia.Controls.Presenters case NotifyCollectionChangedAction.Add: CreateAndRemoveContainers(); - if (e.NewStartingIndex >= FirstIndex && - e.NewStartingIndex < NextIndex) + if (e.NewStartingIndex < NextIndex) { RecycleContainers(); } diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index d9f0260e39..e41c4e1e28 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -111,7 +111,7 @@ namespace Avalonia.Controls.Presenters /// The target visual. /// The portion of the target visual to bring into view. /// True if the scroll offset was changed; otherwise false. - public bool BringDescendentIntoView(IVisual target, Rect targetRect) + public bool BringDescendantIntoView(IVisual target, Rect targetRect) { if (Child == null) { @@ -133,7 +133,7 @@ namespace Avalonia.Controls.Presenters return false; } - var rect = targetRect * transform.Value; + var rect = targetRect.TransformToAABB(transform.Value); var offset = Offset; var result = false; @@ -262,7 +262,7 @@ namespace Avalonia.Controls.Presenters private void BringIntoViewRequested(object sender, RequestBringIntoViewEventArgs e) { - e.Handled = BringDescendentIntoView(e.TargetObject, e.TargetRect); + e.Handled = BringDescendantIntoView(e.TargetObject, e.TargetRect); } private void ChildChanged(AvaloniaPropertyChangedEventArgs e) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 39759f78f1..a2b5f3f8b4 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -127,11 +127,11 @@ namespace Avalonia.Controls.Presenters base.Render(context); if (selectionStart == selectionEnd) - { + { var backgroundColor = (((Control)TemplatedParent).GetValue(BackgroundProperty) as SolidColorBrush)?.Color; var caretBrush = Brushes.Black; - if(backgroundColor.HasValue) + if (backgroundColor.HasValue) { byte red = (byte)~(backgroundColor.Value.R); byte green = (byte)~(backgroundColor.Value.G); @@ -139,7 +139,7 @@ namespace Avalonia.Controls.Presenters caretBrush = new SolidColorBrush(Color.FromRgb(red, green, blue)); } - + if (_caretBlink) { var charPos = FormattedText.HitTestTextPosition(CaretIndex); @@ -212,7 +212,10 @@ namespace Avalonia.Controls.Presenters if (length > 0) { - result.SetForegroundBrush(Brushes.White, start, length); + result.Spans = new[] + { + new FormattedTextStyleSpan(start, length, foregroundBrush: Brushes.White), + }; } return result; @@ -228,17 +231,13 @@ namespace Avalonia.Controls.Presenters } else { - // TODO: Pretty sure that measuring "X" isn't the right way to do this... - using (var formattedText = new FormattedText( - "X", - FontFamily, - FontSize, - FontStyle, - TextAlignment, - FontWeight)) + return new FormattedText { - return formattedText.Measure(); - } + Text = "X", + Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight), + TextAlignment = TextAlignment, + Constraint = availableSize, + }.Measure(); } } diff --git a/src/Avalonia.Controls/Primitives/AccessText.cs b/src/Avalonia.Controls/Primitives/AccessText.cs index 42a6f9123b..4bb80e6d3f 100644 --- a/src/Avalonia.Controls/Primitives/AccessText.cs +++ b/src/Avalonia.Controls/Primitives/AccessText.cs @@ -85,15 +85,14 @@ namespace Avalonia.Controls.Primitives /// A object. protected override FormattedText CreateFormattedText(Size constraint) { - var result = new FormattedText( - StripAccessKey(Text), - FontFamily, - FontSize, - FontStyle, - TextAlignment, - FontWeight); - result.Constraint = constraint; - return result; + return new FormattedText + { + Constraint = constraint, + Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight), + Text = StripAccessKey(Text), + TextAlignment = TextAlignment, + Wrapping = TextWrapping, + }; } /// diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs index bb2a61c024..daea187a69 100644 --- a/src/Avalonia.Controls/Primitives/Popup.cs +++ b/src/Avalonia.Controls/Primitives/Popup.cs @@ -340,11 +340,11 @@ namespace Avalonia.Controls.Primitives switch (mode) { case PlacementMode.Pointer: - if (MouseDevice.Instance != null) + if(PopupRoot != null) { // Scales the Horizontal and Vertical offset to screen co-ordinates. var screenOffset = new Point(HorizontalOffset * (PopupRoot as ILayoutRoot).LayoutScaling, VerticalOffset * (PopupRoot as ILayoutRoot).LayoutScaling); - return MouseDevice.Instance.Position + screenOffset; + return (((IInputRoot)PopupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset; } return default(Point); diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs index 5e5177b29d..a999e4ae37 100644 --- a/src/Avalonia.Controls/Primitives/PopupRoot.cs +++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs @@ -9,6 +9,7 @@ using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using Avalonia.VisualTree; +using JetBrains.Annotations; namespace Avalonia.Controls.Primitives { @@ -49,6 +50,7 @@ namespace Avalonia.Controls.Primitives /// /// Gets the platform-specific window implementation. /// + [CanBeNull] public new IPopupImpl PlatformImpl => (IPopupImpl)base.PlatformImpl; /// @@ -65,37 +67,14 @@ namespace Avalonia.Controls.Primitives IVisual IHostedVisualTreeRoot.Host => Parent; /// - public void Dispose() - { - this.PlatformImpl.Dispose(); - } - - /// - /// Hides the popup. - /// - public void Hide() - { - PlatformImpl.Hide(); - IsVisible = false; - } - - /// - /// Shows the popup. - /// - public void Show() - { - EnsureInitialized(); - PlatformImpl.Show(); - LayoutManager.Instance.ExecuteInitialLayoutPass(this); - IsVisible = true; - } + public void Dispose() => PlatformImpl?.Dispose(); /// protected override void OnTemplateApplied(TemplateAppliedEventArgs e) { base.OnTemplateApplied(e); - if (Parent.TemplatedParent != null) + if (Parent?.TemplatedParent != null) { if (_presenterSubscription != null) { @@ -109,16 +88,6 @@ namespace Avalonia.Controls.Primitives } } - private void EnsureInitialized() - { - if (!this.IsInitialized) - { - var init = (ISupportInitialize)this; - init.BeginInit(); - init.EndInit(); - } - } - private void SetTemplatedParentAndApplyChildTemplates(IControl control) { var templatedParent = Parent.TemplatedParent; diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index 7a42c48053..1ddfb97c14 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -285,6 +285,17 @@ namespace Avalonia.Controls.Primitives return this; } + /// + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + if (VisualChildren.Count > 0) + { + ((ILogical)VisualChildren[0]).NotifyAttachedToLogicalTree(e); + } + + base.OnAttachedToLogicalTree(e); + } + /// protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) { diff --git a/src/Avalonia.Controls/Primitives/Thumb.cs b/src/Avalonia.Controls/Primitives/Thumb.cs index 1fef48f55b..065b1aedbe 100644 --- a/src/Avalonia.Controls/Primitives/Thumb.cs +++ b/src/Avalonia.Controls/Primitives/Thumb.cs @@ -86,7 +86,7 @@ namespace Avalonia.Controls.Primitives RaiseEvent(ev); } - protected override void OnPointerReleased(PointerEventArgs e) + protected override void OnPointerReleased(PointerReleasedEventArgs e) { if (_lastPoint.HasValue) { diff --git a/src/Avalonia.Controls/Templates/TemplateExtensions.cs b/src/Avalonia.Controls/Templates/TemplateExtensions.cs index 535be23b23..09da737836 100644 --- a/src/Avalonia.Controls/Templates/TemplateExtensions.cs +++ b/src/Avalonia.Controls/Templates/TemplateExtensions.cs @@ -31,9 +31,9 @@ namespace Avalonia.Controls.Templates if (child.TemplatedParent != null) { - foreach (var descendent in GetTemplateChildren(child, templatedParent)) + foreach (var descendant in GetTemplateChildren(child, templatedParent)) { - yield return descendent; + yield return descendant; } } } diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs index 028df729a5..c7a77bdf0e 100644 --- a/src/Avalonia.Controls/TextBlock.cs +++ b/src/Avalonia.Controls/TextBlock.cs @@ -116,7 +116,7 @@ namespace Avalonia.Controls this.GetObservable(TextAlignmentProperty).Select(_ => Unit.Default), this.GetObservable(FontSizeProperty).Select(_ => Unit.Default), this.GetObservable(FontStyleProperty).Select(_ => Unit.Default), - this.GetObservable(FontWeightProperty).Select(_=>Unit.Default)) + this.GetObservable(FontWeightProperty).Select(_ => Unit.Default)) .Subscribe(_ => { InvalidateFormattedText(); @@ -350,16 +350,14 @@ namespace Avalonia.Controls /// A object. protected virtual FormattedText CreateFormattedText(Size constraint) { - var result = new FormattedText( - Text ?? string.Empty, - FontFamily, - FontSize, - FontStyle, - TextAlignment, - FontWeight, - TextWrapping); - result.Constraint = constraint; - return result; + return new FormattedText + { + Constraint = constraint, + Typeface = new Typeface(FontFamily, FontSize, FontStyle, FontWeight), + Text = Text ?? string.Empty, + TextAlignment = TextAlignment, + Wrapping = TextWrapping, + }; } /// @@ -370,7 +368,6 @@ namespace Avalonia.Controls if (_formattedText != null) { _constraint = _formattedText.Constraint; - _formattedText.Dispose(); _formattedText = null; } diff --git a/src/Avalonia.Controls/TextBox.cs b/src/Avalonia.Controls/TextBox.cs index 719e888dd4..92ab12f82e 100644 --- a/src/Avalonia.Controls/TextBox.cs +++ b/src/Avalonia.Controls/TextBox.cs @@ -236,6 +236,11 @@ namespace Avalonia.Controls { _presenter = e.NameScope.Get("PART_TextPresenter"); _presenter.Cursor = new Cursor(StandardCursorType.Ibeam); + + if(IsFocused) + { + _presenter.ShowCaret(); + } } protected override void OnGotFocus(GotFocusEventArgs e) @@ -254,7 +259,7 @@ namespace Avalonia.Controls } else { - _presenter.ShowCaret(); + _presenter?.ShowCaret(); } } @@ -263,7 +268,7 @@ namespace Avalonia.Controls base.OnLostFocus(e); SelectionStart = 0; SelectionEnd = 0; - _presenter.HideCaret(); + _presenter?.HideCaret(); } protected override void OnTextInput(TextInputEventArgs e) @@ -522,7 +527,7 @@ namespace Avalonia.Controls } } - protected override void OnPointerReleased(PointerEventArgs e) + protected override void OnPointerReleased(PointerReleasedEventArgs e) { if (_presenter != null && e.Device.Captured == _presenter) { @@ -720,7 +725,7 @@ namespace Avalonia.Controls if (pos < text.Length) { --pos; - if (pos > 0 && Text[pos - 1] == '\r' && Text[pos] == '\n') + if (pos > 0 && text[pos - 1] == '\r' && text[pos] == '\n') { --pos; } @@ -771,6 +776,9 @@ namespace Avalonia.Controls private string GetSelection() { + var text = Text; + if (string.IsNullOrEmpty(text)) + return ""; var selectionStart = SelectionStart; var selectionEnd = SelectionEnd; var start = Math.Min(selectionStart, selectionEnd); @@ -779,7 +787,7 @@ namespace Avalonia.Controls { return ""; } - return Text.Substring(start, end - start); + return text.Substring(start, end - start); } private int GetLine(int caretIndex, IList lines) diff --git a/src/Avalonia.Controls/ToolTip.cs b/src/Avalonia.Controls/ToolTip.cs index cff3dab150..e1b69637af 100644 --- a/src/Avalonia.Controls/ToolTip.cs +++ b/src/Avalonia.Controls/ToolTip.cs @@ -105,21 +105,29 @@ namespace Avalonia.Controls { if (control != null && control.IsVisible && control.GetVisualRoot() != null) { - if (s_popup != null) - { - throw new AvaloniaInternalException("Previous ToolTip not disposed."); - } + var cp = (control.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(control); - var cp = MouseDevice.Instance?.GetPosition(control); - var position = control.PointToScreen(cp ?? new Point(0, 0)) + new Vector(0, 22); + if (cp.HasValue && control.IsVisible && new Rect(control.Bounds.Size).Contains(cp.Value)) + { + var position = control.PointToScreen(cp.Value) + new Vector(0, 22); + + if (s_popup == null) + { + s_popup = new PopupRoot(); + s_popup.Content = new ToolTip(); + } + else + { + ((ISetLogicalParent)s_popup).SetParent(null); + } - s_popup = new PopupRoot(); ((ISetLogicalParent)s_popup).SetParent(control); - s_popup.Content = new ToolTip { Content = GetTip(control) }; - s_popup.Position = position; - s_popup.Show(); + ((ToolTip)s_popup.Content).Content = GetTip(control); + s_popup.Position = position; + s_popup.Show(); - s_current = control; + s_current = control; + } } } @@ -147,16 +155,23 @@ namespace Avalonia.Controls { if (s_popup != null) { - // Clear the ToolTip's Content in case it has control content: this will - // reset its visual parent allowing it to be used again. - ((ToolTip)s_popup.Content).Content = null; - - // Dispose of the popup. - s_popup.Dispose(); - s_popup = null; + DisposeTooltip(); + s_show.OnNext(null); } + } + } + + private static void DisposeTooltip() + { + if (s_popup != null) + { + // Clear the ToolTip's Content in case it has control content: this will + // reset its visual parent allowing it to be used again. + ((ToolTip)s_popup.Content).Content = null; - s_show.OnNext(null); + // Dispose of the popup. + s_popup.Dispose(); + s_popup = null; } } } diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 4de0256a7d..f8db0e2a5b 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -14,6 +14,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Styling; using Avalonia.VisualTree; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -89,28 +90,27 @@ namespace Avalonia.Controls _renderInterface = TryGetService(dependencyResolver); var renderLoop = TryGetService(dependencyResolver); - var rendererFactory = TryGetService(dependencyResolver); - Renderer = rendererFactory?.CreateRenderer(this, renderLoop); + Renderer = impl.CreateRenderer(this); - PlatformImpl.SetInputRoot(this); + impl.SetInputRoot(this); + + impl.Closed = HandleClosed; + impl.Input = HandleInput; + impl.Paint = HandlePaint; + impl.Resized = HandleResized; + impl.ScalingChanged = HandleScalingChanged; - PlatformImpl.Closed = HandleClosed; - PlatformImpl.Input = HandleInput; - PlatformImpl.Paint = Renderer != null ? (Action)Renderer.Render : null; - PlatformImpl.Resized = HandleResized; - PlatformImpl.ScalingChanged = HandleScalingChanged; - _keyboardNavigationHandler?.SetOwner(this); _accessKeyHandler?.SetOwner(this); styler?.ApplyStyles(this); - ClientSize = PlatformImpl.ClientSize; + ClientSize = impl.ClientSize; this.GetObservable(PointerOverElementProperty) .Select( x => (x as InputElement)?.GetObservable(CursorProperty) ?? Observable.Empty()) - .Switch().Subscribe(cursor => PlatformImpl.SetCursor(cursor?.PlatformCursor)); + .Switch().Subscribe(cursor => PlatformImpl?.SetCursor(cursor?.PlatformCursor)); if (_applicationLifecycle != null) { @@ -135,10 +135,8 @@ namespace Avalonia.Controls /// /// Gets the platform-specific window implementation. /// - public ITopLevelImpl PlatformImpl - { - get; - } + [CanBeNull] + public ITopLevelImpl PlatformImpl { get; private set; } /// /// Gets the renderer for the window. @@ -164,6 +162,9 @@ namespace Avalonia.Controls set { SetValue(PointerOverElementProperty, value); } } + /// + IMouseDevice IInputRoot.MouseDevice => PlatformImpl?.MouseDevice; + /// /// Gets or sets a value indicating whether access keys are shown in the window. /// @@ -177,37 +178,65 @@ namespace Avalonia.Controls Size ILayoutRoot.MaxClientSize => Size.Infinity; /// - double ILayoutRoot.LayoutScaling => PlatformImpl.Scaling; + double ILayoutRoot.LayoutScaling => PlatformImpl?.Scaling ?? 1; + + /// + double IRenderRoot.RenderScaling => PlatformImpl?.Scaling ?? 1; IStyleHost IStyleHost.StylingParent { get { return AvaloniaLocator.Current.GetService(); } } + IRenderTarget IRenderRoot.CreateRenderTarget() => CreateRenderTarget(); + /// - IRenderTarget IRenderRoot.CreateRenderTarget() + protected virtual IRenderTarget CreateRenderTarget() { + if(PlatformImpl == null) + throw new InvalidOperationException("Cann't create render target, PlatformImpl is null (might be already disposed)"); return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces); } /// void IRenderRoot.Invalidate(Rect rect) { - PlatformImpl.Invalidate(rect); + PlatformImpl?.Invalidate(rect); } /// Point IRenderRoot.PointToClient(Point p) { - return PlatformImpl.PointToClient(p); + return PlatformImpl?.PointToClient(p) ?? default(Point); } /// Point IRenderRoot.PointToScreen(Point p) { - return PlatformImpl.PointToScreen(p); + return PlatformImpl?.PointToScreen(p) ?? default(Point); } + /// + /// Handles a paint notification from . + /// + /// The dirty area. + protected virtual void HandlePaint(Rect rect) + { + Renderer?.Paint(rect); + } + + /// + /// Handles a closed notification from . + /// + protected virtual void HandleClosed() + { + PlatformImpl = null; + + Closed?.Invoke(this, EventArgs.Empty); + Renderer?.Dispose(); + Renderer = null; + _applicationLifecycle.OnExit -= OnApplicationExiting; + } /// /// Handles a resize notification from . @@ -219,7 +248,7 @@ namespace Avalonia.Controls Width = clientSize.Width; Height = clientSize.Height; LayoutManager.Instance.ExecuteLayoutPass(); - PlatformImpl.Invalidate(new Rect(clientSize)); + Renderer?.Resized(clientSize); } /// @@ -229,7 +258,7 @@ namespace Avalonia.Controls /// The window scaling. protected virtual void HandleScalingChanged(double scaling) { - foreach (ILayoutable control in this.GetSelfAndVisualDescendents()) + foreach (ILayoutable control in this.GetSelfAndVisualDescendants()) { control.InvalidateMeasure(); } @@ -267,17 +296,6 @@ namespace Avalonia.Controls return result; } - /// - /// Handles a closed notification from . - /// - private void HandleClosed() - { - Closed?.Invoke(this, EventArgs.Empty); - Renderer?.Dispose(); - Renderer = null; - _applicationLifecycle.OnExit -= OnApplicationExiting; - } - private void OnApplicationExiting(object sender, EventArgs args) { HandleApplicationExiting(); diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index b966d09b1f..5d1b9a1462 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -16,7 +16,7 @@ namespace Avalonia.Controls /// /// Displays a hierachical tree of data. /// - public class TreeView : ItemsControl + public class TreeView : ItemsControl, ICustomKeyboardNavigation { /// /// Defines the property. @@ -90,6 +90,26 @@ namespace Avalonia.Controls } } + (bool handled, IInputElement next) ICustomKeyboardNavigation.GetNext(IInputElement element, NavigationDirection direction) + { + if (direction == NavigationDirection.Next || direction == NavigationDirection.Previous) + { + if (!this.IsVisualAncestorOf(element)) + { + IControl result = _selectedItem != null ? + ItemContainerGenerator.Index.ContainerFromItem(_selectedItem) : + ItemContainerGenerator.ContainerFromIndex(0); + return (true, result); + } + else + { + return (true, null); + } + } + + return (false, null); + } + /// protected override IItemContainerGenerator CreateItemContainerGenerator() { diff --git a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs index 71ba3793d1..be4c1aa6c4 100644 --- a/src/Avalonia.Controls/Utils/UndoRedoHelper.cs +++ b/src/Avalonia.Controls/Utils/UndoRedoHelper.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using JetBrains.Annotations; using Avalonia.Utilities; namespace Avalonia.Controls.Utils diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 992279d981..46c625cc4c 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -11,6 +11,8 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Styling; using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -45,12 +47,12 @@ namespace Avalonia.Controls /// public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope { - private static IList s_windows = new List(); + private static List s_windows = new List(); /// /// Retrieves an enumeration of all Windows in the currently running application. /// - public static IList OpenWindows => s_windows; + public static IReadOnlyList OpenWindows => s_windows; /// /// Defines the property. @@ -86,11 +88,11 @@ namespace Avalonia.Controls static Window() { BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); - TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue)); + TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue)); HasSystemDecorationsProperty.Changed.AddClassHandler( - (s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue)); + (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); - IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); + IconProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetIcon(((WindowIcon)e.NewValue).PlatformImpl)); } /// @@ -108,7 +110,7 @@ namespace Avalonia.Controls public Window(IWindowImpl impl) : base(impl) { - _maxPlatformClientSize = this.PlatformImpl.MaxClientSize; + _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); } /// @@ -128,6 +130,7 @@ namespace Avalonia.Controls /// /// Gets the platform-specific window implementation. /// + [CanBeNull] public new IWindowImpl PlatformImpl => (IWindowImpl)base.PlatformImpl; /// @@ -163,8 +166,12 @@ namespace Avalonia.Controls /// public WindowState WindowState { - get { return this.PlatformImpl.WindowState; } - set { this.PlatformImpl.WindowState = value; } + get { return PlatformImpl?.WindowState ?? WindowState.Normal; } + set + { + if (PlatformImpl != null) + PlatformImpl.WindowState = value; + } } /// @@ -188,7 +195,8 @@ namespace Avalonia.Controls public void Close() { s_windows.Remove(this); - PlatformImpl.Dispose(); + PlatformImpl?.Dispose(); + IsVisible = false; } protected override void HandleApplicationExiting() @@ -215,27 +223,42 @@ namespace Avalonia.Controls /// /// Hides the window but does not close it. /// - public void Hide() + public override void Hide() { + if (!IsVisible) + { + return; + } + using (BeginAutoSizing()) { - PlatformImpl.Hide(); + Renderer?.Stop(); + PlatformImpl?.Hide(); } + + IsVisible = false; } /// /// Shows the window. /// - public void Show() + public override void Show() { + if (IsVisible) + { + return; + } + s_windows.Add(this); EnsureInitialized(); + IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) { - PlatformImpl.Show(); + PlatformImpl?.Show(); + Renderer?.Start(); } } @@ -261,21 +284,37 @@ namespace Avalonia.Controls /// public Task ShowDialog() { + if (IsVisible) + { + throw new InvalidOperationException("The window is already being shown."); + } + s_windows.Add(this); EnsureInitialized(); + IsVisible = true; LayoutManager.Instance.ExecuteInitialLayoutPass(this); using (BeginAutoSizing()) { - var modal = PlatformImpl.ShowDialog(); + var affectedWindows = s_windows.Where(w => w.IsEnabled && w != this).ToList(); + var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); + SetIsEnabled(affectedWindows, false); + + var modal = PlatformImpl?.ShowDialog(); var result = new TaskCompletionSource(); - Observable.FromEventPattern(this, nameof(Closed)) + Renderer?.Start(); + + Observable.FromEventPattern( + x => this.Closed += x, + x => this.Closed -= x) .Take(1) .Subscribe(_ => { - modal.Dispose(); + modal?.Dispose(); + SetIsEnabled(affectedWindows, true); + activated?.Activate(); result.SetResult((TResult)_dialogResult); }); @@ -283,6 +322,14 @@ namespace Avalonia.Controls } } + void SetIsEnabled(IEnumerable windows, bool isEnabled) + { + foreach (var window in windows) + { + window.IsEnabled = isEnabled; + } + } + /// void INameScope.Register(string name, object element) { @@ -329,6 +376,13 @@ namespace Avalonia.Controls return size; } + protected override void HandleClosed() + { + IsVisible = false; + s_windows.Remove(this); + base.HandleClosed(); + } + /// protected override void HandleResized(Size clientSize) { @@ -339,16 +393,6 @@ namespace Avalonia.Controls base.HandleResized(clientSize); } - - private void EnsureInitialized() - { - if (!this.IsInitialized) - { - var init = (ISupportInitialize)this; - init.BeginInit(); - init.EndInit(); - } - } } } diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index ac6eea8641..bcc09936ba 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -9,6 +9,7 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Platform; +using JetBrains.Annotations; namespace Avalonia.Controls { @@ -28,7 +29,15 @@ namespace Avalonia.Controls public static readonly DirectProperty IsActiveProperty = AvaloniaProperty.RegisterDirect(nameof(IsActive), o => o.IsActive); + private bool _hasExecutedInitialLayoutPass; private bool _isActive; + private bool _ignoreVisibilityChange; + + static WindowBase() + { + IsVisibleProperty.OverrideDefaultValue(false); + IsVisibleProperty.Changed.AddClassHandler(x => x.IsVisibleChanged); + } public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current) { @@ -36,10 +45,10 @@ namespace Avalonia.Controls public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver) { - PlatformImpl.Activated = HandleActivated; - PlatformImpl.Deactivated = HandleDeactivated; - PlatformImpl.PositionChanged = HandlePositionChanged; - this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl.Resize(x)); + impl.Activated = HandleActivated; + impl.Deactivated = HandleDeactivated; + impl.PositionChanged = HandlePositionChanged; + this.GetObservable(ClientSizeProperty).Skip(1).Subscribe(x => PlatformImpl?.Resize(x)); } /// @@ -57,9 +66,9 @@ namespace Avalonia.Controls /// public event EventHandler PositionChanged; + [CanBeNull] public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl; - /// /// Gets a value that indicates whether the window is active. /// @@ -74,8 +83,12 @@ namespace Avalonia.Controls /// public Point Position { - get { return PlatformImpl.Position; } - set { PlatformImpl.Position = value; } + get { return PlatformImpl?.Position ?? default(Point); } + set + { + if (PlatformImpl is IWindowBaseImpl impl) + impl.Position = value; + } } /// @@ -92,9 +105,54 @@ namespace Avalonia.Controls /// public void Activate() { - PlatformImpl.Activate(); + PlatformImpl?.Activate(); + } + + /// + /// Hides the popup. + /// + public virtual void Hide() + { + _ignoreVisibilityChange = true; + + try + { + Renderer?.Stop(); + PlatformImpl?.Hide(); + IsVisible = false; + } + finally + { + _ignoreVisibilityChange = false; + } } + /// + /// Shows the popup. + /// + public virtual void Show() + { + _ignoreVisibilityChange = true; + + try + { + EnsureInitialized(); + IsVisible = true; + + if (!_hasExecutedInitialLayoutPass) + { + LayoutManager.Instance.ExecuteInitialLayoutPass(this); + _hasExecutedInitialLayoutPass = true; + } + + PlatformImpl?.Show(); + Renderer?.Start(); + } + finally + { + _ignoreVisibilityChange = false; + } + } /// /// Begins an auto-resize operation. @@ -120,10 +178,38 @@ namespace Avalonia.Controls { using (BeginAutoSizing()) { - PlatformImpl.Resize(finalSize); + PlatformImpl?.Resize(finalSize); + } + + return base.ArrangeOverride(PlatformImpl?.ClientSize ?? default(Size)); + } + + /// + /// Ensures that the window is initialized. + /// + protected void EnsureInitialized() + { + if (!IsInitialized) + { + var init = (ISupportInitialize)this; + init.BeginInit(); + init.EndInit(); } + } - return base.ArrangeOverride(PlatformImpl.ClientSize); + protected override void HandleClosed() + { + _ignoreVisibilityChange = true; + + try + { + IsVisible = false; + base.HandleClosed(); + } + finally + { + _ignoreVisibilityChange = false; + } } /// @@ -139,8 +225,7 @@ namespace Avalonia.Controls } ClientSize = clientSize; LayoutManager.Instance.ExecuteLayoutPass(); - PlatformImpl.Invalidate(new Rect(clientSize)); - + Renderer?.Resized(clientSize); } /// @@ -180,15 +265,30 @@ namespace Avalonia.Controls Deactivated?.Invoke(this, EventArgs.Empty); } + private void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e) + { + if (!_ignoreVisibilityChange) + { + if ((bool)e.NewValue) + { + Show(); + } + else + { + Hide(); + } + } + } + /// /// Starts moving a window with left button being held. Should be called from left mouse button press event handler /// - public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag(); + public void BeginMoveDrag() => PlatformImpl?.BeginMoveDrag(); /// /// Starts resizing a window. This function is used if an application has window resizing controls. /// Should be called from left mouse button press event handler /// - public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge); + public void BeginResizeDrag(WindowEdge edge) => PlatformImpl?.BeginResizeDrag(edge); } } diff --git a/src/Avalonia.Controls/packages.config b/src/Avalonia.Controls/packages.config deleted file mode 100644 index c372c92f3f..0000000000 --- a/src/Avalonia.Controls/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index d9174d6f36..a68e8760f2 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -1,22 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {799A7BB5-3C2C-48B6-85A7-406A12C420DA} - Library - Properties - Avalonia - Avalonia.DesignerSupport - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + false true @@ -40,87 +25,23 @@ CS1591 - - - {3e53a01a-b331-47f3-b828-4a5717e77a24} - Avalonia.Markup.Xaml - - - {6417e941-21bc-467b-a771-0de389353ce6} - Avalonia.Markup - - - {d211e587-d8bc-45b9-95a4-f297c8fa5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {D2221C82-4A25-4583-9B43-D791E3F6820C} - Avalonia.Controls - - - {62024B2D-53EB-4638-B26B-85EEAA54866E} - Avalonia.Input - - - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} - Avalonia.Interactivity - - - {42472427-4774-4C81-8AFF-9F27B8E31721} - Avalonia.Layout - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - - - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} - Avalonia.Styling - - - {3E10A5FA-E8DA-48B1-AD44-6A5B6CB7750F} - Avalonia.Themes.Default - + + + + + + + + + + + Properties\SharedAssemblyInfo.cs - - - - - - - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - - - - + + \ No newline at end of file diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.v2.ncrunchproject b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.DesignerSupport/DesignerAssist.cs b/src/Avalonia.DesignerSupport/DesignerAssist.cs index c9ae89354c..2f5fc79147 100644 --- a/src/Avalonia.DesignerSupport/DesignerAssist.cs +++ b/src/Avalonia.DesignerSupport/DesignerAssist.cs @@ -5,8 +5,6 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; -using OmniXaml; -using OmniXaml.ObjectAssembler; using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Markup.Xaml; @@ -75,7 +73,7 @@ namespace Avalonia.DesignerSupport private static void SetScalingFactor(double factor) { PlatformManager.SetDesignerScalingFactor(factor); - s_currentWindow?.PlatformImpl.Resize(s_currentWindow.ClientSize); + s_currentWindow?.PlatformImpl?.Resize(s_currentWindow.ClientSize); } static Window s_currentWindow; @@ -149,6 +147,8 @@ namespace Avalonia.DesignerSupport s_currentWindow = window; window.Show(); Design.ApplyDesignerProperties(window, control); + // ReSharper disable once PossibleNullReferenceException + // Always not null at this point Api.OnWindowCreated?.Invoke(window.PlatformImpl.Handle.Handle); Api.OnResize?.Invoke(); } diff --git a/src/Avalonia.DesignerSupport/packages.config b/src/Avalonia.DesignerSupport/packages.config deleted file mode 100644 index 9f732f1bcb..0000000000 --- a/src/Avalonia.DesignerSupport/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index a56575dab7..be3f397283 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -1,22 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {7062AE20-5DCC-4442-9645-8195BDECE63E} - Library - Properties - Avalonia.Diagnostics - Avalonia.Diagnostics - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + false true @@ -41,117 +26,29 @@ true - - - {3e53a01a-b331-47f3-b828-4a5717e77a24} - Avalonia.Markup.Xaml - - - {6417e941-21bc-467b-a771-0de389353ce6} - Avalonia.Markup - - - {D211E587-D8BC-45B9-95A4-F297C8FA5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {D2221C82-4A25-4583-9B43-D791E3F6820C} - Avalonia.Controls - - - {62024B2D-53EB-4638-B26B-85EEAA54866E} - Avalonia.Input - - - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} - Avalonia.Interactivity - - - {42472427-4774-4C81-8AFF-9F27B8E31721} - Avalonia.Layout - - - {6417B24E-49C2-4985-8DB2-3AB9D898EC91} - Avalonia.ReactiveUI - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - - - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} - Avalonia.Styling - - - {3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f} - Avalonia.Themes.Default - + + + + + + + + + + + Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - TreePageView.xaml + + %(Filename) - - DevTools.xaml - - - - - - - ..\..\packages\Splat.1.6.2\lib\Portable-net45+win+wpa81+wp80\Splat.dll - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - - - - - - Designer - - + Designer - - + + \ No newline at end of file diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.v2.ncrunchproject b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Diagnostics/DevTools.xaml.cs b/src/Avalonia.Diagnostics/DevTools.xaml.cs index 5be252c5c3..6593a8cd42 100644 --- a/src/Avalonia.Diagnostics/DevTools.xaml.cs +++ b/src/Avalonia.Diagnostics/DevTools.xaml.cs @@ -11,15 +11,14 @@ using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using Avalonia.VisualTree; -using ReactiveUI; namespace Avalonia { - public static class WindowExtensions + public static class DevToolsExtensions { - public static void AttachDevTools(this Window window) + public static void AttachDevTools(this TopLevel control) { - Avalonia.Diagnostics.DevTools.Attach(window); + Avalonia.Diagnostics.DevTools.Attach(control); } } } @@ -28,7 +27,7 @@ namespace Avalonia.Diagnostics { public class DevTools : UserControl { - private static Dictionary s_open = new Dictionary(); + private static Dictionary s_open = new Dictionary(); private IDisposable _keySubscription; public DevTools(IControl root) @@ -44,9 +43,9 @@ namespace Avalonia.Diagnostics public IControl Root { get; } - public static IDisposable Attach(Window window) + public static IDisposable Attach(TopLevel control) { - return window.AddHandler( + return control.AddHandler( KeyDownEvent, WindowPreviewKeyDown, RoutingStrategies.Tunnel); @@ -56,16 +55,16 @@ namespace Avalonia.Diagnostics { if (e.Key == Key.F12) { - var window = (Window)sender; + var control = (TopLevel)sender; var devToolsWindow = default(Window); - if (s_open.TryGetValue(window, out devToolsWindow)) + if (s_open.TryGetValue(control, out devToolsWindow)) { devToolsWindow.Activate(); } else { - var devTools = new DevTools(window); + var devTools = new DevTools(control); devToolsWindow = new Window { @@ -74,12 +73,12 @@ namespace Avalonia.Diagnostics Content = devTools, DataTemplates = new DataTemplates { - new ViewLocator(), + new ViewLocator(), } }; devToolsWindow.Closed += devTools.DevToolsClosed; - s_open.Add((Window)sender, devToolsWindow); + s_open.Add(control, devToolsWindow); devToolsWindow.Show(); } } @@ -89,9 +88,7 @@ namespace Avalonia.Diagnostics { var devToolsWindow = (Window)sender; var devTools = (DevTools)devToolsWindow.Content; - var window = (Window)devTools.Root; - - s_open.Remove(window); + s_open.Remove((TopLevel)devTools.Root); _keySubscription.Dispose(); devToolsWindow.Closed -= DevToolsClosed; } @@ -107,7 +104,8 @@ namespace Avalonia.Diagnostics if ((e.Modifiers) == modifiers) { - var point = MouseDevice.Instance.GetPosition(Root); + + var point = (Root.VisualRoot as IInputRoot)?.MouseDevice?.GetPosition(Root) ?? default(Point); var control = Root.GetVisualsAt(point, x => (!(x is AdornerLayer) && x.IsVisible)) .FirstOrDefault(); diff --git a/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs index 34cc32972c..d723890196 100644 --- a/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -3,19 +3,18 @@ using System.Collections.Generic; using System.Linq; -using Avalonia.Controls; -using ReactiveUI; +using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels { - internal class ControlDetailsViewModel : ReactiveObject + internal class ControlDetailsViewModel : ViewModelBase { - public ControlDetailsViewModel(Control control) + public ControlDetailsViewModel(IVisual control) { - if (control != null) + if (control is AvaloniaObject avaloniaObject) { - Properties = AvaloniaPropertyRegistry.Instance.GetRegistered(control) - .Select(x => new PropertyDetails(control, x)) + Properties = AvaloniaPropertyRegistry.Instance.GetRegistered(avaloniaObject) + .Select(x => new PropertyDetails(avaloniaObject, x)) .OrderBy(x => x.IsAttached) .ThenBy(x => x.Name); } diff --git a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs index dbee9ad624..2d3f978462 100644 --- a/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/DevToolsViewModel.cs @@ -5,32 +5,50 @@ using System; using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Input; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { - internal class DevToolsViewModel : ReactiveObject + internal class DevToolsViewModel : ViewModelBase { - private ReactiveObject _content; - + private ViewModelBase _content; private int _selectedTab; - private TreePageViewModel _logicalTree; - private TreePageViewModel _visualTree; - - private readonly ObservableAsPropertyHelper _focusedControl; - - private readonly ObservableAsPropertyHelper _pointerOverElement; + private string _focusedControl; + private string _pointerOverElement; public DevToolsViewModel(IControl root) { _logicalTree = new TreePageViewModel(LogicalTreeNode.Create(root)); _visualTree = new TreePageViewModel(VisualTreeNode.Create(root)); - this.WhenAnyValue(x => x.SelectedTab).Subscribe(index => + UpdateFocusedControl(); + KeyboardDevice.Instance.PropertyChanged += (s, e) => { - switch (index) + if (e.PropertyName == nameof(KeyboardDevice.Instance.FocusedElement)) + { + UpdateFocusedControl(); + } + }; + + root.GetObservable(TopLevel.PointerOverElementProperty) + .Subscribe(x => PointerOverElement = x?.GetType().Name); + } + + public ViewModelBase Content + { + get { return _content; } + private set { RaiseAndSetIfChanged(ref _content, value); } + } + + public int SelectedTab + { + get { return _selectedTab; } + set + { + _selectedTab = value; + + switch (value) { case 0: Content = _logicalTree; @@ -39,34 +57,23 @@ namespace Avalonia.Diagnostics.ViewModels Content = _visualTree; break; } - }); - _focusedControl = KeyboardDevice.Instance - .WhenAnyValue(x => x.FocusedElement) - .Select(x => x?.GetType().Name) - .ToProperty(this, x => x.FocusedControl); - - _pointerOverElement = root.GetObservable(TopLevel.PointerOverElementProperty) - .Select(x => x?.GetType().Name) - .ToProperty(this, x => x.PointerOverElement); + RaisePropertyChanged(); + } } - public ReactiveObject Content + public string FocusedControl { - get { return _content; } - private set { this.RaiseAndSetIfChanged(ref _content, value); } + get { return _focusedControl; } + private set { RaiseAndSetIfChanged(ref _focusedControl, value); } } - public int SelectedTab + public string PointerOverElement { - get { return _selectedTab; } - set { this.RaiseAndSetIfChanged(ref _selectedTab, value); } + get { return _pointerOverElement; } + private set { RaiseAndSetIfChanged(ref _pointerOverElement, value); } } - public string FocusedControl => _focusedControl.Value; - - public string PointerOverElement => _pointerOverElement.Value; - public void SelectControl(IControl control) { var tree = Content as TreePageViewModel; @@ -76,5 +83,10 @@ namespace Avalonia.Diagnostics.ViewModels tree.SelectControl(control); } } + + private void UpdateFocusedControl() + { + _focusedControl = KeyboardDevice.Instance.FocusedElement?.GetType().Name; + } } } diff --git a/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs index dcb4e79402..e8a8951f0d 100644 --- a/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs +++ b/src/Avalonia.Diagnostics/ViewModels/LogicalTreeNode.cs @@ -2,9 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using Avalonia.Collections; using Avalonia.Controls; using Avalonia.LogicalTree; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { @@ -13,7 +13,7 @@ namespace Avalonia.Diagnostics.ViewModels public LogicalTreeNode(ILogical logical, TreeNode parent) : base((Control)logical, parent) { - Children = logical.LogicalChildren.CreateDerivedCollection(x => new LogicalTreeNode(x, this)); + Children = logical.LogicalChildren.CreateDerivedList(x => new LogicalTreeNode(x, this)); } public static LogicalTreeNode[] Create(object control) diff --git a/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs b/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs index c06f2415cf..2609b74ce0 100644 --- a/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs +++ b/src/Avalonia.Diagnostics/ViewModels/PropertyDetails.cs @@ -3,16 +3,13 @@ using System; using Avalonia.Data; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { - internal class PropertyDetails : ReactiveObject + internal class PropertyDetails : ViewModelBase { private object _value; - private string _priority; - private string _diagnostic; public PropertyDetails(AvaloniaObject o, AvaloniaProperty property) @@ -41,19 +38,19 @@ namespace Avalonia.Diagnostics.ViewModels public string Priority { get { return _priority; } - private set { this.RaiseAndSetIfChanged(ref _priority, value); } + private set { RaiseAndSetIfChanged(ref _priority, value); } } public string Diagnostic { get { return _diagnostic; } - private set { this.RaiseAndSetIfChanged(ref _diagnostic, value); } + private set { RaiseAndSetIfChanged(ref _diagnostic, value); } } public object Value { get { return _value; } - private set { this.RaiseAndSetIfChanged(ref _value, value); } + private set { RaiseAndSetIfChanged(ref _value, value); } } } } diff --git a/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs index f186304f7b..7c403e1b04 100644 --- a/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs +++ b/src/Avalonia.Diagnostics/ViewModels/TreeNode.cs @@ -5,46 +5,49 @@ using System; using System.Collections.Specialized; using System.Reactive; using System.Reactive.Linq; -using Avalonia.Controls; +using Avalonia.Collections; using Avalonia.Styling; -using ReactiveUI; +using Avalonia.VisualTree; namespace Avalonia.Diagnostics.ViewModels { - internal class TreeNode : ReactiveObject + internal class TreeNode : ViewModelBase { private string _classes; private bool _isExpanded; - public TreeNode(Control control, TreeNode parent) + public TreeNode(IVisual visual, TreeNode parent) { - Control = control; Parent = parent; - Type = control.GetType().Name; + Type = visual.GetType().Name; + Visual = visual; - var classesChanged = Observable.FromEventPattern< - NotifyCollectionChangedEventHandler, - NotifyCollectionChangedEventArgs>( - x => control.Classes.CollectionChanged += x, - x => control.Classes.CollectionChanged -= x) - .TakeUntil(((IStyleable)control).StyleDetach); + if (visual is IStyleable styleable) + { + var classesChanged = Observable.FromEventPattern< + NotifyCollectionChangedEventHandler, + NotifyCollectionChangedEventArgs>( + x => styleable.Classes.CollectionChanged += x, + x => styleable.Classes.CollectionChanged -= x) + .TakeUntil(((IStyleable)styleable).StyleDetach); - classesChanged.Select(_ => Unit.Default) - .StartWith(Unit.Default) - .Subscribe(_ => - { - if (control.Classes.Count > 0) + classesChanged.Select(_ => Unit.Default) + .StartWith(Unit.Default) + .Subscribe(_ => { - Classes = "(" + string.Join(" ", control.Classes) + ")"; - } - else - { - Classes = string.Empty; - } - }); + if (styleable.Classes.Count > 0) + { + Classes = "(" + string.Join(" ", styleable.Classes) + ")"; + } + else + { + Classes = string.Empty; + } + }); + } } - public IReadOnlyReactiveList Children + public IAvaloniaReadOnlyList Children { get; protected set; @@ -53,10 +56,10 @@ namespace Avalonia.Diagnostics.ViewModels public string Classes { get { return _classes; } - private set { this.RaiseAndSetIfChanged(ref _classes, value); } + private set { RaiseAndSetIfChanged(ref _classes, value); } } - public Control Control + public IVisual Visual { get; } @@ -64,7 +67,7 @@ namespace Avalonia.Diagnostics.ViewModels public bool IsExpanded { get { return _isExpanded; } - set { this.RaiseAndSetIfChanged(ref _isExpanded, value); } + set { RaiseAndSetIfChanged(ref _isExpanded, value); } } public TreeNode Parent diff --git a/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs b/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs index 92be04a056..dba44c5d0c 100644 --- a/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs +++ b/src/Avalonia.Diagnostics/ViewModels/TreePageViewModel.cs @@ -1,25 +1,19 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.VisualTree; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { - internal class TreePageViewModel : ReactiveObject + internal class TreePageViewModel : ViewModelBase { private TreeNode _selected; - - private readonly ObservableAsPropertyHelper _details; + private ControlDetailsViewModel _details; public TreePageViewModel(TreeNode[] nodes) { Nodes = nodes; - _details = this.WhenAnyValue(x => x.SelectedNode) - .Select(x => x != null ? new ControlDetailsViewModel(x.Control) : null) - .ToProperty(this, x => x.Details); } public TreeNode[] Nodes { get; protected set; } @@ -27,10 +21,20 @@ namespace Avalonia.Diagnostics.ViewModels public TreeNode SelectedNode { get { return _selected; } - set { this.RaiseAndSetIfChanged(ref _selected, value); } + set + { + if (RaiseAndSetIfChanged(ref _selected, value)) + { + Details = value != null ? new ControlDetailsViewModel(value.Visual) : null; + } + } } - public ControlDetailsViewModel Details => _details.Value; + public ControlDetailsViewModel Details + { + get { return _details; } + private set { RaiseAndSetIfChanged(ref _details, value); } + } public TreeNode FindNode(IControl control) { @@ -79,7 +83,7 @@ namespace Avalonia.Diagnostics.ViewModels private TreeNode FindNode(TreeNode node, IControl control) { - if (node.Control == control) + if (node.Visual == control) { return node; } diff --git a/src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs b/src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000000..349404603a --- /dev/null +++ b/src/Avalonia.Diagnostics/ViewModels/ViewModelBase.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Avalonia.Diagnostics.ViewModels +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected bool RaiseAndSetIfChanged(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (!EqualityComparer.Default.Equals(field, value)) + { + field = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + return true; + } + + return false; + } + + [NotifyPropertyChangedInvocator] + protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs b/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs index d155572c14..8c070261d9 100644 --- a/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs +++ b/src/Avalonia.Diagnostics/ViewModels/VisualTreeNode.cs @@ -1,31 +1,31 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. -using Avalonia.Controls; +using Avalonia.Collections; +using Avalonia.Styling; using Avalonia.VisualTree; -using ReactiveUI; namespace Avalonia.Diagnostics.ViewModels { internal class VisualTreeNode : TreeNode { public VisualTreeNode(IVisual visual, TreeNode parent) - : base((Control)visual, parent) + : base(visual, parent) { var host = visual as IVisualTreeHost; if (host?.Root == null) { - Children = visual.VisualChildren.CreateDerivedCollection(x => new VisualTreeNode(x, this)); + Children = visual.VisualChildren.CreateDerivedList(x => new VisualTreeNode(x, this)); } else { - Children = new ReactiveList(new[] { new VisualTreeNode(host.Root, this) }); + Children = new AvaloniaList(new[] { new VisualTreeNode(host.Root, this) }); } - if (Control != null) + if ((Visual is IStyleable styleable)) { - IsInTemplate = Control.TemplatedParent != null; + IsInTemplate = styleable.TemplatedParent != null; } } diff --git a/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs b/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs index 7cb74ebb33..d7bd6fd128 100644 --- a/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs +++ b/src/Avalonia.Diagnostics/Views/ControlDetailsView.cs @@ -8,7 +8,6 @@ using Avalonia.Controls; using Avalonia.Diagnostics.ViewModels; using Avalonia.Media; using Avalonia.Styling; -using ReactiveUI; namespace Avalonia.Diagnostics.Views { @@ -16,6 +15,7 @@ namespace Avalonia.Diagnostics.Views { private static readonly StyledProperty ViewModelProperty = AvaloniaProperty.Register("ViewModel"); + private SimpleGrid _grid; public ControlDetailsView() { @@ -27,7 +27,11 @@ namespace Avalonia.Diagnostics.Views public ControlDetailsViewModel ViewModel { get { return GetValue(ViewModelProperty); } - private set { SetValue(ViewModelProperty, value); } + private set + { + SetValue(ViewModelProperty, value); + _grid[GridRepeater.ItemsProperty] = value?.Properties; + } } private void InitializeComponent() @@ -36,7 +40,7 @@ namespace Avalonia.Diagnostics.Views Content = new ScrollViewer { - Content = new SimpleGrid + Content = _grid = new SimpleGrid { Styles = new Styles { @@ -49,7 +53,6 @@ namespace Avalonia.Diagnostics.Views }, }, [GridRepeater.TemplateProperty] = pt, - [!GridRepeater.ItemsProperty] = this.WhenAnyValue(x => x.ViewModel.Properties).ToBinding(), } }; } @@ -62,16 +65,13 @@ namespace Avalonia.Diagnostics.Views { Text = property.Name, TextWrapping = TextWrapping.NoWrap, - [!ToolTip.TipProperty] = property - .WhenAnyValue(x => x.Diagnostic) - .ToBinding(), + [!ToolTip.TipProperty] = property.GetObservable(nameof(property.Diagnostic)).ToBinding(), }; yield return new TextBlock { TextWrapping = TextWrapping.NoWrap, - [!TextBlock.TextProperty] = property - .WhenAnyValue(v => v.Value) + [!TextBlock.TextProperty] = property.GetObservable(nameof(property.Value)) .Select(v => v?.ToString()) .ToBinding(), }; @@ -79,7 +79,7 @@ namespace Avalonia.Diagnostics.Views yield return new TextBlock { TextWrapping = TextWrapping.NoWrap, - [!TextBlock.TextProperty] = property.WhenAnyValue(x => x.Priority).ToBinding(), + [!TextBlock.TextProperty] = property.GetObservable((nameof(property.Priority))).ToBinding(), }; } } diff --git a/src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs b/src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs new file mode 100644 index 0000000000..1d4ad24fd0 --- /dev/null +++ b/src/Avalonia.Diagnostics/Views/PropertyChangedExtenions.cs @@ -0,0 +1,30 @@ +using System; +using System.ComponentModel; +using System.Reactive.Linq; +using System.Reflection; + +namespace Avalonia.Diagnostics.Views +{ + internal static class PropertyChangedExtenions + { + public static IObservable GetObservable(this INotifyPropertyChanged source, string propertyName) + { + Contract.Requires(source != null); + Contract.Requires(propertyName != null); + + var property = source.GetType().GetTypeInfo().GetDeclaredProperty(propertyName); + + if (property == null) + { + throw new ArgumentException($"Property '{propertyName}' not found on '{source}."); + } + + return Observable.FromEventPattern( + e => source.PropertyChanged += e, + e => source.PropertyChanged -= e) + .Where(e => e.EventArgs.PropertyName == propertyName) + .Select(_ => (T)property.GetValue(source)) + .StartWith((T)property.GetValue(source)); + } + } +} diff --git a/src/Avalonia.Diagnostics/Views/TreePage.xaml.cs b/src/Avalonia.Diagnostics/Views/TreePage.xaml.cs index 0a79aa90d7..7e4e5a8564 100644 --- a/src/Avalonia.Diagnostics/Views/TreePage.xaml.cs +++ b/src/Avalonia.Diagnostics/Views/TreePage.xaml.cs @@ -23,14 +23,14 @@ namespace Avalonia.Diagnostics.Views protected void AddAdorner(object sender, PointerEventArgs e) { var node = (TreeNode)((Control)sender).DataContext; - var layer = AdornerLayer.GetAdornerLayer(node.Control); + var layer = AdornerLayer.GetAdornerLayer(node.Visual); if (layer != null) { _adorner = new Rectangle { Fill = new SolidColorBrush(0x80a0c5e8), - [AdornerLayer.AdornedElementProperty] = node.Control, + [AdornerLayer.AdornedElementProperty] = node.Visual, }; layer.Children.Add(_adorner); diff --git a/src/Avalonia.Diagnostics/packages.config b/src/Avalonia.Diagnostics/packages.config deleted file mode 100644 index df107dc625..0000000000 --- a/src/Avalonia.Diagnostics/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj index 8a60d5d9c7..9d5436b2b9 100644 --- a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj +++ b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj @@ -1,21 +1,7 @@ - - - + - 14.0 - Debug - AnyCPU - {7863EA94-F0FB-4386-BF8C-E5BFA761560A} - Library - Properties - Avalonia.DotNetCoreRuntime - Avalonia.DotNetCoreRuntime - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - - - v5.0 + netcoreapp1.0 + false true @@ -36,54 +22,19 @@ 4 bin\Release\Avalonia.DotNetCoreRuntime.XML - - - - - SharedAssemblyInfo.cs + Properties\SharedAssemblyInfo.cs - - - - - - - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} - Avalonia.Base - - - {d2221c82-4a25-4583-9b43-d791e3f6820c} - Avalonia.Controls - - - {eb582467-6abb-43a1-b052-e981ba910e3a} - Avalonia.Visuals - - - {bb1f7bb5-6ad4-4776-94d9-c09d0a972658} - Avalonia.Gtk3 - - - {7d2d3083-71dd-4cc9-8907-39a0d86fb322} - Avalonia.Skia.Desktop.NetStandard - - - {40759a76-d0f2-464e-8000-6ff0f5c4bd7c} - Avalonia.Win32.NetStandard - - + + + + + + + - - \ No newline at end of file diff --git a/src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs b/src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs index b131488109..6aab8e0243 100644 --- a/src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs +++ b/src/Avalonia.DotNetCoreRuntime/NetCoreRuntimePlatform.cs @@ -5,6 +5,8 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; +using Microsoft.DotNet.PlatformAbstractions; +using Microsoft.Extensions.DependencyModel; namespace Avalonia.Shared.PlatformSupport { @@ -15,33 +17,26 @@ namespace Avalonia.Shared.PlatformSupport static Assembly[] LoadAssemblies() { + var assemblies = new List(); + // Mostly copy-pasted from (MIT): + // https://github.com/StefH/System.AppDomain.Core/blob/0b35e676c2721aa367b96e62eb52c97ee0b43a70/src/System.AppDomain.NetCoreApp/AppDomain.cs - var rv = new List(); - var entry = Assembly.GetEntryAssembly(); - rv.Add(entry); - var queue = new Queue(entry.GetReferencedAssemblies()); - var aset = new HashSet(queue.Select(r => r.ToString())); - - while (queue.Count > 0) + foreach (var assemblyName in + DependencyContext.Default.GetRuntimeAssemblyNames(RuntimeEnvironment.GetRuntimeIdentifier())) { - Assembly asm; try { - asm = Assembly.Load(queue.Dequeue()); - } - catch (Exception e) - { - Debug.Write(e.ToString()); - continue; + var assembly = Assembly.Load(assemblyName); + // just load all types and skip this assembly if one or more types cannot be resolved + assembly.DefinedTypes.ToArray(); + assemblies.Add(assembly); } - rv.Add(asm); - foreach (var r in asm.GetReferencedAssemblies()) + catch (Exception ex) { - if (aset.Add(r.ToString())) - queue.Enqueue(r); + Debug.Write(ex.Message); } } - return rv.Distinct().ToArray(); + return assemblies.ToArray(); } } } diff --git a/src/Avalonia.DotNetCoreRuntime/Properties/AssemblyInfo.cs b/src/Avalonia.DotNetCoreRuntime/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..51ec3975c9 --- /dev/null +++ b/src/Avalonia.DotNetCoreRuntime/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System.Reflection; + +[assembly: AssemblyTitle("Avalonia.DotNetCoreRuntime")] diff --git a/src/Avalonia.DotNetCoreRuntime/project.json b/src/Avalonia.DotNetCoreRuntime/project.json deleted file mode 100644 index d6ae90b33b..0000000000 --- a/src/Avalonia.DotNetCoreRuntime/project.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "supports": {}, - "dependencies": { - "Microsoft.NETCore.Portable.Compatibility": "1.0.1", - "NETStandard.Library": "1.6.0", - "System.Threading.ThreadPool": "4.3.0" - }, - "frameworks": { - "netstandard1.5": {} - } -} \ No newline at end of file diff --git a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs index fc7fdd2431..9a54cdbab0 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs +++ b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs @@ -82,7 +82,13 @@ namespace Avalonia private void LoadAssembliesInDirectory() { - foreach (var file in new FileInfo(Assembly.GetEntryAssembly().Location).Directory.EnumerateFiles("*.dll")) + var location = Assembly.GetEntryAssembly().Location; + if (string.IsNullOrWhiteSpace(location)) + return; + var dir = new FileInfo(location).Directory; + if (dir == null) + return; + foreach (var file in dir.EnumerateFiles("*.dll")) { try { diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj index e87ec0b5a7..2fbcba40c8 100644 --- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj +++ b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,7 +9,7 @@ Properties Avalonia.DotNetFrameworkRuntime Avalonia.DotNetFrameworkRuntime - v4.5 + v4.6.1 512 @@ -36,14 +36,6 @@ - - ..\..\packages\System.Reactive.Core.3.0.0\lib\net45\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\net45\System.Reactive.Interfaces.dll - True - @@ -74,16 +66,7 @@ Avalonia.Styling - - - - + \ No newline at end of file diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.v2.ncrunchproject b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.DotNetFrameworkRuntime/packages.config b/src/Avalonia.DotNetFrameworkRuntime/packages.config deleted file mode 100644 index 1e93a8c9e7..0000000000 --- a/src/Avalonia.DotNetFrameworkRuntime/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs b/src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs index 12c040fab3..aab65fcbc7 100644 --- a/src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs +++ b/src/Avalonia.HtmlRenderer/Adapters/AvaloniaAdapter.cs @@ -80,7 +80,7 @@ namespace TheArtOfDev.HtmlRenderer.Avalonia.Adapters { StartPoint = new RelativePoint(x, y, RelativeUnit.Relative), EndPoint = new RelativePoint(1 - x, 1 - y, RelativeUnit.Relative), - GradientStops = + GradientStops = new[] { new GradientStop(startColor, 0), new GradientStop(endColor, 1) diff --git a/src/Avalonia.HtmlRenderer/Adapters/ControlAdapter.cs b/src/Avalonia.HtmlRenderer/Adapters/ControlAdapter.cs index 523da3508e..9a55768f6a 100644 --- a/src/Avalonia.HtmlRenderer/Adapters/ControlAdapter.cs +++ b/src/Avalonia.HtmlRenderer/Adapters/ControlAdapter.cs @@ -10,9 +10,11 @@ // - Sun Tsu, // "The Art of War" +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Html; using Avalonia.Input; +using Avalonia.VisualTree; using TheArtOfDev.HtmlRenderer.Adapters; using TheArtOfDev.HtmlRenderer.Adapters.Entities; using TheArtOfDev.HtmlRenderer.Core.Utils; @@ -54,7 +56,8 @@ namespace TheArtOfDev.HtmlRenderer.Avalonia.Adapters { get { - return Util.Convert(MouseDevice.Instance.GetPosition(_control)); + var pos = (_control.GetVisualRoot() as IInputRoot)?.MouseDevice?.Position ?? default(Point); + return Util.Convert(pos); } } diff --git a/src/Avalonia.HtmlRenderer/Adapters/GraphicsAdapter.cs b/src/Avalonia.HtmlRenderer/Adapters/GraphicsAdapter.cs index c9dd2a786c..93f8790741 100644 --- a/src/Avalonia.HtmlRenderer/Adapters/GraphicsAdapter.cs +++ b/src/Avalonia.HtmlRenderer/Adapters/GraphicsAdapter.cs @@ -117,7 +117,11 @@ namespace TheArtOfDev.HtmlRenderer.Avalonia.Adapters FormattedText GetText(string str, RFont font) { var f = ((FontAdapter)font); - return new FormattedText(str, f.Name, font.Size, f.FontStyle, TextAlignment.Left, f.Weight); + return new FormattedText + { + Text = str, + Typeface = new Typeface(f.Name, font.Size, f.FontStyle, f.Weight), + }; } public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth) diff --git a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj index 8f16bad64a..f715217e42 100644 --- a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj +++ b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.csproj @@ -1,22 +1,9 @@ - - - + - 10.0 - Debug - AnyCPU - {5FB2B005-0A7F-4DAD-ADD4-3ED01444E63D} - Library - Properties - Avalonia.HtmlRenderer - Avalonia.HtmlRenderer - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + False + False + false true @@ -36,7 +23,6 @@ 4 - @@ -51,6 +37,7 @@ + @@ -137,65 +124,14 @@ - - {D211E587-D8BC-45B9-95A4-F297C8FA5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {D2221C82-4A25-4583-9B43-D791E3F6820C} - Avalonia.Controls - - - {62024B2D-53EB-4638-B26B-85EEAA54866E} - Avalonia.Input - - - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} - Avalonia.Interactivity - - - {42472427-4774-4C81-8AFF-9F27B8E31721} - Avalonia.Layout - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - - - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} - Avalonia.Styling - + + + + + + + + - - - - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + \ No newline at end of file diff --git a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.v2.ncrunchproject b/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.HtmlRenderer/Avalonia.HtmlRenderer.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.HtmlRenderer/Compat/Api.cs b/src/Avalonia.HtmlRenderer/Compat/Api.cs new file mode 100644 index 0000000000..798b697277 --- /dev/null +++ b/src/Avalonia.HtmlRenderer/Compat/Api.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Net +{ + internal class AsyncCompletedEventArgs + { + public object UserState { get; set; } + public Exception Error { get; set; } + public bool Cancelled { get; set; } + + public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState) + { + + } + } + + class WebException : Exception + { + public object Response { get; set; } + } + + class HttpWebResponse + { + public HttpStatusCode StatusCode { get; set; } + } +} diff --git a/src/Avalonia.HtmlRenderer/HtmlControl.cs b/src/Avalonia.HtmlRenderer/HtmlControl.cs index ca8dcec2e1..0051f6427b 100644 --- a/src/Avalonia.HtmlRenderer/HtmlControl.cs +++ b/src/Avalonia.HtmlRenderer/HtmlControl.cs @@ -17,6 +17,7 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Media; using Avalonia.Threading; +using Avalonia.VisualTree; using TheArtOfDev.HtmlRenderer.Core; using TheArtOfDev.HtmlRenderer.Core.Entities; using TheArtOfDev.HtmlRenderer.Avalonia; @@ -413,7 +414,7 @@ namespace Avalonia.Controls.Html /// /// Handle mouse up to handle selection and link click. /// - protected override void OnPointerReleased(PointerEventArgs e) + protected override void OnPointerReleased(PointerReleasedEventArgs e) { base.OnPointerReleased(e); LeftMouseButton = false; @@ -512,7 +513,7 @@ namespace Avalonia.Controls.Html protected virtual void InvokeMouseMove() { - _htmlContainer.HandleMouseMove(this, MouseDevice.Instance?.GetPosition(this) ?? default(Point)); + _htmlContainer.HandleMouseMove(this, (this.GetVisualRoot() as IInputRoot)?.MouseDevice?.GetPosition(this) ?? default(Point)); } /// diff --git a/src/Avalonia.HtmlRenderer/packages.config b/src/Avalonia.HtmlRenderer/packages.config deleted file mode 100644 index b6a9c7823c..0000000000 --- a/src/Avalonia.HtmlRenderer/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Input/Avalonia.Input.csproj b/src/Avalonia.Input/Avalonia.Input.csproj index c1ed34c1c7..b5482ebce1 100644 --- a/src/Avalonia.Input/Avalonia.Input.csproj +++ b/src/Avalonia.Input/Avalonia.Input.csproj @@ -1,22 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {62024B2D-53EB-4638-B26B-85EEAA54866E} - Library - Properties - Avalonia.Input - Avalonia.Input - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + false true @@ -41,109 +26,16 @@ true - - - {D211E587-D8BC-45B9-95A4-F297C8FA5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} - Avalonia.Interactivity - - - {42472427-4774-4C81-8AFF-9F27B8E31721} - Avalonia.Layout - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - + + + + + Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - - - - + \ No newline at end of file diff --git a/src/Avalonia.Input/Avalonia.Input.v2.ncrunchproject b/src/Avalonia.Input/Avalonia.Input.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Input/Avalonia.Input.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Input/FocusManager.cs index e5cc5a8557..102da6efc4 100644 --- a/src/Avalonia.Input/FocusManager.cs +++ b/src/Avalonia.Input/FocusManager.cs @@ -176,9 +176,10 @@ namespace Avalonia.Input /// The event args. private void OnPreviewPointerPressed(object sender, RoutedEventArgs e) { - if (sender == e.Source) + var ev = (PointerPressedEventArgs)e; + + if (sender == e.Source && ev.MouseButton == MouseButton.Left) { - var ev = (PointerPressedEventArgs)e; var element = (ev.Device?.Captured as IInputElement) ?? (e.Source as IInputElement); if (element == null || !CanFocus(element)) diff --git a/src/Avalonia.Input/ICustomKeyboardNavigation.cs b/src/Avalonia.Input/ICustomKeyboardNavigation.cs new file mode 100644 index 0000000000..de5f98e04b --- /dev/null +++ b/src/Avalonia.Input/ICustomKeyboardNavigation.cs @@ -0,0 +1,15 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; + +namespace Avalonia.Input +{ + /// + /// Designates a control as handling its own keyboard navigation. + /// + public interface ICustomKeyboardNavigation + { + (bool handled, IInputElement next) GetNext(IInputElement element, NavigationDirection direction); + } +} diff --git a/src/Avalonia.Input/IInputDevice.cs b/src/Avalonia.Input/IInputDevice.cs index 916f29376a..72fa2ab9bf 100644 --- a/src/Avalonia.Input/IInputDevice.cs +++ b/src/Avalonia.Input/IInputDevice.cs @@ -1,9 +1,16 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Avalonia.Input.Raw; + namespace Avalonia.Input { public interface IInputDevice { + /// + /// Processes raw event. Is called after preprocessing by InputManager + /// + /// + void ProcessRawEvent(RawInputEventArgs ev); } } diff --git a/src/Avalonia.Input/IInputElement.cs b/src/Avalonia.Input/IInputElement.cs index 72f03d3ae5..5acb6aa777 100644 --- a/src/Avalonia.Input/IInputElement.cs +++ b/src/Avalonia.Input/IInputElement.cs @@ -1,7 +1,5 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. - -using System.Diagnostics.Contracts; using System; using System.Collections.Generic; using Avalonia.Interactivity; @@ -95,17 +93,17 @@ namespace Avalonia.Input bool IsEnabledCore { get; } /// - /// Gets or sets a value indicating whether the control is focused. + /// Gets a value indicating whether the control is focused. /// bool IsFocused { get; } /// - /// Gets or sets a value indicating whether the control is considered for hit testing. + /// Gets a value indicating whether the control is considered for hit testing. /// bool IsHitTestVisible { get; } /// - /// Gets or sets a value indicating whether the pointer is currently over the control. + /// Gets a value indicating whether the pointer is currently over the control. /// bool IsPointerOver { get; } diff --git a/src/Avalonia.Input/IInputRoot.cs b/src/Avalonia.Input/IInputRoot.cs index 6c01bb9c0e..6b3e1e6bc5 100644 --- a/src/Avalonia.Input/IInputRoot.cs +++ b/src/Avalonia.Input/IInputRoot.cs @@ -1,6 +1,8 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using JetBrains.Annotations; + namespace Avalonia.Input { /// @@ -27,5 +29,11 @@ namespace Avalonia.Input /// Gets or sets a value indicating whether access keys are shown in the window. /// bool ShowAccessKeys { get; set; } + + /// + /// Gets associated mouse device + /// + [CanBeNull] + IMouseDevice MouseDevice { get; } } } diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs index f27fec6907..1410476267 100644 --- a/src/Avalonia.Input/IKeyboardDevice.cs +++ b/src/Avalonia.Input/IKeyboardDevice.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.ComponentModel; namespace Avalonia.Input { @@ -26,7 +27,7 @@ namespace Avalonia.Input Toggled = 2, } - public interface IKeyboardDevice : IInputDevice + public interface IKeyboardDevice : IInputDevice, INotifyPropertyChanged { IInputElement FocusedElement { get; } diff --git a/src/Avalonia.Input/InputElement.cs b/src/Avalonia.Input/InputElement.cs index 652b58f0b5..6385f7197b 100644 --- a/src/Avalonia.Input/InputElement.cs +++ b/src/Avalonia.Input/InputElement.cs @@ -473,7 +473,7 @@ namespace Avalonia.Input /// Called before the event occurs. /// /// The event args. - protected virtual void OnPointerReleased(PointerEventArgs e) + protected virtual void OnPointerReleased(PointerReleasedEventArgs e) { } diff --git a/src/Avalonia.Input/InputManager.cs b/src/Avalonia.Input/InputManager.cs index 7aa609e65c..9e2d5ffea3 100644 --- a/src/Avalonia.Input/InputManager.cs +++ b/src/Avalonia.Input/InputManager.cs @@ -35,6 +35,7 @@ namespace Avalonia.Input public void ProcessInput(RawInputEventArgs e) { _preProcess.OnNext(e); + e.Device?.ProcessRawEvent(e); _process.OnNext(e); _postProcess.OnNext(e); } diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs index 51af01f69b..d815f8082b 100644 --- a/src/Avalonia.Input/KeyboardDevice.cs +++ b/src/Avalonia.Input/KeyboardDevice.cs @@ -16,14 +16,6 @@ namespace Avalonia.Input { private IInputElement _focusedElement; - public KeyboardDevice() - { - InputManager.Process - .OfType() - .Where(e => e.Device == this && !e.Handled) - .Subscribe(ProcessRawEvent); - } - public event PropertyChangedEventHandler PropertyChanged; public static IKeyboardDevice Instance => AvaloniaLocator.Current.GetService(); @@ -77,8 +69,10 @@ namespace Avalonia.Input PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - private void ProcessRawEvent(RawInputEventArgs e) + public void ProcessRawEvent(RawInputEventArgs e) { + if(e.Handled) + return; IInputElement element = FocusedElement; if (element != null) diff --git a/src/Avalonia.Input/KeyboardNavigationHandler.cs b/src/Avalonia.Input/KeyboardNavigationHandler.cs index 57da49fa03..bf2b61d08b 100644 --- a/src/Avalonia.Input/KeyboardNavigationHandler.cs +++ b/src/Avalonia.Input/KeyboardNavigationHandler.cs @@ -2,7 +2,9 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Linq; using Avalonia.Input.Navigation; +using Avalonia.VisualTree; namespace Avalonia.Input { @@ -52,6 +54,31 @@ namespace Avalonia.Input { Contract.Requires(element != null); + var customHandler = element.GetSelfAndVisualAncestors() + .OfType() + .FirstOrDefault(); + + if (customHandler != null) + { + var (handled, next) = customHandler.GetNext(element, direction); + + if (handled) + { + if (next != null) + { + return next; + } + else if (direction == NavigationDirection.Next || direction == NavigationDirection.Previous) + { + return TabNavigation.GetNextInTabOrder((IInputElement)customHandler, direction, true); + } + else + { + return null; + } + } + } + if (direction == NavigationDirection.Next || direction == NavigationDirection.Previous) { return TabNavigation.GetNextInTabOrder(element, direction); diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index 2c0cb8656c..875a5ebaee 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -20,24 +20,7 @@ namespace Avalonia.Input private int _clickCount; private Rect _lastClickRect; private uint _lastClickTime; - private readonly List _pointerOvers = new List(); - - /// - /// Intializes a new instance of . - /// - public MouseDevice() - { - InputManager.Process - .OfType() - .Where(e => e.Device == this && !e.Handled) - .Subscribe(ProcessRawEvent); - } - - /// - /// Gets the current mouse device instance. - /// - public static IMouseDevice Instance => AvaloniaLocator.Current.GetService(); - + /// /// Gets the control that is currently capturing by the mouse, if any. /// @@ -51,12 +34,7 @@ namespace Avalonia.Input get; protected set; } - - /// - /// Gets the application's input manager. - /// - public IInputManager InputManager => AvaloniaLocator.Current.GetService(); - + /// /// Gets the mouse position, in screen coordinates. /// @@ -87,6 +65,8 @@ namespace Avalonia.Input /// The mouse position in the control's coordinates. public Point GetPosition(IVisual relativeTo) { + Contract.Requires(relativeTo != null); + Point p = default(Point); IVisual v = relativeTo; IVisual root = null; @@ -101,8 +81,16 @@ namespace Avalonia.Input return root.PointToClient(Position) - p; } + public void ProcessRawEvent(RawInputEventArgs e) + { + if (!e.Handled && e is RawMouseEventArgs margs) + ProcessRawEvent(margs); + } + private void ProcessRawEvent(RawMouseEventArgs e) { + Contract.Requires(e != null); + var mouse = (IMouseDevice)e.Device; Position = e.Root.PointToScreen(e.Position); @@ -141,11 +129,17 @@ namespace Avalonia.Input private void LeaveWindow(IMouseDevice device, IInputRoot root) { + Contract.Requires(device != null); + Contract.Requires(root != null); + ClearPointerOver(this, root); } private bool MouseDown(IMouseDevice device, uint timestamp, IInputElement root, Point p, MouseButton button, InputModifiers inputModifiers) { + Contract.Requires(device != null); + Contract.Requires(root != null); + var hit = HitTest(root, p); if (hit != null) @@ -187,6 +181,9 @@ namespace Avalonia.Input private bool MouseMove(IMouseDevice device, IInputRoot root, Point p, InputModifiers inputModifiers) { + Contract.Requires(device != null); + Contract.Requires(root != null); + IInputElement source; if (Captured == null) @@ -195,8 +192,7 @@ namespace Avalonia.Input } else { - var elements = Captured.GetSelfAndVisualAncestors().OfType().ToList(); - SetPointerOver(this, root, elements); + SetPointerOver(this, root, Captured); source = Captured; } @@ -208,12 +204,15 @@ namespace Avalonia.Input InputModifiers = inputModifiers }; - source.RaiseEvent(e); + source?.RaiseEvent(e); return e.Handled; } private bool MouseUp(IMouseDevice device, IInputRoot root, Point p, MouseButton button, InputModifiers inputModifiers) { + Contract.Requires(device != null); + Contract.Requires(root != null); + var hit = HitTest(root, p); if (hit != null) @@ -237,6 +236,9 @@ namespace Avalonia.Input private bool MouseWheel(IMouseDevice device, IInputRoot root, Point p, Vector delta, InputModifiers inputModifiers) { + Contract.Requires(device != null); + Contract.Requires(root != null); + var hit = HitTest(root, p); if (hit != null) @@ -260,6 +262,8 @@ namespace Avalonia.Input private IInteractive GetSource(IVisual hit) { + Contract.Requires(hit != null); + return Captured ?? (hit as IInteractive) ?? hit.GetSelfAndVisualAncestors().OfType().FirstOrDefault(); @@ -267,22 +271,28 @@ namespace Avalonia.Input private IInputElement HitTest(IInputElement root, Point p) { + Contract.Requires(root != null); + return Captured ?? root.InputHitTest(p); } private void ClearPointerOver(IPointerDevice device, IInputRoot root) { - foreach (var control in _pointerOvers.ToList()) + Contract.Requires(device != null); + Contract.Requires(root != null); + + var element = root.PointerOverElement; + var e = new PointerEventArgs { - PointerEventArgs e = new PointerEventArgs - { - RoutedEvent = InputElement.PointerLeaveEvent, - Device = device, - Source = control, - }; + RoutedEvent = InputElement.PointerLeaveEvent, + Device = device, + }; - _pointerOvers.Remove(control); - control.RaiseEvent(e); + while (element != null) + { + e.Source = element; + element.RaiseEvent(e); + element = (IInputElement)element.VisualParent; } root.PointerOverElement = null; @@ -290,40 +300,66 @@ namespace Avalonia.Input private IInputElement SetPointerOver(IPointerDevice device, IInputRoot root, Point p) { - var elements = root.GetInputElementsAt(p).ToList(); - return SetPointerOver(device, root, elements); + Contract.Requires(device != null); + Contract.Requires(root != null); + + var element = root.InputHitTest(p); + + if (element != root.PointerOverElement) + { + if (element != null) + { + SetPointerOver(device, root, element); + } + else + { + ClearPointerOver(device, root); + } + } + + return element; } - private IInputElement SetPointerOver(IPointerDevice device, IInputRoot root, IList elements) + private void SetPointerOver(IPointerDevice device, IInputRoot root, IInputElement element) { - foreach (var control in _pointerOvers.Except(elements).ToList()) + Contract.Requires(device != null); + Contract.Requires(root != null); + Contract.Requires(element != null); + + IInputElement branch = null; + + var e = new PointerEventArgs { - PointerEventArgs e = new PointerEventArgs - { - RoutedEvent = InputElement.PointerLeaveEvent, - Device = device, - Source = control, - }; + RoutedEvent = InputElement.PointerEnterEvent, + Device = device, + }; - _pointerOvers.Remove(control); - control.RaiseEvent(e); - } + var el = element; - foreach (var control in elements.Except(_pointerOvers)) + while (el != null) { - PointerEventArgs e = new PointerEventArgs + if (el.IsPointerOver) { - RoutedEvent = InputElement.PointerEnterEvent, - Device = device, - Source = control, - }; + branch = el; + break; + } + + e.Source = el; + el.RaiseEvent(e); + el = (IInputElement)el.VisualParent; + } + + el = root.PointerOverElement; + e.RoutedEvent = InputElement.PointerLeaveEvent; - _pointerOvers.Add(control); - control.RaiseEvent(e); + while (el != null && el != branch) + { + e.Source = el; + el.RaiseEvent(e); + el = (IInputElement)el.VisualParent; } - root.PointerOverElement = elements.FirstOrDefault() ?? root; - return root.PointerOverElement; + root.PointerOverElement = element; } } -} +} \ No newline at end of file diff --git a/src/Avalonia.Input/Navigation/DirectionalNavigation.cs b/src/Avalonia.Input/Navigation/DirectionalNavigation.cs index b2f7a35799..75cb3a39e8 100644 --- a/src/Avalonia.Input/Navigation/DirectionalNavigation.cs +++ b/src/Avalonia.Input/Navigation/DirectionalNavigation.cs @@ -41,10 +41,10 @@ namespace Avalonia.Input.Navigation { case KeyboardNavigationMode.Continue: return GetNextInContainer(element, container, direction) ?? - GetFirstInNextContainer(element, direction); + GetFirstInNextContainer(element, element, direction); case KeyboardNavigationMode.Cycle: return GetNextInContainer(element, container, direction) ?? - GetFocusableDescendent(container, direction); + GetFocusableDescendant(container, direction); case KeyboardNavigationMode.Contained: return GetNextInContainer(element, container, direction); default: @@ -53,7 +53,7 @@ namespace Avalonia.Input.Navigation } else { - return GetFocusableDescendents(element).FirstOrDefault(); + return GetFocusableDescendants(element).FirstOrDefault(); } } @@ -71,24 +71,24 @@ namespace Avalonia.Input.Navigation } /// - /// Gets the first or last focusable descendent of the specified element. + /// Gets the first or last focusable descendant of the specified element. /// /// The element. /// The direction to search. /// The element or null if not found.## - private static IInputElement GetFocusableDescendent(IInputElement container, NavigationDirection direction) + private static IInputElement GetFocusableDescendant(IInputElement container, NavigationDirection direction) { return IsForward(direction) ? - GetFocusableDescendents(container).FirstOrDefault() : - GetFocusableDescendents(container).LastOrDefault(); + GetFocusableDescendants(container).FirstOrDefault() : + GetFocusableDescendants(container).LastOrDefault(); } /// - /// Gets the focusable descendents of the specified element. + /// Gets the focusable descendants of the specified element. /// /// The element. - /// The element's focusable descendents. - private static IEnumerable GetFocusableDescendents(IInputElement element) + /// The element's focusable descendants. + private static IEnumerable GetFocusableDescendants(IInputElement element) { var children = element.GetVisualChildren().OfType(); @@ -99,11 +99,11 @@ namespace Avalonia.Input.Navigation yield return child; } - if (child.CanFocusDescendents()) + if (child.CanFocusDescendants()) { - foreach (var descendent in GetFocusableDescendents(child)) + foreach (var descendant in GetFocusableDescendants(child)) { - yield return descendent; + yield return descendant; } } } @@ -123,11 +123,11 @@ namespace Avalonia.Input.Navigation { if (direction == NavigationDirection.Down) { - var descendent = GetFocusableDescendents(element).FirstOrDefault(); + var descendant = GetFocusableDescendants(element).FirstOrDefault(); - if (descendent != null) + if (descendant != null) { - return descendent; + return descendant; } } @@ -156,11 +156,11 @@ namespace Avalonia.Input.Navigation if (element != null && direction == NavigationDirection.Up) { - var descendent = GetFocusableDescendents(element).LastOrDefault(); + var descendant = GetFocusableDescendants(element).LastOrDefault(); - if (descendent != null) + if (descendant != null) { - return descendent; + return descendant; } } @@ -173,10 +173,12 @@ namespace Avalonia.Input.Navigation /// /// Gets the first item that should be focused in the next container. /// + /// The element being navigated away from. /// The container. /// The direction of the search. /// The first element, or null if there are no more elements. private static IInputElement GetFirstInNextContainer( + IInputElement element, IInputElement container, NavigationDirection direction) { @@ -193,13 +195,23 @@ namespace Avalonia.Input.Navigation var siblings = parent.GetVisualChildren() .OfType() - .Where(FocusExtensions.CanFocusDescendents); + .Where(FocusExtensions.CanFocusDescendants); var sibling = isForward ? siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault() : siblings.TakeWhile(x => x != container).LastOrDefault(); if (sibling != null) { + if (sibling is ICustomKeyboardNavigation custom) + { + var (handled, customNext) = custom.GetNext(element, direction); + + if (handled) + { + return customNext; + } + } + if (sibling.CanFocus()) { next = sibling; @@ -207,21 +219,21 @@ namespace Avalonia.Input.Navigation else { next = isForward ? - GetFocusableDescendents(sibling).FirstOrDefault() : - GetFocusableDescendents(sibling).LastOrDefault(); + GetFocusableDescendants(sibling).FirstOrDefault() : + GetFocusableDescendants(sibling).LastOrDefault(); } } if (next == null) { - next = GetFirstInNextContainer(parent, direction); + next = GetFirstInNextContainer(element, parent, direction); } } else { next = isForward ? - GetFocusableDescendents(container).FirstOrDefault() : - GetFocusableDescendents(container).LastOrDefault(); + GetFocusableDescendants(container).FirstOrDefault() : + GetFocusableDescendants(container).LastOrDefault(); } return next; diff --git a/src/Avalonia.Input/Navigation/FocusExtensions.cs b/src/Avalonia.Input/Navigation/FocusExtensions.cs index 36fda1abb1..41e7c4cd7b 100644 --- a/src/Avalonia.Input/Navigation/FocusExtensions.cs +++ b/src/Avalonia.Input/Navigation/FocusExtensions.cs @@ -16,10 +16,10 @@ namespace Avalonia.Input.Navigation public static bool CanFocus(this IInputElement e) => e.Focusable && e.IsEnabledCore && e.IsVisible; /// - /// Checks if descendents of the specified element can be focused. + /// Checks if descendants of the specified element can be focused. /// /// The element. - /// True if descendents of the element can be focused. - public static bool CanFocusDescendents(this IInputElement e) => e.IsEnabledCore && e.IsVisible; + /// True if descendants of the element can be focused. + public static bool CanFocusDescendants(this IInputElement e) => e.IsEnabledCore && e.IsVisible; } } diff --git a/src/Avalonia.Input/Navigation/TabNavigation.cs b/src/Avalonia.Input/Navigation/TabNavigation.cs index bc3826d90e..6e077e887f 100644 --- a/src/Avalonia.Input/Navigation/TabNavigation.cs +++ b/src/Avalonia.Input/Navigation/TabNavigation.cs @@ -18,13 +18,17 @@ namespace Avalonia.Input.Navigation /// /// The element. /// The tab direction. Must be Next or Previous. + /// + /// If true will not descend into to find next control. + /// /// /// The next element in the specified direction, or null if /// was the last in the requested direction. /// public static IInputElement GetNextInTabOrder( IInputElement element, - NavigationDirection direction) + NavigationDirection direction, + bool outsideElement = false) { Contract.Requires(element != null); Contract.Requires( @@ -40,42 +44,43 @@ namespace Avalonia.Input.Navigation switch (mode) { case KeyboardNavigationMode.Continue: - return GetNextInContainer(element, container, direction) ?? - GetFirstInNextContainer(element, direction); + return GetNextInContainer(element, container, direction, outsideElement) ?? + GetFirstInNextContainer(element, element, direction); case KeyboardNavigationMode.Cycle: - return GetNextInContainer(element, container, direction) ?? - GetFocusableDescendent(container, direction); + return GetNextInContainer(element, container, direction, outsideElement) ?? + GetFocusableDescendant(container, direction); case KeyboardNavigationMode.Contained: - return GetNextInContainer(element, container, direction); + return GetNextInContainer(element, container, direction, outsideElement); default: - return GetFirstInNextContainer(container, direction); + return GetFirstInNextContainer(element, container, direction); } } else { - return GetFocusableDescendents(element).FirstOrDefault(); + return GetFocusableDescendants(element, direction).FirstOrDefault(); } } /// - /// Gets the first or last focusable descendent of the specified element. + /// Gets the first or last focusable descendant of the specified element. /// /// The element. /// The direction to search. /// The element or null if not found.## - private static IInputElement GetFocusableDescendent(IInputElement container, NavigationDirection direction) + private static IInputElement GetFocusableDescendant(IInputElement container, NavigationDirection direction) { return direction == NavigationDirection.Next ? - GetFocusableDescendents(container).FirstOrDefault() : - GetFocusableDescendents(container).LastOrDefault(); + GetFocusableDescendants(container, direction).FirstOrDefault() : + GetFocusableDescendants(container, direction).LastOrDefault(); } /// - /// Gets the focusable descendents of the specified element. + /// Gets the focusable descendants of the specified element. /// /// The element. - /// The element's focusable descendents. - private static IEnumerable GetFocusableDescendents(IInputElement element) + /// The tab direction. Must be Next or Previous. + /// The element's focusable descendants. + private static IEnumerable GetFocusableDescendants(IInputElement element, NavigationDirection direction) { var mode = KeyboardNavigation.GetTabNavigation((InputElement)element); @@ -103,16 +108,25 @@ namespace Avalonia.Input.Navigation foreach (var child in children) { - if (child.CanFocus()) + var customNext = GetCustomNext(child, direction); + + if (customNext.handled) { - yield return child; + yield return customNext.next; } - - if (child.CanFocusDescendents()) + else { - foreach (var descendent in GetFocusableDescendents(child)) + if (child.CanFocus()) { - yield return descendent; + yield return child; + } + + if (child.CanFocusDescendants()) + { + foreach (var descendant in GetFocusableDescendants(child, direction)) + { + yield return descendant; + } } } } @@ -124,19 +138,23 @@ namespace Avalonia.Input.Navigation /// The starting element/ /// The container. /// The direction. + /// + /// If true will not descend into to find next control. + /// /// The next element, or null if the element is the last. private static IInputElement GetNextInContainer( IInputElement element, IInputElement container, - NavigationDirection direction) + NavigationDirection direction, + bool outsideElement) { - if (direction == NavigationDirection.Next) + if (direction == NavigationDirection.Next && !outsideElement) { - var descendent = GetFocusableDescendents(element).FirstOrDefault(); + var descendant = GetFocusableDescendants(element, direction).FirstOrDefault(); - if (descendent != null) + if (descendant != null) { - return descendent; + return descendant; } } @@ -167,11 +185,11 @@ namespace Avalonia.Input.Navigation if (element != null && direction == NavigationDirection.Previous) { - var descendent = GetFocusableDescendents(element).LastOrDefault(); + var descendant = GetFocusableDescendants(element, direction).LastOrDefault(); - if (descendent != null) + if (descendant != null) { - return descendent; + return descendant; } } @@ -184,10 +202,12 @@ namespace Avalonia.Input.Navigation /// /// Gets the first item that should be focused in the next container. /// + /// The element being navigated away from. /// The container. /// The direction of the search. /// The first element, or null if there are no more elements. private static IInputElement GetFirstInNextContainer( + IInputElement element, IInputElement container, NavigationDirection direction) { @@ -203,13 +223,20 @@ namespace Avalonia.Input.Navigation var siblings = parent.GetVisualChildren() .OfType() - .Where(FocusExtensions.CanFocusDescendents); + .Where(FocusExtensions.CanFocusDescendants); var sibling = direction == NavigationDirection.Next ? siblings.SkipWhile(x => x != container).Skip(1).FirstOrDefault() : siblings.TakeWhile(x => x != container).LastOrDefault(); if (sibling != null) { + var customNext = GetCustomNext(sibling, direction); + + if (customNext.handled) + { + return customNext.next; + } + if (sibling.CanFocus()) { next = sibling; @@ -217,24 +244,34 @@ namespace Avalonia.Input.Navigation else { next = direction == NavigationDirection.Next ? - GetFocusableDescendents(sibling).FirstOrDefault() : - GetFocusableDescendents(sibling).LastOrDefault(); + GetFocusableDescendants(sibling, direction).FirstOrDefault() : + GetFocusableDescendants(sibling, direction).LastOrDefault(); } } if (next == null) { - next = GetFirstInNextContainer(parent, direction); + next = GetFirstInNextContainer(element, parent, direction); } } else { next = direction == NavigationDirection.Next ? - GetFocusableDescendents(container).FirstOrDefault() : - GetFocusableDescendents(container).LastOrDefault(); + GetFocusableDescendants(container, direction).FirstOrDefault() : + GetFocusableDescendants(container, direction).LastOrDefault(); } return next; } + + private static (bool handled, IInputElement next) GetCustomNext(IInputElement element, NavigationDirection direction) + { + if (element is ICustomKeyboardNavigation custom) + { + return custom.GetNext(element, direction); + } + + return (false, null); + } } } diff --git a/src/Avalonia.Input/packages.config b/src/Avalonia.Input/packages.config deleted file mode 100644 index 9f732f1bcb..0000000000 --- a/src/Avalonia.Input/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj index 0d81690de0..9d22de86b3 100644 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj +++ b/src/Avalonia.Interactivity/Avalonia.Interactivity.csproj @@ -1,22 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {6B0ED19D-A08B-461C-A9D9-A9EE40B0C06B} - Library - Properties - Avalonia.Interactivity - Avalonia.Interactivity - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + false true @@ -41,63 +26,15 @@ true - - - {D211E587-D8BC-45B9-95A4-F297C8FA5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {42472427-4774-4C81-8AFF-9F27B8E31721} - Avalonia.Layout - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - + + + + Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - - - - + \ No newline at end of file diff --git a/src/Avalonia.Interactivity/Avalonia.Interactivity.v2.ncrunchproject b/src/Avalonia.Interactivity/Avalonia.Interactivity.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Interactivity/Avalonia.Interactivity.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Interactivity/InteractiveExtensions.cs b/src/Avalonia.Interactivity/InteractiveExtensions.cs index 2507fc6866..b03590951b 100644 --- a/src/Avalonia.Interactivity/InteractiveExtensions.cs +++ b/src/Avalonia.Interactivity/InteractiveExtensions.cs @@ -1,8 +1,10 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using System.Collections.Generic; using System.Linq; +using System.Reactive.Linq; namespace Avalonia.Interactivity { @@ -11,12 +13,36 @@ namespace Avalonia.Interactivity /// public static class InteractiveExtensions { + /// + /// Gets an observable for a . + /// + /// The object to listen for events on. + /// The routed event. + /// The routing strategies to listen to. + /// Whether handled events should also be listened for. + /// + /// An observable which fires each time the event is raised. + /// + public static IObservable GetObservable( + this IInteractive o, + RoutedEvent routedEvent, + RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble, + bool handledEventsToo = false) + where TEventArgs : RoutedEventArgs + { + return Observable.Create(x => o.AddHandler( + routedEvent, + (_, e) => x.OnNext(e), + routes, + handledEventsToo)); + } + /// /// Gets the route for bubbling events from the specified interactive. /// /// The interactive. /// The event route. - public static IEnumerable GetBubbleEventRoute(this IInteractive interactive) + internal static IEnumerable GetBubbleEventRoute(this IInteractive interactive) { while (interactive != null) { @@ -30,7 +56,7 @@ namespace Avalonia.Interactivity /// /// The interactive. /// The event route. - public static IEnumerable GetTunnelEventRoute(this IInteractive interactive) + internal static IEnumerable GetTunnelEventRoute(this IInteractive interactive) { return interactive.GetBubbleEventRoute().Reverse(); } diff --git a/src/Avalonia.Interactivity/packages.config b/src/Avalonia.Interactivity/packages.config deleted file mode 100644 index 9f732f1bcb..0000000000 --- a/src/Avalonia.Interactivity/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Layout/Avalonia.Layout.csproj b/src/Avalonia.Layout/Avalonia.Layout.csproj index 41b545b5cb..d0260391d8 100644 --- a/src/Avalonia.Layout/Avalonia.Layout.csproj +++ b/src/Avalonia.Layout/Avalonia.Layout.csproj @@ -1,22 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {42472427-4774-4C81-8AFF-9F27B8E31721} - Library - Properties - Avalonia - Avalonia.Layout - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + false true @@ -41,59 +26,14 @@ true - - - {D211E587-D8BC-45B9-95A4-F297C8FA5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - + + + Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - - - - + \ No newline at end of file diff --git a/src/Avalonia.Layout/Avalonia.Layout.v2.ncrunchproject b/src/Avalonia.Layout/Avalonia.Layout.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Layout/Avalonia.Layout.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs b/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs new file mode 100644 index 0000000000..24f0ccd82e --- /dev/null +++ b/src/Avalonia.Layout/IEmbeddedLayoutRoot.cs @@ -0,0 +1,10 @@ +namespace Avalonia.Layout +{ + /// + /// A special layout root with enforced size for Arrange pass + /// + public interface IEmbeddedLayoutRoot : ILayoutRoot + { + Size AllocatedSize { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index b7b83bf852..3244f5e7dc 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -14,8 +14,8 @@ namespace Avalonia.Layout /// public class LayoutManager : ILayoutManager { - private readonly HashSet _toMeasure = new HashSet(); - private readonly HashSet _toArrange = new HashSet(); + private readonly Queue _toMeasure = new Queue(); + private readonly Queue _toArrange = new Queue(); private bool _queued; private bool _running; @@ -30,8 +30,18 @@ namespace Avalonia.Layout Contract.Requires(control != null); Dispatcher.UIThread.VerifyAccess(); - _toMeasure.Add(control); - _toArrange.Add(control); + if (!control.IsAttachedToVisualTree) + { +#if DEBUG + throw new AvaloniaInternalException( + "LayoutManager.InvalidateMeasure called on a control that is detached from the visual tree."); +#else + return; +#endif + } + + _toMeasure.Enqueue(control); + _toArrange.Enqueue(control); QueueLayoutPass(); } @@ -41,7 +51,17 @@ namespace Avalonia.Layout Contract.Requires(control != null); Dispatcher.UIThread.VerifyAccess(); - _toArrange.Add(control); + if (!control.IsAttachedToVisualTree) + { +#if DEBUG + throw new AvaloniaInternalException( + "LayoutManager.InvalidateArrange called on a control that is detached from the visual tree."); +#else + return; +#endif + } + + _toArrange.Enqueue(control); QueueLayoutPass(); } @@ -108,8 +128,12 @@ namespace Avalonia.Layout { while (_toMeasure.Count > 0) { - var next = _toMeasure.First(); - Measure(next); + var control = _toMeasure.Dequeue(); + + if (!control.IsMeasureValid && control.IsAttachedToVisualTree) + { + Measure(control); + } } } @@ -117,53 +141,62 @@ namespace Avalonia.Layout { while (_toArrange.Count > 0 && _toMeasure.Count == 0) { - var next = _toArrange.First(); - Arrange(next); + var control = _toArrange.Dequeue(); + + if (!control.IsArrangeValid && control.IsAttachedToVisualTree) + { + Arrange(control); + } } } private void Measure(ILayoutable control) { - var root = control as ILayoutRoot; - var parent = control.VisualParent as ILayoutable; - - if (root != null) - { - root.Measure(root.MaxClientSize); - } - else if (parent != null) + // Controls closest to the visual root need to be arranged first. We don't try to store + // ordered invalidation lists, instead we traverse the tree upwards, measuring the + // controls closest to the root first. This has been shown by benchmarks to be the + // fastest and most memory-efficent algorithm. + if (control.VisualParent is ILayoutable parent) { Measure(parent); } - if (!control.IsMeasureValid) + // If the control being measured has IsMeasureValid == true here then its measure was + // handed by an ancestor and can be ignored. The measure may have also caused the + // control to be removed. + if (!control.IsMeasureValid && control.IsAttachedToVisualTree) { - control.Measure(control.PreviousMeasure.Value); + if (control is ILayoutRoot root) + { + root.Measure(Size.Infinity); + } + else + { + control.Measure(control.PreviousMeasure.Value); + } } - - _toMeasure.Remove(control); } private void Arrange(ILayoutable control) { - var root = control as ILayoutRoot; - var parent = control.VisualParent as ILayoutable; - - if (root != null) - { - root.Arrange(new Rect(root.DesiredSize)); - } - else if (parent != null) + if (control.VisualParent is ILayoutable parent) { Arrange(parent); } - if (control.PreviousArrange.HasValue) + if (!control.IsArrangeValid && control.IsAttachedToVisualTree) { - control.Arrange(control.PreviousArrange.Value); + if (control is IEmbeddedLayoutRoot embeddedRoot) + control.Arrange(new Rect(embeddedRoot.AllocatedSize)); + else if (control is ILayoutRoot root) + control.Arrange(new Rect(root.DesiredSize)); + else if (control.PreviousArrange != null) + { + // Has been observed that PreviousArrange sometimes is null, probably a bug somewhere else. + // Condition observed: control.VisualParent is Scrollbar, control is Border. + control.Arrange(control.PreviousArrange.Value); + } } - - _toArrange.Remove(control); } private void QueueLayoutPass() diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 5abd6c52f7..523c720e2f 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -367,6 +367,14 @@ namespace Avalonia.Layout } } + + /// + /// Called by InvalidateMeasure + /// + protected virtual void OnMeasureInvalidated() + { + } + /// /// Invalidates the measurement of the control and queues a new layout pass. /// @@ -378,8 +386,13 @@ namespace Avalonia.Layout IsMeasureValid = false; IsArrangeValid = false; - LayoutManager.Instance?.InvalidateMeasure(this); - InvalidateVisual(); + + if (((ILayoutable)this).IsAttachedToVisualTree) + { + LayoutManager.Instance?.InvalidateMeasure(this); + InvalidateVisual(); + } + OnMeasureInvalidated(); } } @@ -393,8 +406,12 @@ namespace Avalonia.Layout Logger.Verbose(LogArea.Layout, this, "Invalidated arrange"); IsArrangeValid = false; - LayoutManager.Instance?.InvalidateArrange(this); - InvalidateVisual(); + + if (((ILayoutable)this).IsAttachedToVisualTree) + { + LayoutManager.Instance?.InvalidateArrange(this); + InvalidateVisual(); + } } } @@ -456,10 +473,9 @@ namespace Avalonia.Layout ApplyTemplate(); - var constrained = LayoutHelper - .ApplyLayoutConstraints(this, availableSize) - .Deflate(margin); - + var constrained = LayoutHelper.ApplyLayoutConstraints( + this, + availableSize.Deflate(margin)); var measured = MeasureOverride(constrained); var width = measured.Width; @@ -613,7 +629,7 @@ namespace Avalonia.Layout /// protected override sealed void OnVisualParentChanged(IVisual oldParent, IVisual newParent) { - foreach (ILayoutable i in this.GetSelfAndVisualDescendents()) + foreach (ILayoutable i in this.GetSelfAndVisualDescendants()) { i.InvalidateMeasure(); } diff --git a/src/Avalonia.Layout/packages.config b/src/Avalonia.Layout/packages.config deleted file mode 100644 index 9f732f1bcb..0000000000 --- a/src/Avalonia.Layout/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj index 0fb85db795..508ede7f7d 100644 --- a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj +++ b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj @@ -1,20 +1,7 @@ - - - + - 10.0 - Debug - AnyCPU - {B61B66A3-B82D-4875-8001-89D3394FE0C9} - Library - Properties - Avalonia.Logging.Serilog - Avalonia.Logging.Serilog - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 + netstandard1.3 + false true @@ -37,30 +24,7 @@ true - - + - - - ..\..\packages\Serilog.1.5.14\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll - True - - - - - - - - {b09b78d8-9b26-48b0-9149-d64a2f120f3f} - Avalonia.Base - - - - + \ No newline at end of file diff --git a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.v2.ncrunchproject b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Logging.Serilog/packages.config b/src/Avalonia.Logging.Serilog/packages.config deleted file mode 100644 index dbc72b5331..0000000000 --- a/src/Avalonia.Logging.Serilog/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 98cf115eaf..2d66b62eab 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -1,27 +1,15 @@ - - - + - 10.0 - Debug - AnyCPU - {6417B24E-49C2-4985-8DB2-3AB9D898EC91} - Library - Properties - Avalonia.ReactiveUI - Avalonia.ReactiveUI - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile259 - v4.5 + netstandard1.3 + False + false true full false bin\Debug\ - TRACE;DEBUG;MONO PORTABLE + TRACE;DEBUG;MONO PORTABLE;NETSTANDARD1_1 prompt 4 @@ -29,15 +17,19 @@ pdbonly true bin\Release\ - TRACE;MONO PORTABLE + TRACE;MONO PORTABLE;RELEASE;NETSTANDARD1_1 prompt 4 + + + + @@ -65,9 +57,7 @@ - - Code - + @@ -87,35 +77,15 @@ - - ..\..\packages\Splat.1.6.2\lib\Portable-net45+win+wpa81+wp80\Splat.dll - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.0\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.0\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - - - - + + + + + + + + - - + + \ No newline at end of file diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.v2.ncrunchproject b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.ReactiveUI/Shims.cs b/src/Avalonia.ReactiveUI/Shims.cs new file mode 100644 index 0000000000..2729a68d6f --- /dev/null +++ b/src/Avalonia.ReactiveUI/Shims.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Runtime.Serialization +{ + class IgnoreDataMemberAttribute : Attribute + { + } + + class DataMemberAttribute : Attribute + { + } + class OnDeserializedAttribute : Attribute + { + } + + class DataContractAttribute : Attribute + { + } + + class StreamingContext { } +} + +namespace System.Diagnostics.Contracts +{ + static class Contract + { + public static void Requires(bool condition) + { + + } + } +} diff --git a/src/Avalonia.ReactiveUI/packages.config b/src/Avalonia.ReactiveUI/packages.config deleted file mode 100644 index f10d6d8873..0000000000 --- a/src/Avalonia.ReactiveUI/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Styling/Avalonia.Styling.csproj b/src/Avalonia.Styling/Avalonia.Styling.csproj index d38406caa7..6bf37b522b 100644 --- a/src/Avalonia.Styling/Avalonia.Styling.csproj +++ b/src/Avalonia.Styling/Avalonia.Styling.csproj @@ -1,22 +1,7 @@ - - - + - 11.0 - Debug - AnyCPU - {F1BAA01A-F176-4C6A-B39D-5B40BB1B148F} - Library - Properties - Avalonia - Avalonia.Styling - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile7 - v4.5 - - + netstandard1.3 + false true @@ -44,84 +29,11 @@ Properties\SharedAssemblyInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\..\packages\System.Reactive.Core.3.0.0\lib\netstandard1.1\System.Reactive.Core.dll - True - - - ..\..\packages\System.Reactive.Interfaces.3.0.0\lib\netstandard1.0\System.Reactive.Interfaces.dll - True - - - ..\..\packages\System.Reactive.Linq.3.0.0\lib\netstandard1.1\System.Reactive.Linq.dll - True - - - ..\..\packages\System.Reactive.PlatformServices.3.0.0\lib\netstandard1.0\System.Reactive.PlatformServices.dll - True - + + + - - - - - - - - {d211e587-d8bc-45b9-95a4-f297c8fa5200} - Avalonia.Animation - - - {B09B78D8-9B26-48B0-9149-D64A2F120F3F} - Avalonia.Base - - - {EB582467-6ABB-43A1-B052-E981BA910E3A} - Avalonia.Visuals - - - - + \ No newline at end of file diff --git a/src/Avalonia.Styling/Avalonia.Styling.v2.ncrunchproject b/src/Avalonia.Styling/Avalonia.Styling.v2.ncrunchproject deleted file mode 100644 index 30815b1937..0000000000 --- a/src/Avalonia.Styling/Avalonia.Styling.v2.ncrunchproject +++ /dev/null @@ -1,26 +0,0 @@ - - true - 1000 - false - false - false - true - false - false - false - false - false - true - true - false - true - true - true - 60000 - - - - AutoDetect - STA - x86 - \ No newline at end of file diff --git a/src/Avalonia.Styling/Controls/NameScope.cs b/src/Avalonia.Styling/Controls/NameScope.cs index 4c5875479e..8b5bd81d3c 100644 --- a/src/Avalonia.Styling/Controls/NameScope.cs +++ b/src/Avalonia.Styling/Controls/NameScope.cs @@ -50,7 +50,7 @@ namespace Avalonia.Controls return result; } - visual = (visual as ILogical).LogicalParent as Visual; + visual = (visual as ILogical)?.LogicalParent as Visual; } return null; diff --git a/src/Avalonia.Styling/LogicalTree/ILogical.cs b/src/Avalonia.Styling/LogicalTree/ILogical.cs index f2291b42e9..006a9f5cc1 100644 --- a/src/Avalonia.Styling/LogicalTree/ILogical.cs +++ b/src/Avalonia.Styling/LogicalTree/ILogical.cs @@ -36,6 +36,16 @@ namespace Avalonia.LogicalTree /// IAvaloniaReadOnlyList LogicalChildren { get; } + /// + /// Notifies the control that it is being attached to a rooted logical tree. + /// + /// The event args. + /// + /// This method will be called automatically by the framework, you should not need to call + /// this method yourself. + /// + void NotifyAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e); + /// /// Notifies the control that it is being detached from a rooted logical tree. /// diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs index 0d9c0f6daa..a72a558258 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs @@ -37,15 +37,15 @@ namespace Avalonia.LogicalTree return logical.LogicalChildren; } - public static IEnumerable GetLogicalDescendents(this ILogical logical) + public static IEnumerable GetLogicalDescendants(this ILogical logical) { foreach (ILogical child in logical.LogicalChildren) { yield return child; - foreach (ILogical descendent in child.GetLogicalDescendents()) + foreach (ILogical descendant in child.GetLogicalDescendants()) { - yield return descendent; + yield return descendant; } } } diff --git a/src/Avalonia.Styling/Styling/DescendentSelector.cs b/src/Avalonia.Styling/Styling/DescendentSelector.cs index 525f31cbf1..943b3f0161 100644 --- a/src/Avalonia.Styling/Styling/DescendentSelector.cs +++ b/src/Avalonia.Styling/Styling/DescendentSelector.cs @@ -7,16 +7,16 @@ using Avalonia.LogicalTree; namespace Avalonia.Styling { - internal class DescendentSelector : Selector + internal class DescendantSelector : Selector { private readonly Selector _parent; private string _selectorString; - public DescendentSelector(Selector parent) + public DescendantSelector(Selector parent) { if (parent == null) { - throw new InvalidOperationException("Descendent selector must be preceeded by a selector."); + throw new InvalidOperationException("Descendant selector must be preceeded by a selector."); } _parent = parent; @@ -41,7 +41,7 @@ namespace Avalonia.Styling protected override SelectorMatch Evaluate(IStyleable control, bool subscribe) { ILogical c = (ILogical)control; - List> descendentMatches = new List>(); + List> descendantMatches = new List>(); while (c != null) { @@ -60,14 +60,14 @@ namespace Avalonia.Styling } else { - descendentMatches.Add(match.ObservableResult); + descendantMatches.Add(match.ObservableResult); } } } - if (descendentMatches.Count > 0) + if (descendantMatches.Count > 0) { - return new SelectorMatch(StyleActivator.Or(descendentMatches)); + return new SelectorMatch(StyleActivator.Or(descendantMatches)); } else { diff --git a/src/Avalonia.Styling/Styling/Selectors.cs b/src/Avalonia.Styling/Styling/Selectors.cs index 1b4c876f81..c91cc7af04 100644 --- a/src/Avalonia.Styling/Styling/Selectors.cs +++ b/src/Avalonia.Styling/Styling/Selectors.cs @@ -42,13 +42,13 @@ namespace Avalonia.Styling } /// - /// Returns a selector which matches a descendent of a previous selector. + /// Returns a selector which matches a descendant of a previous selector. /// /// The previous selector. /// The selector. - public static Selector Descendent(this Selector previous) + public static Selector Descendant(this Selector previous) { - return new DescendentSelector(previous); + return new DescendantSelector(previous); } /// diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs index be4282cdc0..3dfd9118af 100644 --- a/src/Avalonia.Styling/Styling/Style.cs +++ b/src/Avalonia.Styling/Styling/Style.cs @@ -13,10 +13,10 @@ namespace Avalonia.Styling /// public class Style : IStyle { - private static Dictionary> _applied = + private static Dictionary> _applied = new Dictionary>(); - private Dictionary _resources; + private StyleResources _resources; /// /// Initializes a new instance of the class. @@ -37,13 +37,13 @@ namespace Avalonia.Styling /// /// Gets or sets a dictionary of style resources. /// - public IDictionary Resources + public StyleResources Resources { get { if (_resources == null) { - _resources = new Dictionary(); + _resources = new StyleResources(); } return _resources; @@ -51,25 +51,30 @@ namespace Avalonia.Styling set { + var resources = Resources; - - foreach (var i in value) + if (!Equals(resources, value)) { - resources.Add(i); + foreach (var i in value) + { + resources[i.Key] = i.Value; + //resources.Add(i.Key, i.Value); + //(resources as IDictionary).Add(i); + } } } } /// - /// Gets or sets style's selector. + /// Gets or sets the style's selector. /// public Selector Selector { get; set; } /// - /// Gets or sets style's setters. + /// Gets or sets the style's setters. /// [Content] - public IEnumerable Setters { get; set; } = new List(); + public IList Setters { get; set; } = new List(); /// /// Attaches the style to a control if the style's selector matches. diff --git a/src/Avalonia.Styling/Styling/StyleResources.cs b/src/Avalonia.Styling/Styling/StyleResources.cs new file mode 100644 index 0000000000..e447c6adfd --- /dev/null +++ b/src/Avalonia.Styling/Styling/StyleResources.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Avalonia.Styling +{ + /// + /// Holds resources for a . + /// + public class StyleResources : IDictionary, IDictionary + { + private Dictionary _inner = new Dictionary(); + + public object this[string key] + { + get { return _inner[key]; } + set { _inner[key] = value; } + } + + public int Count => _inner.Count; + + ICollection IDictionary.Keys => _inner.Keys; + + ICollection IDictionary.Values => _inner.Values; + + bool ICollection>.IsReadOnly => false; + + object IDictionary.this[object key] + { + get { return ((IDictionary)_inner)[key]; } + set { ((IDictionary)_inner)[key] = value; } + } + + ICollection IDictionary.Keys => _inner.Keys; + + ICollection IDictionary.Values => _inner.Values; + + bool ICollection.IsSynchronized => false; + + object ICollection.SyncRoot => ((IDictionary)_inner).SyncRoot; + + bool IDictionary.IsFixedSize => false; + + bool IDictionary.IsReadOnly => false; + + public void Add(string key, object value) => _inner.Add(key, value); + + public void Clear() => _inner.Clear(); + + public bool ContainsKey(string key) => _inner.ContainsKey(key); + + public bool Remove(string key) => _inner.Remove(key); + + public IEnumerator> GetEnumerator() => _inner.GetEnumerator(); + + public bool TryGetValue(string key, out object value) => _inner.TryGetValue(key, out value); + + bool ICollection>.Contains(KeyValuePair item) + { + return ((IDictionary)_inner).Contains(item); + } + + void ICollection>.Add(KeyValuePair item) + { + ((IDictionary)_inner).Add(item); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)_inner).CopyTo(array, arrayIndex); + } + + bool ICollection>.Remove(KeyValuePair item) + { + return ((IDictionary)_inner).Remove(item); + } + + void ICollection.CopyTo(Array array, int index) => ((IDictionary)_inner).CopyTo(array, index); + + IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator(); + + IDictionaryEnumerator IDictionary.GetEnumerator() => ((IDictionary)_inner).GetEnumerator(); + + void IDictionary.Add(object key, object value) => ((IDictionary)_inner).Add(key, value); + + bool IDictionary.Contains(object key) => ((IDictionary)_inner).Contains(key); + + void IDictionary.Remove(object key) => ((IDictionary)_inner).Remove(key); + } +} diff --git a/src/Avalonia.Styling/packages.config b/src/Avalonia.Styling/packages.config deleted file mode 100644 index 9f732f1bcb..0000000000 --- a/src/Avalonia.Styling/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml index 38786f54d3..cb86598a42 100644 --- a/src/Avalonia.Themes.Default/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Default/Accents/BaseLight.xaml @@ -1,6 +1,5 @@ +"; + + var styles = AvaloniaXamlLoader.Parse(xaml); + + Assert.Equal(1, styles.Count); + + var style = (Style)styles[0]; + + var setters = style.Setters.Cast().ToArray(); + + Assert.Equal(2, setters.Length); + + Assert.Equal(TextBlock.BackgroundProperty, setters[0].Property); + Assert.Equal(Brushes.White.Color, ((ISolidColorBrush)setters[0].Value).Color); + + Assert.Equal(TextBlock.WidthProperty, setters[1].Property); + Assert.Equal(100.0, setters[1].Value); + } + + [Fact] + public void Style_Setter_With_AttachedProperty_Is_Parsed() + { + var xaml = @" + + +"; + + var styles = AvaloniaXamlLoader.Parse(xaml); + + Assert.Equal(1, styles.Count); + + var style = (Style)styles[0]; + + var setters = style.Setters.Cast().ToArray(); + + Assert.Equal(1, setters.Length); + + Assert.Equal(TextBlock.FontSizeProperty, setters[0].Property); + Assert.Equal(21.0, setters[0].Value); + } + + [Fact] + public void Complex_Style_Is_Parsed() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + +"; + var styles = AvaloniaXamlLoader.Parse(xaml); + + Assert.Equal(1, styles.Count); + + var style = (Style)styles[0]; + + var setters = style.Setters.Cast().ToArray(); + + Assert.Equal(3, setters.Length); + + Assert.Equal(CheckBox.BorderBrushProperty, setters[0].Property); + Assert.Equal(CheckBox.BorderThicknessProperty, setters[1].Property); + Assert.Equal(CheckBox.TemplateProperty, setters[2].Property); + + Assert.IsType(setters[0].Value); + Assert.IsType(setters[1].Value); + Assert.IsType(setters[2].Value); + } + } + + [Fact] + public void Style_Resources_Are_Built() + { + var xaml = @" +"; + + var style = AvaloniaXamlLoader.Parse "; + + var style = AvaloniaXamlLoader.Parse + + +"; + + + var window = AvaloniaXamlLoader.Parse(xaml); + var tracker = (InitializationOrderTracker)window.Content; + + //ensure binding is set and operational first + Assert.Equal(100.0, tracker.Tag); + + Assert.Equal("EndInit 0", tracker.Order.Last()); + } + } + + [Fact] + public void BeginInit_Matches_EndInit() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + + var window = AvaloniaXamlLoader.Parse(xaml); + var tracker = (InitializationOrderTracker)window.Content; + + Assert.Equal(0, tracker.InitState); + } + } + + [Fact] + public void DeferedXamlLoader_Should_Preserve_NamespacesContext() + { + var xaml = +@" + + + + + +"; + + var contentControl = AvaloniaXamlLoader.Parse(xaml); + var template = contentControl.ContentTemplate; + + Assert.NotNull(template); + + var txt = (TextBlock)template.Build(null); + + Assert.Equal((object)NonControl.StringProperty, txt.Tag); + } + + [Fact] + public void Binding_To_List_AvaloniaProperty_Is_Operational() + { + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) + { + var xaml = @" + + +"; + + var window = AvaloniaXamlLoader.Parse(xaml); + var listBox = (ListBox)window.Content; + + var vm = new SelectedItemsViewModel() + { + Items = new string[] { "foo", "bar", "baz" } + }; + + window.DataContext = vm; + + Assert.Equal(vm.Items, listBox.Items); + + Assert.Equal(vm.SelectedItems, listBox.SelectedItems); + } + } + + private class SelectedItemsViewModel : INotifyPropertyChanged + { + public string[] Items { get; set; } + + public event PropertyChangedEventHandler PropertyChanged; + + private IList _selectedItems = new AvaloniaList(); + + public IList SelectedItems + { + get { return _selectedItems; } + set + { + _selectedItems = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItems))); + } + } + } } -} +} \ No newline at end of file diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs index a66d6ac6d8..a44d09dee7 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System.Reactive.Subjects; using Avalonia.Controls; using Avalonia.UnitTests; using Xunit; @@ -83,7 +84,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml [Fact] public void Can_Bind_To_DataContext_Of_Anchor_On_Non_Control() { - using (UnitTestApplication.Start(TestServices.StyledWindow)) + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) { var xaml = @" "; - var loader = new AvaloniaXamlLoader(); - var window = (Window)loader.Load(xaml); + + var window = AvaloniaXamlLoader.Parse(xaml); var textBlock = (TextBlock)window.Content; - window.ApplyTemplate(); + textBlock.Tag = "foo"; Assert.Equal("foo", textBlock.Text); } @@ -169,7 +171,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml [Fact] public void Longform_Binding_To_Self_Works() { - using (UnitTestApplication.Start(TestServices.StyledWindow)) + using (UnitTestApplication.Start(TestServices.MockWindowingPlatform)) { var xaml = @" + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = (TextBlock)window.Content; + var observable = new BehaviorSubject("foo"); + + window.DataContext = new { Observable = observable }; + window.ApplyTemplate(); + + Assert.Equal("foo", textBlock.Text); + observable.OnNext("bar"); + Assert.Equal("bar", textBlock.Text); + } + } } -} +} \ No newline at end of file diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests_RelativeSource.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests_RelativeSource.cs new file mode 100644 index 0000000000..9125e1e643 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests_RelativeSource.cs @@ -0,0 +1,129 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using Avalonia.Controls; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Markup.Xaml.UnitTests.Xaml +{ + public class BindingTests_RelativeSource + { + [Fact] + public void Binding_To_DataContext_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + +