test_tab_container.h 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  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. #pragma once
  31. #include "scene/gui/tab_container.h"
  32. #include "tests/test_macros.h"
  33. namespace TestTabContainer {
  34. TEST_CASE("[SceneTree][TabContainer] tab operations") {
  35. TabContainer *tab_container = memnew(TabContainer);
  36. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  37. MessageQueue::get_singleton()->flush();
  38. SIGNAL_WATCH(tab_container, "tab_selected");
  39. SIGNAL_WATCH(tab_container, "tab_changed");
  40. Control *tab0 = memnew(Control);
  41. tab0->set_name("tab0");
  42. Control *tab1 = memnew(Control);
  43. tab1->set_name("tab1");
  44. Control *tab2 = memnew(Control);
  45. tab2->set_name("tab2");
  46. SUBCASE("[TabContainer] add tabs by adding children") {
  47. CHECK(tab_container->get_tab_count() == 0);
  48. CHECK(tab_container->get_current_tab() == -1);
  49. CHECK(tab_container->get_previous_tab() == -1);
  50. // Add first tab child.
  51. tab_container->add_child(tab0);
  52. // MessageQueue::get_singleton()->flush();
  53. CHECK(tab_container->get_tab_count() == 1);
  54. CHECK(tab_container->get_current_tab() == 0);
  55. CHECK(tab_container->get_previous_tab() == -1);
  56. SIGNAL_CHECK("tab_selected", { { 0 } });
  57. SIGNAL_CHECK("tab_changed", { { 0 } });
  58. // Add second tab child.
  59. tab_container->add_child(tab1);
  60. CHECK(tab_container->get_tab_count() == 2);
  61. CHECK(tab_container->get_current_tab() == 0);
  62. CHECK(tab_container->get_previous_tab() == -1);
  63. SIGNAL_CHECK_FALSE("tab_selected");
  64. SIGNAL_CHECK_FALSE("tab_changed");
  65. // Check default values, the title is the name of the child.
  66. CHECK(tab_container->get_tab_control(0) == tab0);
  67. CHECK(tab_container->get_tab_idx_from_control(tab0) == 0);
  68. CHECK(tab_container->get_tab_title(0) == "tab0");
  69. CHECK(tab_container->get_tab_tooltip(0) == "");
  70. CHECK_FALSE(tab_container->is_tab_disabled(0));
  71. CHECK_FALSE(tab_container->is_tab_hidden(0));
  72. CHECK(tab_container->get_tab_control(1) == tab1);
  73. CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
  74. CHECK(tab_container->get_tab_title(1) == "tab1");
  75. CHECK(tab_container->get_tab_tooltip(1) == "");
  76. CHECK_FALSE(tab_container->is_tab_disabled(1));
  77. CHECK_FALSE(tab_container->is_tab_hidden(1));
  78. }
  79. SUBCASE("[TabContainer] remove tabs by removing children") {
  80. tab_container->add_child(tab0);
  81. tab_container->add_child(tab1);
  82. tab_container->add_child(tab2);
  83. tab_container->set_current_tab(1);
  84. CHECK(tab_container->get_tab_count() == 3);
  85. CHECK(tab_container->get_current_tab() == 1);
  86. CHECK(tab_container->get_previous_tab() == 0);
  87. SIGNAL_DISCARD("tab_selected");
  88. SIGNAL_DISCARD("tab_changed");
  89. // Remove first tab.
  90. tab_container->remove_child(tab0);
  91. CHECK(tab_container->get_tab_count() == 2);
  92. CHECK(tab_container->get_tab_title(0) == "tab1");
  93. CHECK(tab_container->get_tab_title(1) == "tab2");
  94. CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
  95. CHECK(tab_container->get_tab_idx_from_control(tab2) == 1);
  96. CHECK(tab_container->get_current_tab() == 0);
  97. CHECK(tab_container->get_previous_tab() == 0);
  98. SIGNAL_CHECK_FALSE("tab_selected");
  99. SIGNAL_CHECK_FALSE("tab_changed");
  100. // Remove last tab.
  101. tab_container->remove_child(tab2);
  102. CHECK(tab_container->get_tab_count() == 1);
  103. CHECK(tab_container->get_tab_title(0) == "tab1");
  104. CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
  105. CHECK(tab_container->get_current_tab() == 0);
  106. CHECK(tab_container->get_previous_tab() == 0);
  107. SIGNAL_CHECK_FALSE("tab_selected");
  108. SIGNAL_CHECK_FALSE("tab_changed");
  109. // Remove only tab.
  110. tab_container->remove_child(tab1);
  111. CHECK(tab_container->get_tab_count() == 0);
  112. CHECK(tab_container->get_current_tab() == -1);
  113. CHECK(tab_container->get_previous_tab() == -1);
  114. SIGNAL_CHECK_FALSE("tab_selected");
  115. SIGNAL_CHECK("tab_changed", { { -1 } });
  116. // Remove current tab when there are other tabs.
  117. tab_container->add_child(tab0);
  118. tab_container->add_child(tab1);
  119. tab_container->add_child(tab2);
  120. tab_container->set_current_tab(1);
  121. tab_container->set_current_tab(2);
  122. CHECK(tab_container->get_tab_count() == 3);
  123. CHECK(tab_container->get_current_tab() == 2);
  124. CHECK(tab_container->get_previous_tab() == 1);
  125. SIGNAL_DISCARD("tab_selected");
  126. SIGNAL_DISCARD("tab_changed");
  127. tab_container->remove_child(tab2);
  128. CHECK(tab_container->get_tab_count() == 2);
  129. CHECK(tab_container->get_current_tab() == 1);
  130. CHECK(tab_container->get_previous_tab() == 1);
  131. SIGNAL_CHECK_FALSE("tab_selected");
  132. SIGNAL_CHECK("tab_changed", { { 1 } });
  133. }
  134. SUBCASE("[TabContainer] move tabs by moving children") {
  135. tab_container->add_child(tab0);
  136. tab_container->add_child(tab1);
  137. tab_container->add_child(tab2);
  138. tab_container->set_current_tab(1);
  139. CHECK(tab_container->get_current_tab() == 1);
  140. CHECK(tab_container->get_previous_tab() == 0);
  141. SIGNAL_DISCARD("tab_selected");
  142. SIGNAL_DISCARD("tab_changed");
  143. // Move the first tab to the end.
  144. tab_container->move_child(tab0, 2);
  145. CHECK(tab_container->get_tab_idx_from_control(tab0) == 2);
  146. CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
  147. CHECK(tab_container->get_tab_idx_from_control(tab2) == 1);
  148. CHECK(tab_container->get_current_tab() == 0);
  149. CHECK(tab_container->get_previous_tab() == 2);
  150. SIGNAL_CHECK_FALSE("tab_selected");
  151. SIGNAL_CHECK_FALSE("tab_changed");
  152. // Move the second tab to the front.
  153. tab_container->move_child(tab2, 0);
  154. CHECK(tab_container->get_tab_idx_from_control(tab0) == 2);
  155. CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
  156. CHECK(tab_container->get_tab_idx_from_control(tab2) == 0);
  157. CHECK(tab_container->get_current_tab() == 1);
  158. CHECK(tab_container->get_previous_tab() == 2);
  159. SIGNAL_CHECK_FALSE("tab_selected");
  160. SIGNAL_CHECK_FALSE("tab_changed");
  161. }
  162. SUBCASE("[TabContainer] set current tab") {
  163. tab_container->add_child(tab0);
  164. tab_container->add_child(tab1);
  165. tab_container->add_child(tab2);
  166. CHECK(tab_container->get_current_tab() == 0);
  167. CHECK(tab_container->get_previous_tab() == -1);
  168. SIGNAL_CHECK("tab_selected", { { 0 } });
  169. SIGNAL_CHECK("tab_changed", { { 0 } });
  170. MessageQueue::get_singleton()->flush();
  171. CHECK(tab0->is_visible());
  172. CHECK_FALSE(tab1->is_visible());
  173. CHECK_FALSE(tab2->is_visible());
  174. // Set the current tab.
  175. tab_container->set_current_tab(1);
  176. CHECK(tab_container->get_current_tab() == 1);
  177. CHECK(tab_container->get_previous_tab() == 0);
  178. SIGNAL_CHECK("tab_selected", { { 1 } });
  179. SIGNAL_CHECK("tab_changed", { { 1 } });
  180. MessageQueue::get_singleton()->flush();
  181. CHECK_FALSE(tab0->is_visible());
  182. CHECK(tab1->is_visible());
  183. CHECK_FALSE(tab2->is_visible());
  184. // Set to same tab.
  185. tab_container->set_current_tab(1);
  186. CHECK(tab_container->get_current_tab() == 1);
  187. CHECK(tab_container->get_previous_tab() == 1);
  188. SIGNAL_CHECK("tab_selected", { { 1 } });
  189. SIGNAL_CHECK_FALSE("tab_changed");
  190. MessageQueue::get_singleton()->flush();
  191. CHECK_FALSE(tab0->is_visible());
  192. CHECK(tab1->is_visible());
  193. CHECK_FALSE(tab2->is_visible());
  194. // Out of bounds.
  195. ERR_PRINT_OFF;
  196. tab_container->set_current_tab(-5);
  197. CHECK(tab_container->get_current_tab() == 1);
  198. CHECK(tab_container->get_previous_tab() == 1);
  199. SIGNAL_CHECK_FALSE("tab_selected");
  200. SIGNAL_CHECK_FALSE("tab_changed");
  201. MessageQueue::get_singleton()->flush();
  202. CHECK_FALSE(tab0->is_visible());
  203. CHECK(tab1->is_visible());
  204. CHECK_FALSE(tab2->is_visible());
  205. tab_container->set_current_tab(5);
  206. CHECK(tab_container->get_current_tab() == 1);
  207. CHECK(tab_container->get_previous_tab() == 1);
  208. SIGNAL_CHECK_FALSE("tab_selected");
  209. SIGNAL_CHECK_FALSE("tab_changed");
  210. MessageQueue::get_singleton()->flush();
  211. CHECK_FALSE(tab0->is_visible());
  212. CHECK(tab1->is_visible());
  213. CHECK_FALSE(tab2->is_visible());
  214. ERR_PRINT_ON;
  215. }
  216. SUBCASE("[TabContainer] change current tab by changing visibility of children") {
  217. tab_container->add_child(tab0);
  218. tab_container->add_child(tab1);
  219. tab_container->add_child(tab2);
  220. SIGNAL_DISCARD("tab_selected");
  221. SIGNAL_DISCARD("tab_changed");
  222. MessageQueue::get_singleton()->flush();
  223. CHECK(tab0->is_visible());
  224. CHECK_FALSE(tab1->is_visible());
  225. CHECK_FALSE(tab2->is_visible());
  226. // Show a child to make it the current tab.
  227. tab1->show();
  228. CHECK(tab_container->get_current_tab() == 1);
  229. CHECK(tab_container->get_previous_tab() == 0);
  230. SIGNAL_CHECK("tab_selected", { { 1 } });
  231. SIGNAL_CHECK("tab_changed", { { 1 } });
  232. MessageQueue::get_singleton()->flush();
  233. CHECK_FALSE(tab0->is_visible());
  234. CHECK(tab1->is_visible());
  235. CHECK_FALSE(tab2->is_visible());
  236. // Hide the visible child to select the next tab.
  237. tab1->hide();
  238. CHECK(tab_container->get_current_tab() == 2);
  239. CHECK(tab_container->get_previous_tab() == 1);
  240. SIGNAL_CHECK("tab_selected", { { 2 } });
  241. SIGNAL_CHECK("tab_changed", { { 2 } });
  242. MessageQueue::get_singleton()->flush();
  243. CHECK_FALSE(tab0->is_visible());
  244. CHECK_FALSE(tab1->is_visible());
  245. CHECK(tab2->is_visible());
  246. // Hide the visible child to select the previous tab if there is no next.
  247. tab2->hide();
  248. CHECK(tab_container->get_current_tab() == 1);
  249. CHECK(tab_container->get_previous_tab() == 2);
  250. SIGNAL_CHECK("tab_selected", { { 1 } });
  251. SIGNAL_CHECK("tab_changed", { { 1 } });
  252. MessageQueue::get_singleton()->flush();
  253. CHECK_FALSE(tab0->is_visible());
  254. CHECK(tab1->is_visible());
  255. CHECK_FALSE(tab2->is_visible());
  256. // Cannot hide if there is only one valid child since deselection is not enabled.
  257. tab_container->remove_child(tab1);
  258. tab_container->remove_child(tab2);
  259. CHECK(tab_container->get_current_tab() == 0);
  260. CHECK(tab_container->get_previous_tab() == 0);
  261. SIGNAL_DISCARD("tab_selected");
  262. SIGNAL_DISCARD("tab_changed");
  263. MessageQueue::get_singleton()->flush();
  264. CHECK(tab0->is_visible());
  265. tab0->hide();
  266. CHECK(tab_container->get_current_tab() == 0);
  267. CHECK(tab_container->get_previous_tab() == 0);
  268. SIGNAL_CHECK_FALSE("tab_selected");
  269. SIGNAL_CHECK_FALSE("tab_changed");
  270. MessageQueue::get_singleton()->flush();
  271. CHECK(tab0->is_visible());
  272. // Can hide the last tab if deselection is enabled.
  273. tab_container->set_deselect_enabled(true);
  274. tab0->hide();
  275. CHECK(tab_container->get_current_tab() == -1);
  276. CHECK(tab_container->get_previous_tab() == 0);
  277. SIGNAL_CHECK("tab_selected", { { -1 } });
  278. SIGNAL_CHECK("tab_changed", { { -1 } });
  279. MessageQueue::get_singleton()->flush();
  280. CHECK_FALSE(tab0->is_visible());
  281. }
  282. SIGNAL_UNWATCH(tab_container, "tab_selected");
  283. SIGNAL_UNWATCH(tab_container, "tab_changed");
  284. memdelete(tab2);
  285. memdelete(tab1);
  286. memdelete(tab0);
  287. memdelete(tab_container);
  288. }
  289. TEST_CASE("[SceneTree][TabContainer] initialization") {
  290. TabContainer *tab_container = memnew(TabContainer);
  291. Control *tab0 = memnew(Control);
  292. tab0->set_name("tab0");
  293. Control *tab1 = memnew(Control);
  294. tab1->set_name("tab1 ");
  295. Control *tab2 = memnew(Control);
  296. tab2->set_name("tab2 ");
  297. SIGNAL_WATCH(tab_container, "tab_selected");
  298. SIGNAL_WATCH(tab_container, "tab_changed");
  299. SUBCASE("[TabContainer] add children before entering tree") {
  300. CHECK(tab_container->get_current_tab() == -1);
  301. CHECK(tab_container->get_previous_tab() == -1);
  302. tab_container->add_child(tab0);
  303. CHECK(tab_container->get_tab_count() == 1);
  304. CHECK(tab_container->get_current_tab() == 0);
  305. CHECK(tab_container->get_previous_tab() == -1);
  306. tab_container->add_child(tab1);
  307. CHECK(tab_container->get_tab_count() == 2);
  308. CHECK(tab_container->get_current_tab() == 0);
  309. CHECK(tab_container->get_previous_tab() == -1);
  310. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  311. MessageQueue::get_singleton()->flush();
  312. CHECK(tab_container->get_tab_count() == 2);
  313. CHECK(tab_container->get_current_tab() == 0);
  314. CHECK(tab_container->get_previous_tab() == -1);
  315. SIGNAL_CHECK_FALSE("tab_selected");
  316. SIGNAL_CHECK_FALSE("tab_changed");
  317. CHECK(tab0->is_visible());
  318. CHECK_FALSE(tab1->is_visible());
  319. }
  320. SUBCASE("[TabContainer] current tab can be set before children are added") {
  321. // Set the current tab before there are any tabs.
  322. // This queues the current tab to update on entering the tree.
  323. tab_container->set_current_tab(1);
  324. CHECK(tab_container->get_current_tab() == -1);
  325. CHECK(tab_container->get_previous_tab() == -1);
  326. SIGNAL_CHECK_FALSE("tab_selected");
  327. SIGNAL_CHECK_FALSE("tab_changed");
  328. tab_container->add_child(tab0);
  329. CHECK(tab_container->get_tab_count() == 1);
  330. CHECK(tab_container->get_current_tab() == 0);
  331. CHECK(tab_container->get_previous_tab() == -1);
  332. tab_container->add_child(tab1);
  333. CHECK(tab_container->get_tab_count() == 2);
  334. CHECK(tab_container->get_current_tab() == 0);
  335. CHECK(tab_container->get_previous_tab() == -1);
  336. tab_container->add_child(tab2);
  337. CHECK(tab_container->get_tab_count() == 3);
  338. CHECK(tab_container->get_current_tab() == 0);
  339. CHECK(tab_container->get_previous_tab() == -1);
  340. SIGNAL_CHECK_FALSE("tab_selected");
  341. SIGNAL_CHECK_FALSE("tab_changed");
  342. // Current tab is set when entering the tree.
  343. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  344. MessageQueue::get_singleton()->flush();
  345. CHECK(tab_container->get_tab_count() == 3);
  346. CHECK(tab_container->get_current_tab() == 1);
  347. CHECK(tab_container->get_previous_tab() == 0);
  348. SIGNAL_CHECK("tab_selected", { { 1 } });
  349. SIGNAL_CHECK("tab_changed", { { 1 } });
  350. CHECK_FALSE(tab0->is_visible());
  351. CHECK(tab1->is_visible());
  352. CHECK_FALSE(tab2->is_visible());
  353. }
  354. SUBCASE("[TabContainer] cannot set current tab to an invalid value before tabs are set") {
  355. tab_container->set_current_tab(100);
  356. CHECK(tab_container->get_current_tab() == -1);
  357. CHECK(tab_container->get_previous_tab() == -1);
  358. SIGNAL_CHECK_FALSE("tab_selected");
  359. SIGNAL_CHECK_FALSE("tab_changed");
  360. tab_container->add_child(tab0);
  361. CHECK(tab_container->get_tab_count() == 1);
  362. CHECK(tab_container->get_current_tab() == 0);
  363. CHECK(tab_container->get_previous_tab() == -1);
  364. SIGNAL_CHECK_FALSE("tab_selected");
  365. SIGNAL_CHECK_FALSE("tab_changed");
  366. tab_container->add_child(tab1);
  367. CHECK(tab_container->get_tab_count() == 2);
  368. CHECK(tab_container->get_current_tab() == 0);
  369. CHECK(tab_container->get_previous_tab() == -1);
  370. SIGNAL_CHECK_FALSE("tab_selected");
  371. SIGNAL_CHECK_FALSE("tab_changed");
  372. // This will print an error message as if `set_current_tab` was called after.
  373. ERR_PRINT_OFF;
  374. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  375. MessageQueue::get_singleton()->flush();
  376. CHECK(tab_container->get_tab_count() == 2);
  377. CHECK(tab_container->get_current_tab() == 0);
  378. CHECK(tab_container->get_previous_tab() == -1);
  379. SIGNAL_CHECK_FALSE("tab_selected");
  380. SIGNAL_CHECK_FALSE("tab_changed");
  381. ERR_PRINT_ON;
  382. }
  383. SUBCASE("[TabContainer] children visibility before entering tree") {
  384. CHECK(tab_container->get_current_tab() == -1);
  385. CHECK(tab_container->get_previous_tab() == -1);
  386. // Adding a hidden child first will change visibility because it is the current tab.
  387. tab0->hide();
  388. tab_container->add_child(tab0);
  389. CHECK(tab_container->get_tab_count() == 1);
  390. CHECK(tab_container->get_current_tab() == 0);
  391. CHECK(tab_container->get_previous_tab() == -1);
  392. MessageQueue::get_singleton()->flush();
  393. CHECK(tab0->is_visible());
  394. // Adding a visible child after will hide it because it is not the current tab.
  395. tab_container->add_child(tab1);
  396. CHECK(tab_container->get_tab_count() == 2);
  397. CHECK(tab_container->get_current_tab() == 0);
  398. CHECK(tab_container->get_previous_tab() == -1);
  399. MessageQueue::get_singleton()->flush();
  400. CHECK(tab0->is_visible());
  401. CHECK_FALSE(tab1->is_visible());
  402. // Can change current by showing child now after children have been added.
  403. // This queues the current tab to update on entering the tree.
  404. tab1->show();
  405. MessageQueue::get_singleton()->flush();
  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. SIGNAL_CHECK_FALSE("tab_selected");
  410. SIGNAL_CHECK_FALSE("tab_changed");
  411. CHECK(tab0->is_visible());
  412. CHECK(tab1->is_visible());
  413. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  414. MessageQueue::get_singleton()->flush();
  415. CHECK(tab_container->get_tab_count() == 2);
  416. CHECK(tab_container->get_current_tab() == 1);
  417. CHECK(tab_container->get_previous_tab() == 0);
  418. SIGNAL_CHECK("tab_selected", { { 1 } });
  419. SIGNAL_CHECK("tab_changed", { { 1 } });
  420. CHECK_FALSE(tab0->is_visible());
  421. CHECK(tab1->is_visible());
  422. }
  423. SUBCASE("[TabContainer] setting current tab and changing child visibility after adding") {
  424. tab_container->add_child(tab0);
  425. tab_container->add_child(tab1);
  426. tab_container->add_child(tab2);
  427. MessageQueue::get_singleton()->flush();
  428. CHECK(tab_container->get_current_tab() == 0);
  429. CHECK(tab_container->get_previous_tab() == -1);
  430. tab2->show();
  431. MessageQueue::get_singleton()->flush();
  432. CHECK(tab_container->get_tab_count() == 3);
  433. CHECK(tab_container->get_current_tab() == 0);
  434. CHECK(tab_container->get_previous_tab() == -1);
  435. SIGNAL_CHECK_FALSE("tab_selected");
  436. SIGNAL_CHECK_FALSE("tab_changed");
  437. CHECK(tab0->is_visible());
  438. CHECK_FALSE(tab1->is_visible());
  439. CHECK(tab2->is_visible());
  440. // Whichever happens last will have priority.
  441. tab_container->set_current_tab(1);
  442. CHECK(tab_container->get_current_tab() == 0);
  443. CHECK(tab_container->get_previous_tab() == -1);
  444. // Current tab is set when entering the tree.
  445. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  446. MessageQueue::get_singleton()->flush();
  447. CHECK(tab_container->get_tab_count() == 3);
  448. CHECK(tab_container->get_current_tab() == 1);
  449. CHECK(tab_container->get_previous_tab() == 0);
  450. SIGNAL_CHECK("tab_selected", { { 1 } });
  451. SIGNAL_CHECK("tab_changed", { { 1 } });
  452. CHECK_FALSE(tab0->is_visible());
  453. CHECK(tab1->is_visible());
  454. CHECK_FALSE(tab2->is_visible());
  455. }
  456. SIGNAL_UNWATCH(tab_container, "tab_selected");
  457. SIGNAL_UNWATCH(tab_container, "tab_changed");
  458. memdelete(tab2);
  459. memdelete(tab1);
  460. memdelete(tab0);
  461. memdelete(tab_container);
  462. }
  463. TEST_CASE("[SceneTree][TabContainer] layout and offset") {
  464. TabContainer *tab_container = memnew(TabContainer);
  465. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  466. tab_container->set_clip_tabs(false);
  467. Control *tab0 = memnew(Control);
  468. tab0->set_name("tab0");
  469. Control *tab1 = memnew(Control);
  470. tab1->set_name("tab1 ");
  471. Control *tab2 = memnew(Control);
  472. tab2->set_name("tab2 ");
  473. tab_container->add_child(tab0);
  474. tab_container->add_child(tab1);
  475. tab_container->add_child(tab2);
  476. MessageQueue::get_singleton()->flush();
  477. Size2 all_tabs_size = tab_container->get_size();
  478. const float side_margin = tab_container->get_theme_constant("side_margin");
  479. TabBar *tab_bar = tab_container->get_tab_bar();
  480. Vector<Rect2> tab_rects = {
  481. tab_bar->get_tab_rect(0),
  482. tab_bar->get_tab_rect(1),
  483. tab_bar->get_tab_rect(2)
  484. };
  485. SUBCASE("[TabContainer] tabs are arranged next to each other") {
  486. // Horizontal positions are next to each other.
  487. CHECK(tab_rects[0].position.x == 0);
  488. CHECK(tab_rects[1].position.x == tab_rects[0].size.x);
  489. CHECK(tab_rects[2].position.x == tab_rects[1].position.x + tab_rects[1].size.x);
  490. // Fills the entire width.
  491. CHECK(tab_rects[2].position.x + tab_rects[2].size.x == all_tabs_size.x - side_margin);
  492. // Horizontal sizes are positive.
  493. CHECK(tab_rects[0].size.x > 0);
  494. CHECK(tab_rects[1].size.x > 0);
  495. CHECK(tab_rects[2].size.x > 0);
  496. // Vertical positions are at 0.
  497. CHECK(tab_rects[0].position.y == 0);
  498. CHECK(tab_rects[1].position.y == 0);
  499. CHECK(tab_rects[2].position.y == 0);
  500. // Vertical sizes are the same.
  501. CHECK(tab_rects[0].size.y == tab_rects[1].size.y);
  502. CHECK(tab_rects[1].size.y == tab_rects[2].size.y);
  503. }
  504. SUBCASE("[TabContainer] tab position") {
  505. float tab_height = tab_rects[0].size.y;
  506. Ref<StyleBox> panel_style = tab_container->get_theme_stylebox("panel_style");
  507. // Initial position, same as top position.
  508. // Tab bar is at the top.
  509. CHECK(tab_bar->get_anchor(SIDE_TOP) == 0);
  510. CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 0);
  511. CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
  512. CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
  513. CHECK(tab_bar->get_offset(SIDE_TOP) == 0);
  514. CHECK(tab_bar->get_offset(SIDE_BOTTOM) == tab_height);
  515. CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
  516. CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
  517. // Child is expanded and below the tab bar.
  518. CHECK(tab0->get_anchor(SIDE_TOP) == 0);
  519. CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
  520. CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
  521. CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
  522. CHECK(tab0->get_offset(SIDE_TOP) == tab_height);
  523. CHECK(tab0->get_offset(SIDE_BOTTOM) == 0);
  524. CHECK(tab0->get_offset(SIDE_LEFT) == 0);
  525. CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
  526. // Bottom position.
  527. tab_container->set_tabs_position(TabContainer::POSITION_BOTTOM);
  528. CHECK(tab_container->get_tabs_position() == TabContainer::POSITION_BOTTOM);
  529. MessageQueue::get_singleton()->flush();
  530. // Tab bar is at the bottom.
  531. CHECK(tab_bar->get_anchor(SIDE_TOP) == 1);
  532. CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 1);
  533. CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
  534. CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
  535. CHECK(tab_bar->get_offset(SIDE_TOP) == -tab_height);
  536. CHECK(tab_bar->get_offset(SIDE_BOTTOM) == 0);
  537. CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
  538. CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
  539. // Child is expanded and above the tab bar.
  540. CHECK(tab0->get_anchor(SIDE_TOP) == 0);
  541. CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
  542. CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
  543. CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
  544. CHECK(tab0->get_offset(SIDE_TOP) == 0);
  545. CHECK(tab0->get_offset(SIDE_BOTTOM) == -tab_height);
  546. CHECK(tab0->get_offset(SIDE_LEFT) == 0);
  547. CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
  548. // Top position.
  549. tab_container->set_tabs_position(TabContainer::POSITION_TOP);
  550. CHECK(tab_container->get_tabs_position() == TabContainer::POSITION_TOP);
  551. MessageQueue::get_singleton()->flush();
  552. // Tab bar is at the top.
  553. CHECK(tab_bar->get_anchor(SIDE_TOP) == 0);
  554. CHECK(tab_bar->get_anchor(SIDE_BOTTOM) == 0);
  555. CHECK(tab_bar->get_anchor(SIDE_LEFT) == 0);
  556. CHECK(tab_bar->get_anchor(SIDE_RIGHT) == 1);
  557. CHECK(tab_bar->get_offset(SIDE_TOP) == 0);
  558. CHECK(tab_bar->get_offset(SIDE_BOTTOM) == tab_height);
  559. CHECK(tab_bar->get_offset(SIDE_LEFT) == side_margin);
  560. CHECK(tab_bar->get_offset(SIDE_RIGHT) == 0);
  561. // Child is expanded and below the tab bar.
  562. CHECK(tab0->get_anchor(SIDE_TOP) == 0);
  563. CHECK(tab0->get_anchor(SIDE_BOTTOM) == 1);
  564. CHECK(tab0->get_anchor(SIDE_LEFT) == 0);
  565. CHECK(tab0->get_anchor(SIDE_RIGHT) == 1);
  566. CHECK(tab0->get_offset(SIDE_TOP) == tab_height);
  567. CHECK(tab0->get_offset(SIDE_BOTTOM) == 0);
  568. CHECK(tab0->get_offset(SIDE_LEFT) == 0);
  569. CHECK(tab0->get_offset(SIDE_RIGHT) == 0);
  570. }
  571. memdelete(tab_container);
  572. }
  573. TEST_CASE("[SceneTree][TabContainer] Mouse interaction") {
  574. TabContainer *tab_container = memnew(TabContainer);
  575. SceneTree::get_singleton()->get_root()->add_child(tab_container);
  576. tab_container->set_clip_tabs(false);
  577. Control *tab0 = memnew(Control);
  578. tab0->set_name("tab0");
  579. Control *tab1 = memnew(Control);
  580. tab1->set_name("tab1 ");
  581. Control *tab2 = memnew(Control);
  582. tab2->set_name("tab2 ");
  583. tab_container->add_child(tab0);
  584. tab_container->add_child(tab1);
  585. tab_container->add_child(tab2);
  586. MessageQueue::get_singleton()->flush();
  587. const float side_margin = tab_container->get_theme_constant("side_margin");
  588. TabBar *tab_bar = tab_container->get_tab_bar();
  589. Vector<Rect2> tab_rects = {
  590. tab_bar->get_tab_rect(0),
  591. tab_bar->get_tab_rect(1),
  592. tab_bar->get_tab_rect(2)
  593. };
  594. SIGNAL_WATCH(tab_container, "active_tab_rearranged");
  595. SIGNAL_WATCH(tab_container, "tab_changed");
  596. SIGNAL_WATCH(tab_container, "tab_clicked");
  597. SIGNAL_WATCH(tab_container, "tab_selected");
  598. SUBCASE("[TabContainer] Click to change current") {
  599. CHECK(tab_container->get_current_tab() == 0);
  600. CHECK(tab_container->get_previous_tab() == -1);
  601. SIGNAL_DISCARD("tab_selected");
  602. SIGNAL_DISCARD("tab_changed");
  603. // Click to set the current tab.
  604. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[1].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  605. CHECK(tab_container->get_current_tab() == 1);
  606. CHECK(tab_container->get_previous_tab() == 0);
  607. SIGNAL_CHECK("tab_selected", { { 1 } });
  608. SIGNAL_CHECK("tab_changed", { { 1 } });
  609. SIGNAL_CHECK("tab_clicked", { { 1 } });
  610. // Click on the same tab.
  611. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[1].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  612. CHECK(tab_container->get_current_tab() == 1);
  613. CHECK(tab_container->get_previous_tab() == 1);
  614. SIGNAL_CHECK("tab_selected", { { 1 } });
  615. SIGNAL_CHECK_FALSE("tab_changed");
  616. SIGNAL_CHECK("tab_clicked", { { 1 } });
  617. // Click outside of tabs.
  618. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(0, 0), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  619. CHECK(tab_container->get_current_tab() == 1);
  620. CHECK(tab_container->get_previous_tab() == 1);
  621. SIGNAL_CHECK_FALSE("tab_selected");
  622. SIGNAL_CHECK_FALSE("tab_changed");
  623. SIGNAL_CHECK_FALSE("tab_clicked");
  624. }
  625. SUBCASE("[TabContainer] Drag and drop internally") {
  626. // Cannot drag if not enabled.
  627. CHECK_FALSE(tab_container->get_drag_to_rearrange_enabled());
  628. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  629. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[1].position, MouseButtonMask::LEFT, Key::NONE);
  630. SIGNAL_CHECK("tab_selected", { { 0 } });
  631. SIGNAL_CHECK_FALSE("tab_changed");
  632. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  633. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Point2(side_margin, 0) + tab_rects[1].position, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  634. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  635. CHECK(tab_container->get_tab_idx_from_control(tab0) == 0);
  636. CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
  637. CHECK(tab_container->get_tab_idx_from_control(tab2) == 2);
  638. SIGNAL_CHECK_FALSE("active_tab_rearranged");
  639. SIGNAL_CHECK_FALSE("tab_selected");
  640. SIGNAL_CHECK_FALSE("tab_changed");
  641. tab_container->set_drag_to_rearrange_enabled(true);
  642. CHECK(tab_container->get_drag_to_rearrange_enabled());
  643. // Release over the same tab to not move.
  644. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  645. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[1].position, MouseButtonMask::LEFT, Key::NONE);
  646. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButtonMask::LEFT, Key::NONE);
  647. SIGNAL_CHECK("tab_selected", { { 0 } });
  648. SIGNAL_CHECK_FALSE("tab_changed");
  649. CHECK(tab_container->get_viewport()->gui_is_dragging());
  650. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  651. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  652. CHECK(tab_container->get_tab_idx_from_control(tab0) == 0);
  653. CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
  654. CHECK(tab_container->get_tab_idx_from_control(tab2) == 2);
  655. SIGNAL_CHECK_FALSE("active_tab_rearranged");
  656. SIGNAL_CHECK_FALSE("tab_selected");
  657. SIGNAL_CHECK_FALSE("tab_changed");
  658. // Move the first tab after the second.
  659. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  660. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[1].position, MouseButtonMask::LEFT, Key::NONE);
  661. SIGNAL_CHECK("tab_selected", { { 0 } });
  662. SIGNAL_CHECK_FALSE("tab_changed");
  663. CHECK(tab_container->get_viewport()->gui_is_dragging());
  664. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Point2(side_margin, 0) + tab_rects[1].position + Point2(tab_rects[1].size.x / 2 + 1, 0), MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  665. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  666. CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
  667. CHECK(tab_container->get_tab_idx_from_control(tab0) == 1);
  668. CHECK(tab_container->get_tab_idx_from_control(tab2) == 2);
  669. SIGNAL_CHECK("active_tab_rearranged", { { 1 } });
  670. SIGNAL_CHECK("tab_selected", { { 1 } });
  671. SIGNAL_CHECK_FALSE("tab_changed");
  672. tab_rects = { tab_bar->get_tab_rect(0), tab_bar->get_tab_rect(1), tab_bar->get_tab_rect(2) };
  673. // Move the last tab to be the first.
  674. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[2].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  675. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButtonMask::LEFT, Key::NONE);
  676. SIGNAL_CHECK("tab_selected", { { 2 } });
  677. SIGNAL_CHECK("tab_changed", { { 2 } });
  678. CHECK(tab_container->get_viewport()->gui_is_dragging());
  679. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  680. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  681. CHECK(tab_container->get_tab_idx_from_control(tab2) == 0);
  682. CHECK(tab_container->get_tab_idx_from_control(tab1) == 1);
  683. CHECK(tab_container->get_tab_idx_from_control(tab0) == 2);
  684. SIGNAL_CHECK("active_tab_rearranged", { { 0 } });
  685. SIGNAL_CHECK("tab_selected", { { 0 } });
  686. SIGNAL_CHECK_FALSE("tab_changed");
  687. }
  688. SUBCASE("[TabContainer] Drag and drop to different TabContainer") {
  689. TabContainer *target_tab_container = memnew(TabContainer);
  690. SceneTree::get_singleton()->get_root()->add_child(target_tab_container);
  691. target_tab_container->set_clip_tabs(false);
  692. Control *other_tab0 = memnew(Control);
  693. other_tab0->set_name("other_tab0");
  694. target_tab_container->add_child(other_tab0);
  695. target_tab_container->set_position(tab_container->get_size());
  696. MessageQueue::get_singleton()->flush();
  697. Vector<Rect2> target_tab_rects = {
  698. target_tab_container->get_tab_bar()->get_tab_rect(0)
  699. };
  700. tab_container->set_drag_to_rearrange_enabled(true);
  701. tab_container->set_tabs_rearrange_group(1);
  702. Point2 target_tab_after_first = Point2(side_margin, 0) + target_tab_container->get_position() + target_tab_rects[0].position + Point2(target_tab_rects[0].size.x / 2 + 1, 0);
  703. // Cannot drag to another TabContainer that does not have drag to rearrange enabled.
  704. CHECK_FALSE(target_tab_container->get_drag_to_rearrange_enabled());
  705. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  706. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[0].position + Point2(20, 0), MouseButtonMask::LEFT, Key::NONE);
  707. SEND_GUI_MOUSE_MOTION_EVENT(target_tab_after_first, MouseButtonMask::LEFT, Key::NONE);
  708. SIGNAL_CHECK("tab_selected", { { 0 } });
  709. SIGNAL_CHECK_FALSE("tab_changed");
  710. CHECK(tab_container->get_viewport()->gui_is_dragging());
  711. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_tab_after_first, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  712. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  713. CHECK(tab_container->get_tab_count() == 3);
  714. CHECK(target_tab_container->get_tab_count() == 1);
  715. SIGNAL_CHECK_FALSE("active_tab_rearranged");
  716. SIGNAL_CHECK_FALSE("tab_selected");
  717. SIGNAL_CHECK_FALSE("tab_changed");
  718. // Cannot drag to another TabContainer that has a tabs rearrange group of -1.
  719. target_tab_container->set_drag_to_rearrange_enabled(true);
  720. tab_container->set_tabs_rearrange_group(-1);
  721. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  722. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[0].position + Point2(20, 0), MouseButtonMask::LEFT, Key::NONE);
  723. SEND_GUI_MOUSE_MOTION_EVENT(target_tab_after_first, MouseButtonMask::LEFT, Key::NONE);
  724. SIGNAL_CHECK("tab_selected", { { 0 } });
  725. SIGNAL_CHECK_FALSE("tab_changed");
  726. CHECK(tab_container->get_viewport()->gui_is_dragging());
  727. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_tab_after_first, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  728. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  729. CHECK(tab_container->get_tab_count() == 3);
  730. CHECK(target_tab_container->get_tab_count() == 1);
  731. SIGNAL_CHECK_FALSE("active_tab_rearranged");
  732. SIGNAL_CHECK_FALSE("tab_selected");
  733. SIGNAL_CHECK_FALSE("tab_changed");
  734. // Cannot drag to another TabContainer that has a different tabs rearrange group.
  735. tab_container->set_tabs_rearrange_group(1);
  736. target_tab_container->set_tabs_rearrange_group(2);
  737. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  738. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[0].position + Point2(20, 0), MouseButtonMask::LEFT, Key::NONE);
  739. SEND_GUI_MOUSE_MOTION_EVENT(target_tab_after_first, MouseButtonMask::LEFT, Key::NONE);
  740. SIGNAL_CHECK("tab_selected", { { 0 } });
  741. SIGNAL_CHECK_FALSE("tab_changed");
  742. CHECK(tab_container->get_viewport()->gui_is_dragging());
  743. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_tab_after_first, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  744. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  745. CHECK(tab_container->get_tab_count() == 3);
  746. CHECK(target_tab_container->get_tab_count() == 1);
  747. SIGNAL_CHECK_FALSE("active_tab_rearranged");
  748. SIGNAL_CHECK_FALSE("tab_selected");
  749. SIGNAL_CHECK_FALSE("tab_changed");
  750. // Drag to target container.
  751. target_tab_container->set_tabs_rearrange_group(1);
  752. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  753. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[0].position + Point2(20, 0), MouseButtonMask::LEFT, Key::NONE);
  754. SEND_GUI_MOUSE_MOTION_EVENT(target_tab_after_first, MouseButtonMask::LEFT, Key::NONE);
  755. SIGNAL_CHECK("tab_selected", { { 0 } });
  756. SIGNAL_CHECK_FALSE("tab_changed");
  757. CHECK(tab_container->get_viewport()->gui_is_dragging());
  758. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_tab_after_first, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  759. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  760. CHECK(tab_container->get_tab_count() == 2);
  761. CHECK(target_tab_container->get_tab_count() == 2);
  762. CHECK(tab_container->get_child_count(false) == 2);
  763. CHECK(target_tab_container->get_child_count(false) == 2);
  764. CHECK(tab_container->get_tab_idx_from_control(tab1) == 0);
  765. CHECK(tab_container->get_tab_idx_from_control(tab2) == 1);
  766. CHECK(target_tab_container->get_tab_idx_from_control(other_tab0) == 0);
  767. CHECK(target_tab_container->get_tab_idx_from_control(tab0) == 1);
  768. CHECK(tab_container->get_current_tab() == 0);
  769. CHECK(target_tab_container->get_current_tab() == 1);
  770. SIGNAL_CHECK_FALSE("active_tab_rearranged");
  771. SIGNAL_CHECK_FALSE("tab_selected"); // Does not send since tab was removed.
  772. SIGNAL_CHECK("tab_changed", { { 0 } });
  773. Point2 target_tab = Point2(side_margin, 0) + target_tab_container->get_position();
  774. // Drag to target container at first index.
  775. target_tab_container->set_tabs_rearrange_group(1);
  776. SEND_GUI_MOUSE_BUTTON_EVENT(Point2(side_margin, 0) + tab_rects[0].position, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
  777. SEND_GUI_MOUSE_MOTION_EVENT(Point2(side_margin, 0) + tab_rects[0].position + Point2(20, 0), MouseButtonMask::LEFT, Key::NONE);
  778. SEND_GUI_MOUSE_MOTION_EVENT(target_tab, MouseButtonMask::LEFT, Key::NONE);
  779. SIGNAL_CHECK("tab_selected", { { 0 } });
  780. SIGNAL_CHECK_FALSE("tab_changed");
  781. CHECK(tab_container->get_viewport()->gui_is_dragging());
  782. SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(target_tab, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
  783. CHECK_FALSE(tab_container->get_viewport()->gui_is_dragging());
  784. CHECK(tab_container->get_tab_count() == 1);
  785. CHECK(target_tab_container->get_tab_count() == 3);
  786. CHECK(tab_container->get_tab_idx_from_control(tab2) == 0);
  787. CHECK(target_tab_container->get_tab_idx_from_control(tab1) == 0);
  788. CHECK(target_tab_container->get_tab_idx_from_control(other_tab0) == 1);
  789. CHECK(target_tab_container->get_tab_idx_from_control(tab0) == 2);
  790. CHECK(tab_container->get_current_tab() == 0);
  791. CHECK(target_tab_container->get_current_tab() == 0);
  792. SIGNAL_CHECK_FALSE("active_tab_rearranged");
  793. SIGNAL_CHECK_FALSE("tab_selected"); // Does not send since tab was removed.
  794. SIGNAL_CHECK("tab_changed", { { 0 } });
  795. memdelete(target_tab_container);
  796. }
  797. SIGNAL_UNWATCH(tab_container, "active_tab_rearranged");
  798. SIGNAL_UNWATCH(tab_container, "tab_changed");
  799. SIGNAL_UNWATCH(tab_container, "tab_clicked");
  800. SIGNAL_UNWATCH(tab_container, "tab_selected");
  801. memdelete(tab_container);
  802. }
  803. // FIXME: Add tests for keyboard navigation and other methods.
  804. } // namespace TestTabContainer