30namespace KeyboardFocusTraverserHelpers
32 static bool isKeyboardFocusable (
const Component* comp,
const Component* container)
34 return comp->getWantsKeyboardFocus() && container->isParentOf (comp);
37 static Component* traverse (Component* current, Component* container,
38 detail::FocusHelpers::NavigationDirection direction)
40 if (
auto* comp = detail::FocusHelpers::navigateFocus (current, container, direction,
43 if (isKeyboardFocusable (comp, container))
46 return traverse (comp, container, direction);
56 detail::FocusHelpers::NavigationDirection::forwards);
62 detail::FocusHelpers::NavigationDirection::backwards);
68 if (KeyboardFocusTraverserHelpers::isKeyboardFocusable (comp, parentComponent))
77 detail::FocusHelpers::findAllComponents (parentComponent,
81 auto removePredicate = [parentComponent] (
const Component* comp)
83 return ! KeyboardFocusTraverserHelpers::isKeyboardFocusable (comp, parentComponent);
97struct KeyboardFocusTraverserTests final :
public UnitTest
99 KeyboardFocusTraverserTests()
100 :
UnitTest (
"KeyboardFocusTraverser", UnitTestCategories::gui)
103 void runTest()
override
105 ScopedJuceInitialiser_GUI libraryInitialiser;
106 const MessageManagerLock mml;
108 beginTest (
"No child wants keyboard focus");
110 TestComponent parent;
112 expect (traverser.getDefaultComponent (&parent) ==
nullptr);
113 expect (traverser.getAllComponents (&parent).empty());
116 beginTest (
"Single child wants keyboard focus");
118 TestComponent parent;
120 parent.children[5].setWantsKeyboardFocus (
true);
122 auto* defaultComponent = traverser.getDefaultComponent (&parent);
124 expect (defaultComponent == &parent.children[5]);
125 expect (defaultComponent->getWantsKeyboardFocus());
127 expect (traverser.getNextComponent (defaultComponent) ==
nullptr);
128 expect (traverser.getPreviousComponent (defaultComponent) ==
nullptr);
129 expect (traverser.getAllComponents (&parent).size() == 1);
132 beginTest (
"Multiple children want keyboard focus");
134 TestComponent parent;
136 Component* focusChildren[]
146 for (
auto* focusChild : focusChildren)
147 focusChild->setWantsKeyboardFocus (true);
149 auto allComponents = traverser.getAllComponents (&parent);
151 for (
auto* focusChild : focusChildren)
152 expect (
std::
find (allComponents.
cbegin(), allComponents.
cend(), focusChild) != allComponents.
cend());
154 auto* componentToTest = traverser.getDefaultComponent (&parent);
158 expect (componentToTest->getWantsKeyboardFocus());
161 componentToTest = traverser.getNextComponent (componentToTest);
163 if (componentToTest ==
nullptr)
168 for (
auto* focusChild : focusChildren)
169 focusChild->setExplicitFocusOrder (focusOrder++);
171 componentToTest = traverser.getDefaultComponent (&parent);
173 for (
auto* focusChild : focusChildren)
175 expect (componentToTest == focusChild);
176 expect (componentToTest->getWantsKeyboardFocus());
178 componentToTest = traverser.getNextComponent (componentToTest);
182 beginTest (
"Single nested child wants keyboard focus");
184 TestComponent parent;
185 Component grandparent;
187 grandparent.addAndMakeVisible (parent);
189 auto& focusChild = parent.children[5];
191 focusChild.setWantsKeyboardFocus (
true);
193 expect (traverser.getDefaultComponent (&grandparent) == &focusChild);
194 expect (traverser.getDefaultComponent (&parent) == &focusChild);
195 expect (traverser.getNextComponent (&focusChild) ==
nullptr);
196 expect (traverser.getPreviousComponent (&focusChild) ==
nullptr);
197 expect (traverser.getAllComponents (&parent).size() == 1);
200 beginTest (
"Multiple nested children want keyboard focus");
202 TestComponent parent;
203 Component grandparent;
205 grandparent.addAndMakeVisible (parent);
207 Component* focusChildren[]
214 for (
auto* focusChild : focusChildren)
215 focusChild->setWantsKeyboardFocus (true);
217 auto allComponents = traverser.getAllComponents (&parent);
219 expect (
std::equal (allComponents.cbegin(), allComponents.cend(), focusChildren,
220 [] (
const Component* c1,
const Component* c2) { return c1 == c2; }));
222 const auto front = *focusChildren;
225 expect (traverser.getDefaultComponent (&grandparent) == front);
226 expect (traverser.getDefaultComponent (&parent) == front);
228 expect (traverser.getPreviousComponent (back) == *
std::prev (
std::end (focusChildren), 2));
232 for (
auto& p : otherParents)
234 grandparent.addAndMakeVisible (p);
235 p.setWantsKeyboardFocus (
true);
238 expect (traverser.getDefaultComponent (&grandparent) == front);
239 expect (traverser.getDefaultComponent (&parent) == front);
240 expect (traverser.getNextComponent (back) == &otherParents.
front());
241 expect (traverser.getNextComponent (&otherParents.
back()) ==
nullptr);
242 expect (traverser.getAllComponents (&grandparent).size() ==
numElementsInArray (focusChildren) + otherParents.
size());
243 expect (traverser.getAllComponents (&parent).size() == (
size_t)
numElementsInArray (focusChildren));
245 for (
auto* focusChild : focusChildren)
246 focusChild->setWantsKeyboardFocus (false);
248 expect (traverser.getDefaultComponent (&grandparent) == &otherParents.
front());
249 expect (traverser.getDefaultComponent (&parent) ==
nullptr);
250 expect (traverser.getAllComponents (&grandparent).size() == otherParents.
size());
251 expect (traverser.getAllComponents (&parent).empty());
256 struct TestComponent final :
public Component
260 for (
auto& child : children)
261 addAndMakeVisible (child);
267 KeyboardFocusTraverser traverser;
270static KeyboardFocusTraverserTests keyboardFocusTraverserTests;
The base class for all JUCE user-interface objects.
bool isKeyboardFocusContainer() const noexcept
Returns true if this component has been marked as a keyboard focus container.
Component * findKeyboardFocusContainer() const
Returns the keyboard focus container for this component.
Component * getPreviousComponent(Component *current) override
Returns the component that should be given keyboard focus after the specified one when moving "backwa...
std::vector< Component * > getAllComponents(Component *parentComponent) override
Returns all of the components that can receive keyboard focus within the given parent component in tr...
Component * getNextComponent(Component *current) override
Returns the component that should be given keyboard focus after the specified one when moving "forwar...
Component * getDefaultComponent(Component *parentComponent) override
Returns the component that should receive keyboard focus by default within the given parent component...
This is a base class for classes that perform a unit test.
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.