Skip to content

FocusHelpers.CanHaveFocusableChildren always returns true if its argument is non-null #21620

Description

@freddiesmartbox

Describe the bug

FocusHelpers.CanHaveFocusableChildren returns true when it probably shouldn't, and so
GetFirstFocusableElementInternal can report elements that are not focusable. This means GetFirstFocusableElement can return null when there is actually something focusable, so GetNextTabStop, so FindNextFocus and friends do not behave as expected.

To Reproduce

We see this problem with the navigation APIs: things like TextBlock end up behaving like broken tab-stops.

If you have e.g.

    <StackPanel>
        <TextBox GotFocus="TextBox_GotFocus"></TextBox>
        <TextBox></TextBox>
        <TextBlock></TextBlock>

        <Border Height="4" Background="Black" />

        <TextBox GotFocus="TextBox_GotFocus"></TextBox>
        <TextBlock></TextBlock>
        <TextBox></TextBox>
    </StackPanel>

with this in the code-behind

        private void TextBox_GotFocus(object? sender, Avalonia.Input.FocusChangedEventArgs e)
        {
            if (TopLevel.GetTopLevel(sender as Control)?.FocusManager.FindNextElement(NavigationDirection.Next, new() { FocusedElement = sender as Control }) is { } focusTarget)
                focusTarget.Focus();
            // equivalently TopLevel.GetTopLevel(sender as Control)?.FocusManager.TryMoveFocus(NavigationDirection.Next, new() { FocusedElement = sender as Control });
        }

Then focussing the first TextBox will correctly jump to the second (consistent with pressing Tab); focussing the third TextBox will not focus the fourth (inconsistent with pressing Tab)

Expected behavior

I'm not entirely certain, but I would expect that the FocusManager APIs should be consistent with pressing Tab:
TextBlock and other controls that do not contain focusable elements should not behave like a tab-stop. It's not clear to me exactly what purpose the of CanHaveFocusableChildren but it probably shouldn't be returning true quite as often as it does: at a minimum, it looks like hasFocusChildren should be false at the start.

Avalonia version

12.0.3

OS

Windows

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions