test_tab_bar.h 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. /**************************************************************************/
  2. /* test_tab_bar.h */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #ifndef TEST_TAB_BAR_H
  31. #define TEST_TAB_BAR_H
  32. #include "scene/gui/tab_bar.h"
  33. #include "scene/main/window.h"
  34. #include "tests/test_macros.h"
  35. namespace TestTabBar {
  36. static inline Array build_array() {
  37. return Array();
  38. }
  39. template <typename... Targs>
  40. static inline Array build_array(Variant item, Targs... Fargs) {
  41. Array a = build_array(Fargs...);
  42. a.push_front(item);
  43. return a;
  44. }
  45. TEST_CASE("[SceneTree][TabBar] tab operations") {
  46. TabBar *tab_bar = memnew(TabBar);
  47. SceneTree::get_singleton()->get_root()->add_child(tab_bar);
  48. tab_bar->set_clip_tabs(false);
  49. MessageQueue::get_singleton()->flush();
  50. SIGNAL_WATCH(tab_bar, "tab_selected");
  51. SIGNAL_WATCH(tab_bar, "tab_changed");
  52. SUBCASE("[TabBar] no tabs") {
  53. CHECK(tab_bar->get_tab_count() == 0);
  54. CHECK(tab_bar->get_current_tab() == -1);
  55. CHECK(tab_bar->get_previous_tab() == -1);
  56. }
  57. SUBCASE("[TabBar] add tabs") {
  58. tab_bar->add_tab("tab0");
  59. CHECK(tab_bar->get_tab_count() == 1);
  60. CHECK(tab_bar->get_current_tab() == 0);
  61. CHECK(tab_bar->get_previous_tab() == -1);
  62. SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
  63. SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
  64. tab_bar->add_tab("tab1");
  65. CHECK(tab_bar->get_tab_count() == 2);
  66. CHECK(tab_bar->get_current_tab() == 0);
  67. CHECK(tab_bar->get_previous_tab() == -1);
  68. SIGNAL_CHECK_FALSE("tab_selected");
  69. SIGNAL_CHECK_FALSE("tab_changed");
  70. tab_bar->add_tab("tab2");
  71. CHECK(tab_bar->get_tab_count() == 3);
  72. CHECK(tab_bar->get_current_tab() == 0);
  73. CHECK(tab_bar->get_previous_tab() == -1);
  74. SIGNAL_CHECK_FALSE("tab_selected");
  75. SIGNAL_CHECK_FALSE("tab_changed");
  76. CHECK(tab_bar->get_tab_title(0) == "tab0");
  77. CHECK(tab_bar->get_tab_tooltip(0) == "");
  78. CHECK(tab_bar->get_tab_text_direction(0) == Control::TEXT_DIRECTION_INHERITED);
  79. CHECK_FALSE(tab_bar->is_tab_disabled(0));
  80. CHECK_FALSE(tab_bar->is_tab_hidden(0));
  81. CHECK(tab_bar->get_tab_title(1) == "tab1");
  82. CHECK(tab_bar->get_tab_tooltip(1) == "");
  83. CHECK(tab_bar->get_tab_text_direction(1) == Control::TEXT_DIRECTION_INHERITED);
  84. CHECK_FALSE(tab_bar->is_tab_disabled(1));
  85. CHECK_FALSE(tab_bar->is_tab_hidden(1));
  86. CHECK(tab_bar->get_tab_title(2) == "tab2");
  87. CHECK(tab_bar->get_tab_tooltip(2) == "");
  88. CHECK(tab_bar->get_tab_text_direction(2) == Control::TEXT_DIRECTION_INHERITED);
  89. CHECK_FALSE(tab_bar->is_tab_disabled(2));
  90. CHECK_FALSE(tab_bar->is_tab_hidden(2));
  91. }
  92. SUBCASE("[TabBar] set tab count") {
  93. // Adds multiple tabs at once.
  94. tab_bar->set_tab_count(3);
  95. CHECK(tab_bar->get_tab_count() == 3);
  96. CHECK(tab_bar->get_current_tab() == 0);
  97. CHECK(tab_bar->get_previous_tab() == -1);
  98. SIGNAL_CHECK_FALSE("tab_selected");
  99. SIGNAL_CHECK_FALSE("tab_changed");
  100. CHECK(tab_bar->get_tab_title(0) == "");
  101. CHECK(tab_bar->get_tab_tooltip(0) == "");
  102. CHECK(tab_bar->get_tab_text_direction(0) == Control::TEXT_DIRECTION_INHERITED);
  103. CHECK_FALSE(tab_bar->is_tab_disabled(0));
  104. CHECK_FALSE(tab_bar->is_tab_hidden(0));
  105. CHECK(tab_bar->get_tab_title(1) == "");
  106. CHECK(tab_bar->get_tab_tooltip(1) == "");
  107. CHECK(tab_bar->get_tab_text_direction(1) == Control::TEXT_DIRECTION_INHERITED);
  108. CHECK_FALSE(tab_bar->is_tab_disabled(1));
  109. CHECK_FALSE(tab_bar->is_tab_hidden(1));
  110. CHECK(tab_bar->get_tab_title(2) == "");
  111. CHECK(tab_bar->get_tab_tooltip(2) == "");
  112. CHECK(tab_bar->get_tab_text_direction(2) == Control::TEXT_DIRECTION_INHERITED);
  113. CHECK_FALSE(tab_bar->is_tab_disabled(2));
  114. CHECK_FALSE(tab_bar->is_tab_hidden(2));
  115. // Setting to less tabs than there are removes from the end.
  116. tab_bar->set_tab_title(0, "tab0");
  117. tab_bar->set_tab_title(1, "tab1");
  118. tab_bar->set_tab_title(2, "tab2");
  119. tab_bar->set_tab_count(2);
  120. CHECK(tab_bar->get_tab_count() == 2);
  121. CHECK(tab_bar->get_current_tab() == 0);
  122. CHECK(tab_bar->get_previous_tab() == -1);
  123. CHECK(tab_bar->get_tab_title(0) == "tab0");
  124. CHECK(tab_bar->get_tab_title(1) == "tab1");
  125. // Remove all tabs.
  126. tab_bar->set_tab_count(0);
  127. CHECK(tab_bar->get_tab_count() == 0);
  128. CHECK(tab_bar->get_current_tab() == -1);
  129. CHECK(tab_bar->get_previous_tab() == -1);
  130. }
  131. SUBCASE("[TabBar] clear tabs") {
  132. CHECK(tab_bar->get_tab_count() == 0);
  133. CHECK(tab_bar->get_current_tab() == -1);
  134. CHECK(tab_bar->get_previous_tab() == -1);
  135. tab_bar->set_tab_count(2);
  136. CHECK(tab_bar->get_tab_count() == 2);
  137. CHECK(tab_bar->get_current_tab() == 0);
  138. CHECK(tab_bar->get_previous_tab() == -1);
  139. SIGNAL_DISCARD("tab_selected");
  140. SIGNAL_DISCARD("tab_changed");
  141. tab_bar->clear_tabs();
  142. CHECK(tab_bar->get_tab_count() == 0);
  143. CHECK(tab_bar->get_current_tab() == -1);
  144. CHECK(tab_bar->get_previous_tab() == -1);
  145. SIGNAL_CHECK_FALSE("tab_selected");
  146. SIGNAL_CHECK_FALSE("tab_changed");
  147. }
  148. SUBCASE("[TabBar] remove tabs") {
  149. tab_bar->add_tab("tab0");
  150. tab_bar->add_tab("tab1");
  151. tab_bar->add_tab("tab2");
  152. tab_bar->set_current_tab(1);
  153. CHECK(tab_bar->get_tab_count() == 3);
  154. CHECK(tab_bar->get_current_tab() == 1);
  155. CHECK(tab_bar->get_previous_tab() == 0);
  156. SIGNAL_DISCARD("tab_selected");
  157. SIGNAL_DISCARD("tab_changed");
  158. // Remove first tab.
  159. tab_bar->remove_tab(0);
  160. CHECK(tab_bar->get_tab_count() == 2);
  161. CHECK(tab_bar->get_tab_title(0) == "tab1");
  162. CHECK(tab_bar->get_tab_title(1) == "tab2");
  163. CHECK(tab_bar->get_current_tab() == 0);
  164. CHECK(tab_bar->get_previous_tab() == 0);
  165. SIGNAL_CHECK_FALSE("tab_selected");
  166. SIGNAL_CHECK_FALSE("tab_changed");
  167. // Remove last tab.
  168. tab_bar->remove_tab(1);
  169. CHECK(tab_bar->get_tab_count() == 1);
  170. CHECK(tab_bar->get_tab_title(0) == "tab1");
  171. CHECK(tab_bar->get_current_tab() == 0);
  172. CHECK(tab_bar->get_previous_tab() == 0);
  173. SIGNAL_CHECK_FALSE("tab_selected");
  174. SIGNAL_CHECK_FALSE("tab_changed");
  175. // Remove only tab.
  176. tab_bar->remove_tab(0);
  177. CHECK(tab_bar->get_tab_count() == 0);
  178. CHECK(tab_bar->get_current_tab() == -1);
  179. CHECK(tab_bar->get_previous_tab() == -1);
  180. SIGNAL_CHECK_FALSE("tab_selected");
  181. SIGNAL_CHECK("tab_changed", build_array(build_array(-1)));
  182. // Remove current tab when there are other tabs.
  183. tab_bar->add_tab("tab0");
  184. tab_bar->add_tab("tab1");
  185. tab_bar->add_tab("tab2");
  186. tab_bar->set_current_tab(1);
  187. tab_bar->set_current_tab(2);
  188. CHECK(tab_bar->get_tab_count() == 3);
  189. CHECK(tab_bar->get_current_tab() == 2);
  190. CHECK(tab_bar->get_previous_tab() == 1);
  191. SIGNAL_DISCARD("tab_selected");
  192. SIGNAL_DISCARD("tab_changed");
  193. tab_bar->remove_tab(2);
  194. CHECK(tab_bar->get_tab_count() == 2);
  195. CHECK(tab_bar->get_current_tab() == 1);
  196. CHECK(tab_bar->get_previous_tab() == 1);
  197. SIGNAL_CHECK_FALSE("tab_selected");
  198. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  199. }
  200. SUBCASE("[TabBar] move tabs") {
  201. tab_bar->add_tab("tab0");
  202. tab_bar->add_tab("tab1");
  203. tab_bar->add_tab("tab2");
  204. tab_bar->set_current_tab(1);
  205. CHECK(tab_bar->get_current_tab() == 1);
  206. CHECK(tab_bar->get_previous_tab() == 0);
  207. SIGNAL_DISCARD("tab_selected");
  208. SIGNAL_DISCARD("tab_changed");
  209. // Don't move if index is the same.
  210. tab_bar->move_tab(0, 0);
  211. CHECK(tab_bar->get_tab_title(0) == "tab0");
  212. CHECK(tab_bar->get_tab_title(1) == "tab1");
  213. CHECK(tab_bar->get_tab_title(2) == "tab2");
  214. CHECK(tab_bar->get_current_tab() == 1);
  215. CHECK(tab_bar->get_previous_tab() == 0);
  216. SIGNAL_CHECK_FALSE("tab_selected");
  217. SIGNAL_CHECK_FALSE("tab_changed");
  218. // Move the first tab to the end.
  219. tab_bar->move_tab(0, 2);
  220. CHECK(tab_bar->get_tab_title(0) == "tab1");
  221. CHECK(tab_bar->get_tab_title(1) == "tab2");
  222. CHECK(tab_bar->get_tab_title(2) == "tab0");
  223. CHECK(tab_bar->get_current_tab() == 0);
  224. CHECK(tab_bar->get_previous_tab() == 2);
  225. SIGNAL_CHECK_FALSE("tab_selected");
  226. SIGNAL_CHECK_FALSE("tab_changed");
  227. // Move the second tab to the front.
  228. tab_bar->move_tab(1, 0);
  229. CHECK(tab_bar->get_tab_title(0) == "tab2");
  230. CHECK(tab_bar->get_tab_title(1) == "tab1");
  231. CHECK(tab_bar->get_tab_title(2) == "tab0");
  232. CHECK(tab_bar->get_current_tab() == 1);
  233. CHECK(tab_bar->get_previous_tab() == 2);
  234. SIGNAL_CHECK_FALSE("tab_selected");
  235. SIGNAL_CHECK_FALSE("tab_changed");
  236. }
  237. SUBCASE("[TabBar] set current tab") {
  238. tab_bar->add_tab("tab0");
  239. tab_bar->add_tab("tab1");
  240. tab_bar->add_tab("tab2");
  241. CHECK(tab_bar->get_current_tab() == 0);
  242. CHECK(tab_bar->get_previous_tab() == -1);
  243. SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
  244. SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
  245. // Set the current tab.
  246. tab_bar->set_current_tab(1);
  247. CHECK(tab_bar->get_current_tab() == 1);
  248. CHECK(tab_bar->get_previous_tab() == 0);
  249. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  250. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  251. // Set to same tab.
  252. tab_bar->set_current_tab(1);
  253. CHECK(tab_bar->get_current_tab() == 1);
  254. CHECK(tab_bar->get_previous_tab() == 1);
  255. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  256. SIGNAL_CHECK_FALSE("tab_changed");
  257. // Out of bounds.
  258. ERR_PRINT_OFF;
  259. tab_bar->set_current_tab(-5);
  260. CHECK(tab_bar->get_current_tab() == 1);
  261. CHECK(tab_bar->get_previous_tab() == 1);
  262. SIGNAL_CHECK_FALSE("tab_selected");
  263. SIGNAL_CHECK_FALSE("tab_changed");
  264. tab_bar->set_current_tab(5);
  265. CHECK(tab_bar->get_current_tab() == 1);
  266. CHECK(tab_bar->get_previous_tab() == 1);
  267. SIGNAL_CHECK_FALSE("tab_selected");
  268. SIGNAL_CHECK_FALSE("tab_changed");
  269. ERR_PRINT_ON;
  270. }
  271. SUBCASE("[TabBar] deselection enabled") {
  272. tab_bar->add_tab("tab0");
  273. tab_bar->add_tab("tab1");
  274. tab_bar->add_tab("tab2");
  275. CHECK(tab_bar->get_current_tab() == 0);
  276. CHECK(tab_bar->get_previous_tab() == -1);
  277. SIGNAL_DISCARD("tab_selected");
  278. SIGNAL_DISCARD("tab_changed");
  279. // Setting deselect enabled doesn't change current tab.
  280. tab_bar->set_deselect_enabled(true);
  281. CHECK(tab_bar->get_deselect_enabled());
  282. CHECK(tab_bar->get_current_tab() == 0);
  283. CHECK(tab_bar->get_previous_tab() == -1);
  284. SIGNAL_CHECK_FALSE("tab_selected");
  285. SIGNAL_CHECK_FALSE("tab_changed");
  286. // Can deselect all tabs by setting current to -1.
  287. tab_bar->set_current_tab(-1);
  288. CHECK(tab_bar->get_current_tab() == -1);
  289. CHECK(tab_bar->get_previous_tab() == 0);
  290. SIGNAL_CHECK("tab_selected", build_array(build_array(-1)));
  291. SIGNAL_CHECK("tab_changed", build_array(build_array(-1)));
  292. // Adding a tab will still set the current tab to 0.
  293. tab_bar->clear_tabs();
  294. CHECK(tab_bar->get_current_tab() == -1);
  295. CHECK(tab_bar->get_previous_tab() == -1);
  296. tab_bar->add_tab("tab0");
  297. tab_bar->add_tab("tab1");
  298. tab_bar->add_tab("tab2");
  299. CHECK(tab_bar->get_tab_count() == 3);
  300. CHECK(tab_bar->get_current_tab() == 0);
  301. CHECK(tab_bar->get_previous_tab() == -1);
  302. SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
  303. SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
  304. tab_bar->set_current_tab(-1);
  305. SIGNAL_DISCARD("tab_selected");
  306. SIGNAL_DISCARD("tab_changed");
  307. // Disabling while at -1 will select the first available tab.
  308. tab_bar->set_deselect_enabled(false);
  309. CHECK_FALSE(tab_bar->get_deselect_enabled());
  310. CHECK(tab_bar->get_current_tab() == 0);
  311. CHECK(tab_bar->get_previous_tab() == -1);
  312. SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
  313. SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
  314. // Cannot set to -1 if disabled.
  315. ERR_PRINT_OFF;
  316. tab_bar->set_current_tab(-1);
  317. CHECK(tab_bar->get_current_tab() == 0);
  318. CHECK(tab_bar->get_previous_tab() == -1);
  319. SIGNAL_CHECK_FALSE("tab_selected");
  320. SIGNAL_CHECK_FALSE("tab_changed");
  321. ERR_PRINT_ON;
  322. // Disabling while at -1 skips any disabled or hidden tabs.
  323. tab_bar->set_deselect_enabled(true);
  324. tab_bar->set_tab_disabled(0, true);
  325. tab_bar->set_tab_hidden(1, true);
  326. tab_bar->set_current_tab(-1);
  327. SIGNAL_DISCARD("tab_selected");
  328. SIGNAL_DISCARD("tab_changed");
  329. tab_bar->set_deselect_enabled(false);
  330. CHECK(tab_bar->get_current_tab() == 2);
  331. CHECK(tab_bar->get_previous_tab() == -1);
  332. SIGNAL_CHECK("tab_selected", build_array(build_array(2)));
  333. SIGNAL_CHECK("tab_changed", build_array(build_array(2)));
  334. }
  335. SUBCASE("[TabBar] hidden tabs") {
  336. tab_bar->add_tab("tab0");
  337. tab_bar->add_tab("tab1");
  338. tab_bar->add_tab("tab2");
  339. tab_bar->set_current_tab(1);
  340. CHECK(tab_bar->get_current_tab() == 1);
  341. CHECK(tab_bar->get_previous_tab() == 0);
  342. CHECK_FALSE(tab_bar->is_tab_hidden(1));
  343. SIGNAL_DISCARD("tab_selected");
  344. SIGNAL_DISCARD("tab_changed");
  345. MessageQueue::get_singleton()->flush();
  346. Vector<Rect2> tab_rects = {
  347. tab_bar->get_tab_rect(0),
  348. tab_bar->get_tab_rect(1),
  349. tab_bar->get_tab_rect(2)
  350. };
  351. // Hiding a tab does not affect current tab.
  352. tab_bar->set_tab_hidden(1, true);
  353. CHECK(tab_bar->is_tab_hidden(1));
  354. CHECK(tab_bar->get_current_tab() == 1);
  355. CHECK(tab_bar->get_previous_tab() == 0);
  356. SIGNAL_CHECK_FALSE("tab_selected");
  357. SIGNAL_CHECK_FALSE("tab_changed");
  358. // The tabs after are moved over.
  359. MessageQueue::get_singleton()->flush();
  360. CHECK(tab_bar->get_tab_rect(0) == tab_rects[0]);
  361. CHECK(tab_bar->get_tab_rect(2) == tab_rects[1]);
  362. // Unhiding a tab does not affect current tab.
  363. tab_bar->set_tab_hidden(1, false);
  364. CHECK_FALSE(tab_bar->is_tab_hidden(1));
  365. CHECK(tab_bar->get_current_tab() == 1);
  366. CHECK(tab_bar->get_previous_tab() == 0);
  367. SIGNAL_CHECK_FALSE("tab_selected");
  368. SIGNAL_CHECK_FALSE("tab_changed");
  369. // The tabs are back where they were.
  370. MessageQueue::get_singleton()->flush();
  371. CHECK(tab_bar->get_tab_rect(0) == tab_rects[0]);
  372. CHECK(tab_bar->get_tab_rect(1) == tab_rects[1]);
  373. CHECK(tab_bar->get_tab_rect(2) == tab_rects[2]);
  374. }
  375. SUBCASE("[TabBar] disabled tabs") {
  376. tab_bar->add_tab("tab0");
  377. tab_bar->add_tab("tab1");
  378. tab_bar->add_tab("tab2");
  379. CHECK_FALSE(tab_bar->is_tab_disabled(1));
  380. tab_bar->set_current_tab(1);
  381. CHECK(tab_bar->get_current_tab() == 1);
  382. CHECK(tab_bar->get_previous_tab() == 0);
  383. CHECK_FALSE(tab_bar->is_tab_hidden(1));
  384. SIGNAL_DISCARD("tab_selected");
  385. SIGNAL_DISCARD("tab_changed");
  386. // Disabling a tab does not affect current tab.
  387. tab_bar->set_tab_disabled(1, true);
  388. CHECK(tab_bar->is_tab_disabled(1));
  389. CHECK(tab_bar->get_current_tab() == 1);
  390. CHECK(tab_bar->get_previous_tab() == 0);
  391. SIGNAL_CHECK_FALSE("tab_selected");
  392. SIGNAL_CHECK_FALSE("tab_changed");
  393. // Enabling a tab does not affect current tab.
  394. tab_bar->set_tab_disabled(1, false);
  395. CHECK_FALSE(tab_bar->is_tab_disabled(1));
  396. CHECK(tab_bar->get_current_tab() == 1);
  397. CHECK(tab_bar->get_previous_tab() == 0);
  398. SIGNAL_CHECK_FALSE("tab_selected");
  399. SIGNAL_CHECK_FALSE("tab_changed");
  400. }
  401. SUBCASE("[TabBar] select next available") {
  402. tab_bar->add_tab("tab0");
  403. tab_bar->add_tab("tab1");
  404. tab_bar->add_tab("tab2");
  405. tab_bar->add_tab("tab3");
  406. tab_bar->add_tab("tab4");
  407. tab_bar->set_tab_disabled(2, true);
  408. tab_bar->set_tab_hidden(3, true);
  409. tab_bar->set_current_tab(0);
  410. CHECK(tab_bar->get_current_tab() == 0);
  411. CHECK(tab_bar->get_previous_tab() == 0);
  412. SIGNAL_DISCARD("tab_selected");
  413. SIGNAL_DISCARD("tab_changed");
  414. // Selects the next tab.
  415. CHECK(tab_bar->select_next_available());
  416. CHECK(tab_bar->get_current_tab() == 1);
  417. CHECK(tab_bar->get_previous_tab() == 0);
  418. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  419. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  420. // Skips over disabled and hidden tabs.
  421. CHECK(tab_bar->select_next_available());
  422. CHECK(tab_bar->get_current_tab() == 4);
  423. CHECK(tab_bar->get_previous_tab() == 1);
  424. SIGNAL_CHECK("tab_selected", build_array(build_array(4)));
  425. SIGNAL_CHECK("tab_changed", build_array(build_array(4)));
  426. // Does not wrap around.
  427. CHECK_FALSE(tab_bar->select_next_available());
  428. CHECK(tab_bar->get_current_tab() == 4);
  429. CHECK(tab_bar->get_previous_tab() == 1);
  430. SIGNAL_CHECK_FALSE("tab_selected");
  431. SIGNAL_CHECK_FALSE("tab_changed");
  432. // Fails if there is only one valid tab.
  433. tab_bar->remove_tab(0);
  434. tab_bar->remove_tab(3);
  435. CHECK(tab_bar->get_current_tab() == 0);
  436. CHECK(tab_bar->get_previous_tab() == 0);
  437. SIGNAL_DISCARD("tab_selected");
  438. SIGNAL_DISCARD("tab_changed");
  439. CHECK_FALSE(tab_bar->select_next_available());
  440. CHECK(tab_bar->get_current_tab() == 0);
  441. CHECK(tab_bar->get_previous_tab() == 0);
  442. SIGNAL_CHECK_FALSE("tab_selected");
  443. SIGNAL_CHECK_FALSE("tab_changed");
  444. // Fails if there are no valid tabs.
  445. tab_bar->remove_tab(0);
  446. CHECK(tab_bar->get_current_tab() == -1);
  447. CHECK(tab_bar->get_previous_tab() == 0);
  448. SIGNAL_DISCARD("tab_selected");
  449. SIGNAL_DISCARD("tab_changed");
  450. CHECK_FALSE(tab_bar->select_next_available());
  451. CHECK(tab_bar->get_current_tab() == -1);
  452. CHECK(tab_bar->get_previous_tab() == 0);
  453. SIGNAL_CHECK_FALSE("tab_selected");
  454. SIGNAL_CHECK_FALSE("tab_changed");
  455. // Fails if there are no tabs.
  456. tab_bar->clear_tabs();
  457. CHECK(tab_bar->get_current_tab() == -1);
  458. CHECK(tab_bar->get_previous_tab() == -1);
  459. SIGNAL_DISCARD("tab_selected");
  460. SIGNAL_DISCARD("tab_changed");
  461. CHECK_FALSE(tab_bar->select_next_available());
  462. CHECK(tab_bar->get_current_tab() == -1);
  463. CHECK(tab_bar->get_previous_tab() == -1);
  464. SIGNAL_CHECK_FALSE("tab_selected");
  465. SIGNAL_CHECK_FALSE("tab_changed");
  466. }
  467. SUBCASE("[TabBar] select previous available") {
  468. tab_bar->add_tab("tab0");
  469. tab_bar->add_tab("tab1");
  470. tab_bar->add_tab("tab2");
  471. tab_bar->add_tab("tab3");
  472. tab_bar->add_tab("tab4");
  473. tab_bar->set_tab_disabled(1, true);
  474. tab_bar->set_tab_hidden(2, true);
  475. tab_bar->set_current_tab(4);
  476. CHECK(tab_bar->get_current_tab() == 4);
  477. CHECK(tab_bar->get_previous_tab() == 0);
  478. SIGNAL_DISCARD("tab_selected");
  479. SIGNAL_DISCARD("tab_changed");
  480. // Selects the previous tab.
  481. CHECK(tab_bar->select_previous_available());
  482. CHECK(tab_bar->get_current_tab() == 3);
  483. CHECK(tab_bar->get_previous_tab() == 4);
  484. SIGNAL_CHECK("tab_selected", build_array(build_array(3)));
  485. SIGNAL_CHECK("tab_changed", build_array(build_array(3)));
  486. // Skips over disabled and hidden tabs.
  487. CHECK(tab_bar->select_previous_available());
  488. CHECK(tab_bar->get_current_tab() == 0);
  489. CHECK(tab_bar->get_previous_tab() == 3);
  490. SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
  491. SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
  492. // Does not wrap around.
  493. CHECK_FALSE(tab_bar->select_previous_available());
  494. CHECK(tab_bar->get_current_tab() == 0);
  495. CHECK(tab_bar->get_previous_tab() == 3);
  496. SIGNAL_CHECK_FALSE("tab_selected");
  497. SIGNAL_CHECK_FALSE("tab_changed");
  498. // Fails if there is only one valid tab.
  499. tab_bar->remove_tab(4);
  500. tab_bar->remove_tab(3);
  501. CHECK(tab_bar->get_current_tab() == 0);
  502. CHECK(tab_bar->get_previous_tab() == 2);
  503. SIGNAL_DISCARD("tab_selected");
  504. SIGNAL_DISCARD("tab_changed");
  505. CHECK_FALSE(tab_bar->select_previous_available());
  506. CHECK(tab_bar->get_current_tab() == 0);
  507. CHECK(tab_bar->get_previous_tab() == 2);
  508. SIGNAL_CHECK_FALSE("tab_selected");
  509. SIGNAL_CHECK_FALSE("tab_changed");
  510. // Fails if there are no valid tabs.
  511. tab_bar->remove_tab(0);
  512. CHECK(tab_bar->get_current_tab() == -1);
  513. CHECK(tab_bar->get_previous_tab() == 1);
  514. SIGNAL_DISCARD("tab_selected");
  515. SIGNAL_DISCARD("tab_changed");
  516. CHECK_FALSE(tab_bar->select_previous_available());
  517. CHECK(tab_bar->get_current_tab() == -1);
  518. CHECK(tab_bar->get_previous_tab() == 1);
  519. SIGNAL_CHECK_FALSE("tab_selected");
  520. SIGNAL_CHECK_FALSE("tab_changed");
  521. // Fails if there are no tabs.
  522. tab_bar->clear_tabs();
  523. CHECK(tab_bar->get_current_tab() == -1);
  524. CHECK(tab_bar->get_previous_tab() == -1);
  525. SIGNAL_DISCARD("tab_selected");
  526. SIGNAL_DISCARD("tab_changed");
  527. CHECK_FALSE(tab_bar->select_previous_available());
  528. CHECK(tab_bar->get_current_tab() == -1);
  529. CHECK(tab_bar->get_previous_tab() == -1);
  530. SIGNAL_CHECK_FALSE("tab_selected");
  531. SIGNAL_CHECK_FALSE("tab_changed");
  532. }
  533. SIGNAL_UNWATCH(tab_bar, "tab_selected");
  534. SIGNAL_UNWATCH(tab_bar, "tab_changed");
  535. memdelete(tab_bar);
  536. }
  537. TEST_CASE("[SceneTree][TabBar] initialization") {
  538. TabBar *tab_bar = memnew(TabBar);
  539. SIGNAL_WATCH(tab_bar, "tab_selected");
  540. SIGNAL_WATCH(tab_bar, "tab_changed");
  541. SUBCASE("[TabBar] current tab can be set before tabs are set") {
  542. // This queues the current tab to update on when tabs are set.
  543. tab_bar->set_current_tab(1);
  544. CHECK(tab_bar->get_current_tab() == -1);
  545. CHECK(tab_bar->get_previous_tab() == -1);
  546. SIGNAL_CHECK_FALSE("tab_selected");
  547. SIGNAL_CHECK_FALSE("tab_changed");
  548. tab_bar->set_tab_count(2);
  549. CHECK(tab_bar->get_tab_count() == 2);
  550. CHECK(tab_bar->get_current_tab() == 1);
  551. CHECK(tab_bar->get_previous_tab() == 0);
  552. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  553. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  554. // Does not work again.
  555. ERR_PRINT_OFF;
  556. tab_bar->set_current_tab(2);
  557. CHECK(tab_bar->get_tab_count() == 2);
  558. CHECK(tab_bar->get_current_tab() == 1);
  559. CHECK(tab_bar->get_previous_tab() == 0);
  560. SIGNAL_CHECK_FALSE("tab_selected");
  561. SIGNAL_CHECK_FALSE("tab_changed");
  562. tab_bar->set_tab_count(3);
  563. CHECK(tab_bar->get_tab_count() == 3);
  564. CHECK(tab_bar->get_current_tab() == 1);
  565. CHECK(tab_bar->get_previous_tab() == 0);
  566. SIGNAL_CHECK_FALSE("tab_selected");
  567. SIGNAL_CHECK_FALSE("tab_changed");
  568. ERR_PRINT_ON;
  569. }
  570. SUBCASE("[TabBar] setting tabs works normally if no current tab was set") {
  571. CHECK(tab_bar->get_current_tab() == -1);
  572. CHECK(tab_bar->get_previous_tab() == -1);
  573. tab_bar->set_tab_count(2);
  574. CHECK(tab_bar->get_tab_count() == 2);
  575. CHECK(tab_bar->get_current_tab() == 0);
  576. CHECK(tab_bar->get_previous_tab() == -1);
  577. SIGNAL_CHECK_FALSE("tab_selected");
  578. SIGNAL_CHECK_FALSE("tab_changed");
  579. }
  580. SUBCASE("[TabBar] cannot set current tab to an invalid value before tabs are set") {
  581. tab_bar->set_current_tab(100);
  582. CHECK(tab_bar->get_current_tab() == -1);
  583. CHECK(tab_bar->get_previous_tab() == -1);
  584. SIGNAL_CHECK_FALSE("tab_selected");
  585. SIGNAL_CHECK_FALSE("tab_changed");
  586. // This will print an error message as if `set_current_tab` was called after.
  587. ERR_PRINT_OFF;
  588. tab_bar->set_tab_count(2);
  589. CHECK(tab_bar->get_tab_count() == 2);
  590. CHECK(tab_bar->get_current_tab() == 0);
  591. CHECK(tab_bar->get_previous_tab() == -1);
  592. SIGNAL_CHECK_FALSE("tab_selected");
  593. SIGNAL_CHECK_FALSE("tab_changed");
  594. ERR_PRINT_ON;
  595. }
  596. SUBCASE("[TabBar] setting the current tab before tabs only works when out of tree") {
  597. tab_bar->set_current_tab(1);
  598. CHECK(tab_bar->get_current_tab() == -1);
  599. CHECK(tab_bar->get_previous_tab() == -1);
  600. SIGNAL_CHECK_FALSE("tab_selected");
  601. SIGNAL_CHECK_FALSE("tab_changed");
  602. SceneTree::get_singleton()->get_root()->add_child(tab_bar);
  603. MessageQueue::get_singleton()->flush();
  604. CHECK(tab_bar->get_tab_count() == 0);
  605. CHECK(tab_bar->get_current_tab() == -1);
  606. CHECK(tab_bar->get_previous_tab() == -1);
  607. SIGNAL_CHECK_FALSE("tab_selected");
  608. SIGNAL_CHECK_FALSE("tab_changed");
  609. // Works normally.
  610. tab_bar->set_tab_count(2);
  611. CHECK(tab_bar->get_tab_count() == 2);
  612. CHECK(tab_bar->get_current_tab() == 0);
  613. CHECK(tab_bar->get_previous_tab() == -1);
  614. SIGNAL_CHECK_FALSE("tab_selected");
  615. SIGNAL_CHECK_FALSE("tab_changed");
  616. }
  617. SIGNAL_UNWATCH(tab_bar, "tab_selected");
  618. SIGNAL_UNWATCH(tab_bar, "tab_changed");
  619. memdelete(tab_bar);
  620. }
  621. TEST_CASE("[SceneTree][TabBar] layout and offset") {
  622. TabBar *tab_bar = memnew(TabBar);
  623. SceneTree::get_singleton()->get_root()->add_child(tab_bar);
  624. tab_bar->set_clip_tabs(false);
  625. tab_bar->add_tab("tab0");
  626. tab_bar->add_tab("tab1 ");
  627. tab_bar->add_tab("tab2 ");
  628. MessageQueue::get_singleton()->flush();
  629. Size2 all_tabs_size = tab_bar->get_size();
  630. Vector<Rect2> tab_rects = {
  631. tab_bar->get_tab_rect(0),
  632. tab_bar->get_tab_rect(1),
  633. tab_bar->get_tab_rect(2)
  634. };
  635. SUBCASE("[TabBar] tabs are arranged next to each other") {
  636. // Horizontal positions are next to each other.
  637. CHECK(tab_rects[0].position.x == 0);
  638. CHECK(tab_rects[1].position.x == tab_rects[0].size.x);
  639. CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
  640. // Fills the entire width.
  641. CHECK(tab_rects[2].position.x + tab_rects[2].size.x == all_tabs_size.x);
  642. // Horizontal sizes are positive.
  643. CHECK(tab_rects[0].size.x > 0);
  644. CHECK(tab_rects[1].size.x > 0);
  645. CHECK(tab_rects[2].size.x > 0);
  646. // Vertical positions are at 0.
  647. CHECK(tab_rects[0].position.y == 0);
  648. CHECK(tab_rects[1].position.y == 0);
  649. CHECK(tab_rects[2].position.y == 0);
  650. // Vertical sizes are the same.
  651. CHECK(tab_rects[0].size.y == tab_rects[1].size.y);
  652. CHECK(tab_rects[1].size.y == tab_rects[2].size.y);
  653. }
  654. SUBCASE("[TabBar] tab alignment") {
  655. // Add extra space so the alignment can be seen.
  656. tab_bar->set_size(Size2(all_tabs_size.x + 100, all_tabs_size.y));
  657. // Left alignment.
  658. tab_bar->set_tab_alignment(TabBar::ALIGNMENT_LEFT);
  659. MessageQueue::get_singleton()->flush();
  660. tab_rects = {
  661. tab_bar->get_tab_rect(0),
  662. tab_bar->get_tab_rect(1),
  663. tab_bar->get_tab_rect(2)
  664. };
  665. CHECK(tab_bar->get_tab_alignment() == TabBar::ALIGNMENT_LEFT);
  666. CHECK(tab_rects[0].position.x == 0);
  667. CHECK(tab_rects[1].position.x == tab_rects[0].size.x);
  668. CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
  669. // Right alignment.
  670. tab_bar->set_tab_alignment(TabBar::ALIGNMENT_RIGHT);
  671. MessageQueue::get_singleton()->flush();
  672. tab_rects = {
  673. tab_bar->get_tab_rect(0),
  674. tab_bar->get_tab_rect(1),
  675. tab_bar->get_tab_rect(2)
  676. };
  677. CHECK(tab_bar->get_tab_alignment() == TabBar::ALIGNMENT_RIGHT);
  678. CHECK(tab_rects[2].position.x == tab_bar->get_size().x - tab_rects[2].size.x);
  679. CHECK(tab_rects[1].position.x == tab_rects[2].position.x - tab_rects[1].size.x);
  680. CHECK(tab_rects[0].position.x == tab_rects[1].position.x - tab_rects[0].size.x);
  681. // Center alignment.
  682. tab_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
  683. MessageQueue::get_singleton()->flush();
  684. tab_rects = {
  685. tab_bar->get_tab_rect(0),
  686. tab_bar->get_tab_rect(1),
  687. tab_bar->get_tab_rect(2)
  688. };
  689. CHECK(tab_bar->get_tab_alignment() == TabBar::ALIGNMENT_CENTER);
  690. float center_pos = tab_bar->get_size().x / 2;
  691. CHECK(tab_rects[0].position.x == center_pos - all_tabs_size.x / 2);
  692. CHECK(tab_rects[1].position.x == tab_rects[0].position.x + tab_rects[0].size.x);
  693. CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
  694. }
  695. SUBCASE("[TabBar] clip tabs") {
  696. // Clip tabs disabled means all tabs are visible and the minimum size holds all of them.
  697. tab_bar->set_clip_tabs(false);
  698. CHECK_FALSE(tab_bar->get_clip_tabs());
  699. MessageQueue::get_singleton()->flush();
  700. CHECK(tab_bar->get_tab_offset() == 0);
  701. CHECK(tab_bar->get_minimum_size() == tab_bar->get_size());
  702. CHECK(tab_bar->get_size().x == tab_rects[0].size.x + tab_rects[1].size.x + tab_rects[2].size.x);
  703. CHECK(tab_bar->get_size().y == MAX(tab_rects[0].size.y, MAX(tab_rects[1].size.y, tab_rects[2].size.y)));
  704. tab_bar->set_clip_tabs(true);
  705. CHECK(tab_bar->get_clip_tabs());
  706. MessageQueue::get_singleton()->flush();
  707. CHECK(tab_bar->get_tab_offset() == 0);
  708. // Horizontal size and minimum size get set to 0.
  709. CHECK(tab_bar->get_minimum_size().x == 0);
  710. CHECK(tab_bar->get_minimum_size().y == all_tabs_size.y);
  711. CHECK(tab_bar->get_size().x == 0);
  712. CHECK(tab_bar->get_size().y == all_tabs_size.y);
  713. }
  714. SUBCASE("[TabBar] ensure tab visible") {
  715. tab_bar->set_scroll_to_selected(false);
  716. tab_bar->set_clip_tabs(true);
  717. // Resize tab bar to only be able to fit 2 tabs.
  718. const float offset_button_size = tab_bar->get_theme_icon("decrement_icon")->get_width() + tab_bar->get_theme_icon("increment_icon")->get_width();
  719. tab_bar->set_size(Size2(tab_rects[2].size.x + tab_rects[1].size.x + offset_button_size, all_tabs_size.y));
  720. MessageQueue::get_singleton()->flush();
  721. CHECK(tab_bar->get_tab_offset() == 0);
  722. CHECK(tab_bar->get_offset_buttons_visible());
  723. // Scroll right to a tab that is not visible.
  724. tab_bar->ensure_tab_visible(2);
  725. CHECK(tab_bar->get_tab_offset() == 1);
  726. CHECK(tab_bar->get_tab_rect(1).position.x == 0);
  727. CHECK(tab_bar->get_tab_rect(2).position.x == tab_rects[1].size.x);
  728. tab_bar->set_tab_offset(2);
  729. CHECK(tab_bar->get_tab_offset() == 2);
  730. CHECK(tab_bar->get_tab_rect(2).position.x == 0);
  731. // Scroll left to a previous tab.
  732. tab_bar->ensure_tab_visible(1);
  733. CHECK(tab_bar->get_tab_offset() == 1);
  734. CHECK(tab_bar->get_tab_rect(1).position.x == 0);
  735. CHECK(tab_bar->get_tab_rect(2).position.x == tab_rects[1].size.x);
  736. // Will not scroll if the tab is already visible.
  737. tab_bar->ensure_tab_visible(2);
  738. CHECK(tab_bar->get_tab_offset() == 1);
  739. CHECK(tab_bar->get_tab_rect(1).position.x == 0);
  740. CHECK(tab_bar->get_tab_rect(2).position.x == tab_rects[1].size.x);
  741. }
  742. memdelete(tab_bar);
  743. }
  744. // FIXME: Add tests for mouse click, keyboard navigation, and drag and drop.
  745. } // namespace TestTabBar
  746. #endif // TEST_TAB_BAR_H