Browse Source

Track IsEffectivelyVisible state (#13972)

* add IsEffectivelyVisible test

* test IEV update

* update self and descendants

* sync from parent's isvisible and own on attach/detach

* update test

* update from review

* add comments

* remove whitespace changes

* Added some more IsEffectivelyVisible tests.

One failing.

* Rework UpdateIsEffectivelyVisible.

- Pass the parent state to `UpdateIsEffectivelyVisible`
- Remove some unneeded methods
- Update `IsEffectivelyVisible` before calling visual tree attach/detach events.

---------

Co-authored-by: Steven Kirk <grokys@users.noreply.github.com>
pull/14329/head
Jumar Macato 2 years ago
committed by GitHub
parent
commit
92e1c653a5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 54
      src/Avalonia.Base/Visual.cs
  2. 103
      tests/Avalonia.Base.UnitTests/VisualTests.cs

54
src/Avalonia.Base/Visual.cs

@ -195,23 +195,32 @@ namespace Avalonia
/// <summary>
/// Gets a value indicating whether this control and all its parents are visible.
/// </summary>
public bool IsEffectivelyVisible
public bool IsEffectivelyVisible { get; private set; } = true;
/// <summary>
/// Updates the <see cref="IsEffectivelyVisible"/> property based on the parent's
/// <see cref="IsEffectivelyVisible"/>.
/// </summary>
/// <param name="parentState">The effective visibility of the parent control.</param>
private void UpdateIsEffectivelyVisible(bool parentState)
{
get
{
Visual? node = this;
var isEffectivelyVisible = parentState && IsVisible;
while (node != null)
{
if (!node.IsVisible)
{
return false;
}
if (IsEffectivelyVisible == isEffectivelyVisible)
return;
node = node.VisualParent;
}
IsEffectivelyVisible = isEffectivelyVisible;
return true;
// PERF-SENSITIVE: This is called on entire hierarchy and using foreach or LINQ
// will cause extra allocations and overhead.
var children = VisualChildren;
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < children.Count; ++i)
{
var child = children[i];
child.UpdateIsEffectivelyVisible(isEffectivelyVisible);
}
}
@ -453,7 +462,11 @@ namespace Avalonia
{
base.OnPropertyChanged(change);
if (change.Property == FlowDirectionProperty)
if (change.Property == IsVisibleProperty)
{
UpdateIsEffectivelyVisible(VisualParent?.IsEffectivelyVisible ?? true);
}
else if (change.Property == FlowDirectionProperty)
{
InvalidateMirrorTransform();
@ -463,7 +476,7 @@ namespace Avalonia
}
}
}
protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
base.LogicalChildrenCollectionChanged(sender, e);
@ -492,14 +505,16 @@ namespace Avalonia
AttachToCompositor(compositingRenderer.Compositor);
}
InvalidateMirrorTransform();
UpdateIsEffectivelyVisible(_visualParent!.IsEffectivelyVisible);
OnAttachedToVisualTree(e);
AttachedToVisualTree?.Invoke(this, e);
InvalidateVisual();
_visualRoot.Renderer.RecalculateChildren(_visualParent!);
if (ZIndex != 0 && VisualParent is Visual parent)
parent.HasNonUniformZIndexChildren = true;
if (ZIndex != 0 && _visualParent is { })
_visualParent.HasNonUniformZIndexChildren = true;
var visualChildren = VisualChildren;
var visualChildrenCount = visualChildren.Count;
@ -529,6 +544,7 @@ namespace Avalonia
}
DisableTransitions();
UpdateIsEffectivelyVisible(true);
OnDetachedFromVisualTree(e);
DetachFromCompositor();

103
tests/Avalonia.Base.UnitTests/VisualTests.cs

@ -349,5 +349,108 @@ namespace Avalonia.Base.UnitTests
renderer.Verify(x => x.RecalculateChildren(stackPanel));
}
[Theory]
[InlineData(new[] { 1, 2, 3 }, true, true, true, true, true, true)]
[InlineData(new[] { 3, 2, 1 }, true, true, true, true, true, true)]
[InlineData(new[] { 1 }, false, true, true, false, false, false)]
[InlineData(new[] { 2 }, true, false, true, true, false, false)]
[InlineData(new[] { 3 }, true, true, false, true, true, false)]
[InlineData(new[] { 3, 1}, true, true, false, true, true, false)]
[InlineData(new[] { 2, 3, 1 }, true, false, true, true, false, false, true)]
[InlineData(new[] { 3, 1, 2 }, true, true, false, true, true, false, true)]
[InlineData(new[] { 3, 2, 1 }, true, true, false, true, true, false, true)]
public void IsEffectivelyVisible_Propagates_To_Visual_Children(int[] assignOrder, bool rootV, bool child1V,
bool child2V, bool rootExpected, bool child1Expected, bool child2Expected, bool initialSetToFalse = false)
{
var child2 = new Decorator();
var child1 = new Decorator { Child = child2 };
var root = new TestRoot { Child = child1 };
Assert.True(child2.IsEffectivelyVisible);
if (initialSetToFalse)
{
root.IsVisible = false;
child1.IsVisible = false;
child2.IsVisible = false;
}
foreach (var order in assignOrder)
{
switch (order)
{
case 1:
root.IsVisible = rootV;
break;
case 2:
child1.IsVisible = child1V;
break;
case 3:
child2.IsVisible = child2V;
break;
}
}
Assert.Equal(rootExpected, root.IsEffectivelyVisible);
Assert.Equal(child1Expected, child1.IsEffectivelyVisible);
Assert.Equal(child2Expected, child2.IsEffectivelyVisible);
}
[Fact]
public void Added_Child_Has_Correct_IsEffectivelyVisible()
{
var root = new TestRoot { IsVisible = false };
var child = new Decorator();
root.Child = child;
Assert.False(child.IsEffectivelyVisible);
}
[Fact]
public void Added_Grandchild_Has_Correct_IsEffectivelyVisible()
{
var child = new Decorator();
var grandchild = new Decorator();
var root = new TestRoot
{
IsVisible = false,
Child = child
};
child.Child = grandchild;
Assert.False(grandchild.IsEffectivelyVisible);
}
[Fact]
public void Removing_Child_Resets_IsEffectivelyVisible()
{
var child = new Decorator();
var root = new TestRoot { Child = child, IsVisible = false };
Assert.False(child.IsEffectivelyVisible);
root.Child = null;
Assert.True(child.IsEffectivelyVisible);
}
[Fact]
public void Removing_Child_Resets_IsEffectivelyVisible_Of_Grandchild()
{
var grandchild = new Decorator();
var child = new Decorator { Child = grandchild };
var root = new TestRoot { Child = child, IsVisible = false };
Assert.False(child.IsEffectivelyVisible);
Assert.False(grandchild.IsEffectivelyVisible);
root.Child = null;
Assert.True(child.IsEffectivelyVisible);
Assert.True(grandchild.IsEffectivelyVisible);
}
}
}

Loading…
Cancel
Save