Audio plugin host https://kx.studio/carla
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.

354 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. namespace FocusHelpers
  16. {
  17. static int getOrder (const Component* c)
  18. {
  19. auto order = c->getExplicitFocusOrder();
  20. return order > 0 ? order : std::numeric_limits<int>::max();
  21. }
  22. template <typename FocusContainerFn>
  23. static void findAllComponents (Component* parent,
  24. std::vector<Component*>& components,
  25. FocusContainerFn isFocusContainer)
  26. {
  27. if (parent == nullptr || parent->getNumChildComponents() == 0)
  28. return;
  29. std::vector<Component*> localComponents;
  30. for (auto* c : parent->getChildren())
  31. if (c->isVisible() && c->isEnabled())
  32. localComponents.push_back (c);
  33. const auto compareComponents = [&] (const Component* a, const Component* b)
  34. {
  35. const auto getComponentOrderAttributes = [] (const Component* c)
  36. {
  37. return std::make_tuple (getOrder (c),
  38. c->isAlwaysOnTop() ? 0 : 1,
  39. c->getY(),
  40. c->getX());
  41. };
  42. return getComponentOrderAttributes (a) < getComponentOrderAttributes (b);
  43. };
  44. // This will sort so that they are ordered in terms of explicit focus,
  45. // always on top, left-to-right, and then top-to-bottom.
  46. std::stable_sort (localComponents.begin(), localComponents.end(), compareComponents);
  47. for (auto* c : localComponents)
  48. {
  49. components.push_back (c);
  50. if (! (c->*isFocusContainer)())
  51. findAllComponents (c, components, isFocusContainer);
  52. }
  53. }
  54. enum class NavigationDirection { forwards, backwards };
  55. template <typename FocusContainerFn>
  56. static Component* navigateFocus (Component* current,
  57. Component* focusContainer,
  58. NavigationDirection direction,
  59. FocusContainerFn isFocusContainer)
  60. {
  61. if (focusContainer != nullptr)
  62. {
  63. std::vector<Component*> components;
  64. findAllComponents (focusContainer, components, isFocusContainer);
  65. const auto iter = std::find (components.cbegin(), components.cend(), current);
  66. if (iter == components.cend())
  67. return nullptr;
  68. switch (direction)
  69. {
  70. case NavigationDirection::forwards:
  71. if (iter != std::prev (components.cend()))
  72. return *std::next (iter);
  73. break;
  74. case NavigationDirection::backwards:
  75. if (iter != components.cbegin())
  76. return *std::prev (iter);
  77. break;
  78. }
  79. }
  80. return nullptr;
  81. }
  82. }
  83. //==============================================================================
  84. Component* FocusTraverser::getNextComponent (Component* current)
  85. {
  86. jassert (current != nullptr);
  87. return FocusHelpers::navigateFocus (current,
  88. current->findFocusContainer(),
  89. FocusHelpers::NavigationDirection::forwards,
  90. &Component::isFocusContainer);
  91. }
  92. Component* FocusTraverser::getPreviousComponent (Component* current)
  93. {
  94. jassert (current != nullptr);
  95. return FocusHelpers::navigateFocus (current,
  96. current->findFocusContainer(),
  97. FocusHelpers::NavigationDirection::backwards,
  98. &Component::isFocusContainer);
  99. }
  100. Component* FocusTraverser::getDefaultComponent (Component* parentComponent)
  101. {
  102. if (parentComponent != nullptr)
  103. {
  104. std::vector<Component*> components;
  105. FocusHelpers::findAllComponents (parentComponent,
  106. components,
  107. &Component::isFocusContainer);
  108. if (! components.empty())
  109. return components.front();
  110. }
  111. return nullptr;
  112. }
  113. std::vector<Component*> FocusTraverser::getAllComponents (Component* parentComponent)
  114. {
  115. std::vector<Component*> components;
  116. FocusHelpers::findAllComponents (parentComponent,
  117. components,
  118. &Component::isFocusContainer);
  119. return components;
  120. }
  121. //==============================================================================
  122. //==============================================================================
  123. #if JUCE_UNIT_TESTS
  124. struct FocusTraverserTests : public UnitTest
  125. {
  126. FocusTraverserTests()
  127. : UnitTest ("FocusTraverser", UnitTestCategories::gui)
  128. {}
  129. void runTest() override
  130. {
  131. ScopedJuceInitialiser_GUI libraryInitialiser;
  132. const MessageManagerLock mml;
  133. beginTest ("Basic traversal");
  134. {
  135. TestComponent parent;
  136. expect (traverser.getDefaultComponent (&parent) == &parent.children.front());
  137. for (auto iter = parent.children.begin(); iter != parent.children.end(); ++iter)
  138. expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (parent.children.cend()) ? nullptr
  139. : &(*std::next (iter))));
  140. for (auto iter = parent.children.rbegin(); iter != parent.children.rend(); ++iter)
  141. expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (parent.children.rend()) ? nullptr
  142. : &(*std::next (iter))));
  143. auto allComponents = traverser.getAllComponents (&parent);
  144. expect (std::equal (allComponents.cbegin(), allComponents.cend(), parent.children.cbegin(),
  145. [] (const Component* c1, const Component& c2) { return c1 == &c2; }));
  146. }
  147. beginTest ("Disabled components are ignored");
  148. {
  149. checkIgnored ([] (Component& c) { c.setEnabled (false); });
  150. }
  151. beginTest ("Invisible components are ignored");
  152. {
  153. checkIgnored ([] (Component& c) { c.setVisible (false); });
  154. }
  155. beginTest ("Explicit focus order comes before unspecified");
  156. {
  157. TestComponent parent;
  158. auto& explicitFocusComponent = parent.children[2];
  159. explicitFocusComponent.setExplicitFocusOrder (1);
  160. expect (traverser.getDefaultComponent (&parent) == &explicitFocusComponent);
  161. expect (traverser.getAllComponents (&parent).front() == &explicitFocusComponent);
  162. }
  163. beginTest ("Explicit focus order comparison");
  164. {
  165. checkComponentProperties ([this] (Component& child) { child.setExplicitFocusOrder (getRandom().nextInt ({ 1, 100 })); },
  166. [] (const Component& c1, const Component& c2) { return c1.getExplicitFocusOrder()
  167. <= c2.getExplicitFocusOrder(); });
  168. }
  169. beginTest ("Left to right");
  170. {
  171. checkComponentProperties ([this] (Component& child) { child.setTopLeftPosition (getRandom().nextInt ({ 0, 100 }), 0); },
  172. [] (const Component& c1, const Component& c2) { return c1.getX() <= c2.getX(); });
  173. }
  174. beginTest ("Top to bottom");
  175. {
  176. checkComponentProperties ([this] (Component& child) { child.setTopLeftPosition (0, getRandom().nextInt ({ 0, 100 })); },
  177. [] (const Component& c1, const Component& c2) { return c1.getY() <= c2.getY(); });
  178. }
  179. beginTest ("Focus containers have their own focus");
  180. {
  181. Component root;
  182. TestComponent container;
  183. container.setFocusContainerType (Component::FocusContainerType::focusContainer);
  184. root.addAndMakeVisible (container);
  185. expect (traverser.getDefaultComponent (&root) == &container);
  186. expect (traverser.getNextComponent (&container) == nullptr);
  187. expect (traverser.getPreviousComponent (&container) == nullptr);
  188. expect (traverser.getDefaultComponent (&container) == &container.children.front());
  189. for (auto iter = container.children.begin(); iter != container.children.end(); ++iter)
  190. expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (container.children.cend()) ? nullptr
  191. : &(*std::next (iter))));
  192. for (auto iter = container.children.rbegin(); iter != container.children.rend(); ++iter)
  193. expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (container.children.rend()) ? nullptr
  194. : &(*std::next (iter))));
  195. expect (traverser.getAllComponents (&root).size() == 1);
  196. auto allContainerComponents = traverser.getAllComponents (&container);
  197. expect (std::equal (allContainerComponents.cbegin(), allContainerComponents.cend(), container.children.cbegin(),
  198. [] (const Component* c1, const Component& c2) { return c1 == &c2; }));
  199. }
  200. beginTest ("Non-focus containers pass-through focus");
  201. {
  202. Component root;
  203. TestComponent container;
  204. container.setFocusContainerType (Component::FocusContainerType::none);
  205. root.addAndMakeVisible (container);
  206. expect (traverser.getDefaultComponent (&root) == &container);
  207. expect (traverser.getNextComponent (&container) == &container.children.front());
  208. expect (traverser.getPreviousComponent (&container) == nullptr);
  209. expect (traverser.getDefaultComponent (&container) == &container.children.front());
  210. for (auto iter = container.children.begin(); iter != container.children.end(); ++iter)
  211. expect (traverser.getNextComponent (&(*iter)) == (iter == std::prev (container.children.cend()) ? nullptr
  212. : &(*std::next (iter))));
  213. for (auto iter = container.children.rbegin(); iter != container.children.rend(); ++iter)
  214. expect (traverser.getPreviousComponent (&(*iter)) == (iter == std::prev (container.children.rend()) ? &container
  215. : &(*std::next (iter))));
  216. expect (traverser.getAllComponents (&root).size() == container.children.size() + 1);
  217. }
  218. }
  219. private:
  220. struct TestComponent : public Component
  221. {
  222. TestComponent()
  223. {
  224. for (auto& child : children)
  225. addAndMakeVisible (child);
  226. }
  227. std::array<Component, 10> children;
  228. };
  229. void checkComponentProperties (std::function<void (Component&)>&& childFn,
  230. std::function<bool (const Component&, const Component&)>&& testProperty)
  231. {
  232. TestComponent parent;
  233. for (auto& child : parent.children)
  234. childFn (child);
  235. auto* comp = traverser.getDefaultComponent (&parent);
  236. for (const auto& child : parent.children)
  237. if (&child != comp)
  238. expect (testProperty (*comp, child));
  239. for (;;)
  240. {
  241. auto* next = traverser.getNextComponent (comp);
  242. if (next == nullptr)
  243. break;
  244. expect (testProperty (*comp, *next));
  245. comp = next;
  246. }
  247. }
  248. void checkIgnored (const std::function<void(Component&)>& makeIgnored)
  249. {
  250. TestComponent parent;
  251. auto iter = parent.children.begin();
  252. makeIgnored (*iter);
  253. expect (traverser.getDefaultComponent (&parent) == std::addressof (*std::next (iter)));
  254. iter += 5;
  255. makeIgnored (*iter);
  256. expect (traverser.getNextComponent (std::addressof (*std::prev (iter))) == std::addressof (*std::next (iter)));
  257. expect (traverser.getPreviousComponent (std::addressof (*std::next (iter))) == std::addressof (*std::prev (iter)));
  258. auto allComponents = traverser.getAllComponents (&parent);
  259. expect (std::find (allComponents.cbegin(), allComponents.cend(), &parent.children.front()) == allComponents.cend());
  260. expect (std::find (allComponents.cbegin(), allComponents.cend(), std::addressof (*iter)) == allComponents.cend());
  261. }
  262. FocusTraverser traverser;
  263. };
  264. static FocusTraverserTests focusTraverserTests;
  265. #endif
  266. } // namespace juce