test_tab_container.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /**************************************************************************/
  2. /* test_tab_container.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_CONTAINER_H
  31. #define TEST_TAB_CONTAINER_H
  32. #include "scene/gui/tab_container.h"
  33. #include "tests/test_macros.h"
  34. namespace TestTabContainer {
  35. static inline Array build_array() {
  36. return Array();
  37. }
  38. template <typename... Targs>
  39. static inline Array build_array(Variant item, Targs... Fargs) {
  40. Array a = build_array(Fargs...);
  41. a.push_front(item);
  42. return a;
  43. }
  44. TEST_CASE("[SceneTree][TabContainer] tab operations") {
  45. TabContainer *tab_container = memnew(TabContainer);
  46. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  47. MessageQueue::get_singleton()->flush();
  48. SIGNAL_WATCH(tab_container, "tab_selected");
  49. SIGNAL_WATCH(tab_container, "tab_changed");
  50. Control *tab0 = memnew(Control);
  51. tab0->set_name("tab0");
  52. Control *tab1 = memnew(Control);
  53. tab1->set_name("tab1");
  54. Control *tab2 = memnew(Control);
  55. tab2->set_name("tab2");
  56. SUBCASE("[TabContainer] add tabs by adding children") {
  57. CHECK(tab_container->get_tab_count() == 0);
  58. CHECK(tab_container->get_current_tab() == -1);
  59. CHECK(tab_container->get_previous_tab() == -1);
  60. // Add first tab child.
  61. tab_container->add_child(tab0);
  62. // MessageQueue::get_singleton()->flush();
  63. CHECK(tab_container->get_tab_count() == 1);
  64. CHECK(tab_container->get_current_tab() == 0);
  65. CHECK(tab_container->get_previous_tab() == -1);
  66. SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
  67. SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
  68. // Add second tab child.
  69. tab_container->add_child(tab1);
  70. CHECK(tab_container->get_tab_count() == 2);
  71. CHECK(tab_container->get_current_tab() == 0);
  72. CHECK(tab_container->get_previous_tab() == -1);
  73. SIGNAL_CHECK_FALSE("tab_selected");
  74. SIGNAL_CHECK_FALSE("tab_changed");
  75. // Check default values, the title is the name of the child.
  76. CHECK(tab_container->get_tab_control(0) == tab0);
  77. CHECK(tab_container->get_tab_idx_from_control(tab0) == 0);
  78. CHECK(tab_container->get_tab_title(0) == "tab0");
  79. CHECK(tab_container->get_tab_tooltip(0) == "");
  80. CHECK_FALSE(tab_container->is_tab_disabled(0));
  81. CHECK_FALSE(tab_container->is_tab_hidden(0));
  82. CHECK(tab_container->get_tab_control(1) == tab1);
  83. CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
  84. CHECK(tab_container->get_tab_title(1) == "tab1");
  85. CHECK(tab_container->get_tab_tooltip(1) == "");
  86. CHECK_FALSE(tab_container->is_tab_disabled(1));
  87. CHECK_FALSE(tab_container->is_tab_hidden(1));
  88. }
  89. SUBCASE("[TabContainer] remove tabs by removing children") {
  90. tab_container->add_child(tab0);
  91. tab_container->add_child(tab1);
  92. tab_container->add_child(tab2);
  93. tab_container->set_current_tab(1);
  94. CHECK(tab_container->get_tab_count() == 3);
  95. CHECK(tab_container->get_current_tab() == 1);
  96. CHECK(tab_container->get_previous_tab() == 0);
  97. SIGNAL_DISCARD("tab_selected");
  98. SIGNAL_DISCARD("tab_changed");
  99. // Remove first tab.
  100. tab_container->remove_child(tab0);
  101. CHECK(tab_container->get_tab_count() == 2);
  102. CHECK(tab_container->get_tab_title(0) == "tab1");
  103. CHECK(tab_container->get_tab_title(1) == "tab2");
  104. CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
  105. CHECK(tab_container->get_tab_idx_from_control(tab2) == 1);
  106. CHECK(tab_container->get_current_tab() == 0);
  107. CHECK(tab_container->get_previous_tab() == 0);
  108. SIGNAL_CHECK_FALSE("tab_selected");
  109. SIGNAL_CHECK_FALSE("tab_changed");
  110. // Remove last tab.
  111. tab_container->remove_child(tab2);
  112. CHECK(tab_container->get_tab_count() == 1);
  113. CHECK(tab_container->get_tab_title(0) == "tab1");
  114. CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
  115. CHECK(tab_container->get_current_tab() == 0);
  116. CHECK(tab_container->get_previous_tab() == 0);
  117. SIGNAL_CHECK_FALSE("tab_selected");
  118. SIGNAL_CHECK_FALSE("tab_changed");
  119. // Remove only tab.
  120. tab_container->remove_child(tab1);
  121. CHECK(tab_container->get_tab_count() == 0);
  122. CHECK(tab_container->get_current_tab() == -1);
  123. CHECK(tab_container->get_previous_tab() == -1);
  124. SIGNAL_CHECK_FALSE("tab_selected");
  125. SIGNAL_CHECK("tab_changed", build_array(build_array(-1)));
  126. // Remove current tab when there are other tabs.
  127. tab_container->add_child(tab0);
  128. tab_container->add_child(tab1);
  129. tab_container->add_child(tab2);
  130. tab_container->set_current_tab(1);
  131. tab_container->set_current_tab(2);
  132. CHECK(tab_container->get_tab_count() == 3);
  133. CHECK(tab_container->get_current_tab() == 2);
  134. CHECK(tab_container->get_previous_tab() == 1);
  135. SIGNAL_DISCARD("tab_selected");
  136. SIGNAL_DISCARD("tab_changed");
  137. tab_container->remove_child(tab2);
  138. CHECK(tab_container->get_tab_count() == 2);
  139. CHECK(tab_container->get_current_tab() == 1);
  140. CHECK(tab_container->get_previous_tab() == 1);
  141. SIGNAL_CHECK_FALSE("tab_selected");
  142. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  143. }
  144. SUBCASE("[TabContainer] move tabs by moving children") {
  145. tab_container->add_child(tab0);
  146. tab_container->add_child(tab1);
  147. tab_container->add_child(tab2);
  148. tab_container->set_current_tab(1);
  149. CHECK(tab_container->get_current_tab() == 1);
  150. CHECK(tab_container->get_previous_tab() == 0);
  151. SIGNAL_DISCARD("tab_selected");
  152. SIGNAL_DISCARD("tab_changed");
  153. // Move the first tab to the end.
  154. tab_container->move_child(tab0, 2);
  155. CHECK(tab_container->get_tab_idx_from_control(tab0) == 2);
  156. CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
  157. CHECK(tab_container->get_tab_idx_from_control(tab2) == 1);
  158. CHECK(tab_container->get_current_tab() == 0);
  159. CHECK(tab_container->get_previous_tab() == 2);
  160. SIGNAL_CHECK_FALSE("tab_selected");
  161. SIGNAL_CHECK_FALSE("tab_changed");
  162. // Move the second tab to the front.
  163. tab_container->move_child(tab2, 0);
  164. CHECK(tab_container->get_tab_idx_from_control(tab0) == 2);
  165. CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
  166. CHECK(tab_container->get_tab_idx_from_control(tab2) == 0);
  167. CHECK(tab_container->get_current_tab() == 1);
  168. CHECK(tab_container->get_previous_tab() == 2);
  169. SIGNAL_CHECK_FALSE("tab_selected");
  170. SIGNAL_CHECK_FALSE("tab_changed");
  171. }
  172. SUBCASE("[TabContainer] set current tab") {
  173. tab_container->add_child(tab0);
  174. tab_container->add_child(tab1);
  175. tab_container->add_child(tab2);
  176. CHECK(tab_container->get_current_tab() == 0);
  177. CHECK(tab_container->get_previous_tab() == -1);
  178. SIGNAL_CHECK("tab_selected", build_array(build_array(0)));
  179. SIGNAL_CHECK("tab_changed", build_array(build_array(0)));
  180. MessageQueue::get_singleton()->flush();
  181. CHECK(tab0->is_visible());
  182. CHECK_FALSE(tab1->is_visible());
  183. CHECK_FALSE(tab2->is_visible());
  184. // Set the current tab.
  185. tab_container->set_current_tab(1);
  186. CHECK(tab_container->get_current_tab() == 1);
  187. CHECK(tab_container->get_previous_tab() == 0);
  188. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  189. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  190. MessageQueue::get_singleton()->flush();
  191. CHECK_FALSE(tab0->is_visible());
  192. CHECK(tab1->is_visible());
  193. CHECK_FALSE(tab2->is_visible());
  194. // Set to same tab.
  195. tab_container->set_current_tab(1);
  196. CHECK(tab_container->get_current_tab() == 1);
  197. CHECK(tab_container->get_previous_tab() == 1);
  198. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  199. SIGNAL_CHECK_FALSE("tab_changed");
  200. MessageQueue::get_singleton()->flush();
  201. CHECK_FALSE(tab0->is_visible());
  202. CHECK(tab1->is_visible());
  203. CHECK_FALSE(tab2->is_visible());
  204. // Out of bounds.
  205. ERR_PRINT_OFF;
  206. tab_container->set_current_tab(-5);
  207. CHECK(tab_container->get_current_tab() == 1);
  208. CHECK(tab_container->get_previous_tab() == 1);
  209. SIGNAL_CHECK_FALSE("tab_selected");
  210. SIGNAL_CHECK_FALSE("tab_changed");
  211. MessageQueue::get_singleton()->flush();
  212. CHECK_FALSE(tab0->is_visible());
  213. CHECK(tab1->is_visible());
  214. CHECK_FALSE(tab2->is_visible());
  215. tab_container->set_current_tab(5);
  216. CHECK(tab_container->get_current_tab() == 1);
  217. CHECK(tab_container->get_previous_tab() == 1);
  218. SIGNAL_CHECK_FALSE("tab_selected");
  219. SIGNAL_CHECK_FALSE("tab_changed");
  220. MessageQueue::get_singleton()->flush();
  221. CHECK_FALSE(tab0->is_visible());
  222. CHECK(tab1->is_visible());
  223. CHECK_FALSE(tab2->is_visible());
  224. ERR_PRINT_ON;
  225. }
  226. SUBCASE("[TabContainer] change current tab by changing visibility of children") {
  227. tab_container->add_child(tab0);
  228. tab_container->add_child(tab1);
  229. tab_container->add_child(tab2);
  230. SIGNAL_DISCARD("tab_selected");
  231. SIGNAL_DISCARD("tab_changed");
  232. MessageQueue::get_singleton()->flush();
  233. CHECK(tab0->is_visible());
  234. CHECK_FALSE(tab1->is_visible());
  235. CHECK_FALSE(tab2->is_visible());
  236. // Show a child to make it the current tab.
  237. tab1->show();
  238. CHECK(tab_container->get_current_tab() == 1);
  239. CHECK(tab_container->get_previous_tab() == 0);
  240. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  241. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  242. MessageQueue::get_singleton()->flush();
  243. CHECK_FALSE(tab0->is_visible());
  244. CHECK(tab1->is_visible());
  245. CHECK_FALSE(tab2->is_visible());
  246. // Hide the visible child to select the next tab.
  247. tab1->hide();
  248. CHECK(tab_container->get_current_tab() == 2);
  249. CHECK(tab_container->get_previous_tab() == 1);
  250. SIGNAL_CHECK("tab_selected", build_array(build_array(2)));
  251. SIGNAL_CHECK("tab_changed", build_array(build_array(2)));
  252. MessageQueue::get_singleton()->flush();
  253. CHECK_FALSE(tab0->is_visible());
  254. CHECK_FALSE(tab1->is_visible());
  255. CHECK(tab2->is_visible());
  256. // Hide the visible child to select the previous tab if there is no next.
  257. tab2->hide();
  258. CHECK(tab_container->get_current_tab() == 1);
  259. CHECK(tab_container->get_previous_tab() == 2);
  260. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  261. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  262. MessageQueue::get_singleton()->flush();
  263. CHECK_FALSE(tab0->is_visible());
  264. CHECK(tab1->is_visible());
  265. CHECK_FALSE(tab2->is_visible());
  266. // Cannot hide if there is only one valid child since deselection is not enabled.
  267. tab_container->remove_child(tab1);
  268. tab_container->remove_child(tab2);
  269. CHECK(tab_container->get_current_tab() == 0);
  270. CHECK(tab_container->get_previous_tab() == 0);
  271. SIGNAL_DISCARD("tab_selected");
  272. SIGNAL_DISCARD("tab_changed");
  273. MessageQueue::get_singleton()->flush();
  274. CHECK(tab0->is_visible());
  275. tab0->hide();
  276. CHECK(tab_container->get_current_tab() == 0);
  277. CHECK(tab_container->get_previous_tab() == 0);
  278. SIGNAL_CHECK_FALSE("tab_selected");
  279. SIGNAL_CHECK_FALSE("tab_changed");
  280. MessageQueue::get_singleton()->flush();
  281. CHECK(tab0->is_visible());
  282. // Can hide the last tab if deselection is enabled.
  283. tab_container->set_deselect_enabled(true);
  284. tab0->hide();
  285. CHECK(tab_container->get_current_tab() == -1);
  286. CHECK(tab_container->get_previous_tab() == 0);
  287. SIGNAL_CHECK("tab_selected", build_array(build_array(-1)));
  288. SIGNAL_CHECK("tab_changed", build_array(build_array(-1)));
  289. MessageQueue::get_singleton()->flush();
  290. CHECK_FALSE(tab0->is_visible());
  291. }
  292. SIGNAL_UNWATCH(tab_container, "tab_selected");
  293. SIGNAL_UNWATCH(tab_container, "tab_changed");
  294. memdelete(tab2);
  295. memdelete(tab1);
  296. memdelete(tab0);
  297. memdelete(tab_container);
  298. }
  299. TEST_CASE("[SceneTree][TabContainer] initialization") {
  300. TabContainer *tab_container = memnew(TabContainer);
  301. Control *tab0 = memnew(Control);
  302. tab0->set_name("tab0");
  303. Control *tab1 = memnew(Control);
  304. tab1->set_name("tab1 ");
  305. Control *tab2 = memnew(Control);
  306. tab2->set_name("tab2 ");
  307. SIGNAL_WATCH(tab_container, "tab_selected");
  308. SIGNAL_WATCH(tab_container, "tab_changed");
  309. SUBCASE("[TabContainer] add children before entering tree") {
  310. CHECK(tab_container->get_current_tab() == -1);
  311. CHECK(tab_container->get_previous_tab() == -1);
  312. tab_container->add_child(tab0);
  313. CHECK(tab_container->get_tab_count() == 1);
  314. CHECK(tab_container->get_current_tab() == 0);
  315. CHECK(tab_container->get_previous_tab() == -1);
  316. tab_container->add_child(tab1);
  317. CHECK(tab_container->get_tab_count() == 2);
  318. CHECK(tab_container->get_current_tab() == 0);
  319. CHECK(tab_container->get_previous_tab() == -1);
  320. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  321. MessageQueue::get_singleton()->flush();
  322. CHECK(tab_container->get_tab_count() == 2);
  323. CHECK(tab_container->get_current_tab() == 0);
  324. CHECK(tab_container->get_previous_tab() == -1);
  325. SIGNAL_CHECK_FALSE("tab_selected");
  326. SIGNAL_CHECK_FALSE("tab_changed");
  327. CHECK(tab0->is_visible());
  328. CHECK_FALSE(tab1->is_visible());
  329. }
  330. SUBCASE("[TabContainer] current tab can be set before children are added") {
  331. // Set the current tab before there are any tabs.
  332. // This queues the current tab to update on entering the tree.
  333. tab_container->set_current_tab(1);
  334. CHECK(tab_container->get_current_tab() == -1);
  335. CHECK(tab_container->get_previous_tab() == -1);
  336. SIGNAL_CHECK_FALSE("tab_selected");
  337. SIGNAL_CHECK_FALSE("tab_changed");
  338. tab_container->add_child(tab0);
  339. CHECK(tab_container->get_tab_count() == 1);
  340. CHECK(tab_container->get_current_tab() == 0);
  341. CHECK(tab_container->get_previous_tab() == -1);
  342. tab_container->add_child(tab1);
  343. CHECK(tab_container->get_tab_count() == 2);
  344. CHECK(tab_container->get_current_tab() == 0);
  345. CHECK(tab_container->get_previous_tab() == -1);
  346. tab_container->add_child(tab2);
  347. CHECK(tab_container->get_tab_count() == 3);
  348. CHECK(tab_container->get_current_tab() == 0);
  349. CHECK(tab_container->get_previous_tab() == -1);
  350. SIGNAL_CHECK_FALSE("tab_selected");
  351. SIGNAL_CHECK_FALSE("tab_changed");
  352. // Current tab is set when entering the tree.
  353. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  354. MessageQueue::get_singleton()->flush();
  355. CHECK(tab_container->get_tab_count() == 3);
  356. CHECK(tab_container->get_current_tab() == 1);
  357. CHECK(tab_container->get_previous_tab() == 0);
  358. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  359. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  360. CHECK_FALSE(tab0->is_visible());
  361. CHECK(tab1->is_visible());
  362. CHECK_FALSE(tab2->is_visible());
  363. }
  364. SUBCASE("[TabContainer] cannot set current tab to an invalid value before tabs are set") {
  365. tab_container->set_current_tab(100);
  366. CHECK(tab_container->get_current_tab() == -1);
  367. CHECK(tab_container->get_previous_tab() == -1);
  368. SIGNAL_CHECK_FALSE("tab_selected");
  369. SIGNAL_CHECK_FALSE("tab_changed");
  370. tab_container->add_child(tab0);
  371. CHECK(tab_container->get_tab_count() == 1);
  372. CHECK(tab_container->get_current_tab() == 0);
  373. CHECK(tab_container->get_previous_tab() == -1);
  374. SIGNAL_CHECK_FALSE("tab_selected");
  375. SIGNAL_CHECK_FALSE("tab_changed");
  376. tab_container->add_child(tab1);
  377. CHECK(tab_container->get_tab_count() == 2);
  378. CHECK(tab_container->get_current_tab() == 0);
  379. CHECK(tab_container->get_previous_tab() == -1);
  380. SIGNAL_CHECK_FALSE("tab_selected");
  381. SIGNAL_CHECK_FALSE("tab_changed");
  382. // This will print an error message as if `set_current_tab` was called after.
  383. ERR_PRINT_OFF;
  384. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  385. MessageQueue::get_singleton()->flush();
  386. CHECK(tab_container->get_tab_count() == 2);
  387. CHECK(tab_container->get_current_tab() == 0);
  388. CHECK(tab_container->get_previous_tab() == -1);
  389. SIGNAL_CHECK_FALSE("tab_selected");
  390. SIGNAL_CHECK_FALSE("tab_changed");
  391. ERR_PRINT_ON;
  392. }
  393. SUBCASE("[TabContainer] children visibility before entering tree") {
  394. CHECK(tab_container->get_current_tab() == -1);
  395. CHECK(tab_container->get_previous_tab() == -1);
  396. // Adding a hidden child first will change visibility because it is the current tab.
  397. tab0->hide();
  398. tab_container->add_child(tab0);
  399. CHECK(tab_container->get_tab_count() == 1);
  400. CHECK(tab_container->get_current_tab() == 0);
  401. CHECK(tab_container->get_previous_tab() == -1);
  402. MessageQueue::get_singleton()->flush();
  403. CHECK(tab0->is_visible());
  404. // Adding a visible child after will hide it because it is not the current tab.
  405. tab_container->add_child(tab1);
  406. CHECK(tab_container->get_tab_count() == 2);
  407. CHECK(tab_container->get_current_tab() == 0);
  408. CHECK(tab_container->get_previous_tab() == -1);
  409. MessageQueue::get_singleton()->flush();
  410. CHECK(tab0->is_visible());
  411. CHECK_FALSE(tab1->is_visible());
  412. // Can change current by showing child now after children have been added.
  413. // This queues the current tab to update on entering the tree.
  414. tab1->show();
  415. MessageQueue::get_singleton()->flush();
  416. CHECK(tab_container->get_tab_count() == 2);
  417. CHECK(tab_container->get_current_tab() == 0);
  418. CHECK(tab_container->get_previous_tab() == -1);
  419. SIGNAL_CHECK_FALSE("tab_selected");
  420. SIGNAL_CHECK_FALSE("tab_changed");
  421. CHECK(tab0->is_visible());
  422. CHECK(tab1->is_visible());
  423. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  424. MessageQueue::get_singleton()->flush();
  425. CHECK(tab_container->get_tab_count() == 2);
  426. CHECK(tab_container->get_current_tab() == 1);
  427. CHECK(tab_container->get_previous_tab() == 0);
  428. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  429. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  430. CHECK_FALSE(tab0->is_visible());
  431. CHECK(tab1->is_visible());
  432. }
  433. SUBCASE("[TabContainer] setting current tab and changing child visibility after adding") {
  434. tab_container->add_child(tab0);
  435. tab_container->add_child(tab1);
  436. tab_container->add_child(tab2);
  437. MessageQueue::get_singleton()->flush();
  438. CHECK(tab_container->get_current_tab() == 0);
  439. CHECK(tab_container->get_previous_tab() == -1);
  440. tab2->show();
  441. MessageQueue::get_singleton()->flush();
  442. CHECK(tab_container->get_tab_count() == 3);
  443. CHECK(tab_container->get_current_tab() == 0);
  444. CHECK(tab_container->get_previous_tab() == -1);
  445. SIGNAL_CHECK_FALSE("tab_selected");
  446. SIGNAL_CHECK_FALSE("tab_changed");
  447. CHECK(tab0->is_visible());
  448. CHECK_FALSE(tab1->is_visible());
  449. CHECK(tab2->is_visible());
  450. // Whichever happens last will have priority.
  451. tab_container->set_current_tab(1);
  452. CHECK(tab_container->get_current_tab() == 0);
  453. CHECK(tab_container->get_previous_tab() == -1);
  454. // Current tab is set when entering the tree.
  455. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  456. MessageQueue::get_singleton()->flush();
  457. CHECK(tab_container->get_tab_count() == 3);
  458. CHECK(tab_container->get_current_tab() == 1);
  459. CHECK(tab_container->get_previous_tab() == 0);
  460. SIGNAL_CHECK("tab_selected", build_array(build_array(1)));
  461. SIGNAL_CHECK("tab_changed", build_array(build_array(1)));
  462. CHECK_FALSE(tab0->is_visible());
  463. CHECK(tab1->is_visible());
  464. CHECK_FALSE(tab2->is_visible());
  465. }
  466. SIGNAL_UNWATCH(tab_container, "tab_selected");
  467. SIGNAL_UNWATCH(tab_container, "tab_changed");
  468. memdelete(tab2);
  469. memdelete(tab1);
  470. memdelete(tab0);
  471. memdelete(tab_container);
  472. }
  473. TEST_CASE("[SceneTree][TabContainer] layout and offset") {
  474. TabContainer *tab_container = memnew(TabContainer);
  475. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  476. tab_container->set_clip_tabs(false);
  477. Control *tab0 = memnew(Control);
  478. tab0->set_name("tab0");
  479. Control *tab1 = memnew(Control);
  480. tab1->set_name("tab1 ");
  481. Control *tab2 = memnew(Control);
  482. tab2->set_name("tab2 ");
  483. tab_container->add_child(tab0);
  484. tab_container->add_child(tab1);
  485. tab_container->add_child(tab2);
  486. MessageQueue::get_singleton()->flush();
  487. Size2 all_tabs_size = tab_container->get_size();
  488. const float side_margin = tab_container->get_theme_constant("side_margin");
  489. TabBar *tab_bar = tab_container->get_tab_bar();
  490. Vector<Rect2> tab_rects = {
  491. tab_bar->get_tab_rect(0),
  492. tab_bar->get_tab_rect(1),
  493. tab_bar->get_tab_rect(2)
  494. };
  495. SUBCASE("[TabContainer] tabs are arranged next to each other") {
  496. // Horizontal positions are next to each other.
  497. CHECK(tab_rects[0].position.x == 0);
  498. CHECK(tab_rects[1].position.x == tab_rects[0].size.x);
  499. CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
  500. // Fills the entire width.
  501. CHECK(tab_rects[2].position.x + tab_rects[2].size.x == all_tabs_size.x - side_margin);
  502. // Horizontal sizes are positive.
  503. CHECK(tab_rects[0].size.x > 0);
  504. CHECK(tab_rects[1].size.x > 0);
  505. CHECK(tab_rects[2].size.x > 0);
  506. // Vertical positions are at 0.
  507. CHECK(tab_rects[0].position.y == 0);
  508. CHECK(tab_rects[1].position.y == 0);
  509. CHECK(tab_rects[2].position.y == 0);
  510. // Vertical sizes are the same.
  511. CHECK(tab_rects[0].size.y == tab_rects[1].size.y);
  512. CHECK(tab_rects[1].size.y == tab_rects[2].size.y);
  513. }
  514. SUBCASE("[TabContainer] tab position") {
  515. float tab_height = tab_rects[0].size.y;
  516. Ref<StyleBox> panel_style = tab_container->get_theme_stylebox("panel_style");
  517. // Initial position, same as top position.
  518. // Tab bar is at the top.
  519. CHECK(tab_bar->get_anchor(SIDE_TOP) == 0);
  520. CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 0);
  521. CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
  522. CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
  523. CHECK(tab_bar->get_offset(SIDE_TOP) == 0);
  524. CHECK(tab_bar->get_offset(SIDE_BOTTOM) == tab_height);
  525. CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
  526. CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
  527. // Child is expanded and below the tab bar.
  528. CHECK(tab0->get_anchor(SIDE_TOP) == 0);
  529. CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
  530. CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
  531. CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
  532. CHECK(tab0->get_offset(SIDE_TOP) == tab_height);
  533. CHECK(tab0->get_offset(SIDE_BOTTOM) == 0);
  534. CHECK(tab0->get_offset(SIDE_LEFT) == 0);
  535. CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
  536. // Bottom position.
  537. tab_container->set_tabs_position(TabContainer::POSITION_BOTTOM);
  538. CHECK(tab_container->get_tabs_position() == TabContainer::POSITION_BOTTOM);
  539. MessageQueue::get_singleton()->flush();
  540. // Tab bar is at the bottom.
  541. CHECK(tab_bar->get_anchor(SIDE_TOP) == 1);
  542. CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 1);
  543. CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
  544. CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
  545. CHECK(tab_bar->get_offset(SIDE_TOP) == -tab_height);
  546. CHECK(tab_bar->get_offset(SIDE_BOTTOM) == 0);
  547. CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
  548. CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
  549. // Child is expanded and above the tab bar.
  550. CHECK(tab0->get_anchor(SIDE_TOP) == 0);
  551. CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
  552. CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
  553. CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
  554. CHECK(tab0->get_offset(SIDE_TOP) == 0);
  555. CHECK(tab0->get_offset(SIDE_BOTTOM) == -tab_height);
  556. CHECK(tab0->get_offset(SIDE_LEFT) == 0);
  557. CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
  558. // Top position.
  559. tab_container->set_tabs_position(TabContainer::POSITION_TOP);
  560. CHECK(tab_container->get_tabs_position() == TabContainer::POSITION_TOP);
  561. MessageQueue::get_singleton()->flush();
  562. // Tab bar is at the top.
  563. CHECK(tab_bar->get_anchor(SIDE_TOP) == 0);
  564. CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 0);
  565. CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
  566. CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
  567. CHECK(tab_bar->get_offset(SIDE_TOP) == 0);
  568. CHECK(tab_bar->get_offset(SIDE_BOTTOM) == tab_height);
  569. CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
  570. CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
  571. // Child is expanded and below the tab bar.
  572. CHECK(tab0->get_anchor(SIDE_TOP) == 0);
  573. CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
  574. CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
  575. CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
  576. CHECK(tab0->get_offset(SIDE_TOP) == tab_height);
  577. CHECK(tab0->get_offset(SIDE_BOTTOM) == 0);
  578. CHECK(tab0->get_offset(SIDE_LEFT) == 0);
  579. CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
  580. }
  581. memdelete(tab_container);
  582. }
  583. // FIXME: Add tests for mouse click, keyboard navigation, and drag and drop.
  584. } // namespace TestTabContainer
  585. #endif // TEST_TAB_CONTAINER_H