tab_container.cpp 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294
  1. /**************************************************************************/
  2. /* tab_container.cpp */
  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. #include "tab_container.h"
  31. #include "scene/theme/theme_db.h"
  32. Rect2 TabContainer::_get_tab_rect() const {
  33. Rect2 rect;
  34. if (tabs_visible && get_tab_count() > 0) {
  35. rect = Rect2(theme_cache.tabbar_style->get_offset(), tab_bar->get_size());
  36. rect.position.x += is_layout_rtl() ? theme_cache.menu_icon->get_width() : theme_cache.side_margin;
  37. }
  38. return rect;
  39. }
  40. TabContainer::CachedTab &TabContainer::get_pending_tab(int p_idx) const {
  41. if (p_idx >= pending_tabs.size()) {
  42. pending_tabs.resize(p_idx + 1);
  43. }
  44. return pending_tabs.write[p_idx];
  45. }
  46. int TabContainer::_get_tab_height() const {
  47. int height = 0;
  48. if (tabs_visible && get_tab_count() > 0) {
  49. height = tab_bar->get_minimum_size().height + theme_cache.tabbar_style->get_margin(SIDE_TOP) + theme_cache.tabbar_style->get_margin(SIDE_BOTTOM);
  50. }
  51. return height;
  52. }
  53. void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
  54. ERR_FAIL_COND(p_event.is_null());
  55. Ref<InputEventMouseButton> mb = p_event;
  56. Popup *popup = get_popup();
  57. if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
  58. Point2 pos = mb->get_position();
  59. real_t content_height = get_size().height - _get_tab_height();
  60. Rect2 popup_rect = _get_tab_rect();
  61. popup_rect.position.x += is_layout_rtl() ? -theme_cache.menu_icon->get_width() : popup_rect.size.x;
  62. popup_rect.position.y += tabs_position == POSITION_BOTTOM ? content_height : 0;
  63. popup_rect.size.x = theme_cache.menu_icon->get_width();
  64. // Click must be on tabs in the tab header area.
  65. if (!tabs_visible || pos.y < popup_rect.position.y || pos.y >= popup_rect.position.y + popup_rect.size.y) {
  66. return;
  67. }
  68. // Handle menu button.
  69. if (popup) {
  70. if (popup_rect.has_point(pos)) {
  71. emit_signal(SNAME("pre_popup_pressed"));
  72. Vector2 popup_pos = get_screen_position();
  73. popup_pos.x += popup_rect.position.x + (is_layout_rtl() ? 0 : popup_rect.size.x - popup->get_size().width);
  74. popup_pos.y += popup_rect.position.y + popup_rect.size.y / 2.0;
  75. if (tabs_position == POSITION_BOTTOM) {
  76. popup_pos.y -= popup->get_size().height;
  77. popup_pos.y -= theme_cache.menu_icon->get_height() / 2.0;
  78. } else {
  79. popup_pos.y += theme_cache.menu_icon->get_height() / 2.0;
  80. }
  81. popup->set_position(popup_pos);
  82. popup->popup();
  83. return;
  84. }
  85. }
  86. }
  87. Ref<InputEventMouseMotion> mm = p_event;
  88. if (mm.is_valid()) {
  89. Point2 pos = mm->get_position();
  90. real_t content_height = get_size().height - _get_tab_height();
  91. Rect2 popup_rect = _get_tab_rect();
  92. popup_rect.position.x += is_layout_rtl() ? -theme_cache.menu_icon->get_width() : popup_rect.size.x;
  93. popup_rect.position.y += tabs_position == POSITION_BOTTOM ? content_height : 0;
  94. popup_rect.size.x = theme_cache.menu_icon->get_width();
  95. // Mouse must be on tabs in the tab header area.
  96. if (!tabs_visible || pos.y < popup_rect.position.y || pos.y >= popup_rect.position.y + popup_rect.size.y) {
  97. if (menu_hovered) {
  98. menu_hovered = false;
  99. queue_redraw();
  100. }
  101. return;
  102. }
  103. if (popup) {
  104. if (popup_rect.has_point(pos)) {
  105. if (!menu_hovered) {
  106. menu_hovered = true;
  107. queue_redraw();
  108. return;
  109. }
  110. } else if (menu_hovered) {
  111. menu_hovered = false;
  112. queue_redraw();
  113. }
  114. if (menu_hovered) {
  115. return;
  116. }
  117. }
  118. }
  119. }
  120. void TabContainer::_get_property_list(List<PropertyInfo> *p_list) const {
  121. List<PropertyInfo> properties;
  122. property_helper.get_property_list(&properties);
  123. for (PropertyInfo &info : properties) {
  124. int index;
  125. if (info.name.ends_with("title") && property_helper.is_property_valid(info.name, &index)) {
  126. Control *tab_control = get_tab_control(index);
  127. ERR_FAIL_NULL(tab_control);
  128. if (get_tab_title(index) == tab_control->get_name()) {
  129. // Don't store default title.
  130. info.usage &= ~PROPERTY_USAGE_STORAGE;
  131. }
  132. }
  133. p_list->push_back(info);
  134. }
  135. }
  136. bool TabContainer::_property_get_revert(const StringName &p_name, Variant &r_property) const {
  137. const String sname = p_name;
  138. int index;
  139. if (sname.ends_with("title") && property_helper.is_property_valid(sname, &index)) {
  140. Control *tab_control = get_tab_control(index);
  141. ERR_FAIL_NULL_V(tab_control, false);
  142. r_property = String(tab_control->get_name());
  143. return true;
  144. }
  145. return property_helper.property_get_revert(p_name, r_property);
  146. }
  147. void TabContainer::_notification(int p_what) {
  148. switch (p_what) {
  149. case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
  150. tab_panels.clear();
  151. } break;
  152. case NOTIFICATION_ACCESSIBILITY_UPDATE: {
  153. RID ae = get_accessibility_element();
  154. ERR_FAIL_COND(ae.is_null());
  155. int tab_index = 0;
  156. int tab_cur = tab_bar->get_current_tab();
  157. for (int i = 0; i < get_child_count(); i++) {
  158. Node *child_node = get_child(i);
  159. Window *child_wnd = Object::cast_to<Window>(child_node);
  160. if (child_wnd && !child_wnd->is_embedded()) {
  161. continue;
  162. }
  163. if (child_node->is_part_of_edited_scene()) {
  164. continue;
  165. }
  166. Control *control = as_sortable_control(child_node, SortableVisibilityMode::IGNORE);
  167. if (!control || control == tab_bar || children_removing.has(control)) {
  168. DisplayServer::get_singleton()->accessibility_update_add_child(ae, child_node->get_accessibility_element());
  169. } else {
  170. if (!tab_panels.has(child_node)) {
  171. tab_panels[child_node] = DisplayServer::get_singleton()->accessibility_create_sub_element(ae, DisplayServer::AccessibilityRole::ROLE_TAB_PANEL);
  172. }
  173. RID panel = tab_panels[child_node];
  174. RID tab = tab_bar->get_tab_accessibility_element(tab_index);
  175. DisplayServer::get_singleton()->accessibility_update_add_related_controls(tab, panel);
  176. DisplayServer::get_singleton()->accessibility_update_add_related_labeled_by(panel, tab);
  177. DisplayServer::get_singleton()->accessibility_update_set_flag(panel, DisplayServer::AccessibilityFlags::FLAG_HIDDEN, tab_index != tab_cur);
  178. DisplayServer::get_singleton()->accessibility_update_add_child(panel, child_node->get_accessibility_element());
  179. tab_index++;
  180. }
  181. }
  182. } break;
  183. case NOTIFICATION_ENTER_TREE: {
  184. // If some nodes happen to be renamed outside the tree, the tab names need to be updated manually.
  185. if (get_tab_count() > 0) {
  186. _refresh_tab_names();
  187. }
  188. if (setup_current_tab >= -1) {
  189. set_current_tab(setup_current_tab);
  190. setup_current_tab = -2;
  191. }
  192. } break;
  193. case NOTIFICATION_READY: {
  194. for (int i = 0; i < pending_tabs.size(); i++) {
  195. const CachedTab &tab = pending_tabs[i];
  196. if (tab.has_title) {
  197. set_tab_title(i, tab.title);
  198. }
  199. set_tab_icon(i, tab.icon);
  200. set_tab_disabled(i, tab.disabled);
  201. set_tab_hidden(i, tab.hidden);
  202. }
  203. pending_tabs.clear();
  204. [[fallthrough]];
  205. }
  206. case NOTIFICATION_RESIZED: {
  207. _update_margins();
  208. } break;
  209. case NOTIFICATION_DRAW: {
  210. RID canvas = get_canvas_item();
  211. Size2 size = get_size();
  212. Rect2 tabbar_rect = _get_tab_rect();
  213. // Draw only the tab area if the header is hidden.
  214. if (!tabs_visible) {
  215. theme_cache.panel_style->draw(canvas, Rect2(0, 0, size.width, size.height));
  216. return;
  217. }
  218. int header_height = _get_tab_height();
  219. int header_voffset = int(tabs_position == POSITION_BOTTOM) * (size.height - header_height);
  220. // Draw background for the tabbar.
  221. theme_cache.tabbar_style->draw(canvas, Rect2(0, header_voffset, size.width, header_height));
  222. // Draw the background for the tab's content.
  223. theme_cache.panel_style->draw(canvas, Rect2(0, int(tabs_position == POSITION_TOP) * header_height, size.width, size.height - header_height));
  224. // Draw the popup menu.
  225. if (get_popup()) {
  226. int x = is_layout_rtl() ? tabbar_rect.position.x - theme_cache.menu_icon->get_width() : tabbar_rect.position.x + tabbar_rect.size.x;
  227. header_voffset += tabbar_rect.position.y;
  228. if (menu_hovered) {
  229. theme_cache.menu_hl_icon->draw(get_canvas_item(), Point2(x, header_voffset + (tabbar_rect.size.y - theme_cache.menu_hl_icon->get_height()) / 2));
  230. } else {
  231. theme_cache.menu_icon->draw(get_canvas_item(), Point2(x, header_voffset + (tabbar_rect.size.y - theme_cache.menu_icon->get_height()) / 2));
  232. }
  233. }
  234. } break;
  235. case NOTIFICATION_VISIBILITY_CHANGED: {
  236. if (!is_visible()) {
  237. return;
  238. }
  239. updating_visibility = true;
  240. // As the visibility change notification will be triggered for all children soon after,
  241. // beat it to the punch and make sure that the correct node is the only one visible first.
  242. // Otherwise, it can prevent a tab change done right before this container was made visible.
  243. Vector<Control *> controls = _get_tab_controls();
  244. int current = setup_current_tab > -2 ? setup_current_tab : get_current_tab();
  245. for (int i = 0; i < controls.size(); i++) {
  246. controls[i]->set_visible(i == current);
  247. }
  248. updating_visibility = false;
  249. } break;
  250. case NOTIFICATION_TRANSLATION_CHANGED:
  251. case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
  252. case NOTIFICATION_THEME_CHANGED: {
  253. theme_changing = true;
  254. callable_mp(this, &TabContainer::_on_theme_changed).call_deferred(); // Wait until all changed theme.
  255. } break;
  256. }
  257. }
  258. void TabContainer::_on_theme_changed() {
  259. if (!theme_changing) {
  260. return;
  261. }
  262. tab_bar->begin_bulk_theme_override();
  263. tab_bar->add_theme_style_override(SNAME("tab_unselected"), theme_cache.tab_unselected_style);
  264. tab_bar->add_theme_style_override(SNAME("tab_hovered"), theme_cache.tab_hovered_style);
  265. tab_bar->add_theme_style_override(SNAME("tab_selected"), theme_cache.tab_selected_style);
  266. tab_bar->add_theme_style_override(SNAME("tab_disabled"), theme_cache.tab_disabled_style);
  267. tab_bar->add_theme_style_override(SNAME("tab_focus"), theme_cache.tab_focus_style);
  268. tab_bar->add_theme_icon_override(SNAME("increment"), theme_cache.increment_icon);
  269. tab_bar->add_theme_icon_override(SNAME("increment_highlight"), theme_cache.increment_hl_icon);
  270. tab_bar->add_theme_icon_override(SNAME("decrement"), theme_cache.decrement_icon);
  271. tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), theme_cache.decrement_hl_icon);
  272. tab_bar->add_theme_icon_override(SNAME("drop_mark"), theme_cache.drop_mark_icon);
  273. tab_bar->add_theme_color_override(SNAME("drop_mark_color"), theme_cache.drop_mark_color);
  274. tab_bar->add_theme_color_override(SNAME("font_selected_color"), theme_cache.font_selected_color);
  275. tab_bar->add_theme_color_override(SNAME("font_hovered_color"), theme_cache.font_hovered_color);
  276. tab_bar->add_theme_color_override(SNAME("font_unselected_color"), theme_cache.font_unselected_color);
  277. tab_bar->add_theme_color_override(SNAME("font_disabled_color"), theme_cache.font_disabled_color);
  278. tab_bar->add_theme_color_override(SNAME("font_outline_color"), theme_cache.font_outline_color);
  279. tab_bar->add_theme_color_override(SNAME("icon_selected_color"), theme_cache.icon_selected_color);
  280. tab_bar->add_theme_color_override(SNAME("icon_hovered_color"), theme_cache.icon_hovered_color);
  281. tab_bar->add_theme_color_override(SNAME("icon_unselected_color"), theme_cache.icon_unselected_color);
  282. tab_bar->add_theme_color_override(SNAME("icon_disabled_color"), theme_cache.icon_disabled_color);
  283. tab_bar->add_theme_font_override(SceneStringName(font), theme_cache.tab_font);
  284. tab_bar->add_theme_font_size_override(SceneStringName(font_size), theme_cache.tab_font_size);
  285. tab_bar->add_theme_constant_override(SNAME("h_separation"), theme_cache.icon_separation);
  286. tab_bar->add_theme_constant_override(SNAME("tab_separation"), theme_cache.tab_separation);
  287. tab_bar->add_theme_constant_override(SNAME("icon_max_width"), theme_cache.icon_max_width);
  288. tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size);
  289. tab_bar->end_bulk_theme_override();
  290. _update_margins();
  291. if (get_tab_count() > 0) {
  292. _repaint();
  293. } else {
  294. update_minimum_size();
  295. }
  296. queue_redraw();
  297. theme_changing = false;
  298. }
  299. void TabContainer::_repaint() {
  300. Vector<Control *> controls = _get_tab_controls();
  301. int current = get_current_tab();
  302. float top_margin = theme_cache.tabbar_style->get_margin(SIDE_TOP);
  303. float bottom_margin = theme_cache.tabbar_style->get_margin(SIDE_BOTTOM);
  304. // Move the TabBar to the top or bottom.
  305. // Don't change the left and right offsets since the TabBar will resize and may change tab offset.
  306. if (tabs_position == POSITION_BOTTOM) {
  307. tab_bar->set_anchor_and_offset(SIDE_BOTTOM, 1.0, -bottom_margin);
  308. tab_bar->set_anchor_and_offset(SIDE_TOP, 1.0, top_margin - _get_tab_height());
  309. } else {
  310. tab_bar->set_anchor_and_offset(SIDE_TOP, 0.0, top_margin);
  311. tab_bar->set_anchor_and_offset(SIDE_BOTTOM, 0.0, _get_tab_height() - bottom_margin);
  312. }
  313. updating_visibility = true;
  314. for (int i = 0; i < controls.size(); i++) {
  315. Control *c = controls[i];
  316. if (i == current) {
  317. c->show();
  318. c->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
  319. if (tabs_visible) {
  320. if (tabs_position == POSITION_BOTTOM) {
  321. c->set_offset(SIDE_BOTTOM, -_get_tab_height());
  322. } else {
  323. c->set_offset(SIDE_TOP, _get_tab_height());
  324. }
  325. }
  326. c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_TOP));
  327. c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_LEFT));
  328. c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - theme_cache.panel_style->get_margin(SIDE_RIGHT));
  329. c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - theme_cache.panel_style->get_margin(SIDE_BOTTOM));
  330. } else {
  331. c->hide();
  332. }
  333. }
  334. updating_visibility = false;
  335. update_minimum_size();
  336. }
  337. void TabContainer::_update_margins() {
  338. // Directly check for validity, to avoid errors when quitting.
  339. bool has_popup = popup_obj_id.is_valid();
  340. int left_margin = theme_cache.tabbar_style->get_margin(SIDE_LEFT);
  341. int right_margin = theme_cache.tabbar_style->get_margin(SIDE_RIGHT);
  342. if (is_layout_rtl()) {
  343. SWAP(left_margin, right_margin);
  344. }
  345. if (has_popup) {
  346. right_margin += theme_cache.menu_icon->get_width();
  347. }
  348. if (get_tab_count() == 0) {
  349. tab_bar->set_offset(SIDE_LEFT, left_margin);
  350. tab_bar->set_offset(SIDE_RIGHT, -right_margin);
  351. return;
  352. }
  353. switch (get_tab_alignment()) {
  354. case TabBar::ALIGNMENT_LEFT: {
  355. tab_bar->set_offset(SIDE_LEFT, left_margin + theme_cache.side_margin);
  356. tab_bar->set_offset(SIDE_RIGHT, -right_margin);
  357. } break;
  358. case TabBar::ALIGNMENT_CENTER: {
  359. tab_bar->set_offset(SIDE_LEFT, left_margin);
  360. tab_bar->set_offset(SIDE_RIGHT, -right_margin);
  361. } break;
  362. case TabBar::ALIGNMENT_RIGHT: {
  363. tab_bar->set_offset(SIDE_LEFT, left_margin);
  364. if (has_popup) {
  365. tab_bar->set_offset(SIDE_RIGHT, -right_margin);
  366. return;
  367. }
  368. int first_tab_pos = tab_bar->get_tab_rect(0).position.x;
  369. Rect2 last_tab_rect = tab_bar->get_tab_rect(get_tab_count() - 1);
  370. int total_tabs_width = left_margin + right_margin + last_tab_rect.position.x - first_tab_pos + last_tab_rect.size.width;
  371. // Calculate if all the tabs would still fit if the margin was present.
  372. if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + theme_cache.side_margin) > get_size().width))) {
  373. tab_bar->set_offset(SIDE_RIGHT, -right_margin);
  374. } else {
  375. tab_bar->set_offset(SIDE_RIGHT, -right_margin - theme_cache.side_margin);
  376. }
  377. } break;
  378. case TabBar::ALIGNMENT_MAX:
  379. break; // Can't happen, but silences warning.
  380. }
  381. }
  382. void TabContainer::_on_mouse_exited() {
  383. if (menu_hovered) {
  384. menu_hovered = false;
  385. queue_redraw();
  386. }
  387. }
  388. Vector<Control *> TabContainer::_get_tab_controls() const {
  389. Vector<Control *> controls;
  390. for (int i = 0; i < get_child_count(); i++) {
  391. Control *control = as_sortable_control(get_child(i), SortableVisibilityMode::IGNORE);
  392. if (!control || control == tab_bar || children_removing.has(control)) {
  393. continue;
  394. }
  395. controls.push_back(control);
  396. }
  397. return controls;
  398. }
  399. Variant TabContainer::_get_drag_data_fw(const Point2 &p_point, Control *p_from_control) {
  400. if (!drag_to_rearrange_enabled) {
  401. return Variant();
  402. }
  403. return tab_bar->_handle_get_drag_data("tab_container_tab", p_point);
  404. }
  405. bool TabContainer::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const {
  406. if (!drag_to_rearrange_enabled) {
  407. return false;
  408. }
  409. return tab_bar->_handle_can_drop_data("tab_container_tab", p_point, p_data);
  410. }
  411. void TabContainer::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) {
  412. if (!drag_to_rearrange_enabled) {
  413. return;
  414. }
  415. return tab_bar->_handle_drop_data("tab_container_tab", p_point, p_data, callable_mp(this, &TabContainer::_drag_move_tab), callable_mp(this, &TabContainer::_drag_move_tab_from));
  416. }
  417. void TabContainer::_drag_move_tab(int p_from_index, int p_to_index) {
  418. move_child(get_tab_control(p_from_index), get_tab_control(p_to_index)->get_index(false));
  419. }
  420. void TabContainer::_drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index) {
  421. Node *parent = p_from_tabbar->get_parent();
  422. if (!parent) {
  423. return;
  424. }
  425. TabContainer *from_tab_container = Object::cast_to<TabContainer>(parent);
  426. if (!from_tab_container) {
  427. return;
  428. }
  429. move_tab_from_tab_container(from_tab_container, p_from_index, p_to_index);
  430. }
  431. void TabContainer::move_tab_from_tab_container(TabContainer *p_from, int p_from_index, int p_to_index) {
  432. ERR_FAIL_NULL(p_from);
  433. ERR_FAIL_INDEX(p_from_index, p_from->get_tab_count());
  434. ERR_FAIL_INDEX(p_to_index, get_tab_count() + 1);
  435. // Get the tab properties before they get erased by the child removal.
  436. String tab_title = p_from->get_tab_title(p_from_index);
  437. String tab_tooltip = p_from->get_tab_tooltip(p_from_index);
  438. Ref<Texture2D> tab_icon = p_from->get_tab_icon(p_from_index);
  439. Ref<Texture2D> tab_button_icon = p_from->get_tab_button_icon(p_from_index);
  440. bool tab_disabled = p_from->is_tab_disabled(p_from_index);
  441. bool tab_hidden = p_from->is_tab_hidden(p_from_index);
  442. Variant tab_metadata = p_from->get_tab_metadata(p_from_index);
  443. int tab_icon_max_width = p_from->get_tab_bar()->get_tab_icon_max_width(p_from_index);
  444. Control *moving_tabc = p_from->get_tab_control(p_from_index);
  445. p_from->remove_child(moving_tabc);
  446. add_child(moving_tabc, true);
  447. if (p_to_index < 0 || p_to_index > get_tab_count() - 1) {
  448. p_to_index = get_tab_count() - 1;
  449. }
  450. move_child(moving_tabc, get_tab_control(p_to_index)->get_index(false));
  451. set_tab_title(p_to_index, tab_title);
  452. set_tab_tooltip(p_to_index, tab_tooltip);
  453. set_tab_icon(p_to_index, tab_icon);
  454. set_tab_button_icon(p_to_index, tab_button_icon);
  455. set_tab_disabled(p_to_index, tab_disabled);
  456. set_tab_hidden(p_to_index, tab_hidden);
  457. set_tab_metadata(p_to_index, tab_metadata);
  458. get_tab_bar()->set_tab_icon_max_width(p_to_index, tab_icon_max_width);
  459. if (!is_tab_disabled(p_to_index)) {
  460. set_current_tab(p_to_index);
  461. }
  462. }
  463. void TabContainer::_on_tab_clicked(int p_tab) {
  464. emit_signal(SNAME("tab_clicked"), p_tab);
  465. }
  466. void TabContainer::_on_tab_hovered(int p_tab) {
  467. emit_signal(SNAME("tab_hovered"), p_tab);
  468. }
  469. void TabContainer::_on_tab_changed(int p_tab) {
  470. callable_mp(this, &TabContainer::_repaint).call_deferred();
  471. queue_redraw();
  472. emit_signal(SNAME("tab_changed"), p_tab);
  473. }
  474. void TabContainer::_on_tab_selected(int p_tab) {
  475. if (p_tab != get_previous_tab()) {
  476. callable_mp(this, &TabContainer::_repaint).call_deferred();
  477. }
  478. emit_signal(SNAME("tab_selected"), p_tab);
  479. }
  480. void TabContainer::_on_tab_button_pressed(int p_tab) {
  481. emit_signal(SNAME("tab_button_pressed"), p_tab);
  482. }
  483. void TabContainer::_on_active_tab_rearranged(int p_tab) {
  484. emit_signal(SNAME("active_tab_rearranged"), p_tab);
  485. }
  486. void TabContainer::_on_tab_visibility_changed(Control *p_child) {
  487. if (updating_visibility) {
  488. return;
  489. }
  490. int tab_index = get_tab_idx_from_control(p_child);
  491. if (tab_index == -1) {
  492. return;
  493. }
  494. // Only allow one tab to be visible.
  495. bool made_visible = p_child->is_visible();
  496. updating_visibility = true;
  497. if (!made_visible && get_current_tab() == tab_index) {
  498. if (get_deselect_enabled() || get_tab_count() == 0) {
  499. // Deselect.
  500. set_current_tab(-1);
  501. } else if (get_tab_count() == 1) {
  502. // Only tab, cannot deselect.
  503. p_child->show();
  504. } else {
  505. // Set a different tab to be the current tab.
  506. bool selected = select_next_available();
  507. if (!selected) {
  508. selected = select_previous_available();
  509. }
  510. if (!selected) {
  511. // No available tabs, deselect.
  512. set_current_tab(-1);
  513. }
  514. }
  515. } else if (made_visible && get_current_tab() != tab_index) {
  516. set_current_tab(tab_index);
  517. }
  518. updating_visibility = false;
  519. }
  520. void TabContainer::_refresh_tab_indices() {
  521. Vector<Control *> controls = _get_tab_controls();
  522. for (int i = 0; i < controls.size(); i++) {
  523. controls[i]->set_meta("_tab_index", i);
  524. }
  525. }
  526. void TabContainer::_refresh_tab_names() {
  527. Vector<Control *> controls = _get_tab_controls();
  528. for (int i = 0; i < controls.size(); i++) {
  529. tab_bar->set_tab_title(i, controls[i]->get_meta("_tab_name", controls[i]->get_name()));
  530. }
  531. }
  532. void TabContainer::add_child_notify(Node *p_child) {
  533. Container::add_child_notify(p_child);
  534. if (p_child == tab_bar) {
  535. return;
  536. }
  537. Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE);
  538. if (!c) {
  539. return;
  540. }
  541. c->hide();
  542. tab_bar->add_tab(p_child->get_meta("_tab_name", p_child->get_name()));
  543. c->set_meta("_tab_index", tab_bar->get_tab_count() - 1);
  544. _update_margins();
  545. if (get_tab_count() == 1) {
  546. queue_redraw();
  547. }
  548. queue_accessibility_update();
  549. p_child->connect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names));
  550. p_child->connect(SceneStringName(visibility_changed), callable_mp(this, &TabContainer::_on_tab_visibility_changed).bind(c));
  551. // TabBar won't emit the "tab_changed" signal when not inside the tree.
  552. if (!is_inside_tree()) {
  553. callable_mp(this, &TabContainer::_repaint).call_deferred();
  554. }
  555. notify_property_list_changed();
  556. }
  557. void TabContainer::move_child_notify(Node *p_child) {
  558. Container::move_child_notify(p_child);
  559. if (p_child == tab_bar) {
  560. return;
  561. }
  562. Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE);
  563. if (c) {
  564. tab_bar->move_tab(c->get_meta("_tab_index"), get_tab_idx_from_control(c));
  565. }
  566. _refresh_tab_indices();
  567. queue_accessibility_update();
  568. notify_property_list_changed();
  569. }
  570. void TabContainer::remove_child_notify(Node *p_child) {
  571. Container::remove_child_notify(p_child);
  572. if (tab_panels.has(p_child)) {
  573. DisplayServer::get_singleton()->accessibility_free_element(tab_panels[p_child]);
  574. tab_panels.erase(p_child);
  575. }
  576. if (p_child == tab_bar) {
  577. return;
  578. }
  579. Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE);
  580. if (!c) {
  581. return;
  582. }
  583. int idx = get_tab_idx_from_control(c);
  584. // As the child hasn't been removed yet, keep track of it so when the "tab_changed" signal is fired it can be ignored.
  585. children_removing.push_back(c);
  586. tab_bar->remove_tab(idx);
  587. _refresh_tab_indices();
  588. children_removing.erase(c);
  589. _update_margins();
  590. if (get_tab_count() == 0) {
  591. queue_redraw();
  592. }
  593. queue_accessibility_update();
  594. p_child->remove_meta("_tab_index");
  595. p_child->remove_meta("_tab_name");
  596. p_child->disconnect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names));
  597. p_child->disconnect(SceneStringName(visibility_changed), callable_mp(this, &TabContainer::_on_tab_visibility_changed));
  598. // TabBar won't emit the "tab_changed" signal when not inside the tree.
  599. if (!is_inside_tree()) {
  600. callable_mp(this, &TabContainer::_repaint).call_deferred();
  601. }
  602. notify_property_list_changed();
  603. }
  604. TabBar *TabContainer::get_tab_bar() const {
  605. return tab_bar;
  606. }
  607. int TabContainer::get_tab_count() const {
  608. return tab_bar->get_tab_count();
  609. }
  610. void TabContainer::set_current_tab(int p_current) {
  611. if (!is_inside_tree()) {
  612. setup_current_tab = p_current;
  613. return;
  614. }
  615. tab_bar->set_current_tab(p_current);
  616. }
  617. int TabContainer::get_current_tab() const {
  618. return tab_bar->get_current_tab();
  619. }
  620. int TabContainer::get_previous_tab() const {
  621. return tab_bar->get_previous_tab();
  622. }
  623. bool TabContainer::select_previous_available() {
  624. return tab_bar->select_previous_available();
  625. }
  626. bool TabContainer::select_next_available() {
  627. return tab_bar->select_next_available();
  628. }
  629. void TabContainer::set_deselect_enabled(bool p_enabled) {
  630. tab_bar->set_deselect_enabled(p_enabled);
  631. }
  632. bool TabContainer::get_deselect_enabled() const {
  633. return tab_bar->get_deselect_enabled();
  634. }
  635. Control *TabContainer::get_tab_control(int p_idx) const {
  636. Vector<Control *> controls = _get_tab_controls();
  637. if (p_idx >= 0 && p_idx < controls.size()) {
  638. return controls[p_idx];
  639. } else {
  640. return nullptr;
  641. }
  642. }
  643. Control *TabContainer::get_current_tab_control() const {
  644. return get_tab_control(tab_bar->get_current_tab());
  645. }
  646. int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
  647. return tab_bar->get_tab_idx_at_point(p_point);
  648. }
  649. int TabContainer::get_tab_idx_from_control(Control *p_child) const {
  650. ERR_FAIL_NULL_V(p_child, -1);
  651. ERR_FAIL_COND_V(p_child->get_parent() != this, -1);
  652. Vector<Control *> controls = _get_tab_controls();
  653. for (int i = 0; i < controls.size(); i++) {
  654. if (controls[i] == p_child) {
  655. return i;
  656. }
  657. }
  658. return -1;
  659. }
  660. void TabContainer::set_tab_alignment(TabBar::AlignmentMode p_alignment) {
  661. if (tab_bar->get_tab_alignment() == p_alignment) {
  662. return;
  663. }
  664. tab_bar->set_tab_alignment(p_alignment);
  665. _update_margins();
  666. }
  667. TabBar::AlignmentMode TabContainer::get_tab_alignment() const {
  668. return tab_bar->get_tab_alignment();
  669. }
  670. void TabContainer::set_tabs_position(TabPosition p_tabs_position) {
  671. ERR_FAIL_INDEX(p_tabs_position, POSITION_MAX);
  672. if (p_tabs_position == tabs_position) {
  673. return;
  674. }
  675. tabs_position = p_tabs_position;
  676. tab_bar->set_tab_style_v_flip(tabs_position == POSITION_BOTTOM);
  677. callable_mp(this, &TabContainer::_repaint).call_deferred();
  678. queue_redraw();
  679. }
  680. TabContainer::TabPosition TabContainer::get_tabs_position() const {
  681. return tabs_position;
  682. }
  683. void TabContainer::set_tab_focus_mode(Control::FocusMode p_focus_mode) {
  684. tab_bar->set_focus_mode(p_focus_mode);
  685. }
  686. Control::FocusMode TabContainer::get_tab_focus_mode() const {
  687. return tab_bar->get_focus_mode();
  688. }
  689. void TabContainer::set_clip_tabs(bool p_clip_tabs) {
  690. tab_bar->set_clip_tabs(p_clip_tabs);
  691. }
  692. bool TabContainer::get_clip_tabs() const {
  693. return tab_bar->get_clip_tabs();
  694. }
  695. void TabContainer::set_tabs_visible(bool p_visible) {
  696. if (p_visible == tabs_visible) {
  697. return;
  698. }
  699. tabs_visible = p_visible;
  700. tab_bar->set_visible(tabs_visible);
  701. callable_mp(this, &TabContainer::_repaint).call_deferred();
  702. queue_redraw();
  703. }
  704. bool TabContainer::are_tabs_visible() const {
  705. return tabs_visible;
  706. }
  707. void TabContainer::set_all_tabs_in_front(bool p_in_front) {
  708. if (p_in_front == all_tabs_in_front) {
  709. return;
  710. }
  711. all_tabs_in_front = p_in_front;
  712. remove_child(tab_bar);
  713. add_child(tab_bar, false, all_tabs_in_front ? INTERNAL_MODE_FRONT : INTERNAL_MODE_BACK);
  714. }
  715. bool TabContainer::is_all_tabs_in_front() const {
  716. return all_tabs_in_front;
  717. }
  718. void TabContainer::set_tab_title(int p_tab, const String &p_title) {
  719. Control *child = get_tab_control(p_tab);
  720. if (!child && !is_ready()) {
  721. CachedTab &tab = get_pending_tab(p_tab);
  722. tab.title = p_title;
  723. tab.has_title = true;
  724. return;
  725. }
  726. ERR_FAIL_NULL(child);
  727. if (tab_bar->get_tab_title(p_tab) == p_title) {
  728. return;
  729. }
  730. tab_bar->set_tab_title(p_tab, p_title);
  731. if (p_title == child->get_name()) {
  732. child->remove_meta("_tab_name");
  733. } else {
  734. child->set_meta("_tab_name", p_title);
  735. }
  736. _repaint();
  737. queue_redraw();
  738. }
  739. String TabContainer::get_tab_title(int p_tab) const {
  740. return tab_bar->get_tab_title(p_tab);
  741. }
  742. void TabContainer::set_tab_tooltip(int p_tab, const String &p_tooltip) {
  743. tab_bar->set_tab_tooltip(p_tab, p_tooltip);
  744. }
  745. String TabContainer::get_tab_tooltip(int p_tab) const {
  746. return tab_bar->get_tab_tooltip(p_tab);
  747. }
  748. void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
  749. Control *child = get_tab_control(p_tab);
  750. if (!child && !is_ready()) {
  751. get_pending_tab(p_tab).icon = p_icon;
  752. return;
  753. }
  754. if (tab_bar->get_tab_icon(p_tab) == p_icon) {
  755. return;
  756. }
  757. tab_bar->set_tab_icon(p_tab, p_icon);
  758. _update_margins();
  759. _repaint();
  760. queue_redraw();
  761. }
  762. Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const {
  763. return tab_bar->get_tab_icon(p_tab);
  764. }
  765. void TabContainer::set_tab_icon_max_width(int p_tab, int p_width) {
  766. if (tab_bar->get_tab_icon_max_width(p_tab) == p_width) {
  767. return;
  768. }
  769. tab_bar->set_tab_icon_max_width(p_tab, p_width);
  770. _update_margins();
  771. _repaint();
  772. queue_redraw();
  773. }
  774. int TabContainer::get_tab_icon_max_width(int p_tab) const {
  775. return tab_bar->get_tab_icon_max_width(p_tab);
  776. }
  777. void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) {
  778. Control *child = get_tab_control(p_tab);
  779. if (!child && !is_ready()) {
  780. get_pending_tab(p_tab).disabled = p_disabled;
  781. return;
  782. }
  783. if (tab_bar->is_tab_disabled(p_tab) == p_disabled) {
  784. return;
  785. }
  786. tab_bar->set_tab_disabled(p_tab, p_disabled);
  787. _update_margins();
  788. if (!get_clip_tabs()) {
  789. update_minimum_size();
  790. }
  791. }
  792. bool TabContainer::is_tab_disabled(int p_tab) const {
  793. return tab_bar->is_tab_disabled(p_tab);
  794. }
  795. void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
  796. Control *child = get_tab_control(p_tab);
  797. if (!child && !is_ready()) {
  798. get_pending_tab(p_tab).hidden = p_hidden;
  799. return;
  800. }
  801. ERR_FAIL_NULL(child);
  802. if (tab_bar->is_tab_hidden(p_tab) == p_hidden) {
  803. return;
  804. }
  805. tab_bar->set_tab_hidden(p_tab, p_hidden);
  806. child->hide();
  807. _update_margins();
  808. if (!get_clip_tabs()) {
  809. update_minimum_size();
  810. }
  811. callable_mp(this, &TabContainer::_repaint).call_deferred();
  812. }
  813. bool TabContainer::is_tab_hidden(int p_tab) const {
  814. return tab_bar->is_tab_hidden(p_tab);
  815. }
  816. void TabContainer::set_tab_metadata(int p_tab, const Variant &p_metadata) {
  817. tab_bar->set_tab_metadata(p_tab, p_metadata);
  818. }
  819. Variant TabContainer::get_tab_metadata(int p_tab) const {
  820. return tab_bar->get_tab_metadata(p_tab);
  821. }
  822. void TabContainer::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
  823. tab_bar->set_tab_button_icon(p_tab, p_icon);
  824. _update_margins();
  825. _repaint();
  826. }
  827. Ref<Texture2D> TabContainer::get_tab_button_icon(int p_tab) const {
  828. return tab_bar->get_tab_button_icon(p_tab);
  829. }
  830. Size2 TabContainer::get_minimum_size() const {
  831. Size2 ms;
  832. if (tabs_visible) {
  833. ms = tab_bar->get_minimum_size();
  834. ms.width += theme_cache.tabbar_style->get_margin(SIDE_LEFT) + theme_cache.tabbar_style->get_margin(SIDE_RIGHT);
  835. ms.height += theme_cache.tabbar_style->get_margin(SIDE_TOP) + theme_cache.tabbar_style->get_margin(SIDE_BOTTOM);
  836. if (get_popup()) {
  837. ms.width += theme_cache.menu_icon->get_width();
  838. }
  839. if (theme_cache.side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER &&
  840. (get_tab_alignment() != TabBar::ALIGNMENT_RIGHT || !get_popup())) {
  841. ms.width += theme_cache.side_margin;
  842. }
  843. }
  844. Vector<Control *> controls = _get_tab_controls();
  845. Size2 largest_child_min_size;
  846. for (int i = 0; i < controls.size(); i++) {
  847. Control *c = controls[i];
  848. if (!c->is_visible() && !use_hidden_tabs_for_min_size) {
  849. continue;
  850. }
  851. Size2 cms = c->get_combined_minimum_size();
  852. largest_child_min_size = largest_child_min_size.max(cms);
  853. }
  854. ms.height += largest_child_min_size.height;
  855. Size2 panel_ms = theme_cache.panel_style->get_minimum_size();
  856. ms.width = MAX(ms.width, largest_child_min_size.width + panel_ms.width);
  857. ms.height += panel_ms.height;
  858. return ms;
  859. }
  860. void TabContainer::set_popup(Node *p_popup) {
  861. bool had_popup = get_popup();
  862. Popup *popup = Object::cast_to<Popup>(p_popup);
  863. ObjectID popup_id = popup ? popup->get_instance_id() : ObjectID();
  864. if (popup_obj_id == popup_id) {
  865. return;
  866. }
  867. popup_obj_id = popup_id;
  868. if (had_popup != bool(popup)) {
  869. queue_redraw();
  870. _update_margins();
  871. update_minimum_size();
  872. }
  873. }
  874. Popup *TabContainer::get_popup() const {
  875. if (popup_obj_id.is_valid()) {
  876. Popup *popup = ObjectDB::get_instance<Popup>(popup_obj_id);
  877. if (popup) {
  878. return popup;
  879. } else {
  880. #ifdef DEBUG_ENABLED
  881. ERR_PRINT("Popup assigned to TabContainer is gone!");
  882. #endif
  883. popup_obj_id = ObjectID();
  884. }
  885. }
  886. return nullptr;
  887. }
  888. void TabContainer::set_switch_on_drag_hover(bool p_enabled) {
  889. tab_bar->set_switch_on_drag_hover(p_enabled);
  890. }
  891. bool TabContainer::get_switch_on_drag_hover() const {
  892. return tab_bar->get_switch_on_drag_hover();
  893. }
  894. void TabContainer::set_drag_to_rearrange_enabled(bool p_enabled) {
  895. drag_to_rearrange_enabled = p_enabled;
  896. }
  897. bool TabContainer::get_drag_to_rearrange_enabled() const {
  898. return drag_to_rearrange_enabled;
  899. }
  900. void TabContainer::set_tabs_rearrange_group(int p_group_id) {
  901. tab_bar->set_tabs_rearrange_group(p_group_id);
  902. }
  903. int TabContainer::get_tabs_rearrange_group() const {
  904. return tab_bar->get_tabs_rearrange_group();
  905. }
  906. void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) {
  907. if (use_hidden_tabs_for_min_size == p_use_hidden_tabs) {
  908. return;
  909. }
  910. use_hidden_tabs_for_min_size = p_use_hidden_tabs;
  911. update_minimum_size();
  912. }
  913. bool TabContainer::get_use_hidden_tabs_for_min_size() const {
  914. return use_hidden_tabs_for_min_size;
  915. }
  916. Vector<int> TabContainer::get_allowed_size_flags_horizontal() const {
  917. return Vector<int>();
  918. }
  919. Vector<int> TabContainer::get_allowed_size_flags_vertical() const {
  920. return Vector<int>();
  921. }
  922. void TabContainer::_bind_methods() {
  923. ClassDB::bind_method(D_METHOD("get_tab_count"), &TabContainer::get_tab_count);
  924. ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabContainer::set_current_tab);
  925. ClassDB::bind_method(D_METHOD("get_current_tab"), &TabContainer::get_current_tab);
  926. ClassDB::bind_method(D_METHOD("get_previous_tab"), &TabContainer::get_previous_tab);
  927. ClassDB::bind_method(D_METHOD("select_previous_available"), &TabContainer::select_previous_available);
  928. ClassDB::bind_method(D_METHOD("select_next_available"), &TabContainer::select_next_available);
  929. ClassDB::bind_method(D_METHOD("get_current_tab_control"), &TabContainer::get_current_tab_control);
  930. ClassDB::bind_method(D_METHOD("get_tab_bar"), &TabContainer::get_tab_bar);
  931. ClassDB::bind_method(D_METHOD("get_tab_control", "tab_idx"), &TabContainer::get_tab_control);
  932. ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabContainer::set_tab_alignment);
  933. ClassDB::bind_method(D_METHOD("get_tab_alignment"), &TabContainer::get_tab_alignment);
  934. ClassDB::bind_method(D_METHOD("set_tabs_position", "tabs_position"), &TabContainer::set_tabs_position);
  935. ClassDB::bind_method(D_METHOD("get_tabs_position"), &TabContainer::get_tabs_position);
  936. ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabContainer::set_clip_tabs);
  937. ClassDB::bind_method(D_METHOD("get_clip_tabs"), &TabContainer::get_clip_tabs);
  938. ClassDB::bind_method(D_METHOD("set_tabs_visible", "visible"), &TabContainer::set_tabs_visible);
  939. ClassDB::bind_method(D_METHOD("are_tabs_visible"), &TabContainer::are_tabs_visible);
  940. ClassDB::bind_method(D_METHOD("set_all_tabs_in_front", "is_front"), &TabContainer::set_all_tabs_in_front);
  941. ClassDB::bind_method(D_METHOD("is_all_tabs_in_front"), &TabContainer::is_all_tabs_in_front);
  942. ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &TabContainer::set_tab_title);
  943. ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabContainer::get_tab_title);
  944. ClassDB::bind_method(D_METHOD("set_tab_tooltip", "tab_idx", "tooltip"), &TabContainer::set_tab_tooltip);
  945. ClassDB::bind_method(D_METHOD("get_tab_tooltip", "tab_idx"), &TabContainer::get_tab_tooltip);
  946. ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon);
  947. ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon);
  948. ClassDB::bind_method(D_METHOD("set_tab_icon_max_width", "tab_idx", "width"), &TabContainer::set_tab_icon_max_width);
  949. ClassDB::bind_method(D_METHOD("get_tab_icon_max_width", "tab_idx"), &TabContainer::get_tab_icon_max_width);
  950. ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled);
  951. ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabContainer::is_tab_disabled);
  952. ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden);
  953. ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabContainer::is_tab_hidden);
  954. ClassDB::bind_method(D_METHOD("set_tab_metadata", "tab_idx", "metadata"), &TabContainer::set_tab_metadata);
  955. ClassDB::bind_method(D_METHOD("get_tab_metadata", "tab_idx"), &TabContainer::get_tab_metadata);
  956. ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabContainer::set_tab_button_icon);
  957. ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabContainer::get_tab_button_icon);
  958. ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point);
  959. ClassDB::bind_method(D_METHOD("get_tab_idx_from_control", "control"), &TabContainer::get_tab_idx_from_control);
  960. ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
  961. ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
  962. ClassDB::bind_method(D_METHOD("set_switch_on_drag_hover", "enabled"), &TabContainer::set_switch_on_drag_hover);
  963. ClassDB::bind_method(D_METHOD("get_switch_on_drag_hover"), &TabContainer::get_switch_on_drag_hover);
  964. ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled);
  965. ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled);
  966. ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group);
  967. ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group);
  968. ClassDB::bind_method(D_METHOD("set_use_hidden_tabs_for_min_size", "enabled"), &TabContainer::set_use_hidden_tabs_for_min_size);
  969. ClassDB::bind_method(D_METHOD("get_use_hidden_tabs_for_min_size"), &TabContainer::get_use_hidden_tabs_for_min_size);
  970. ClassDB::bind_method(D_METHOD("set_tab_focus_mode", "focus_mode"), &TabContainer::set_tab_focus_mode);
  971. ClassDB::bind_method(D_METHOD("get_tab_focus_mode"), &TabContainer::get_tab_focus_mode);
  972. ClassDB::bind_method(D_METHOD("set_deselect_enabled", "enabled"), &TabContainer::set_deselect_enabled);
  973. ClassDB::bind_method(D_METHOD("get_deselect_enabled"), &TabContainer::get_deselect_enabled);
  974. ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to")));
  975. ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
  976. ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
  977. ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
  978. ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
  979. ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab")));
  980. ADD_SIGNAL(MethodInfo("pre_popup_pressed"));
  981. ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
  982. ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1"), "set_current_tab", "get_current_tab");
  983. ADD_PROPERTY(PropertyInfo(Variant::INT, "tabs_position", PROPERTY_HINT_ENUM, "Top,Bottom"), "set_tabs_position", "get_tabs_position");
  984. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs");
  985. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible");
  986. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "all_tabs_in_front"), "set_all_tabs_in_front", "is_all_tabs_in_front");
  987. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_drag_hover"), "set_switch_on_drag_hover", "get_switch_on_drag_hover");
  988. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
  989. ADD_PROPERTY(PropertyInfo(Variant::INT, "tabs_rearrange_group"), "set_tabs_rearrange_group", "get_tabs_rearrange_group");
  990. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size");
  991. ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_tab_focus_mode", "get_tab_focus_mode");
  992. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_enabled"), "set_deselect_enabled", "get_deselect_enabled");
  993. ADD_CLASS_DEPENDENCY("TabBar");
  994. BIND_ENUM_CONSTANT(POSITION_TOP);
  995. BIND_ENUM_CONSTANT(POSITION_BOTTOM);
  996. BIND_ENUM_CONSTANT(POSITION_MAX);
  997. BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, side_margin);
  998. BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, tab_separation);
  999. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, panel_style, "panel");
  1000. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, tabbar_style, "tabbar_background");
  1001. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabContainer, menu_icon, "menu");
  1002. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabContainer, menu_hl_icon, "menu_highlight");
  1003. // TabBar overrides.
  1004. BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, icon_separation);
  1005. BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, icon_max_width);
  1006. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, tab_unselected_style, "tab_unselected");
  1007. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, tab_hovered_style, "tab_hovered");
  1008. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, tab_selected_style, "tab_selected");
  1009. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, tab_disabled_style, "tab_disabled");
  1010. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, tab_focus_style, "tab_focus");
  1011. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabContainer, increment_icon, "increment");
  1012. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabContainer, increment_hl_icon, "increment_highlight");
  1013. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabContainer, decrement_icon, "decrement");
  1014. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabContainer, decrement_hl_icon, "decrement_highlight");
  1015. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabContainer, drop_mark_icon, "drop_mark");
  1016. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, drop_mark_color);
  1017. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, font_selected_color);
  1018. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, font_hovered_color);
  1019. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, font_unselected_color);
  1020. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, font_disabled_color);
  1021. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, font_outline_color);
  1022. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, icon_selected_color);
  1023. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, icon_hovered_color);
  1024. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, icon_unselected_color);
  1025. BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabContainer, icon_disabled_color);
  1026. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT, TabContainer, tab_font, "font");
  1027. BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT_SIZE, TabContainer, tab_font_size, "font_size");
  1028. BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, outline_size);
  1029. CachedTab defaults;
  1030. base_property_helper.set_prefix("tab_");
  1031. base_property_helper.set_array_length_getter(&TabContainer::get_tab_count);
  1032. base_property_helper.register_property(PropertyInfo(Variant::STRING, "title"), defaults.title, &TabContainer::set_tab_title, &TabContainer::get_tab_title);
  1033. base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &TabContainer::set_tab_icon, &TabContainer::get_tab_icon);
  1034. base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &TabContainer::set_tab_disabled, &TabContainer::is_tab_disabled);
  1035. base_property_helper.register_property(PropertyInfo(Variant::BOOL, "hidden"), defaults.hidden, &TabContainer::set_tab_hidden, &TabContainer::is_tab_hidden);
  1036. PropertyListHelper::register_base_helper(&base_property_helper);
  1037. }
  1038. TabContainer::TabContainer() {
  1039. tab_bar = memnew(TabBar);
  1040. SET_DRAG_FORWARDING_GCDU(tab_bar, TabContainer);
  1041. add_child(tab_bar, false, INTERNAL_MODE_FRONT);
  1042. tab_bar->set_use_parent_material(true);
  1043. tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE);
  1044. tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed));
  1045. tab_bar->connect("tab_clicked", callable_mp(this, &TabContainer::_on_tab_clicked));
  1046. tab_bar->connect("tab_hovered", callable_mp(this, &TabContainer::_on_tab_hovered));
  1047. tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected));
  1048. tab_bar->connect("tab_button_pressed", callable_mp(this, &TabContainer::_on_tab_button_pressed));
  1049. tab_bar->connect("active_tab_rearranged", callable_mp(this, &TabContainer::_on_active_tab_rearranged));
  1050. connect(SceneStringName(mouse_exited), callable_mp(this, &TabContainer::_on_mouse_exited));
  1051. property_helper.setup_for_instance(base_property_helper, this);
  1052. property_helper.enable_out_of_bounds_assign();
  1053. }