diff --git a/build.cake b/build.cake index a3d7cf64d2..e902c5d24e 100644 --- a/build.cake +++ b/build.cake @@ -662,11 +662,9 @@ Task("Run-Unit-Tests") if (isRunningOnWindows) { - var windowsTests = GetFiles("./tests/Avalonia.DesignerSupport.Tests/bin/" + dirSuffix + "/*.Tests.dll") + - GetFiles("./tests/Avalonia.LeakTests/bin/" + dirSuffix + "/*.LeakTests.dll") + - GetFiles("./tests/Avalonia.RenderTests/bin/" + dirSuffix + "/*.RenderTests.dll"); + var leakTests = GetFiles("./tests/Avalonia.LeakTests/bin/" + dirSuffix + "/*.LeakTests.dll"); - unitTests.AddRange(windowsTests); + unitTests.AddRange(leakTests); } var toolPath = (isPlatformAnyCPU || isPlatformX86) ? @@ -688,20 +686,32 @@ Task("Run-Unit-Tests") .WithFilter("-[Avalonia.*]OmniXaml.* -[Avalonia.*]Glass.*") .WithFilter("-[Avalonia.HtmlRenderer]TheArtOfDev.HtmlRenderer.* +[Avalonia.HtmlRenderer]TheArtOfDev.HtmlRenderer.Avalonia.* -[Avalonia.ReactiveUI]*"); - foreach(var test in unitTests) + openCoverSettings.ReturnTargetCodeOffset = 0; + + foreach(var test in unitTests.Where(testFile => FileExists(testFile))) { CopyDirectory(test.GetDirectory(), testsRoot); } + var testsInDirectoryToRun = new List(); + if(isRunningOnWindows) + { + testsInDirectoryToRun.AddRange(GetFiles("./artifacts/tests/*Tests.dll")); + } + else + { + testsInDirectoryToRun.AddRange(GetFiles("./artifacts/tests/*.UnitTests.dll")); + } + if(isRunningOnWindows) { OpenCover(context => { - context.XUnit2(unitTests.Select(test => testsRoot.GetFilePath(test).FullPath), xUnitSettings); + context.XUnit2(testsInDirectoryToRun, xUnitSettings); }, openCoverOutput, openCoverSettings); } else { - XUnit2(unitTests.Select(test => test.FullPath), xUnitSettings); + XUnit2(testsInDirectoryToRun, xUnitSettings); } }); diff --git a/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs index ba584e33b9..d139c95fd4 100644 --- a/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/IItemContainerGenerator.cs @@ -22,6 +22,11 @@ namespace Avalonia.Controls.Generators /// IDataTemplate ItemTemplate { get; set; } + /// + /// Gets the ContainerType, or null if its an untyped ContainerGenerator. + /// + Type ContainerType { get; } + /// /// Signalled whenever new containers are materialized. /// diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs index 4d65210002..882d2f4ddd 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs @@ -50,6 +50,9 @@ namespace Avalonia.Controls.Generators /// public IControl Owner { get; } + /// + public virtual Type ContainerType => null; + /// public ItemContainerInfo Materialize( int index, diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs index 3aa2181cd4..259c524d59 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs @@ -34,6 +34,9 @@ namespace Avalonia.Controls.Generators ContentTemplateProperty = contentTemplateProperty; } + /// + public override Type ContainerType => typeof(T); + /// /// Gets the container's Content property. /// diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index 8f92cc43b7..5d12c9963f 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -4,7 +4,6 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; -using System.Diagnostics.CodeAnalysis; using System.Linq; using Avalonia.Collections; using Avalonia.Controls.Generators; @@ -226,19 +225,35 @@ namespace Avalonia.Controls /// The details of the containers. protected virtual void OnContainersMaterialized(ItemContainerEventArgs e) { - var toAdd = new List(); - foreach (var container in e.Containers) { // If the item is its own container, then it will be added to the logical tree when // it was added to the Items collection. if (container.ContainerControl != null && container.ContainerControl != container.Item) { - toAdd.Add(container.ContainerControl); + if (ItemContainerGenerator.ContainerType == null) + { + var containerControl = container.ContainerControl as ContentPresenter; + + if (containerControl != null) + { + ((ISetLogicalParent)containerControl).SetParent(this); + containerControl.SetValue(TemplatedParentProperty, null); + + containerControl.UpdateChild(); + + if (containerControl.Child != null) + { + LogicalChildren.Add(containerControl.Child); + } + } + } + else + { + LogicalChildren.Add(container.ContainerControl); + } } } - - LogicalChildren.AddRange(toAdd); } /// @@ -248,19 +263,32 @@ namespace Avalonia.Controls /// The details of the containers. protected virtual void OnContainersDematerialized(ItemContainerEventArgs e) { - var toRemove = new List(); - foreach (var container in e.Containers) { // If the item is its own container, then it will be removed from the logical tree // when it is removed from the Items collection. if (container?.ContainerControl != container?.Item) { - toRemove.Add(container.ContainerControl); + if (ItemContainerGenerator.ContainerType == null) + { + var containerControl = container.ContainerControl as ContentPresenter; + + if (containerControl != null) + { + ((ISetLogicalParent)containerControl).SetParent(null); + + if (containerControl.Child != null) + { + LogicalChildren.Remove(containerControl.Child); + } + } + } + else + { + LogicalChildren.Remove(container.ContainerControl); + } } } - - LogicalChildren.RemoveAll(toRemove); } /// diff --git a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs index 51f4b2b724..7e70f9354e 100644 --- a/src/Skia/Avalonia.Skia/FormattedTextImpl.cs +++ b/src/Skia/Avalonia.Skia/FormattedTextImpl.cs @@ -585,7 +585,7 @@ namespace Avalonia.Skia if (_skiaLines.Count == 0) { _lines.Add(new FormattedTextLine(0, _lineHeight)); - _size = new Size(0, _lineHeight + lastLineDescent); + _size = new Size(0, _lineHeight); } else { diff --git a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs index 0e3c6f5953..e91ab02327 100644 --- a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs +++ b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs @@ -50,8 +50,9 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(1, target.GetLogicalChildren().Count()); var child = target.GetLogicalChildren().Single(); - Assert.IsType(child); - Assert.Equal("Foo", ((ContentPresenter)child).Content); + + Assert.IsType(child); + Assert.Equal("Foo", ((TextBlock)child).Text); } [Fact] diff --git a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs index dcbc71b9a1..e88d1881e6 100644 --- a/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs @@ -9,6 +9,8 @@ using Avalonia.Controls.Templates; using Avalonia.LogicalTree; using Avalonia.VisualTree; using Xunit; +using System.Collections.ObjectModel; +using Avalonia.UnitTests; namespace Avalonia.Controls.UnitTests { @@ -61,6 +63,32 @@ namespace Avalonia.Controls.UnitTests Assert.Null(container.TemplatedParent); } + [Fact] + public void Container_Child_Should_Have_LogicalParent_Set_To_Container() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var root = new Window(); + var target = new ItemsControl(); + + root.Content = target; + + var templatedParent = new Button(); + target.TemplatedParent = templatedParent; + target.Template = GetTemplate(); + + target.Items = new[] { "Foo" }; + + root.ApplyTemplate(); + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + var container = (ContentPresenter)target.Presenter.Panel.Children[0]; + + Assert.Equal(container, container.Child.Parent); + } + } + [Fact] public void Control_Item_Should_Be_Logical_Child_Before_ApplyTemplate() { @@ -138,7 +166,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Adding_String_Item_Should_Make_ContentPresenter_Appear_In_LogicalChildren() + public void Adding_String_Item_Should_Make_TextBlock_Appear_In_LogicalChildren() { var target = new ItemsControl(); var child = new Control(); @@ -150,7 +178,7 @@ namespace Avalonia.Controls.UnitTests var logical = (ILogical)target; Assert.Equal(1, logical.LogicalChildren.Count); - Assert.IsType(logical.LogicalChildren[0]); + Assert.IsType(logical.LogicalChildren[0]); } [Fact] @@ -171,6 +199,7 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new ILogical[0], target.GetLogicalChildren()); } + [Fact] public void Setting_Items_Should_Fire_LogicalChildren_CollectionChanged() { diff --git a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj index 49d7f44102..3519590080 100644 --- a/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj +++ b/tests/Avalonia.DesignerSupport.TestApp/Avalonia.DesignerSupport.TestApp.csproj @@ -18,7 +18,7 @@ true full false - bin\Debug\ + ..\..\artifacts\tests\ DEBUG;TRACE prompt 4 @@ -27,7 +27,7 @@ AnyCPU pdbonly true - bin\Release\ + ..\..\artifacts\tests\ TRACE prompt 4 diff --git a/tests/Avalonia.DesignerSupport.Tests/Avalonia.DesignerSupport.Tests.csproj b/tests/Avalonia.DesignerSupport.Tests/Avalonia.DesignerSupport.Tests.csproj index 7a1c1207ce..97dfda5967 100644 --- a/tests/Avalonia.DesignerSupport.Tests/Avalonia.DesignerSupport.Tests.csproj +++ b/tests/Avalonia.DesignerSupport.Tests/Avalonia.DesignerSupport.Tests.csproj @@ -16,7 +16,7 @@ true full false - bin\Debug\ + ..\..\artifacts\tests\ DEBUG;TRACE prompt 4 @@ -24,7 +24,7 @@ pdbonly true - bin\Release\ + ..\..\artifacts\tests\ TRACE prompt 4 @@ -69,6 +69,12 @@ + + + {f1381f98-4d24-409a-a6c5-1c5b1e08bb08} + Avalonia.DesignerSupport.TestApp + +