Any reference to what has changed with KeyboardFocusTraverser from JUCE 6.0.8 to JUCE 6.1.2?

Is there any reference to what has changed with KeyboardFocusTraverser from JUCE 6.0.8 to JUCE 6.1.2?

I’ve recently update my projects to JUCE 6.1.2 and having a lot of problems with KeyboardFocusTraverser with the tab key - a lot of stuff I previously implemented just doesn’t work any more.

Quite a lot has changed in the component FocusTraverser code since adding accessibility support. It’s difficult to say what your problem may be without seeing any code or more details of the specific issues you are having. Can you provide a minimal example?

It’s also worth looking at the breaking changes document in the repo:

Thanks Ed. I did read the breaking changes.

I’m not using createFocusTraverser() or a custom traverser.

I was using setFocusContainer(true) in several places which I replaced with setFocusContainerType(FocusContainerType::keyboardFocusContainer). That’s correct, right?

Here’s some more detail:

In JUCE 6.0.8, I used to have some debug code that I placed inside FocusHelpers::getIncrementedComponent(), that would print out for me the traversal of components via the Tab Key.

In JUCE 6.1.2, this is all changed/rewritten. It seems the place you would do this is now FocusHelpers::navigateFocus(), but in here it now traverses every single component, not just the ones that I have told to setWantsKeyboardFocus().

I may need to change my approach, but I was doing some things like:

Having a custom component that has setWantsKeyboardFocus(true) so that clicking on it unfocuses other components, but then using focusGained() and focusChangedByTabKey to pass the keyboard focus to the next sibling component. In other words, clicking it grabs focus so that it unfocused any other component. However, if you tab to it, the tab key effectively skips over it to the next component. But this doesn’t seem to work the same way at all.

void IncDec::focusGained (FocusChangeType cause)
{
    if (cause == focusChangedByTabKey)
    {
        auto direction = ComponentPeer::getCurrentModifiersRealtime().isShiftDown() == false;
        moveKeyboardFocusToSibling(direction);
    }
}

Now, in JUCE 6.1.2, it ends up somewhere else entirely, I’m not sure how… because it seems that moveKeyboardFocusToSibling() no longer does the same thing. I’m trying to step through what’s going on but it’s complicated.

My understanding so far (I may be wrong) is that there used to only be the concept of KeyboardFocus Traversal; now however, there is “Accessibility Focus Traversal” and Keyboard Traversal is only a subset of that.

But is there any way to exclude a component from Accessibility Focus Traversal? I haven’t really tried the screen reading stuff yet; I’m still developing the app and I barely had the tab key changing focus the way I wanted it. But let’s say you don’t want a screen reader to notice a certain component. Is there a method to remove it from the transversal completely?

Again it’s hard to say without seeing the actual code, but it sounds like setFocusContainerType() isn’t the method you should be using here. This method will mark the component as a container within which keyboard focus will be passed around when using the tab and shift+tab keys - it effectively “traps” the keyboard focus and will round-robin traverse its children. In your case it sounds like you want to be setting the parent component as a keyboard focus container and then calling setWantsKeyboardFocus() on the children that you want to be navigable within that container.

Yes, I am using setFocusContainerType() on the parent component.

Is there no reference on how Accessibility works now?

Is there a way to exclude components from being traversed period? Keyboard focus or other?

The AccessibilityDemo project in the examples/GUI directory of the repo is a good starting point and the classes are pretty thoroughly documented.

A component is considered focusable if it is visible and enabled, so either making it invisible or disabling it with setVisible() and setEnabled(), respectively, will cause a component to be ignored by the default focus traverser. Alternatively you can provide your own FocusTraverser implementation for more complex behaviours.