Browse Source

Merge branch 'master' into fixes/textIssues

pull/11034/head
Max Katz 3 years ago
committed by GitHub
parent
commit
fdfc4f799c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .ncrunch/SafeAreaDemo.Android.v3.ncrunchproject
  2. 5
      .ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject
  3. 5
      .ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject
  4. 5
      .ncrunch/SafeAreaDemo.v3.ncrunchproject
  5. 67
      src/Avalonia.Controls/ItemsControl.cs
  6. 4
      src/Avalonia.Controls/Templates/FuncControlTemplate.cs
  7. 18
      src/Avalonia.Controls/Templates/IControlTemplate.cs
  8. 3
      src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs
  9. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs
  10. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs
  11. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs
  12. 5
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  13. 2
      src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs
  14. 2
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  15. 32
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  16. 95
      tests/Avalonia.Controls.UnitTests/MenuItemTests.cs
  17. 18
      tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs
  18. 2
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs
  19. 2
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  20. 6
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

5
.ncrunch/SafeAreaDemo.Android.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/SafeAreaDemo.Desktop.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/SafeAreaDemo.iOS.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

5
.ncrunch/SafeAreaDemo.v3.ncrunchproject

@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<IgnoreThisComponentCompletely>True</IgnoreThisComponentCompletely>
</Settings>
</ProjectConfiguration>

67
src/Avalonia.Controls/ItemsControl.cs

@ -378,48 +378,48 @@ namespace Avalonia.Controls
if (container is HeaderedContentControl hcc)
{
hcc.Content = item;
SetIfUnset(hcc, HeaderedContentControl.ContentProperty, item);
if (item is IHeadered headered)
hcc.Header = headered.Header;
SetIfUnset(hcc, HeaderedContentControl.HeaderProperty, headered.Header);
else if (item is not Visual)
hcc.Header = item;
SetIfUnset(hcc, HeaderedContentControl.HeaderProperty, item);
if (itemTemplate is not null)
hcc.HeaderTemplate = itemTemplate;
SetIfUnset(hcc, HeaderedContentControl.HeaderTemplateProperty, itemTemplate);
}
else if (container is ContentControl cc)
{
cc.Content = item;
SetIfUnset(cc, ContentControl.ContentProperty, item);
if (itemTemplate is not null)
cc.ContentTemplate = itemTemplate;
SetIfUnset(cc, ContentControl.ContentTemplateProperty, itemTemplate);
}
else if (container is ContentPresenter p)
{
p.Content = item;
SetIfUnset(p, ContentPresenter.ContentProperty, item);
if (itemTemplate is not null)
p.ContentTemplate = itemTemplate;
SetIfUnset(p, ContentPresenter.ContentTemplateProperty, itemTemplate);
}
else if (container is ItemsControl ic)
{
if (itemTemplate is not null)
ic.ItemTemplate = itemTemplate;
if (ItemContainerTheme is { } ict && !ict.IsSet(ItemContainerThemeProperty))
ic.ItemContainerTheme = ict;
SetIfUnset(ic, ItemTemplateProperty, itemTemplate);
if (ItemContainerTheme is { } ict)
SetIfUnset(ic, ItemContainerThemeProperty, ict);
}
// These conditions are separate because HeaderedItemsControl and
// HeaderedSelectingItemsControl also need to run the ItemsControl preparation.
if (container is HeaderedItemsControl hic)
{
hic.Header = item;
hic.HeaderTemplate = itemTemplate;
SetIfUnset(hic, HeaderedItemsControl.HeaderProperty, item);
SetIfUnset(hic, HeaderedItemsControl.HeaderTemplateProperty, itemTemplate);
hic.PrepareItemContainer(this);
}
else if (container is HeaderedSelectingItemsControl hsic)
{
hsic.Header = item;
hsic.HeaderTemplate = itemTemplate;
SetIfUnset(hsic, HeaderedSelectingItemsControl.HeaderProperty, item);
SetIfUnset(hsic, HeaderedSelectingItemsControl.HeaderTemplateProperty, itemTemplate);
hsic.PrepareItemContainer(this);
}
}
@ -458,30 +458,35 @@ namespace Avalonia.Controls
{
if (container is HeaderedContentControl hcc)
{
if (hcc.Content is Control)
hcc.Content = null;
if (hcc.Header is Control)
hcc.Header = null;
hcc.ClearValue(HeaderedContentControl.ContentProperty);
hcc.ClearValue(HeaderedContentControl.HeaderProperty);
hcc.ClearValue(HeaderedContentControl.HeaderTemplateProperty);
}
else if (container is ContentControl cc)
{
if (cc.Content is Control)
cc.Content = null;
cc.ClearValue(ContentControl.ContentProperty);
cc.ClearValue(ContentControl.ContentTemplateProperty);
}
else if (container is ContentPresenter p)
{
if (p.Content is Control)
p.Content = null;
p.ClearValue(ContentPresenter.ContentProperty);
p.ClearValue(ContentPresenter.ContentTemplateProperty);
}
else if (container is HeaderedItemsControl hic)
else if (container is ItemsControl ic)
{
ic.ClearValue(ItemTemplateProperty);
ic.ClearValue(ItemContainerThemeProperty);
}
if (container is HeaderedItemsControl hic)
{
if (hic.Header is Control)
hic.Header = null;
hic.ClearValue(HeaderedItemsControl.HeaderProperty);
hic.ClearValue(HeaderedItemsControl.HeaderTemplateProperty);
}
else if (container is HeaderedSelectingItemsControl hsic)
{
if (hsic.Header is Control)
hsic.Header = null;
hsic.ClearValue(HeaderedSelectingItemsControl.HeaderProperty);
hsic.ClearValue(HeaderedSelectingItemsControl.HeaderTemplateProperty);
}
// Feels like we should be clearing the HeaderedItemsControl.Items binding here, but looking at
@ -707,6 +712,12 @@ namespace Avalonia.Controls
LogicalChildren.AddRange(toAdd);
}
private void SetIfUnset<T>(AvaloniaObject target, StyledProperty<T> property, T value)
{
if (!target.IsSet(property))
target.SetCurrentValue(property, value);
}
private void RemoveControlItemsFromLogicalChildren(IEnumerable? items)
{
if (items is null)

4
src/Avalonia.Controls/Templates/FuncControlTemplate.cs

@ -18,10 +18,10 @@ namespace Avalonia.Controls.Templates
{
}
public new ControlTemplateResult Build(TemplatedControl param)
public new TemplateResult<Control> Build(TemplatedControl param)
{
var (control, scope) = BuildWithNameScope(param);
return new ControlTemplateResult(control, scope);
return new(control, scope);
}
}
}

18
src/Avalonia.Controls/Templates/IControlTemplate.cs

@ -5,23 +5,7 @@ namespace Avalonia.Controls.Templates
/// <summary>
/// Interface representing a template used to build a <see cref="TemplatedControl"/>.
/// </summary>
public interface IControlTemplate : ITemplate<TemplatedControl, ControlTemplateResult?>
public interface IControlTemplate : ITemplate<TemplatedControl, TemplateResult<Control>?>
{
}
public class ControlTemplateResult : TemplateResult<Control>
{
public Control Control { get; }
public ControlTemplateResult(Control control, INameScope nameScope) : base(control, nameScope)
{
Control = control;
}
public new void Deconstruct(out Control control, out INameScope scope)
{
control = Control;
scope = NameScope;
}
}
}

3
src/Markup/Avalonia.Markup.Xaml/Templates/ControlTemplate.cs

@ -1,4 +1,5 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Metadata;
@ -13,6 +14,6 @@ namespace Avalonia.Markup.Xaml.Templates
public Type? TargetType { get; set; }
public ControlTemplateResult? Build(TemplatedControl control) => TemplateContent.Load(Content);
public TemplateResult<Control>? Build(TemplatedControl control) => TemplateContent.Load(Content);
}
}

2
src/Markup/Avalonia.Markup.Xaml/Templates/DataTemplate.cs

@ -30,7 +30,7 @@ namespace Avalonia.Markup.Xaml.Templates
public Control? Build(object? data, Control? existing)
{
return existing ?? TemplateContent.Load(Content)?.Control;
return existing ?? TemplateContent.Load(Content)?.Result;
}
}
}

2
src/Markup/Avalonia.Markup.Xaml/Templates/ItemsPanelTemplate.cs

@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Templates
[TemplateContent]
public object? Content { get; set; }
public Panel? Build() => (Panel?)TemplateContent.Load(Content)?.Control;
public Panel? Build() => (Panel?)TemplateContent.Load(Content)?.Result;
object? ITemplate.Build() => Build();
}

2
src/Markup/Avalonia.Markup.Xaml/Templates/Template.cs

@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Templates
[TemplateContent]
public object? Content { get; set; }
public Control? Build() => TemplateContent.Load(Content)?.Control;
public Control? Build() => TemplateContent.Load(Content)?.Result;
object? ITemplate.Build() => Build();
}

5
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

@ -1,15 +1,16 @@
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
namespace Avalonia.Markup.Xaml.Templates
{
public static class TemplateContent
{
public static ControlTemplateResult? Load(object? templateContent)
public static TemplateResult<Control>? Load(object? templateContent)
{
if (templateContent is Func<IServiceProvider?, object?> direct)
{
return (ControlTemplateResult?)direct(null);
return (TemplateResult<Control>?)direct(null);
}
if (templateContent is null)

2
src/Markup/Avalonia.Markup.Xaml/Templates/TreeDataTemplate.cs

@ -54,7 +54,7 @@ namespace Avalonia.Markup.Xaml.Templates
public Control? Build(object? data)
{
var visualTreeForItem = TemplateContent.Load(Content)?.Control;
var visualTreeForItem = TemplateContent.Load(Content)?.Result;
if (visualTreeForItem != null)
{
visualTreeForItem.DataContext = data;

2
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@ -35,7 +35,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
scope.Complete();
if(typeof(T) == typeof(Control))
return new ControlTemplateResult((Control)obj, scope);
return new TemplateResult<Control>((Control)obj, scope);
return new TemplateResult<T>((T)obj, scope);
};

32
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -554,6 +554,36 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new[] { "Bar" }, target.Selection.SelectedItems);
}
[Fact]
public void Content_Can_Be_Bound_In_ItemContainerTheme()
{
using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
{
var items = new[] { new ItemViewModel("Foo"), new ItemViewModel("Bar") };
var theme = new ControlTheme(typeof(ListBoxItem))
{
Setters =
{
new Setter(ListBoxItem.ContentProperty, new Binding("Caption")),
}
};
var target = new ListBox
{
Template = ListBoxTemplate(),
ItemsSource = items,
ItemContainerTheme = theme,
};
Prepare(target);
var containers = target.GetRealizedContainers().Cast<ListBoxItem>().ToList();
Assert.Equal(2, containers.Count);
Assert.Equal("Foo", containers[0].Content);
Assert.Equal("Bar", containers[1].Content);
}
}
private static FuncControlTemplate ListBoxTemplate()
{
return new FuncControlTemplate<ListBox>((parent, scope) =>
@ -918,6 +948,8 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(1, raised);
}
private record ItemViewModel(string Caption);
private class ResettingCollection : List<string>, INotifyCollectionChanged
{
public ResettingCollection(int itemCount)

95
tests/Avalonia.Controls.UnitTests/MenuItemTests.cs

@ -1,16 +1,16 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Windows.Input;
using Avalonia.Collections;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq;
using Xunit;
@ -36,7 +36,6 @@ namespace Avalonia.Controls.UnitTests
Assert.False(target.Focusable);
}
[Fact]
public void MenuItem_Is_Disabled_When_Command_Is_Enabled_But_IsEnabled_Is_False()
{
@ -393,6 +392,87 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void Header_And_ItemsSource_Can_Be_Bound_In_Style()
{
using var app = Application();
var items = new[]
{
new MenuViewModel("Foo")
{
Children = new[]
{
new MenuViewModel("FooChild"),
},
},
new MenuViewModel("Bar"),
};
var target = new Menu
{
ItemsSource = items,
Styles =
{
new Style(x => x.OfType<MenuItem>())
{
Setters =
{
new Setter(MenuItem.HeaderProperty, new Binding("Header")),
new Setter(MenuItem.ItemsSourceProperty, new Binding("Children")),
}
}
}
};
var root = new TestRoot(true, target);
root.LayoutManager.ExecuteInitialLayoutPass();
var children = target.GetRealizedContainers().Cast<MenuItem>().ToList();
Assert.Equal(2, children.Count);
Assert.Equal("Foo", children[0].Header);
Assert.Equal("Bar", children[1].Header);
Assert.Same(items[0].Children, children[0].ItemsSource);
}
[Fact]
public void Header_And_ItemsSource_Can_Be_Bound_In_ItemContainerTheme()
{
using var app = Application();
var items = new[]
{
new MenuViewModel("Foo")
{
Children = new[]
{
new MenuViewModel("FooChild"),
},
},
new MenuViewModel("Bar"),
};
var target = new Menu
{
ItemsSource = items,
ItemContainerTheme = new ControlTheme(typeof(MenuItem))
{
Setters =
{
new Setter(MenuItem.HeaderProperty, new Binding("Header")),
new Setter(MenuItem.ItemsSourceProperty, new Binding("Children")),
}
}
};
var root = new TestRoot(true, target);
root.LayoutManager.ExecuteInitialLayoutPass();
var children = target.GetRealizedContainers().Cast<MenuItem>().ToList();
Assert.Equal(2, children.Count);
Assert.Equal("Foo", children[0].Header);
Assert.Equal("Bar", children[1].Header);
Assert.Same(items[0].Children, children[0].ItemsSource);
}
private IDisposable Application()
{
var screen = new PixelRect(new PixelPoint(), new PixelSize(100, 100));
@ -447,6 +527,9 @@ namespace Avalonia.Controls.UnitTests
public void RaiseCanExecuteChanged() => _canExecuteChanged?.Invoke(this, EventArgs.Empty);
}
private record MenuViewModel(string Header);
private record MenuViewModel(string Header)
{
public IList<MenuViewModel> Children { get; set;}
}
}
}

18
tests/Avalonia.IntegrationTests.Appium/NativeMenuTests.cs

@ -18,7 +18,7 @@ namespace Avalonia.IntegrationTests.Appium
}
[PlatformFact(TestPlatforms.MacOS)]
public void View_Menu_Select_Button_Tab()
public void MacOS_View_Menu_Select_Button_Tab()
{
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var buttonTab = tabs.FindElementByName("Button");
@ -33,5 +33,21 @@ namespace Avalonia.IntegrationTests.Appium
Assert.True(buttonTab.Selected);
}
[PlatformFact(TestPlatforms.Windows)]
public void Win32_View_Menu_Select_Button_Tab()
{
var tabs = _session.FindElementByAccessibilityId("MainTabs");
var buttonTab = tabs.FindElementByName("Button");
var viewMenu = _session.FindElementByXPath("//MenuItem[@Name='View']");
Assert.False(buttonTab.Selected);
viewMenu.Click();
var buttonMenu = viewMenu.FindElementByName("Button");
buttonMenu.Click();
Assert.True(buttonTab.Selected);
}
}
}

2
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/CompiledBindingExtensionTests.cs

@ -1978,7 +1978,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public bool Match(object data) => FancyDataType?.IsInstanceOfType(data) ?? true;
public Control Build(object data) => TemplateContent.Load(Content)?.Control;
public Control Build(object data) => TemplateContent.Load(Content)?.Result;
}
public class CustomDataTemplateInherit : CustomDataTemplate { }

2
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@ -605,7 +605,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
var control = new ContentControl();
var result = (ContentPresenter)template.Build(control).Control;
var result = (ContentPresenter)template.Build(control).Result;
Assert.NotNull(result);
}

6
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ControlTemplateTests.cs

@ -258,7 +258,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var parent = (ContentControl)template.Build(new ContentControl()).Control;
var parent = (ContentControl)template.Build(new ContentControl()).Result;
Assert.Equal("parent", parent.Name);
@ -283,7 +283,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal(typeof(ContentControl), template.TargetType);
Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Control);
Assert.IsType(typeof(ContentPresenter), template.Build(new ContentControl()).Result);
}
[Fact]
@ -299,7 +299,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
";
var template = AvaloniaRuntimeXamlLoader.Parse<ControlTemplate>(xaml);
var panel = (Panel)template.Build(new ContentControl()).Control;
var panel = (Panel)template.Build(new ContentControl()).Result;
Assert.Equal(2, panel.Children.Count);

Loading…
Cancel
Save