Browse Source

Merge branch 'master' into fixes/default-theme-control-catalog

pull/4302/head
Jumar Macato 6 years ago
committed by GitHub
parent
commit
a6b9724be2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      samples/ControlCatalog/Pages/ListBoxPage.xaml
  2. 11
      src/Avalonia.Controls/ItemsControl.cs
  3. 32
      src/Avalonia.Controls/RelativePanel.cs
  4. 8
      src/Avalonia.Controls/ToolTipService.cs
  5. 12
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  6. 29
      src/Windows/Avalonia.Win32/PopupImpl.cs
  7. 3
      tests/Avalonia.Controls.UnitTests/CarouselTests.cs
  8. 4
      tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs
  9. 44
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  10. 4
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  11. 50
      tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs
  12. 34
      tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

8
samples/ControlCatalog/Pages/ListBoxPage.xaml

@ -10,7 +10,13 @@
HorizontalAlignment="Center" HorizontalAlignment="Center"
Spacing="16"> Spacing="16">
<StackPanel Orientation="Vertical" Spacing="8"> <StackPanel Orientation="Vertical" Spacing="8">
<ListBox Items="{Binding Items}" SelectedItem="{Binding SelectedItem}" AutoScrollToSelectedItem="True" SelectionMode="{Binding SelectionMode}" Width="250" Height="350"></ListBox> <ListBox Items="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
SelectedItems="{Binding SelectedItems}"
AutoScrollToSelectedItem="True"
SelectionMode="{Binding SelectionMode}"
Width="250"
Height="350"/>
<Button Command="{Binding AddItemCommand}">Add</Button> <Button Command="{Binding AddItemCommand}">Add</Button>

11
src/Avalonia.Controls/ItemsControl.cs

@ -70,7 +70,6 @@ namespace Avalonia.Controls
public ItemsControl() public ItemsControl()
{ {
PseudoClasses.Add(":empty"); PseudoClasses.Add(":empty");
SubscribeToItems(_items);
} }
/// <summary> /// <summary>
@ -265,6 +264,11 @@ namespace Avalonia.Controls
{ {
} }
protected override void OnInitialized()
{
SubscribeToItems(_items);
}
/// <summary> /// <summary>
/// Handles directional navigation within the <see cref="ItemsControl"/>. /// Handles directional navigation within the <see cref="ItemsControl"/>.
/// </summary> /// </summary>
@ -330,7 +334,10 @@ namespace Avalonia.Controls
Presenter.Items = newValue; Presenter.Items = newValue;
} }
SubscribeToItems(newValue); if (IsInitialized)
{
SubscribeToItems(newValue);
}
} }
/// <summary> /// <summary>

32
src/Avalonia.Controls/RelativePanel.cs

@ -54,7 +54,7 @@ namespace Avalonia.Controls
} }
_childGraph.Measure(availableSize); _childGraph.Measure(availableSize);
_childGraph.Reset(); _childGraph.Reset(false);
var boundingSize = _childGraph.GetBoundingSize(Width.IsNaN(), Height.IsNaN()); var boundingSize = _childGraph.GetBoundingSize(Width.IsNaN(), Height.IsNaN());
_childGraph.Reset(); _childGraph.Reset();
_childGraph.Measure(boundingSize); _childGraph.Measure(boundingSize);
@ -119,17 +119,22 @@ namespace Avalonia.Controls
public void Arrange(Size arrangeSize) => Element.Arrange(new Rect(Left, Top, Math.Max(arrangeSize.Width - Left - Right, 0), Math.Max(arrangeSize.Height - Top - Bottom, 0))); public void Arrange(Size arrangeSize) => Element.Arrange(new Rect(Left, Top, Math.Max(arrangeSize.Width - Left - Right, 0), Math.Max(arrangeSize.Height - Top - Bottom, 0)));
public void Reset() public void Reset(bool clearPos)
{ {
Left = double.NaN; if (clearPos)
Top = double.NaN; {
Right = double.NaN; Left = double.NaN;
Bottom = double.NaN; Top = double.NaN;
Right = double.NaN;
Bottom = double.NaN;
}
Measured = false; Measured = false;
} }
public Size GetBoundingSize() public Size GetBoundingSize()
{ {
if (Left < 0 || Top < 0) return default;
if (Measured) if (Measured)
return BoundingSize; return BoundingSize;
@ -209,7 +214,7 @@ namespace Avalonia.Controls
_nodeDic.Clear(); _nodeDic.Clear();
} }
public void Reset() => _nodeDic.Values.Do(node => node.Reset()); public void Reset(bool clearPos = true) => _nodeDic.Values.Do(node => node.Reset(clearPos));
public GraphNode? AddLink(GraphNode from, Layoutable? to) public GraphNode? AddLink(GraphNode from, Layoutable? to)
{ {
@ -255,28 +260,21 @@ namespace Avalonia.Controls
foreach (var node in nodes) foreach (var node in nodes)
{ {
/*
*
*
*/
if (!node.Measured && !node.OutgoingNodes.Any()) if (!node.Measured && !node.OutgoingNodes.Any())
{ {
MeasureChild(node); MeasureChild(node);
continue; continue;
} }
// 判断依赖元素是否全部排列完毕
if (node.OutgoingNodes.All(item => item.Measured)) if (node.OutgoingNodes.All(item => item.Measured))
{ {
MeasureChild(node); MeasureChild(node);
continue; continue;
} }
// 判断是否有循环
if (!set.Add(node.Element)) if (!set.Add(node.Element))
throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete."); throw new Exception("RelativePanel error: Circular dependency detected. Layout could not complete.");
// 没有循环,且有依赖,则继续往下
Measure(node.OutgoingNodes, set); Measure(node.OutgoingNodes, set);
if (!node.Measured) if (!node.Measured)

8
src/Avalonia.Controls/ToolTipService.cs

@ -28,14 +28,22 @@ namespace Avalonia.Controls
{ {
control.PointerEnter -= ControlPointerEnter; control.PointerEnter -= ControlPointerEnter;
control.PointerLeave -= ControlPointerLeave; control.PointerLeave -= ControlPointerLeave;
control.DetachedFromVisualTree -= ControlDetaching;
} }
if (e.NewValue != null) if (e.NewValue != null)
{ {
control.PointerEnter += ControlPointerEnter; control.PointerEnter += ControlPointerEnter;
control.PointerLeave += ControlPointerLeave; control.PointerLeave += ControlPointerLeave;
control.DetachedFromVisualTree += ControlDetaching;
} }
} }
private void ControlDetaching(object sender, VisualTreeAttachmentEventArgs e)
{
var control = (Control)sender;
Close(control);
}
/// <summary> /// <summary>
/// Called when the pointer enters a control with an attached tooltip. /// Called when the pointer enters a control with an attached tooltip.

12
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -1060,9 +1060,21 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool SetFocus(IntPtr hWnd); public static extern bool SetFocus(IntPtr hWnd);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern IntPtr GetFocus();
[DllImport("user32.dll")]
public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent); public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern IntPtr GetParent(IntPtr hWnd); public static extern IntPtr GetParent(IntPtr hWnd);
public enum GetAncestorFlags
{
GA_PARENT = 1,
GA_ROOT = 2,
GA_ROOTOWNER = 3
}
[DllImport("user32.dll")]
public static extern IntPtr GetAncestor(IntPtr hwnd, GetAncestorFlags gaFlags);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow); public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);

29
src/Windows/Avalonia.Win32/PopupImpl.cs

@ -7,6 +7,7 @@ namespace Avalonia.Win32
{ {
class PopupImpl : WindowImpl, IPopupImpl class PopupImpl : WindowImpl, IPopupImpl
{ {
private readonly IWindowBaseImpl _parent;
private bool _dropShadowHint = true; private bool _dropShadowHint = true;
private Size? _maxAutoSize; private Size? _maxAutoSize;
@ -19,18 +20,25 @@ namespace Avalonia.Win32
public override void Show() public override void Show()
{ {
UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate); UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate);
var parent = UnmanagedMethods.GetParent(Handle.Handle);
if (parent != IntPtr.Zero)
{
IntPtr nextParent = parent;
while (nextParent != IntPtr.Zero)
{
parent = nextParent;
nextParent = UnmanagedMethods.GetParent(parent);
}
UnmanagedMethods.SetFocus(parent); // We need to steal focus if it's held by a child window of our toplevel window
var parent = _parent;
while(parent != null)
{
if(parent is PopupImpl pi)
parent = pi._parent;
else
break;
} }
if(parent == null)
return;
var focusOwner = UnmanagedMethods.GetFocus();
if (focusOwner != IntPtr.Zero &&
UnmanagedMethods.GetAncestor(focusOwner, UnmanagedMethods.GetAncestorFlags.GA_ROOT)
== parent.Handle.Handle)
UnmanagedMethods.SetFocus(parent.Handle.Handle);
} }
protected override bool ShouldTakeFocusOnClick => false; protected override bool ShouldTakeFocusOnClick => false;
@ -118,6 +126,7 @@ namespace Avalonia.Win32
private PopupImpl(IWindowBaseImpl parent, bool dummy) : base() private PopupImpl(IWindowBaseImpl parent, bool dummy) : base()
{ {
_parent = parent;
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize)); PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));
} }

3
tests/Avalonia.Controls.UnitTests/CarouselTests.cs

@ -4,6 +4,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.UnitTests;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Xunit; using Xunit;
@ -155,6 +156,7 @@ namespace Avalonia.Controls.UnitTests
IsVirtualized = false IsVirtualized = false
}; };
var root = new TestRoot(target);
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
@ -247,6 +249,7 @@ namespace Avalonia.Controls.UnitTests
IsVirtualized = false IsVirtualized = false
}; };
var root = new TestRoot(target);
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();

4
tests/Avalonia.Controls.UnitTests/ItemsControlTests.cs

@ -131,6 +131,7 @@ namespace Avalonia.Controls.UnitTests
var child = new Control(); var child = new Control();
var items = new AvaloniaList<Control>(child); var items = new AvaloniaList<Control>(child);
var root = new TestRoot(target);
target.Template = GetTemplate(); target.Template = GetTemplate();
target.Items = items; target.Items = items;
items.RemoveAt(0); items.RemoveAt(0);
@ -283,6 +284,7 @@ namespace Avalonia.Controls.UnitTests
var items = new AvaloniaList<string> { "Foo" }; var items = new AvaloniaList<string> { "Foo" };
var called = false; var called = false;
var root = new TestRoot(target);
target.Template = GetTemplate(); target.Template = GetTemplate();
target.Items = items; target.Items = items;
target.ApplyTemplate(); target.ApplyTemplate();
@ -303,6 +305,7 @@ namespace Avalonia.Controls.UnitTests
var items = new AvaloniaList<string> { "Foo", "Bar" }; var items = new AvaloniaList<string> { "Foo", "Bar" };
var called = false; var called = false;
var root = new TestRoot(target);
target.Template = GetTemplate(); target.Template = GetTemplate();
target.Items = items; target.Items = items;
target.ApplyTemplate(); target.ApplyTemplate();
@ -376,6 +379,7 @@ namespace Avalonia.Controls.UnitTests
Items = new[] { 1, 2, 3 }, Items = new[] { 1, 2, 3 },
}; };
var root = new TestRoot(target);
Assert.DoesNotContain(":empty", target.Classes); Assert.DoesNotContain(":empty", target.Classes);
} }

44
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -170,6 +170,8 @@ namespace Avalonia.Controls.UnitTests.Primitives
SelectionMode = SelectionMode.Single | SelectionMode.AlwaysSelected SelectionMode = SelectionMode.Single | SelectionMode.AlwaysSelected
}; };
var root = new TestRoot(listBox);
listBox.BeginInit(); listBox.BeginInit();
listBox.SelectedIndex = 1; listBox.SelectedIndex = 1;
@ -480,6 +482,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Template = Template(), Template = Template(),
}; };
var root = new TestRoot(target);
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
items.Add(new Item { IsSelected = true }); items.Add(new Item { IsSelected = true });
@ -531,6 +534,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
}; };
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedIndex = 1; target.SelectedIndex = 1;
Assert.Equal(items[1], target.SelectedItem); Assert.Equal(items[1], target.SelectedItem);
@ -549,6 +553,45 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.NotNull(receivedArgs); Assert.NotNull(receivedArgs);
Assert.Empty(receivedArgs.AddedItems); Assert.Empty(receivedArgs.AddedItems);
Assert.Equal(new[] { removed }, receivedArgs.RemovedItems); Assert.Equal(new[] { removed }, receivedArgs.RemovedItems);
Assert.False(items.Single().IsSelected);
}
[Fact]
public void Removing_Selected_Item_Should_Clear_Selection_With_BeginInit()
{
var items = new AvaloniaList<Item>
{
new Item(),
new Item(),
};
var target = new SelectingItemsControl();
target.BeginInit();
target.Items = items;
target.Template = Template();
target.EndInit();
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedIndex = 0;
Assert.Equal(items[0], target.SelectedItem);
Assert.Equal(0, target.SelectedIndex);
SelectionChangedEventArgs receivedArgs = null;
target.SelectionChanged += (_, args) => receivedArgs = args;
var removed = items[0];
items.RemoveAt(0);
Assert.Null(target.SelectedItem);
Assert.Equal(-1, target.SelectedIndex);
Assert.NotNull(receivedArgs);
Assert.Empty(receivedArgs.AddedItems);
Assert.Equal(new[] { removed }, receivedArgs.RemovedItems);
Assert.False(items.Single().IsSelected);
} }
[Fact] [Fact]
@ -879,6 +922,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Items = items, Items = items,
}; };
var root = new TestRoot(target);
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();

4
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -1014,6 +1014,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
SelectionMode = SelectionMode.Multiple, SelectionMode = SelectionMode.Multiple,
}; };
var root = new TestRoot(target);
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
@ -1043,6 +1044,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
SelectionMode = SelectionMode.Multiple, SelectionMode = SelectionMode.Multiple,
}; };
var root = new TestRoot(target);
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
@ -1076,6 +1078,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
SelectionMode = SelectionMode.Multiple, SelectionMode = SelectionMode.Multiple,
}; };
var root = new TestRoot(target);
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
@ -1199,6 +1202,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Template = Template(), Template = Template(),
}; };
var root = new TestRoot(target);
target.ApplyTemplate(); target.ApplyTemplate();
target.Presenter.ApplyTemplate(); target.Presenter.ApplyTemplate();
items.Add(new ItemContainer { IsSelected = true }); items.Add(new ItemContainer { IsSelected = true });

50
tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs

@ -82,5 +82,55 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds); Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(0, 20, 20, 20), target.Children[1].Bounds); Assert.Equal(new Rect(0, 20, 20, 20), target.Children[1].Bounds);
} }
[Fact]
public void LeftOf_Measures_Correctly()
{
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
var target = new RelativePanel
{
VerticalAlignment = Layout.VerticalAlignment.Center,
HorizontalAlignment = Layout.HorizontalAlignment.Center,
Children =
{
rect1, rect2
}
};
RelativePanel.SetLeftOf(rect2, rect1);
target.Measure(new Size(400, 400));
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(20, 20), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(-20, 0, 20, 20), target.Children[1].Bounds);
}
[Fact]
public void Above_Measures_Correctly()
{
var rect1 = new Rectangle { Height = 20, Width = 20 };
var rect2 = new Rectangle { Height = 20, Width = 20 };
var target = new RelativePanel
{
VerticalAlignment = Layout.VerticalAlignment.Center,
HorizontalAlignment = Layout.HorizontalAlignment.Center,
Children =
{
rect1, rect2
}
};
RelativePanel.SetAbove(rect2, rect1);
target.Measure(new Size(400, 400));
target.Arrange(new Rect(target.DesiredSize));
Assert.Equal(new Size(20, 20), target.Bounds.Size);
Assert.Equal(new Rect(0, 0, 20, 20), target.Children[0].Bounds);
Assert.Equal(new Rect(0, -20, 20, 20), target.Children[1].Bounds);
}
} }
} }

34
tests/Avalonia.Controls.UnitTests/ToolTipTests.cs

@ -30,6 +30,40 @@ namespace Avalonia.Controls.UnitTests
Assert.False(ToolTip.GetIsOpen(control)); Assert.False(ToolTip.GetIsOpen(control));
} }
[Fact]
public void Should_Close_When_Control_Detaches()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = new Window();
var panel = new Panel();
var target = new Decorator()
{
[ToolTip.TipProperty] = "Tip",
[ToolTip.ShowDelayProperty] = 0
};
panel.Children.Add(target);
window.Content = panel;
window.ApplyTemplate();
window.Presenter.ApplyTemplate();
Assert.True((target as IVisual).IsAttachedToVisualTree);
_mouseHelper.Enter(target);
Assert.True(ToolTip.GetIsOpen(target));
panel.Children.Remove(target);
Assert.False(ToolTip.GetIsOpen(target));
}
}
[Fact] [Fact]
public void Should_Open_On_Pointer_Enter() public void Should_Open_On_Pointer_Enter()

Loading…
Cancel
Save