Browse Source

focus within copes with detach from tree.

pull/4974/head
Dan Walmsley 5 years ago
parent
commit
8e2d555239
  1. 57
      src/Avalonia.Input/KeyboardDevice.cs
  2. 45
      tests/Avalonia.Input.UnitTests/InputElement_Focus.cs

57
src/Avalonia.Input/KeyboardDevice.cs

@ -9,6 +9,7 @@ namespace Avalonia.Input
public class KeyboardDevice : IKeyboardDevice, INotifyPropertyChanged
{
private IInputElement? _focusedElement;
private IInputRoot? _focusedRoot;
public event PropertyChangedEventHandler? PropertyChanged;
@ -28,9 +29,34 @@ namespace Avalonia.Input
private set
{
_focusedElement = value;
if (_focusedElement != null && _focusedElement.IsAttachedToVisualTree)
{
_focusedRoot = _focusedElement.VisualRoot as IInputRoot;
}
else
{
_focusedRoot = null;
}
RaisePropertyChanged();
}
}
private void ClearFocusWithinAncestors(IInputElement element)
{
IInputElement el = element;
while (el != null)
{
if (el is InputElement ie)
{
ie.IsKeyboardFocusWithin = false;
}
el = (IInputElement)el.VisualParent;
}
}
private void ClearFocusWithin(IInputElement element, bool clearRoot)
{
@ -54,9 +80,15 @@ namespace Avalonia.Input
private void SetIsFocusWithin(IInputElement oldElement, IInputElement newElement)
{
if (newElement == null && oldElement != null)
{
ClearFocusWithinAncestors(oldElement);
return;
}
IInputElement? branch = null;
IInputElement el = newElement;
IInputElement? el = newElement;
while (el != null)
{
@ -69,7 +101,7 @@ namespace Avalonia.Input
el = (IInputElement)el.VisualParent;
}
el = oldElement;
el = oldElement!;
if (el != null && branch != null)
{
@ -88,6 +120,22 @@ namespace Avalonia.Input
el = (IInputElement)el.VisualParent;
}
}
private void ClearChildrenFocusWithin(IInputElement element,bool clearRoot)
{
foreach (IInputElement el in element.VisualChildren)
{
if (el.IsKeyboardFocusWithin)
{
ClearChildrenFocusWithin(el, true);
break;
}
}
if(clearRoot && element is InputElement ie)
{
ie.IsKeyboardFocusWithin = false;
}
}
public void SetFocusedElement(
IInputElement? element,
@ -98,6 +146,11 @@ namespace Avalonia.Input
{
var interactive = FocusedElement as IInteractive;
if (FocusedElement != null && !FocusedElement.IsAttachedToVisualTree && _focusedRoot != null)
{
ClearChildrenFocusWithin(_focusedRoot, true);
}
SetIsFocusWithin(FocusedElement, element);
FocusedElement = element;

45
tests/Avalonia.Input.UnitTests/InputElement_Focus.cs

@ -209,5 +209,50 @@ namespace Avalonia.Input.UnitTests
Assert.True(panel2.IsKeyboardFocusWithin);
}
}
[Fact]
public void Control_FocusVsisible_Pseudoclass_Should_Be_Removed_When_Removed_From_Tree()
{
using (UnitTestApplication.Start(TestServices.RealFocus))
{
var target1 = new Decorator();
var target2 = new Decorator();
var root = new TestRoot
{
Child = new StackPanel
{
Children =
{
target1,
target2
}
}
};
target1.ApplyTemplate();
target2.ApplyTemplate();
FocusManager.Instance?.Focus(target1);
Assert.True(target1.IsFocused);
Assert.True(target1.Classes.Contains(":focus-within"));
Assert.True(target1.IsKeyboardFocusWithin);
Assert.True(root.Child.Classes.Contains(":focus-within"));
Assert.True(root.Child.IsKeyboardFocusWithin);
Assert.True(root.Classes.Contains(":focus-within"));
Assert.True(root.IsKeyboardFocusWithin);
Assert.Equal(KeyboardDevice.Instance.FocusedElement, target1);
root.Child = null;
Assert.Null(KeyboardDevice.Instance.FocusedElement);
Assert.False(target1.IsFocused);
Assert.False(target1.Classes.Contains(":focus-within"));
Assert.False(target1.IsKeyboardFocusWithin);
Assert.False(root.Classes.Contains(":focus-within"));
Assert.False(root.IsKeyboardFocusWithin);
}
}
}
}

Loading…
Cancel
Save