diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..c5a719ce90 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: avalonia diff --git a/packages/Avalonia/Avalonia.csproj b/packages/Avalonia/Avalonia.csproj index 489cb228aa..2c5a09bee7 100644 --- a/packages/Avalonia/Avalonia.csproj +++ b/packages/Avalonia/Avalonia.csproj @@ -1,6 +1,7 @@  netstandard2.0;net461;netcoreapp2.0 + Avalonia diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index ec668f2a2b..df840464c1 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -150,7 +150,8 @@ namespace Avalonia.Build.Tasks classType = typeSystem.TargetAssembly.FindType(tn.Text); if (classType == null) throw new XamlIlParseException($"Unable to find type `{tn.Text}`", classDirective); - initialRoot.Type = new XamlIlAstClrTypeReference(classDirective, classType, false); + compiler.OverrideRootType(parsed, + new XamlIlAstClrTypeReference(classDirective, classType, false)); initialRoot.Children.Remove(classDirective); } diff --git a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj index 21d34ae4d6..27853a1540 100644 --- a/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj +++ b/src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj @@ -1,6 +1,7 @@  netstandard2.0 + Avalonia.Controls.DataGrid diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 0e696e0199..9f9fe5eae1 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -255,16 +255,13 @@ namespace Avalonia if (MainWindow == null) { - Dispatcher.UIThread.Post(() => + if (!mainWindow.IsVisible) { - if (!mainWindow.IsVisible) - { - mainWindow.Show(); - } - - MainWindow = mainWindow; - }); - } + mainWindow.Show(); + } + + MainWindow = mainWindow; + } return Run(new CancellationTokenSource()); } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index dcde8117f6..152b551f9a 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -380,6 +380,7 @@ namespace Avalonia.Controls.Primitives } break; + case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Reset: SelectedIndex = IndexOf(Items, SelectedItem); break; diff --git a/src/Avalonia.Controls/Primitives/TemplatedControl.cs b/src/Avalonia.Controls/Primitives/TemplatedControl.cs index ba4c5027d0..32e220b789 100644 --- a/src/Avalonia.Controls/Primitives/TemplatedControl.cs +++ b/src/Avalonia.Controls/Primitives/TemplatedControl.cs @@ -357,7 +357,7 @@ namespace Avalonia.Controls.Primitives if (control.TemplatedParent == this) { - foreach (IControl child in control.GetVisualChildren()) + foreach (IControl child in control.GetLogicalChildren()) { RegisterNames(child, nameScope); } diff --git a/src/Avalonia.Desktop/Avalonia.Desktop.csproj b/src/Avalonia.Desktop/Avalonia.Desktop.csproj index f7c06306f5..f45b0c54e3 100644 --- a/src/Avalonia.Desktop/Avalonia.Desktop.csproj +++ b/src/Avalonia.Desktop/Avalonia.Desktop.csproj @@ -1,6 +1,7 @@ netstandard2.0 + Avalonia.Desktop diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 1851f9fb70..64145b9c3c 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -1,6 +1,7 @@  netstandard2.0 + Avalonia.ReactiveUI diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs index 6361763614..f7e063dfb5 100644 --- a/src/Avalonia.Styling/StyledElement.cs +++ b/src/Avalonia.Styling/StyledElement.cs @@ -677,23 +677,6 @@ namespace Avalonia if (Name != null) { _nameScope?.Register(Name, this); - - var visualParent = Parent as StyledElement; - - if (this is INameScope && visualParent != null) - { - // If we have e.g. a named UserControl in a window then we want that control - // to be findable by name from the Window, so register with both name scopes. - // This differs from WPF's behavior in that XAML manually registers controls - // with name scopes based on the XAML file in which the name attribute appears, - // but we're trying to avoid XAML magic in Avalonia in order to made code- - // created UIs easy. This will cause problems if a UserControl declares a name - // in its XAML and that control is included multiple times in a parent control - // (as the name will be duplicated), however at the moment I'm fine with saying - // "don't do that". - var parentNameScope = NameScope.FindNameScope(visualParent); - parentNameScope?.Register(Name, this); - } } } diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index 5d1c66f872..c83a8436b4 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -528,6 +528,8 @@ namespace Avalonia.Rendering oldScene?.Dispose(); } + _dirty.Clear(); + if (SceneInvalidated != null) { var rect = new Rect(); @@ -540,10 +542,9 @@ namespace Avalonia.Rendering } } + System.Diagnostics.Debug.WriteLine("Invalidated " + rect); SceneInvalidated(this, new SceneInvalidatedEventArgs((IRenderRoot)_root, rect)); } - - _dirty.Clear(); } else { diff --git a/src/Avalonia.X11/Avalonia.X11.csproj b/src/Avalonia.X11/Avalonia.X11.csproj index 1629890568..59afc877de 100644 --- a/src/Avalonia.X11/Avalonia.X11.csproj +++ b/src/Avalonia.X11/Avalonia.X11.csproj @@ -1,8 +1,8 @@  - netstandard2.0 true + Avalonia.X11 diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index 0885ccccfa..95443a364e 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -3,6 +3,7 @@ netstandard2.0 true $(DefineConstants);GTK3_PINVOKE + Avalonia.Gtk3 diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj b/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj index c622bbb8c5..48095a4c25 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj +++ b/src/Linux/Avalonia.LinuxFramebuffer/Avalonia.LinuxFramebuffer.csproj @@ -2,6 +2,7 @@ netstandard2.0 true + Avalonia.LinuxFramebuffer diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 65149a65de..b84f50fa8d 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -112,13 +112,31 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions rootType = new XamlIlAstClrTypeReference(rootObject, overrideRootType, false); } - rootObject.Type = rootType; + OverrideRootType(parsed, rootType); Transform(parsed); Compile(parsed, tb, _contextType, PopulateName, BuildName, "__AvaloniaXamlIlNsInfo", baseUri, fileSource); } - - + + public void OverrideRootType(XamlIlDocument doc, IXamlIlAstTypeReference newType) + { + var root = (XamlIlAstObjectNode)doc.Root; + var oldType = root.Type; + if (oldType.Equals(newType)) + return; + + root.Type = newType; + foreach (var child in root.Children.OfType()) + { + if (child.Property is XamlIlAstNamePropertyReference prop) + { + if (prop.DeclaringType.Equals(oldType)) + prop.DeclaringType = newType; + if (prop.TargetType.Equals(oldType)) + prop.TargetType = newType; + } + } + } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index f91e221ac0..70b7fe6aec 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Avalonia.Controls; using Avalonia.Data; +using Portable.Xaml; using Portable.Xaml.Markup; // ReSharper disable UnusedMember.Global // ReSharper disable UnusedParameter.Global @@ -17,19 +18,24 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime { var resourceNodes = provider.GetService().Parents .OfType().ToList(); - - return sp => builder(new DeferredParentServiceProvider(sp, resourceNodes)); + var rootObject = provider.GetService().RootObject; + return sp => builder(new DeferredParentServiceProvider(sp, resourceNodes, rootObject)); } - class DeferredParentServiceProvider : IAvaloniaXamlIlParentStackProvider, IServiceProvider + class DeferredParentServiceProvider : + IAvaloniaXamlIlParentStackProvider, + IServiceProvider, + IRootObjectProvider { private readonly IServiceProvider _parentProvider; private readonly List _parentResourceNodes; - public DeferredParentServiceProvider(IServiceProvider parentProvider, List parentResourceNodes) + public DeferredParentServiceProvider(IServiceProvider parentProvider, List parentResourceNodes, + object rootObject) { _parentProvider = parentProvider; _parentResourceNodes = parentResourceNodes; + RootObject = rootObject; } public IEnumerable Parents => GetParents(); @@ -46,8 +52,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime { if (serviceType == typeof(IAvaloniaXamlIlParentStackProvider)) return this; + if (serviceType == typeof(IRootObjectProvider)) + return this; return _parentProvider?.GetService(serviceType); } + + public object RootObject { get; } } diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github index 3b3c1f93a5..1e3ffc3154 160000 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github @@ -1 +1 @@ -Subproject commit 3b3c1f93a566080d417b9782f9cc4ea67cd62344 +Subproject commit 1e3ffc315401f0b2eb96a0e79b25c2fc19a80d78 diff --git a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj index 0479a74937..4f884cdf33 100644 --- a/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj +++ b/src/Skia/Avalonia.Skia/Avalonia.Skia.csproj @@ -3,6 +3,7 @@ netstandard2.0 Avalonia.Skia Avalonia.Skia + Avalonia.Skia true true diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj index 70b2703f5e..16f2767096 100644 --- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj +++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj @@ -1,6 +1,7 @@  netstandard2.0 + Avalonia.Direct2D1 diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index d1046c6133..67db56dd1b 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -2,6 +2,7 @@ netstandard2.0 true + Avalonia.Win32 diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 2df925301f..037c16e231 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -341,6 +341,33 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(-1, target.SelectedIndex); } + [Fact] + public void Moving_Selected_Item_Should_Update_Selection() + { + var items = new AvaloniaList + { + new Item(), + new Item(), + }; + + var target = new SelectingItemsControl + { + Items = items, + Template = Template(), + }; + + target.ApplyTemplate(); + target.SelectedIndex = 0; + + Assert.Equal(items[0], target.SelectedItem); + Assert.Equal(0, target.SelectedIndex); + + items.Move(0, 1); + + Assert.Equal(items[1], target.SelectedItem); + Assert.Equal(1, target.SelectedIndex); + } + [Fact] public void Resetting_Items_Collection_Should_Clear_Selection() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index c9eb72757f..3107f4cf22 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -32,7 +32,8 @@ Designer - + + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs index 34e77cb2fd..6b67303b07 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/DataTemplateTests.cs @@ -38,6 +38,37 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml } } + [Fact] + public void DataTemplate_Can_Contain_Named_UserControl() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var itemsControl = window.FindControl("itemsControl"); + + window.DataContext = new[] { "item1", "item2" }; + + window.ApplyTemplate(); + itemsControl.ApplyTemplate(); + itemsControl.Presenter.ApplyTemplate(); + + Assert.Equal(2, itemsControl.Presenter.Panel.Children.Count); + } + } + [Fact] public void Can_Set_DataContext_In_DataTemplate() { diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlClassWithCustomProperty.xaml b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlClassWithCustomProperty.xaml new file mode 100644 index 0000000000..330561a2c6 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlClassWithCustomProperty.xaml @@ -0,0 +1,6 @@ + + + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index a584027768..795aba153a 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -5,8 +5,12 @@ using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using Avalonia.Controls; +using Avalonia.Controls.Presenters; using Avalonia.Data.Converters; +using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.Media; +using Avalonia.Threading; using Avalonia.UnitTests; using Avalonia.VisualTree; using JetBrains.Annotations; @@ -117,6 +121,81 @@ namespace Avalonia.Markup.Xaml.UnitTests Assert.Equal(Brushes.Red.Color, ((ISolidColorBrush)canvas.Background).Color); } } + + [Fact] + public void Event_Handlers_Should_Work_For_Templates() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var w =new XamlIlBugTestsEventHandlerCodeBehind(); + w.ApplyTemplate(); + w.Show(); + + Dispatcher.UIThread.RunJobs(); + var itemsPresenter = ((ItemsControl)w.Content).GetVisualChildren().FirstOrDefault(); + var item = itemsPresenter + .GetVisualChildren().First() + .GetVisualChildren().First() + .GetVisualChildren().First(); + ((Control)item).RaiseEvent(new PointerPressedEventArgs {ClickCount = 20}); + Assert.Equal(20, w.Args.ClickCount); + } + } + + [Fact] + public void Custom_Properties_Should_Work_With_XClass() + { + var precompiled = new XamlIlClassWithCustomProperty(); + Assert.Equal("123", precompiled.Test); + var loaded = (XamlIlClassWithCustomProperty)AvaloniaXamlLoader.Parse(@" + + +"); + Assert.Equal("321", loaded.Test); + + } + } + + public class XamlIlBugTestsEventHandlerCodeBehind : Window + { + public PointerPressedEventArgs Args; + public void HandlePointerPressed(object sender, PointerPressedEventArgs args) + { + Args = args; + } + + public XamlIlBugTestsEventHandlerCodeBehind() + { + new AvaloniaXamlLoader().Load(@" + + + + +