The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. //==============================================================================
  21. Component* FocusTraverser::getNextComponent (Component* current)
  22. {
  23. jassert (current != nullptr);
  24. return detail::FocusHelpers::navigateFocus (current,
  25. current->findFocusContainer(),
  26. detail::FocusHelpers::NavigationDirection::forwards,
  27. &Component::isFocusContainer);
  28. }
  29. Component* FocusTraverser::getPreviousComponent (Component* current)
  30. {
  31. jassert (current != nullptr);
  32. return detail::FocusHelpers::navigateFocus (current,
  33. current->findFocusContainer(),
  34. detail::FocusHelpers::NavigationDirection::backwards,
  35. &Component::isFocusContainer);
  36. }
  37. Component* FocusTraverser::getDefaultComponent (Component* parentComponent)
  38. {
  39. if (parentComponent != nullptr)
  40. {
  41. std::vector<Component*> components;
  42. detail::FocusHelpers::findAllComponents (parentComponent,
  43. components,
  44. &Component::isFocusContainer);
  45. if (! components.empty())
  46. return components.front();
  47. }
  48. return nullptr;
  49. }
  50. std::vector<Component*> FocusTraverser::getAllComponents (Component* parentComponent)
  51. {
  52. std::vector<Component*> components;
  53. detail::FocusHelpers::findAllComponents (parentComponent,
  54. components,
  55. &Component::isFocusContainer);
  56. return components;
  57. }
  58. //==============================================================================
  59. //==============================================================================
  60. #if JUCE_UNIT_TESTS
  61. struct FocusTraverserTests final : public UnitTest
  62. {
  63. FocusTraverserTests()
  64. : UnitTest ("FocusTraverser", UnitTestCategories::gui)
  65. {}
  66. void runTest() override
  67. {
  68. ScopedJuceInitialiser_GUI libraryInitialiser;
  69. const MessageManagerLock mml;
  70. beginTest ("Basic traversal");
  71. {
  72. TestComponent parent;
  73. expect (traverser.getDefaultComponent (&parent) == &parent.children.front());
  74. for (auto iter = parent.children.begin(); iter != parent.children.end(); ++iter)
  75. expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (parent.children.cend()) ? nullptr
  76. : &(*std::next (iter))));
  77. for (auto iter = parent.children.rbegin(); iter != parent.children.rend(); ++iter)
  78. expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (parent.children.rend()) ? nullptr
  79. : &(*std::next (iter))));
  80. auto allComponents = traverser.getAllComponents (&parent);
  81. expect (std::equal (allComponents.cbegin(), allComponents.cend(), parent.children.cbegin(),
  82. [] (const Component* c1, const Component& c2) { return c1 == &c2; }));
  83. }
  84. beginTest ("Disabled components are ignored");
  85. {
  86. checkIgnored ([] (Component& c) { c.setEnabled (false); });
  87. }
  88. beginTest ("Invisible components are ignored");
  89. {
  90. checkIgnored ([] (Component& c) { c.setVisible (false); });
  91. }
  92. beginTest ("Explicit focus order comes before unspecified");
  93. {
  94. TestComponent parent;
  95. auto& explicitFocusComponent = parent.children[2];
  96. explicitFocusComponent.setExplicitFocusOrder (1);
  97. expect (traverser.getDefaultComponent (&parent) == &explicitFocusComponent);
  98. expect (traverser.getAllComponents (&parent).front() == &explicitFocusComponent);
  99. }
  100. beginTest ("Explicit focus order comparison");
  101. {
  102. checkComponentProperties ([this] (Component& child) { child.setExplicitFocusOrder (getRandom().nextInt ({ 1, 100 })); },
  103. [] (const Component& c1, const Component& c2) { return c1.getExplicitFocusOrder()
  104. <= c2.getExplicitFocusOrder(); });
  105. }
  106. beginTest ("Left to right");
  107. {
  108. checkComponentProperties ([this] (Component& child) { child.setTopLeftPosition (getRandom().nextInt ({ 0, 100 }), 0); },
  109. [] (const Component& c1, const Component& c2) { return c1.getX() <= c2.getX(); });
  110. }
  111. beginTest ("Top to bottom");
  112. {
  113. checkComponentProperties ([this] (Component& child) { child.setTopLeftPosition (0, getRandom().nextInt ({ 0, 100 })); },
  114. [] (const Component& c1, const Component& c2) { return c1.getY() <= c2.getY(); });
  115. }
  116. beginTest ("Focus containers have their own focus");
  117. {
  118. Component root;
  119. TestComponent container;
  120. container.setFocusContainerType (Component::FocusContainerType::focusContainer);
  121. root.addAndMakeVisible (container);
  122. expect (traverser.getDefaultComponent (&root) == &container);
  123. expect (traverser.getNextComponent (&container) == nullptr);
  124. expect (traverser.getPreviousComponent (&container) == nullptr);
  125. expect (traverser.getDefaultComponent (&container) == &container.children.front());
  126. for (auto iter = container.children.begin(); iter != container.children.end(); ++iter)
  127. expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (container.children.cend()) ? nullptr
  128. : &(*std::next (iter))));
  129. for (auto iter = container.children.rbegin(); iter != container.children.rend(); ++iter)
  130. expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (container.children.rend()) ? nullptr
  131. : &(*std::next (iter))));
  132. expect (traverser.getAllComponents (&root).size() == 1);
  133. auto allContainerComponents = traverser.getAllComponents (&container);
  134. expect (std::equal (allContainerComponents.cbegin(), allContainerComponents.cend(), container.children.cbegin(),
  135. [] (const Component* c1, const Component& c2) { return c1 == &c2; }));
  136. }
  137. beginTest ("Non-focus containers pass-through focus");
  138. {
  139. Component root;
  140. TestComponent container;
  141. container.setFocusContainerType (Component::FocusContainerType::none);
  142. root.addAndMakeVisible (container);
  143. expect (traverser.getDefaultComponent (&root) == &container);
  144. expect (traverser.getNextComponent (&container) == &container.children.front());
  145. expect (traverser.getPreviousComponent (&container) == nullptr);
  146. expect (traverser.getDefaultComponent (&container) == &container.children.front());
  147. for (auto iter = container.children.begin(); iter != container.children.end(); ++iter)
  148. expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (container.children.cend()) ? nullptr
  149. : &(*std::next (iter))));
  150. for (auto iter = container.children.rbegin(); iter != container.children.rend(); ++iter)
  151. expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (container.children.rend()) ? &container
  152. : &(*std::next (iter))));
  153. expect (traverser.getAllComponents (&root).size() == container.children.size() + 1);
  154. }
  155. }
  156. private:
  157. struct TestComponent final : public Component
  158. {
  159. TestComponent()
  160. {
  161. for (auto& child : children)
  162. addAndMakeVisible (child);
  163. }
  164. std::array<Component, 10> children;
  165. };
  166. void checkComponentProperties (std::function<void (Component&)>&& childFn,
  167. std::function<bool (const Component&, const Component&)>&& testProperty)
  168. {
  169. TestComponent parent;
  170. for (auto& child : parent.children)
  171. childFn (child);
  172. auto* comp = traverser.getDefaultComponent (&parent);
  173. for (const auto& child : parent.children)
  174. if (&child != comp)
  175. expect (testProperty (*comp, child));
  176. for (;;)
  177. {
  178. auto* next = traverser.getNextComponent (comp);
  179. if (next == nullptr)
  180. break;
  181. expect (testProperty (*comp, *next));
  182. comp = next;
  183. }
  184. }
  185. void checkIgnored (const std::function<void(Component&)>& makeIgnored)
  186. {
  187. TestComponent parent;
  188. auto iter = parent.children.begin();
  189. makeIgnored (*iter);
  190. expect (traverser.getDefaultComponent (&parent) == std::addressof (*std::next (iter)));
  191. iter += 5;
  192. makeIgnored (*iter);
  193. expect (traverser.getNextComponent (std::addressof (*std::prev (iter))) == std::addressof (*std::next (iter)));
  194. expect (traverser.getPreviousComponent (std::addressof (*std::next (iter))) == std::addressof (*std::prev (iter)));
  195. auto allComponents = traverser.getAllComponents (&parent);
  196. expect (std::find (allComponents.cbegin(), allComponents.cend(), &parent.children.front()) == allComponents.cend());
  197. expect (std::find (allComponents.cbegin(), allComponents.cend(), std::addressof (*iter)) == allComponents.cend());
  198. }
  199. FocusTraverser traverser;
  200. };
  201. static FocusTraverserTests focusTraverserTests;
  202. #endif
  203. } // namespace juce