Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track IsEffectivelyVisible state #13972

Merged
merged 11 commits into from
Jan 23, 2024
67 changes: 47 additions & 20 deletions src/Avalonia.Base/Visual.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public partial class Visual : StyledElement
/// </summary>
public static readonly DirectProperty<Visual, Visual?> VisualParentProperty =
AvaloniaProperty.RegisterDirect<Visual, Visual?>(nameof(VisualParent), o => o._visualParent);

/// <summary>
/// Defines the <see cref="ZIndex"/> property.
/// </summary>
Expand All @@ -120,6 +120,7 @@ public partial class Visual : StyledElement
private IRenderRoot? _visualRoot;
private Visual? _visualParent;
private bool _hasMirrorTransform;

private TargetWeakEventSubscriber<Visual, EventArgs>? _affectsRenderWeakSubscriber;

/// <summary>
Expand Down Expand Up @@ -147,7 +148,7 @@ public Visual()
{
// Disable transitions until we're added to the visual tree.
DisableTransitions();

var visualChildren = new AvaloniaList<Visual>();
visualChildren.ResetBehavior = ResetBehavior.Remove;
visualChildren.Validate = visual => ValidateVisualChild(visual);
Expand Down Expand Up @@ -195,26 +196,36 @@ public Geometry? Clip
/// <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="parent">The parent control.</param>
jmacato marked this conversation as resolved.
Show resolved Hide resolved
private void UpdateIsEffectivelyEnabled(bool state)
{
get
{
Visual? node = this;
IsEffectivelyVisible = state;

while (node != null)
{
if (!node.IsVisible)
{
return false;
}
// PERF-SENSITIVE: This is called on entire hierarchy and using foreach or LINQ
// will cause extra allocations and overhead.

var children = VisualChildren;

node = node.VisualParent;
}
// ReSharper disable once ForCanBeConvertedToForeach
for (int i = 0; i < children.Count; ++i)
{
var child = children[i];

return true;
child?.UpdateIsEffectivelyEnabled(state);
}
}

private static void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
{
((Visual)e.Sender).UpdateIsEffectivelyEnabled(e.GetNewValue<bool>());
}

/// <summary>
/// Gets or sets a value indicating whether this control is visible.
/// </summary>
Expand Down Expand Up @@ -453,7 +464,11 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
{
base.OnPropertyChanged(change);

if (change.Property == FlowDirectionProperty)
if (change.Property == IsVisibleProperty)
{
IsVisibleChanged(change);
jmacato marked this conversation as resolved.
Show resolved Hide resolved
}
else if (change.Property == FlowDirectionProperty)
{
InvalidateMirrorTransform();

Expand All @@ -463,7 +478,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
}
}
}

protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
base.LogicalChildrenCollectionChanged(sender, e);
Expand Down Expand Up @@ -495,10 +510,13 @@ protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs
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;

SyncIsEffectivelyEnabledFromParent(_visualParent);

var visualChildren = VisualChildren;
var visualChildrenCount = visualChildren.Count;
Expand All @@ -512,6 +530,13 @@ protected virtual void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs
}
}

private void SyncIsEffectivelyEnabledFromParent(Visual? parent)
{
var vis = (parent?.IsVisible ?? false) && IsVisible;

UpdateIsEffectivelyEnabled(vis);
}

/// <summary>
/// Calls the <see cref="OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs)"/> method
/// for this control and all of its visual descendants.
Expand All @@ -535,6 +560,8 @@ protected virtual void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArg
DetachedFromVisualTree?.Invoke(this, e);
e.Root.Renderer.AddDirty(this);

SyncIsEffectivelyEnabledFromParent(_visualParent);

var visualChildren = VisualChildren;
var visualChildrenCount = visualChildren.Count;

Expand Down
15 changes: 15 additions & 0 deletions tests/Avalonia.Base.UnitTests/VisualTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -349,5 +349,20 @@ public void Changing_ZIndex_Should_Recalculate_Parent_Children()

renderer.Verify(x => x.RecalculateChildren(stackPanel));
}

[Fact]
public void IsEffectivelyVisible_Propagates_To_Visual_Children()
{
var child2 = new Decorator();
var child1 = new Decorator { Child = child2 };
var root = new TestRoot { Child = child1 };

Assert.True(child2.IsEffectivelyVisible);

root.IsVisible = false;

Assert.False(child2.IsEffectivelyVisible);
}

}
}