Browse Source

Merge remote-tracking branch 'origin/master' into managed-file-dialog

pull/2777/head
Dan Walmsley 7 years ago
parent
commit
d6c633cc66
  1. 2
      src/Avalonia.Controls/Panel.cs
  2. 13
      src/Avalonia.Input/Gestures.cs
  3. 44
      src/Avalonia.Styling/StyledElement.cs
  4. 6
      src/Avalonia.Themes.Default/Button.xaml
  5. 8
      src/Avalonia.Themes.Default/ToggleButton.xaml
  6. 16
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  7. 6
      src/Avalonia.Visuals/Rendering/IRenderer.cs
  8. 3
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  9. 33
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  10. 19
      src/Avalonia.Visuals/Visual.cs
  11. 2
      tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs
  12. 2
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  13. 208
      tests/Avalonia.Input.UnitTests/GesturesTests.cs
  14. 2
      tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj
  15. 112
      tests/Avalonia.Interactivity.UnitTests/GestureTests.cs
  16. 4
      tests/Avalonia.LeakTests/ControlTests.cs
  17. 1
      tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
  18. 3
      tests/Avalonia.UnitTests/MouseTestHelper.cs
  19. 174
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs
  20. 47
      tests/Avalonia.Visuals.UnitTests/VisualTests.cs

2
src/Avalonia.Controls/Panel.cs

@ -112,7 +112,7 @@ namespace Avalonia.Controls
case NotifyCollectionChangedAction.Add:
controls = e.NewItems.OfType<Control>().ToList();
LogicalChildren.InsertRange(e.NewStartingIndex, controls);
VisualChildren.AddRange(e.NewItems.OfType<Visual>());
VisualChildren.InsertRange(e.NewStartingIndex, e.NewItems.OfType<Visual>());
break;
case NotifyCollectionChangedAction.Move:

13
src/Avalonia.Input/Gestures.cs

@ -18,6 +18,11 @@ namespace Avalonia.Input
RoutingStrategies.Bubble,
typeof(Gestures));
public static readonly RoutedEvent<RoutedEventArgs> RightTappedEvent = RoutedEvent.Register<RoutedEventArgs>(
"RightTapped",
RoutingStrategies.Bubble,
typeof(Gestures));
public static readonly RoutedEvent<ScrollGestureEventArgs> ScrollGestureEvent =
RoutedEvent.Register<ScrollGestureEventArgs>(
"ScrollGesture", RoutingStrategies.Bubble, typeof(Gestures));
@ -46,7 +51,7 @@ namespace Avalonia.Input
}
else if (s_lastPress?.IsAlive == true && e.ClickCount == 2 && s_lastPress.Target == e.Source)
{
if (!ev.Handled)
if (e.MouseButton != MouseButton.Right)
{
e.Source.RaiseEvent(new RoutedEventArgs(DoubleTappedEvent));
}
@ -62,10 +67,8 @@ namespace Avalonia.Input
if (s_lastPress?.IsAlive == true && s_lastPress.Target == e.Source)
{
if (!ev.Handled)
{
((IInteractive)s_lastPress.Target).RaiseEvent(new RoutedEventArgs(TappedEvent));
}
var et = e.MouseButton != MouseButton.Right ? TappedEvent : RightTappedEvent;
((IInteractive)s_lastPress.Target).RaiseEvent(new RoutedEventArgs(et));
}
}
}

44
src/Avalonia.Styling/StyledElement.cs

@ -568,6 +568,28 @@ namespace Avalonia
});
}
protected virtual void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
SetLogicalParent(e.NewItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Remove:
ClearLogicalParent(e.OldItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Replace:
ClearLogicalParent(e.OldItems.Cast<ILogical>());
SetLogicalParent(e.NewItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Reset:
throw new NotSupportedException("Reset should not be signaled on LogicalChildren collection");
}
}
/// <summary>
/// Called when the styled element is added to a rooted logical tree.
/// </summary>
@ -736,28 +758,6 @@ namespace Avalonia
OnDataContextChanged(EventArgs.Empty);
}
private void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
SetLogicalParent(e.NewItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Remove:
ClearLogicalParent(e.OldItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Replace:
ClearLogicalParent(e.OldItems.Cast<ILogical>());
SetLogicalParent(e.NewItems.Cast<ILogical>());
break;
case NotifyCollectionChangedAction.Reset:
throw new NotSupportedException("Reset should not be signaled on LogicalChildren collection");
}
}
private void SetLogicalParent(IEnumerable<ILogical> children)
{
foreach (var i in children)

6
src/Avalonia.Themes.Default/Button.xaml

@ -22,13 +22,13 @@
</ControlTemplate>
</Setter>
</Style>
<Style Selector="Button:pointerover">
<Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="Button:pressed">
<Style Selector="Button:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
</Style>
<Style Selector="Button:disabled">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style>
</Styles>
</Styles>

8
src/Avalonia.Themes.Default/ToggleButton.xaml

@ -22,17 +22,17 @@
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ToggleButton:checked">
<Style Selector="ToggleButton:checked /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="ToggleButton:pointerover">
<Style Selector="ToggleButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="ToggleButton:pressed">
<Style Selector="ToggleButton:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
</Style>
<Style Selector="ToggleButton:disabled">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style>
</Styles>
</Styles>

16
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -30,6 +30,7 @@ namespace Avalonia.Rendering
private bool _disposed;
private volatile IRef<Scene> _scene;
private DirtyVisuals _dirty;
private HashSet<IVisual> _recalculateChildren;
private IRef<IRenderTargetBitmapImpl> _overlay;
private int _lastSceneId = -1;
private DisplayDirtyRects _dirtyRectsDisplay = new DisplayDirtyRects();
@ -135,6 +136,8 @@ namespace Avalonia.Rendering
DisposeRenderTarget();
}
public void RecalculateChildren(IVisual visual) => _recalculateChildren?.Add(visual);
void DisposeRenderTarget()
{
using (var l = _lock.TryLock())
@ -229,6 +232,8 @@ namespace Avalonia.Rendering
internal void UnitTestRender() => Render(false);
internal Scene UnitTestScene() => _scene.Item;
private void Render(bool forceComposite)
{
using (var l = _lock.TryLock())
@ -516,10 +521,19 @@ namespace Avalonia.Rendering
if (_dirty == null)
{
_dirty = new DirtyVisuals();
_recalculateChildren = new HashSet<IVisual>();
_sceneBuilder.UpdateAll(scene);
}
else if (_dirty.Count > 0)
else
{
foreach (var visual in _recalculateChildren)
{
var node = scene.FindNode(visual);
((VisualNode)node)?.SortChildren(scene);
}
_recalculateChildren.Clear();
foreach (var visual in _dirty)
{
_sceneBuilder.Update(scene, visual);

6
src/Avalonia.Visuals/Rendering/IRenderer.cs

@ -50,6 +50,12 @@ namespace Avalonia.Rendering
/// <returns>The visuals at the specified point, topmost first.</returns>
IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter);
/// <summary>
/// Informs the renderer that the z-ordering of a visual's children has changed.
/// </summary>
/// <param name="visual">The visual.</param>
void RecalculateChildren(IVisual visual);
/// <summary>
/// Called when a resize notification is received by the control being rendered.
/// </summary>

3
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@ -163,6 +163,9 @@ namespace Avalonia.Rendering
return HitTest(root, p, filter);
}
/// <inheritdoc/>
public void RecalculateChildren(IVisual visual) => AddDirty(visual);
/// <inheritdoc/>
public void Start()
{

33
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@ -172,6 +172,37 @@ namespace Avalonia.Rendering.SceneGraph
old.Dispose();
}
/// <summary>
/// Sorts the <see cref="Children"/> collection according to the order of the visual's
/// children and their z-index.
/// </summary>
/// <param name="scene">The scene that the node is a part of.</param>
public void SortChildren(Scene scene)
{
var keys = new List<long>();
for (var i = 0; i < Visual.VisualChildren.Count; ++i)
{
var child = Visual.VisualChildren[i];
var zIndex = child.ZIndex;
keys.Add(((long)zIndex << 32) + i);
}
keys.Sort();
_children.Clear();
foreach (var i in keys)
{
var child = Visual.VisualChildren[(int)(i & 0xffffffff)];
var node = scene.FindNode(child);
if (node != null)
{
_children.Add(node);
}
}
}
/// <summary>
/// Removes items in the <see cref="Children"/> collection from the specified index
/// to the end.
@ -236,7 +267,7 @@ namespace Avalonia.Rendering.SceneGraph
{
foreach (var operation in DrawOperations)
{
if (operation.Item.HitTest(p))
if (operation?.Item?.HitTest(p) == true)
{
return true;
}

19
src/Avalonia.Visuals/Visual.cs

@ -111,6 +111,7 @@ namespace Avalonia
IsVisibleProperty,
OpacityProperty);
RenderTransformProperty.Changed.Subscribe(RenderTransformChanged);
ZIndexProperty.Changed.Subscribe(ZIndexChanged);
}
/// <summary>
@ -345,6 +346,12 @@ namespace Avalonia
}
}
protected override void LogicalChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
base.LogicalChildrenCollectionChanged(sender, e);
VisualRoot?.Renderer?.RecalculateChildren(this);
}
/// <summary>
/// Calls the <see cref="OnAttachedToVisualTree(VisualTreeAttachmentEventArgs)"/> method
/// for this control and all of its visual descendants.
@ -501,6 +508,18 @@ namespace Avalonia
}
}
/// <summary>
/// Called when the <see cref="ZIndex"/> property changes on any control.
/// </summary>
/// <param name="e">The event args.</param>
private static void ZIndexChanged(AvaloniaPropertyChangedEventArgs e)
{
var sender = e.Sender as IVisual;
var parent = sender?.VisualParent;
sender?.InvalidateVisual();
parent?.VisualRoot?.Renderer?.RecalculateChildren(parent);
}
/// <summary>
/// Called when the <see cref="RenderTransform"/>'s <see cref="Transform.Changed"/> event
/// is fired.

2
tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs

@ -9,8 +9,8 @@ using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Data;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Xunit;

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

@ -13,7 +13,7 @@ using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Data;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests.Primitives

208
tests/Avalonia.Input.UnitTests/GesturesTests.cs

@ -0,0 +1,208 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Interactivity.UnitTests
{
public class GesturesTests
{
private MouseTestHelper _mouse = new MouseTestHelper();
[Fact]
public void Tapped_Should_Follow_Pointer_Pressed_Released()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var result = new List<string>();
AddHandlers(decorator, border, result, false);
_mouse.Click(border);
Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt" }, result);
}
[Fact]
public void Tapped_Should_Be_Raised_Even_When_Pressed_Released_Handled()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var result = new List<string>();
AddHandlers(decorator, border, result, true);
_mouse.Click(border);
Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt" }, result);
}
[Fact]
public void Tapped_Should_Be_Raised_For_Middle_Button()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var raised = false;
decorator.AddHandler(Gestures.TappedEvent, (s, e) => raised = true);
_mouse.Click(border, MouseButton.Middle);
Assert.True(raised);
}
[Fact]
public void Tapped_Should_Not_Be_Raised_For_Right_Button()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var raised = false;
decorator.AddHandler(Gestures.TappedEvent, (s, e) => raised = true);
_mouse.Click(border, MouseButton.Right);
Assert.False(raised);
}
[Fact]
public void RightTapped_Should_Be_Raised_For_Right_Button()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var raised = false;
decorator.AddHandler(Gestures.RightTappedEvent, (s, e) => raised = true);
_mouse.Click(border, MouseButton.Right);
Assert.True(raised);
}
[Fact]
public void DoubleTapped_Should_Follow_Pointer_Pressed_Released_Pressed()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var result = new List<string>();
AddHandlers(decorator, border, result, false);
_mouse.Click(border);
_mouse.Down(border, clickCount: 2);
Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt", "bp", "dp", "bdt", "ddt" }, result);
}
[Fact]
public void DoubleTapped_Should_Be_Raised_Even_When_Pressed_Released_Handled()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var result = new List<string>();
AddHandlers(decorator, border, result, true);
_mouse.Click(border);
_mouse.Down(border, clickCount: 2);
Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt", "bp", "dp", "bdt", "ddt" }, result);
}
[Fact]
public void DoubleTapped_Should_Be_Raised_For_Middle_Button()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var raised = false;
decorator.AddHandler(Gestures.DoubleTappedEvent, (s, e) => raised = true);
_mouse.Click(border, MouseButton.Middle);
_mouse.Down(border, MouseButton.Middle, clickCount: 2);
Assert.True(raised);
}
[Fact]
public void DoubleTapped_Should_Not_Be_Raised_For_Right_Button()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var raised = false;
decorator.AddHandler(Gestures.DoubleTappedEvent, (s, e) => raised = true);
_mouse.Click(border, MouseButton.Right);
_mouse.Down(border, MouseButton.Right, clickCount: 2);
Assert.False(raised);
}
private void AddHandlers(
Decorator decorator,
Border border,
IList<string> result,
bool markHandled)
{
decorator.AddHandler(Border.PointerPressedEvent, (s, e) =>
{
result.Add("dp");
if (markHandled)
{
e.Handled = true;
}
});
decorator.AddHandler(Border.PointerReleasedEvent, (s, e) =>
{
result.Add("dr");
if (markHandled)
{
e.Handled = true;
}
});
border.AddHandler(Border.PointerPressedEvent, (s, e) => result.Add("bp"));
border.AddHandler(Border.PointerReleasedEvent, (s, e) => result.Add("br"));
decorator.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("dt"));
decorator.AddHandler(Gestures.DoubleTappedEvent, (s, e) => result.Add("ddt"));
border.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("bt"));
border.AddHandler(Gestures.DoubleTappedEvent, (s, e) => result.Add("bdt"));
}
}
}

2
tests/Avalonia.Interactivity.UnitTests/Avalonia.Interactivity.UnitTests.csproj

@ -20,7 +20,7 @@
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj" />
<Compile Include="..\Avalonia.Controls.UnitTests\MouseTestHelper.cs" />
<ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />

112
tests/Avalonia.Interactivity.UnitTests/GestureTests.cs

@ -1,112 +0,0 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Controls.UnitTests;
using Avalonia.Input;
using Xunit;
namespace Avalonia.Interactivity.UnitTests
{
public class GestureTests
{
private MouseTestHelper _mouse = new MouseTestHelper();
[Fact]
public void Tapped_Should_Follow_Pointer_Pressed_Released()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var result = new List<string>();
decorator.AddHandler(Border.PointerPressedEvent, (s, e) => result.Add("dp"));
decorator.AddHandler(Border.PointerReleasedEvent, (s, e) => result.Add("dr"));
decorator.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("dt"));
border.AddHandler(Border.PointerPressedEvent, (s, e) => result.Add("bp"));
border.AddHandler(Border.PointerReleasedEvent, (s, e) => result.Add("br"));
border.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("bt"));
_mouse.Click(border);
Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt" }, result);
}
[Fact]
public void Tapped_Should_Be_Raised_Even_When_PointerPressed_Handled()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var result = new List<string>();
border.AddHandler(Border.PointerPressedEvent, (s, e) => e.Handled = true);
decorator.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("dt"));
border.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("bt"));
_mouse.Click(border);
Assert.Equal(new[] { "bt", "dt" }, result);
}
[Fact]
public void DoubleTapped_Should_Follow_Pointer_Pressed_Released_Pressed()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var result = new List<string>();
decorator.AddHandler(Border.PointerPressedEvent, (s, e) => result.Add("dp"));
decorator.AddHandler(Border.PointerReleasedEvent, (s, e) => result.Add("dr"));
decorator.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("dt"));
decorator.AddHandler(Gestures.DoubleTappedEvent, (s, e) => result.Add("ddt"));
border.AddHandler(Border.PointerPressedEvent, (s, e) => result.Add("bp"));
border.AddHandler(Border.PointerReleasedEvent, (s, e) => result.Add("br"));
border.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("bt"));
border.AddHandler(Gestures.DoubleTappedEvent, (s, e) => result.Add("bdt"));
_mouse.Click(border);
_mouse.Down(border, clickCount: 2);
Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt", "bp", "dp", "bdt", "ddt" }, result);
}
[Fact]
public void DoubleTapped_Should_Not_Be_Rasied_if_Pressed_is_Handled()
{
Border border = new Border();
var decorator = new Decorator
{
Child = border
};
var result = new List<string>();
decorator.AddHandler(Border.PointerPressedEvent, (s, e) =>
{
result.Add("dp");
e.Handled = true;
});
decorator.AddHandler(Border.PointerReleasedEvent, (s, e) => result.Add("dr"));
decorator.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("dt"));
decorator.AddHandler(Gestures.DoubleTappedEvent, (s, e) => result.Add("ddt"));
border.AddHandler(Border.PointerPressedEvent, (s, e) => result.Add("bp"));
border.AddHandler(Border.PointerReleasedEvent, (s, e) => result.Add("br"));
border.AddHandler(Gestures.TappedEvent, (s, e) => result.Add("bt"));
border.AddHandler(Gestures.DoubleTappedEvent, (s, e) => result.Add("bdt"));
_mouse.Click(border);
_mouse.Down(border, clickCount: 2);
Assert.Equal(new[] { "bp", "dp", "br", "dr", "bt", "dt", "bp", "dp" }, result);
}
}
}

4
tests/Avalonia.LeakTests/ControlTests.cs

@ -401,6 +401,10 @@ namespace Avalonia.LeakTests
{
}
public void RecalculateChildren(IVisual visual)
{
}
public void Resized(Size size)
{
}

1
tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputType>Library</OutputType>
<IsPackable>false</IsPackable>

3
tests/Avalonia.Controls.UnitTests/MouseTestHelper.cs → tests/Avalonia.UnitTests/MouseTestHelper.cs

@ -1,9 +1,8 @@
using System.Reactive;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
namespace Avalonia.Controls.UnitTests
namespace Avalonia.UnitTests
{
public class MouseTestHelper
{

174
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -96,6 +96,180 @@ namespace Avalonia.Visuals.UnitTests.Rendering
Assert.Equal(new List<IVisual> { root, decorator, border, canvas }, result);
}
[Fact]
public void Should_Update_VisualNode_Order_On_Child_Remove_Insert()
{
var dispatcher = new ImmediateDispatcher();
var loop = new Mock<IRenderLoop>();
StackPanel stack;
Canvas canvas1;
Canvas canvas2;
var root = new TestRoot
{
Child = stack = new StackPanel
{
Children=
{
(canvas1 = new Canvas()),
(canvas2 = new Canvas()),
}
}
};
var sceneBuilder = new SceneBuilder();
var target = new DeferredRenderer(
root,
loop.Object,
sceneBuilder: sceneBuilder,
dispatcher: dispatcher);
root.Renderer = target;
target.Start();
RunFrame(target);
stack.Children.Remove(canvas2);
stack.Children.Insert(0, canvas2);
RunFrame(target);
var scene = target.UnitTestScene();
var stackNode = scene.FindNode(stack);
Assert.Same(stackNode.Children[0].Visual, canvas2);
Assert.Same(stackNode.Children[1].Visual, canvas1);
}
[Fact]
public void Should_Update_VisualNode_Order_On_Child_Move()
{
var dispatcher = new ImmediateDispatcher();
var loop = new Mock<IRenderLoop>();
StackPanel stack;
Canvas canvas1;
Canvas canvas2;
var root = new TestRoot
{
Child = stack = new StackPanel
{
Children =
{
(canvas1 = new Canvas()),
(canvas2 = new Canvas()),
}
}
};
var sceneBuilder = new SceneBuilder();
var target = new DeferredRenderer(
root,
loop.Object,
sceneBuilder: sceneBuilder,
dispatcher: dispatcher);
root.Renderer = target;
target.Start();
RunFrame(target);
stack.Children.Move(1, 0);
RunFrame(target);
var scene = target.UnitTestScene();
var stackNode = scene.FindNode(stack);
Assert.Same(stackNode.Children[0].Visual, canvas2);
Assert.Same(stackNode.Children[1].Visual, canvas1);
}
[Fact]
public void Should_Update_VisualNode_Order_On_ZIndex_Change()
{
var dispatcher = new ImmediateDispatcher();
var loop = new Mock<IRenderLoop>();
StackPanel stack;
Canvas canvas1;
Canvas canvas2;
var root = new TestRoot
{
Child = stack = new StackPanel
{
Children =
{
(canvas1 = new Canvas { ZIndex = 1 }),
(canvas2 = new Canvas { ZIndex = 2 }),
}
}
};
var sceneBuilder = new SceneBuilder();
var target = new DeferredRenderer(
root,
loop.Object,
sceneBuilder: sceneBuilder,
dispatcher: dispatcher);
root.Renderer = target;
target.Start();
RunFrame(target);
canvas1.ZIndex = 3;
RunFrame(target);
var scene = target.UnitTestScene();
var stackNode = scene.FindNode(stack);
Assert.Same(stackNode.Children[0].Visual, canvas2);
Assert.Same(stackNode.Children[1].Visual, canvas1);
}
[Fact]
public void Should_Update_VisualNode_Order_On_ZIndex_Change_With_Dirty_Ancestor()
{
var dispatcher = new ImmediateDispatcher();
var loop = new Mock<IRenderLoop>();
StackPanel stack;
Canvas canvas1;
Canvas canvas2;
var root = new TestRoot
{
Child = stack = new StackPanel
{
Children =
{
(canvas1 = new Canvas { ZIndex = 1 }),
(canvas2 = new Canvas { ZIndex = 2 }),
}
}
};
var sceneBuilder = new SceneBuilder();
var target = new DeferredRenderer(
root,
loop.Object,
sceneBuilder: sceneBuilder,
dispatcher: dispatcher);
root.Renderer = target;
target.Start();
RunFrame(target);
root.InvalidateVisual();
canvas1.ZIndex = 3;
RunFrame(target);
var scene = target.UnitTestScene();
var stackNode = scene.FindNode(stack);
Assert.Same(stackNode.Children[0].Visual, canvas2);
Assert.Same(stackNode.Children[1].Visual, canvas1);
}
[Fact]
public void Should_Push_Opacity_For_Controls_With_Less_Than_1_Opacity()
{

47
tests/Avalonia.Visuals.UnitTests/VisualTests.cs

@ -282,5 +282,52 @@ namespace Avalonia.Visuals.UnitTests
Assert.True(called);
}
[Fact]
public void Changing_ZIndex_Should_InvalidateVisual()
{
Canvas canvas1;
var renderer = new Mock<IRenderer>();
var root = new TestRoot
{
Child = new StackPanel
{
Children =
{
(canvas1 = new Canvas()),
new Canvas(),
},
},
};
root.Renderer = renderer.Object;
canvas1.ZIndex = 10;
renderer.Verify(x => x.AddDirty(canvas1));
}
[Fact]
public void Changing_ZIndex_Should_Recalculate_Parent_Children()
{
Canvas canvas1;
StackPanel stackPanel;
var renderer = new Mock<IRenderer>();
var root = new TestRoot
{
Child = stackPanel = new StackPanel
{
Children =
{
(canvas1 = new Canvas()),
new Canvas(),
},
},
};
root.Renderer = renderer.Object;
canvas1.ZIndex = 10;
renderer.Verify(x => x.RecalculateChildren(stackPanel));
}
}
}

Loading…
Cancel
Save