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"
Spacing="16">
<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>

11
src/Avalonia.Controls/ItemsControl.cs

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

32
src/Avalonia.Controls/RelativePanel.cs

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

8
src/Avalonia.Controls/ToolTipService.cs

@ -28,14 +28,22 @@ namespace Avalonia.Controls
{
control.PointerEnter -= ControlPointerEnter;
control.PointerLeave -= ControlPointerLeave;
control.DetachedFromVisualTree -= ControlDetaching;
}
if (e.NewValue != null)
{
control.PointerEnter += ControlPointerEnter;
control.PointerLeave += ControlPointerLeave;
control.DetachedFromVisualTree += ControlDetaching;
}
}
private void ControlDetaching(object sender, VisualTreeAttachmentEventArgs e)
{
var control = (Control)sender;
Close(control);
}
/// <summary>
/// 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")]
public static extern bool SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetFocus();
[DllImport("user32.dll")]
public static extern bool SetParent(IntPtr hWnd, IntPtr hWndNewParent);
[DllImport("user32.dll")]
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")]
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
{
private readonly IWindowBaseImpl _parent;
private bool _dropShadowHint = true;
private Size? _maxAutoSize;
@ -19,18 +20,25 @@ namespace Avalonia.Win32
public override void Show()
{
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;
@ -118,6 +126,7 @@ namespace Avalonia.Win32
private PopupImpl(IWindowBaseImpl parent, bool dummy) : base()
{
_parent = parent;
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.Templates;
using Avalonia.LogicalTree;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Xunit;
@ -155,6 +156,7 @@ namespace Avalonia.Controls.UnitTests
IsVirtualized = false
};
var root = new TestRoot(target);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
@ -247,6 +249,7 @@ namespace Avalonia.Controls.UnitTests
IsVirtualized = false
};
var root = new TestRoot(target);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();

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

@ -131,6 +131,7 @@ namespace Avalonia.Controls.UnitTests
var child = new Control();
var items = new AvaloniaList<Control>(child);
var root = new TestRoot(target);
target.Template = GetTemplate();
target.Items = items;
items.RemoveAt(0);
@ -283,6 +284,7 @@ namespace Avalonia.Controls.UnitTests
var items = new AvaloniaList<string> { "Foo" };
var called = false;
var root = new TestRoot(target);
target.Template = GetTemplate();
target.Items = items;
target.ApplyTemplate();
@ -303,6 +305,7 @@ namespace Avalonia.Controls.UnitTests
var items = new AvaloniaList<string> { "Foo", "Bar" };
var called = false;
var root = new TestRoot(target);
target.Template = GetTemplate();
target.Items = items;
target.ApplyTemplate();
@ -376,6 +379,7 @@ namespace Avalonia.Controls.UnitTests
Items = new[] { 1, 2, 3 },
};
var root = new TestRoot(target);
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
};
var root = new TestRoot(listBox);
listBox.BeginInit();
listBox.SelectedIndex = 1;
@ -480,6 +482,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Template = Template(),
};
var root = new TestRoot(target);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
items.Add(new Item { IsSelected = true });
@ -531,6 +534,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectedIndex = 1;
Assert.Equal(items[1], target.SelectedItem);
@ -549,6 +553,45 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.NotNull(receivedArgs);
Assert.Empty(receivedArgs.AddedItems);
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]
@ -879,6 +922,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Items = items,
};
var root = new TestRoot(target);
target.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,
};
var root = new TestRoot(target);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
@ -1043,6 +1044,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
SelectionMode = SelectionMode.Multiple,
};
var root = new TestRoot(target);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
@ -1076,6 +1078,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
SelectionMode = SelectionMode.Multiple,
};
var root = new TestRoot(target);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
@ -1199,6 +1202,7 @@ namespace Avalonia.Controls.UnitTests.Primitives
Template = Template(),
};
var root = new TestRoot(target);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
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, 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));
}
[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]
public void Should_Open_On_Pointer_Enter()

Loading…
Cancel
Save