tabs.cpp 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. /*************************************************************************/
  2. /* tabs.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
  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 "tabs.h"
  31. #include "core/message_queue.h"
  32. #include "scene/gui/box_container.h"
  33. #include "scene/gui/label.h"
  34. #include "scene/gui/texture_rect.h"
  35. Size2 Tabs::get_minimum_size() const {
  36. Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
  37. Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
  38. Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
  39. Ref<Font> font = get_font("font");
  40. Size2 ms(0, MAX(MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height), tab_disabled->get_minimum_size().height) + font->get_height());
  41. for (int i = 0; i < tabs.size(); i++) {
  42. Ref<Texture> tex = tabs[i].icon;
  43. if (tex.is_valid()) {
  44. ms.height = MAX(ms.height, tex->get_size().height);
  45. if (tabs[i].text != "")
  46. ms.width += get_constant("hseparation");
  47. }
  48. ms.width += Math::ceil(font->get_string_size(tabs[i].text).width);
  49. if (tabs[i].disabled)
  50. ms.width += tab_disabled->get_minimum_size().width;
  51. else if (current == i)
  52. ms.width += tab_fg->get_minimum_size().width;
  53. else
  54. ms.width += tab_bg->get_minimum_size().width;
  55. if (tabs[i].right_button.is_valid()) {
  56. Ref<Texture> rb = tabs[i].right_button;
  57. Size2 bms = rb->get_size();
  58. bms.width += get_constant("hseparation");
  59. ms.width += bms.width;
  60. ms.height = MAX(bms.height + tab_bg->get_minimum_size().height, ms.height);
  61. }
  62. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
  63. Ref<Texture> cb = get_icon("close");
  64. Size2 bms = cb->get_size();
  65. bms.width += get_constant("hseparation");
  66. ms.width += bms.width;
  67. ms.height = MAX(bms.height + tab_bg->get_minimum_size().height, ms.height);
  68. }
  69. }
  70. ms.width = 0; //TODO: should make this optional
  71. return ms;
  72. }
  73. void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
  74. Ref<InputEventMouseMotion> mm = p_event;
  75. if (mm.is_valid()) {
  76. Point2 pos = mm->get_position();
  77. highlight_arrow = -1;
  78. if (buttons_visible) {
  79. Ref<Texture> incr = get_icon("increment");
  80. Ref<Texture> decr = get_icon("decrement");
  81. int limit = get_size().width - incr->get_width() - decr->get_width();
  82. if (pos.x > limit + decr->get_width()) {
  83. highlight_arrow = 1;
  84. } else if (pos.x > limit) {
  85. highlight_arrow = 0;
  86. }
  87. }
  88. _update_hover();
  89. update();
  90. return;
  91. }
  92. Ref<InputEventMouseButton> mb = p_event;
  93. if (mb.is_valid()) {
  94. if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) {
  95. if (scrolling_enabled && buttons_visible) {
  96. if (offset > 0) {
  97. offset--;
  98. update();
  99. }
  100. }
  101. }
  102. if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) {
  103. if (scrolling_enabled && buttons_visible) {
  104. if (missing_right) {
  105. offset++;
  106. update();
  107. }
  108. }
  109. }
  110. if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
  111. if (rb_hover != -1) {
  112. //pressed
  113. emit_signal("right_button_pressed", rb_hover);
  114. }
  115. rb_pressing = false;
  116. update();
  117. }
  118. if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
  119. if (cb_hover != -1) {
  120. //pressed
  121. emit_signal("tab_close", cb_hover);
  122. }
  123. cb_pressing = false;
  124. update();
  125. }
  126. if (mb->is_pressed() && (mb->get_button_index() == BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == BUTTON_RIGHT))) {
  127. // clicks
  128. Point2 pos(mb->get_position().x, mb->get_position().y);
  129. if (buttons_visible) {
  130. Ref<Texture> incr = get_icon("increment");
  131. Ref<Texture> decr = get_icon("decrement");
  132. int limit = get_size().width - incr->get_width() - decr->get_width();
  133. if (pos.x > limit + decr->get_width()) {
  134. if (missing_right) {
  135. offset++;
  136. update();
  137. }
  138. return;
  139. } else if (pos.x > limit) {
  140. if (offset > 0) {
  141. offset--;
  142. update();
  143. }
  144. return;
  145. }
  146. }
  147. int found = -1;
  148. for (int i = 0; i < tabs.size(); i++) {
  149. if (i < offset)
  150. continue;
  151. if (tabs[i].rb_rect.has_point(pos)) {
  152. rb_pressing = true;
  153. update();
  154. return;
  155. }
  156. if (tabs[i].cb_rect.has_point(pos)) {
  157. cb_pressing = true;
  158. update();
  159. return;
  160. }
  161. if (pos.x >= tabs[i].ofs_cache && pos.x < tabs[i].ofs_cache + tabs[i].size_cache) {
  162. if (!tabs[i].disabled) {
  163. found = i;
  164. }
  165. break;
  166. }
  167. }
  168. if (found != -1) {
  169. set_current_tab(found);
  170. emit_signal("tab_clicked", found);
  171. }
  172. }
  173. }
  174. }
  175. void Tabs::_notification(int p_what) {
  176. switch (p_what) {
  177. case NOTIFICATION_MOUSE_EXIT: {
  178. rb_hover = -1;
  179. cb_hover = -1;
  180. hover = -1;
  181. update();
  182. } break;
  183. case NOTIFICATION_RESIZED: {
  184. _update_cache();
  185. _ensure_no_over_offset();
  186. ensure_tab_visible(current);
  187. } break;
  188. case NOTIFICATION_DRAW: {
  189. _update_cache();
  190. RID ci = get_canvas_item();
  191. Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
  192. Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
  193. Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
  194. Ref<Font> font = get_font("font");
  195. Color color_fg = get_color("font_color_fg");
  196. Color color_bg = get_color("font_color_bg");
  197. Color color_disabled = get_color("font_color_disabled");
  198. Ref<Texture> close = get_icon("close");
  199. int h = get_size().height;
  200. int w = 0;
  201. int mw = 0;
  202. for (int i = 0; i < tabs.size(); i++) {
  203. tabs.write[i].ofs_cache = mw;
  204. mw += get_tab_width(i);
  205. }
  206. if (tab_align == ALIGN_CENTER) {
  207. w = (get_size().width - mw) / 2;
  208. } else if (tab_align == ALIGN_RIGHT) {
  209. w = get_size().width - mw;
  210. }
  211. if (w < 0) {
  212. w = 0;
  213. }
  214. Ref<Texture> incr = get_icon("increment");
  215. Ref<Texture> decr = get_icon("decrement");
  216. Ref<Texture> incr_hl = get_icon("increment_highlight");
  217. Ref<Texture> decr_hl = get_icon("decrement_highlight");
  218. int limit = get_size().width - incr->get_size().width - decr->get_size().width;
  219. missing_right = false;
  220. for (int i = 0; i < tabs.size(); i++) {
  221. if (i < offset)
  222. continue;
  223. tabs.write[i].ofs_cache = w;
  224. int lsize = tabs[i].size_cache;
  225. Ref<StyleBox> sb;
  226. Color col;
  227. if (tabs[i].disabled) {
  228. sb = tab_disabled;
  229. col = color_disabled;
  230. } else if (i == current) {
  231. sb = tab_fg;
  232. col = color_fg;
  233. } else {
  234. sb = tab_bg;
  235. col = color_bg;
  236. }
  237. if (w + lsize > limit) {
  238. max_drawn_tab = i - 1;
  239. missing_right = true;
  240. break;
  241. } else {
  242. max_drawn_tab = i;
  243. }
  244. Rect2 sb_rect = Rect2(w, 0, tabs[i].size_cache, h);
  245. sb->draw(ci, sb_rect);
  246. w += sb->get_margin(MARGIN_LEFT);
  247. Size2i sb_ms = sb->get_minimum_size();
  248. Ref<Texture> icon = tabs[i].icon;
  249. if (icon.is_valid()) {
  250. icon->draw(ci, Point2i(w, sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
  251. if (tabs[i].text != "")
  252. w += icon->get_width() + get_constant("hseparation");
  253. }
  254. font->draw(ci, Point2i(w, sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - font->get_height()) / 2 + font->get_ascent()), tabs[i].text, col, tabs[i].size_text);
  255. w += tabs[i].size_text;
  256. if (tabs[i].right_button.is_valid()) {
  257. Ref<StyleBox> style = get_stylebox("button");
  258. Ref<Texture> rb = tabs[i].right_button;
  259. w += get_constant("hseparation");
  260. Rect2 rb_rect;
  261. rb_rect.size = style->get_minimum_size() + rb->get_size();
  262. rb_rect.position.x = w;
  263. rb_rect.position.y = sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
  264. if (rb_hover == i) {
  265. if (rb_pressing)
  266. get_stylebox("button_pressed")->draw(ci, rb_rect);
  267. else
  268. style->draw(ci, rb_rect);
  269. }
  270. rb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), rb_rect.position.y + style->get_margin(MARGIN_TOP)));
  271. w += rb->get_width();
  272. tabs.write[i].rb_rect = rb_rect;
  273. }
  274. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
  275. Ref<StyleBox> style = get_stylebox("button");
  276. Ref<Texture> cb = close;
  277. w += get_constant("hseparation");
  278. Rect2 cb_rect;
  279. cb_rect.size = style->get_minimum_size() + cb->get_size();
  280. cb_rect.position.x = w;
  281. cb_rect.position.y = sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
  282. if (!tabs[i].disabled && cb_hover == i) {
  283. if (cb_pressing)
  284. get_stylebox("button_pressed")->draw(ci, cb_rect);
  285. else
  286. style->draw(ci, cb_rect);
  287. }
  288. cb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), cb_rect.position.y + style->get_margin(MARGIN_TOP)));
  289. w += cb->get_width();
  290. tabs.write[i].cb_rect = cb_rect;
  291. }
  292. w += sb->get_margin(MARGIN_RIGHT);
  293. }
  294. if (offset > 0 || missing_right) {
  295. int vofs = (get_size().height - incr->get_size().height) / 2;
  296. if (offset > 0)
  297. draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit, vofs));
  298. else
  299. draw_texture(decr, Point2(limit, vofs), Color(1, 1, 1, 0.5));
  300. if (missing_right)
  301. draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit + decr->get_size().width, vofs));
  302. else
  303. draw_texture(incr, Point2(limit + decr->get_size().width, vofs), Color(1, 1, 1, 0.5));
  304. buttons_visible = true;
  305. } else {
  306. buttons_visible = false;
  307. }
  308. } break;
  309. }
  310. }
  311. int Tabs::get_tab_count() const {
  312. return tabs.size();
  313. }
  314. void Tabs::set_current_tab(int p_current) {
  315. if (current == p_current) return;
  316. ERR_FAIL_INDEX(p_current, get_tab_count());
  317. current = p_current;
  318. _change_notify("current_tab");
  319. _update_cache();
  320. update();
  321. emit_signal("tab_changed", p_current);
  322. }
  323. int Tabs::get_current_tab() const {
  324. return current;
  325. }
  326. int Tabs::get_hovered_tab() const {
  327. return hover;
  328. }
  329. int Tabs::get_tab_offset() const {
  330. return offset;
  331. }
  332. bool Tabs::get_offset_buttons_visible() const {
  333. return buttons_visible;
  334. }
  335. void Tabs::set_tab_title(int p_tab, const String &p_title) {
  336. ERR_FAIL_INDEX(p_tab, tabs.size());
  337. tabs.write[p_tab].text = p_title;
  338. update();
  339. minimum_size_changed();
  340. }
  341. String Tabs::get_tab_title(int p_tab) const {
  342. ERR_FAIL_INDEX_V(p_tab, tabs.size(), "");
  343. return tabs[p_tab].text;
  344. }
  345. void Tabs::set_tab_icon(int p_tab, const Ref<Texture> &p_icon) {
  346. ERR_FAIL_INDEX(p_tab, tabs.size());
  347. tabs.write[p_tab].icon = p_icon;
  348. update();
  349. minimum_size_changed();
  350. }
  351. Ref<Texture> Tabs::get_tab_icon(int p_tab) const {
  352. ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture>());
  353. return tabs[p_tab].icon;
  354. }
  355. void Tabs::set_tab_disabled(int p_tab, bool p_disabled) {
  356. ERR_FAIL_INDEX(p_tab, tabs.size());
  357. tabs.write[p_tab].disabled = p_disabled;
  358. update();
  359. }
  360. bool Tabs::get_tab_disabled(int p_tab) const {
  361. ERR_FAIL_INDEX_V(p_tab, tabs.size(), false);
  362. return tabs[p_tab].disabled;
  363. }
  364. void Tabs::set_tab_right_button(int p_tab, const Ref<Texture> &p_right_button) {
  365. ERR_FAIL_INDEX(p_tab, tabs.size());
  366. tabs.write[p_tab].right_button = p_right_button;
  367. _update_cache();
  368. update();
  369. minimum_size_changed();
  370. }
  371. Ref<Texture> Tabs::get_tab_right_button(int p_tab) const {
  372. ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture>());
  373. return tabs[p_tab].right_button;
  374. }
  375. void Tabs::_update_hover() {
  376. if (!is_inside_tree()) {
  377. return;
  378. }
  379. const Point2 &pos = get_local_mouse_position();
  380. // test hovering to display right or close button
  381. int hover_now = -1;
  382. int hover_buttons = -1;
  383. for (int i = 0; i < tabs.size(); i++) {
  384. if (i < offset)
  385. continue;
  386. Rect2 rect = get_tab_rect(i);
  387. if (rect.has_point(pos)) {
  388. hover_now = i;
  389. }
  390. if (tabs[i].rb_rect.has_point(pos)) {
  391. rb_hover = i;
  392. cb_hover = -1;
  393. hover_buttons = i;
  394. break;
  395. } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) {
  396. cb_hover = i;
  397. rb_hover = -1;
  398. hover_buttons = i;
  399. break;
  400. }
  401. }
  402. if (hover != hover_now) {
  403. hover = hover_now;
  404. emit_signal("tab_hover", hover);
  405. }
  406. if (hover_buttons == -1) { // no hover
  407. rb_hover = hover_buttons;
  408. cb_hover = hover_buttons;
  409. }
  410. }
  411. void Tabs::_update_cache() {
  412. Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
  413. Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
  414. Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
  415. Ref<Font> font = get_font("font");
  416. Ref<Texture> incr = get_icon("increment");
  417. Ref<Texture> decr = get_icon("decrement");
  418. int limit = get_size().width - incr->get_width() - decr->get_width();
  419. int w = 0;
  420. int mw = 0;
  421. int size_fixed = 0;
  422. int count_resize = 0;
  423. for (int i = 0; i < tabs.size(); i++) {
  424. tabs.write[i].ofs_cache = mw;
  425. tabs.write[i].size_cache = get_tab_width(i);
  426. tabs.write[i].size_text = Math::ceil(font->get_string_size(tabs[i].text).width);
  427. mw += tabs[i].size_cache;
  428. if (tabs[i].size_cache <= min_width || i == current) {
  429. size_fixed += tabs[i].size_cache;
  430. } else {
  431. count_resize++;
  432. }
  433. }
  434. int m_width = min_width;
  435. if (count_resize > 0) {
  436. m_width = MAX((limit - size_fixed) / count_resize, min_width);
  437. }
  438. for (int i = 0; i < tabs.size(); i++) {
  439. if (i < offset)
  440. continue;
  441. Ref<StyleBox> sb;
  442. if (tabs[i].disabled) {
  443. sb = tab_disabled;
  444. } else if (i == current) {
  445. sb = tab_fg;
  446. } else {
  447. sb = tab_bg;
  448. }
  449. int lsize = tabs[i].size_cache;
  450. int slen = tabs[i].size_text;
  451. if (min_width > 0 && mw > limit && i != current) {
  452. if (lsize > m_width) {
  453. slen = m_width - (sb->get_margin(MARGIN_LEFT) + sb->get_margin(MARGIN_RIGHT));
  454. if (tabs[i].icon.is_valid()) {
  455. slen -= tabs[i].icon->get_width();
  456. slen -= get_constant("hseparation");
  457. }
  458. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
  459. Ref<Texture> cb = get_icon("close");
  460. slen -= cb->get_width();
  461. slen -= get_constant("hseparation");
  462. }
  463. slen = MAX(slen, 1);
  464. lsize = m_width;
  465. }
  466. }
  467. tabs.write[i].ofs_cache = w;
  468. tabs.write[i].size_cache = lsize;
  469. tabs.write[i].size_text = slen;
  470. w += lsize;
  471. }
  472. }
  473. void Tabs::add_tab(const String &p_str, const Ref<Texture> &p_icon) {
  474. Tab t;
  475. t.text = p_str;
  476. t.icon = p_icon;
  477. t.disabled = false;
  478. t.ofs_cache = 0;
  479. t.size_cache = 0;
  480. tabs.push_back(t);
  481. _update_cache();
  482. call_deferred("_update_hover");
  483. update();
  484. minimum_size_changed();
  485. }
  486. void Tabs::clear_tabs() {
  487. tabs.clear();
  488. current = 0;
  489. call_deferred("_update_hover");
  490. update();
  491. }
  492. void Tabs::remove_tab(int p_idx) {
  493. ERR_FAIL_INDEX(p_idx, tabs.size());
  494. tabs.remove(p_idx);
  495. if (current >= p_idx)
  496. current--;
  497. _update_cache();
  498. call_deferred("_update_hover");
  499. update();
  500. minimum_size_changed();
  501. if (current < 0)
  502. current = 0;
  503. if (current >= tabs.size())
  504. current = tabs.size() - 1;
  505. _ensure_no_over_offset();
  506. }
  507. Variant Tabs::get_drag_data(const Point2 &p_point) {
  508. if (!drag_to_rearrange_enabled)
  509. return Variant();
  510. int tab_over = get_tab_idx_at_point(p_point);
  511. if (tab_over < 0)
  512. return Variant();
  513. HBoxContainer *drag_preview = memnew(HBoxContainer);
  514. if (!tabs[tab_over].icon.is_null()) {
  515. TextureRect *tf = memnew(TextureRect);
  516. tf->set_texture(tabs[tab_over].icon);
  517. drag_preview->add_child(tf);
  518. }
  519. Label *label = memnew(Label(tabs[tab_over].text));
  520. drag_preview->add_child(label);
  521. if (!tabs[tab_over].right_button.is_null()) {
  522. TextureRect *tf = memnew(TextureRect);
  523. tf->set_texture(tabs[tab_over].right_button);
  524. drag_preview->add_child(tf);
  525. }
  526. set_drag_preview(drag_preview);
  527. Dictionary drag_data;
  528. drag_data["type"] = "tab_element";
  529. drag_data["tab_element"] = tab_over;
  530. drag_data["from_path"] = get_path();
  531. return drag_data;
  532. }
  533. bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
  534. if (!drag_to_rearrange_enabled)
  535. return false;
  536. Dictionary d = p_data;
  537. if (!d.has("type"))
  538. return false;
  539. if (String(d["type"]) == "tab_element") {
  540. NodePath from_path = d["from_path"];
  541. NodePath to_path = get_path();
  542. if (from_path == to_path) {
  543. return true;
  544. } else if (get_tabs_rearrange_group() != -1) {
  545. // drag and drop between other Tabs
  546. Node *from_node = get_node(from_path);
  547. Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
  548. if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
  549. return true;
  550. }
  551. }
  552. }
  553. return false;
  554. }
  555. void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) {
  556. if (!drag_to_rearrange_enabled)
  557. return;
  558. int hover_now = get_tab_idx_at_point(p_point);
  559. Dictionary d = p_data;
  560. if (!d.has("type"))
  561. return;
  562. if (String(d["type"]) == "tab_element") {
  563. int tab_from_id = d["tab_element"];
  564. NodePath from_path = d["from_path"];
  565. NodePath to_path = get_path();
  566. if (from_path == to_path) {
  567. if (hover_now < 0)
  568. hover_now = get_tab_count() - 1;
  569. move_tab(tab_from_id, hover_now);
  570. emit_signal("reposition_active_tab_request", hover_now);
  571. set_current_tab(hover_now);
  572. } else if (get_tabs_rearrange_group() != -1) {
  573. // drag and drop between Tabs
  574. Node *from_node = get_node(from_path);
  575. Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
  576. if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
  577. if (tab_from_id >= from_tabs->get_tab_count())
  578. return;
  579. Tab moving_tab = from_tabs->tabs[tab_from_id];
  580. if (hover_now < 0)
  581. hover_now = get_tab_count();
  582. tabs.insert(hover_now, moving_tab);
  583. from_tabs->remove_tab(tab_from_id);
  584. set_current_tab(hover_now);
  585. emit_signal("tab_changed", hover_now);
  586. _update_cache();
  587. }
  588. }
  589. }
  590. update();
  591. }
  592. int Tabs::get_tab_idx_at_point(const Point2 &p_point) const {
  593. int hover_now = -1;
  594. for (int i = 0; i < tabs.size(); i++) {
  595. if (i < offset)
  596. continue;
  597. Rect2 rect = get_tab_rect(i);
  598. if (rect.has_point(p_point)) {
  599. hover_now = i;
  600. }
  601. }
  602. return hover_now;
  603. }
  604. void Tabs::set_tab_align(TabAlign p_align) {
  605. ERR_FAIL_INDEX(p_align, ALIGN_MAX);
  606. tab_align = p_align;
  607. update();
  608. }
  609. Tabs::TabAlign Tabs::get_tab_align() const {
  610. return tab_align;
  611. }
  612. void Tabs::move_tab(int from, int to) {
  613. if (from == to)
  614. return;
  615. ERR_FAIL_INDEX(from, tabs.size());
  616. ERR_FAIL_INDEX(to, tabs.size());
  617. Tab tab_from = tabs[from];
  618. tabs.remove(from);
  619. tabs.insert(to, tab_from);
  620. _update_cache();
  621. update();
  622. }
  623. int Tabs::get_tab_width(int p_idx) const {
  624. ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0);
  625. Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
  626. Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
  627. Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
  628. Ref<Font> font = get_font("font");
  629. int x = 0;
  630. Ref<Texture> tex = tabs[p_idx].icon;
  631. if (tex.is_valid()) {
  632. x += tex->get_width();
  633. if (tabs[p_idx].text != "")
  634. x += get_constant("hseparation");
  635. }
  636. x += Math::ceil(font->get_string_size(tabs[p_idx].text).width);
  637. if (tabs[p_idx].disabled)
  638. x += tab_disabled->get_minimum_size().width;
  639. else if (current == p_idx)
  640. x += tab_fg->get_minimum_size().width;
  641. else
  642. x += tab_bg->get_minimum_size().width;
  643. if (tabs[p_idx].right_button.is_valid()) {
  644. Ref<Texture> rb = tabs[p_idx].right_button;
  645. x += rb->get_width();
  646. x += get_constant("hseparation");
  647. }
  648. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current)) {
  649. Ref<Texture> cb = get_icon("close");
  650. x += cb->get_width();
  651. x += get_constant("hseparation");
  652. }
  653. return x;
  654. }
  655. void Tabs::_ensure_no_over_offset() {
  656. if (!is_inside_tree())
  657. return;
  658. Ref<Texture> incr = get_icon("increment");
  659. Ref<Texture> decr = get_icon("decrement");
  660. int limit = get_size().width - incr->get_width() - decr->get_width();
  661. while (offset > 0) {
  662. int total_w = 0;
  663. for (int i = 0; i < tabs.size(); i++) {
  664. if (i < offset - 1)
  665. continue;
  666. total_w += tabs[i].size_cache;
  667. }
  668. if (total_w < limit) {
  669. offset--;
  670. update();
  671. } else {
  672. break;
  673. }
  674. }
  675. }
  676. void Tabs::ensure_tab_visible(int p_idx) {
  677. if (!is_inside_tree())
  678. return;
  679. if (tabs.size() == 0) return;
  680. ERR_FAIL_INDEX(p_idx, tabs.size());
  681. if (p_idx == offset) {
  682. return;
  683. }
  684. if (p_idx < offset) {
  685. offset = p_idx;
  686. update();
  687. return;
  688. }
  689. int prev_offset = offset;
  690. Ref<Texture> incr = get_icon("increment");
  691. Ref<Texture> decr = get_icon("decrement");
  692. int limit = get_size().width - incr->get_width() - decr->get_width();
  693. for (int i = offset; i <= p_idx; i++) {
  694. if (tabs[i].ofs_cache + tabs[i].size_cache > limit) {
  695. offset++;
  696. }
  697. }
  698. if (prev_offset != offset) {
  699. update();
  700. }
  701. }
  702. Rect2 Tabs::get_tab_rect(int p_tab) const {
  703. return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height);
  704. }
  705. void Tabs::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
  706. ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX);
  707. cb_displaypolicy = p_policy;
  708. update();
  709. }
  710. Tabs::CloseButtonDisplayPolicy Tabs::get_tab_close_display_policy() const {
  711. return cb_displaypolicy;
  712. }
  713. void Tabs::set_min_width(int p_width) {
  714. min_width = p_width;
  715. }
  716. void Tabs::set_scrolling_enabled(bool p_enabled) {
  717. scrolling_enabled = p_enabled;
  718. }
  719. bool Tabs::get_scrolling_enabled() const {
  720. return scrolling_enabled;
  721. }
  722. void Tabs::set_drag_to_rearrange_enabled(bool p_enabled) {
  723. drag_to_rearrange_enabled = p_enabled;
  724. }
  725. bool Tabs::get_drag_to_rearrange_enabled() const {
  726. return drag_to_rearrange_enabled;
  727. }
  728. void Tabs::set_tabs_rearrange_group(int p_group_id) {
  729. tabs_rearrange_group = p_group_id;
  730. }
  731. int Tabs::get_tabs_rearrange_group() const {
  732. return tabs_rearrange_group;
  733. }
  734. void Tabs::set_select_with_rmb(bool p_enabled) {
  735. select_with_rmb = p_enabled;
  736. }
  737. bool Tabs::get_select_with_rmb() const {
  738. return select_with_rmb;
  739. }
  740. void Tabs::_bind_methods() {
  741. ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input);
  742. ClassDB::bind_method(D_METHOD("_update_hover"), &Tabs::_update_hover);
  743. ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count);
  744. ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab);
  745. ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab);
  746. ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &Tabs::set_tab_title);
  747. ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title);
  748. ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon);
  749. ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &Tabs::get_tab_icon);
  750. ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled);
  751. ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &Tabs::get_tab_disabled);
  752. ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &Tabs::remove_tab);
  753. ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &Tabs::add_tab, DEFVAL(""), DEFVAL(Ref<Texture>()));
  754. ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &Tabs::set_tab_align);
  755. ClassDB::bind_method(D_METHOD("get_tab_align"), &Tabs::get_tab_align);
  756. ClassDB::bind_method(D_METHOD("get_tab_offset"), &Tabs::get_tab_offset);
  757. ClassDB::bind_method(D_METHOD("get_offset_buttons_visible"), &Tabs::get_offset_buttons_visible);
  758. ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &Tabs::ensure_tab_visible);
  759. ClassDB::bind_method(D_METHOD("get_tab_rect", "tab_idx"), &Tabs::get_tab_rect);
  760. ClassDB::bind_method(D_METHOD("move_tab", "from", "to"), &Tabs::move_tab);
  761. ClassDB::bind_method(D_METHOD("set_tab_close_display_policy", "policy"), &Tabs::set_tab_close_display_policy);
  762. ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy);
  763. ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &Tabs::set_scrolling_enabled);
  764. ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &Tabs::get_scrolling_enabled);
  765. ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &Tabs::set_drag_to_rearrange_enabled);
  766. ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &Tabs::get_drag_to_rearrange_enabled);
  767. ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group);
  768. ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group);
  769. ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &Tabs::set_select_with_rmb);
  770. ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &Tabs::get_select_with_rmb);
  771. ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
  772. ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab")));
  773. ADD_SIGNAL(MethodInfo("tab_close", PropertyInfo(Variant::INT, "tab")));
  774. ADD_SIGNAL(MethodInfo("tab_hover", PropertyInfo(Variant::INT, "tab")));
  775. ADD_SIGNAL(MethodInfo("reposition_active_tab_request", PropertyInfo(Variant::INT, "idx_to")));
  776. ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
  777. ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
  778. ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
  779. ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
  780. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
  781. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
  782. BIND_ENUM_CONSTANT(ALIGN_LEFT);
  783. BIND_ENUM_CONSTANT(ALIGN_CENTER);
  784. BIND_ENUM_CONSTANT(ALIGN_RIGHT);
  785. BIND_ENUM_CONSTANT(ALIGN_MAX);
  786. BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER);
  787. BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ACTIVE_ONLY);
  788. BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ALWAYS);
  789. BIND_ENUM_CONSTANT(CLOSE_BUTTON_MAX);
  790. }
  791. Tabs::Tabs() {
  792. current = 0;
  793. tab_align = ALIGN_CENTER;
  794. rb_hover = -1;
  795. rb_pressing = false;
  796. highlight_arrow = -1;
  797. cb_hover = -1;
  798. cb_pressing = false;
  799. cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
  800. offset = 0;
  801. max_drawn_tab = 0;
  802. select_with_rmb = false;
  803. min_width = 0;
  804. scrolling_enabled = true;
  805. buttons_visible = false;
  806. hover = -1;
  807. drag_to_rearrange_enabled = false;
  808. tabs_rearrange_group = -1;
  809. }