tabs.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. /*************************************************************************/
  2. /* tabs.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2017 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 "message_queue.h"
  32. Size2 Tabs::get_minimum_size() const {
  33. Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
  34. Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
  35. Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
  36. Ref<Font> font = get_font("font");
  37. 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());
  38. for (int i = 0; i < tabs.size(); i++) {
  39. Ref<Texture> tex = tabs[i].icon;
  40. if (tex.is_valid()) {
  41. ms.height = MAX(ms.height, tex->get_size().height);
  42. if (tabs[i].text != "")
  43. ms.width += get_constant("hseparation");
  44. }
  45. ms.width += font->get_string_size(tabs[i].text).width;
  46. if (tabs[i].disabled)
  47. ms.width += tab_disabled->get_minimum_size().width;
  48. else if (current == i)
  49. ms.width += tab_fg->get_minimum_size().width;
  50. else
  51. ms.width += tab_bg->get_minimum_size().width;
  52. if (tabs[i].right_button.is_valid()) {
  53. Ref<Texture> rb = tabs[i].right_button;
  54. Size2 bms = rb->get_size();
  55. bms.width += get_constant("hseparation");
  56. ms.width += bms.width;
  57. ms.height = MAX(bms.height + tab_bg->get_minimum_size().height, ms.height);
  58. }
  59. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
  60. Ref<Texture> cb = get_icon("close");
  61. Size2 bms = cb->get_size();
  62. bms.width += get_constant("hseparation");
  63. ms.width += bms.width;
  64. ms.height = MAX(bms.height + tab_bg->get_minimum_size().height, ms.height);
  65. }
  66. }
  67. ms.width = 0; //TODO: should make this optional
  68. return ms;
  69. }
  70. void Tabs::_gui_input(const Ref<InputEvent> &p_event) {
  71. Ref<InputEventMouseMotion> mm = p_event;
  72. if (mm.is_valid()) {
  73. Point2 pos = mm->get_position();
  74. highlight_arrow = -1;
  75. if (buttons_visible) {
  76. Ref<Texture> incr = get_icon("increment");
  77. Ref<Texture> decr = get_icon("decrement");
  78. int limit = get_size().width - incr->get_width() - decr->get_width();
  79. if (pos.x > limit + decr->get_width()) {
  80. highlight_arrow = 1;
  81. } else if (pos.x > limit) {
  82. highlight_arrow = 0;
  83. }
  84. }
  85. // test hovering to display right or close button
  86. int hover_now = -1;
  87. int hover_buttons = -1;
  88. for (int i = 0; i < tabs.size(); i++) {
  89. if (i < offset)
  90. continue;
  91. Rect2 rect = get_tab_rect(i);
  92. if (rect.has_point(pos)) {
  93. hover_now = i;
  94. }
  95. if (tabs[i].rb_rect.has_point(pos)) {
  96. rb_hover = i;
  97. cb_hover = -1;
  98. hover_buttons = i;
  99. break;
  100. } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) {
  101. cb_hover = i;
  102. rb_hover = -1;
  103. hover_buttons = i;
  104. break;
  105. }
  106. }
  107. if (hover != hover_now) {
  108. hover = hover_now;
  109. emit_signal("tab_hover", hover);
  110. }
  111. if (hover_buttons == -1) { // no hover
  112. rb_hover = hover_buttons;
  113. cb_hover = hover_buttons;
  114. }
  115. update();
  116. return;
  117. }
  118. Ref<InputEventMouseButton> mb = p_event;
  119. if (rb_pressing && mb.is_valid() &&
  120. !mb->is_pressed() &&
  121. mb->get_button_index() == BUTTON_LEFT) {
  122. if (rb_hover != -1) {
  123. //pressed
  124. emit_signal("right_button_pressed", rb_hover);
  125. }
  126. rb_pressing = false;
  127. update();
  128. }
  129. if (cb_pressing && mb.is_valid() &&
  130. !mb->is_pressed() &&
  131. mb->get_button_index() == BUTTON_LEFT) {
  132. if (cb_hover != -1) {
  133. //pressed
  134. emit_signal("tab_close", cb_hover);
  135. }
  136. cb_pressing = false;
  137. update();
  138. }
  139. if (mb.is_valid() &&
  140. mb->is_pressed() &&
  141. mb->get_button_index() == BUTTON_LEFT) {
  142. // clicks
  143. Point2 pos(mb->get_position().x, mb->get_position().y);
  144. if (buttons_visible) {
  145. Ref<Texture> incr = get_icon("increment");
  146. Ref<Texture> decr = get_icon("decrement");
  147. int limit = get_size().width - incr->get_width() - decr->get_width();
  148. if (pos.x > limit + decr->get_width()) {
  149. if (missing_right) {
  150. offset++;
  151. update();
  152. }
  153. return;
  154. } else if (pos.x > limit) {
  155. if (offset > 0) {
  156. offset--;
  157. update();
  158. }
  159. return;
  160. }
  161. }
  162. int found = -1;
  163. for (int i = 0; i < tabs.size(); i++) {
  164. if (i < offset)
  165. continue;
  166. if (tabs[i].rb_rect.has_point(pos)) {
  167. rb_pressing = true;
  168. update();
  169. return;
  170. }
  171. if (tabs[i].cb_rect.has_point(pos)) {
  172. cb_pressing = true;
  173. update();
  174. return;
  175. }
  176. if (pos.x >= tabs[i].ofs_cache && pos.x < tabs[i].ofs_cache + tabs[i].size_cache) {
  177. if (!tabs[i].disabled) {
  178. found = i;
  179. }
  180. break;
  181. }
  182. }
  183. if (found != -1) {
  184. set_current_tab(found);
  185. emit_signal("tab_clicked", found);
  186. }
  187. }
  188. }
  189. void Tabs::_notification(int p_what) {
  190. switch (p_what) {
  191. case NOTIFICATION_MOUSE_EXIT: {
  192. rb_hover = -1;
  193. cb_hover = -1;
  194. hover = -1;
  195. update();
  196. } break;
  197. case NOTIFICATION_RESIZED: {
  198. _update_cache();
  199. _ensure_no_over_offset();
  200. ensure_tab_visible(current);
  201. } break;
  202. case NOTIFICATION_DRAW: {
  203. _update_cache();
  204. RID ci = get_canvas_item();
  205. Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
  206. Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
  207. Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
  208. Ref<Font> font = get_font("font");
  209. Color color_fg = get_color("font_color_fg");
  210. Color color_bg = get_color("font_color_bg");
  211. Color color_disabled = get_color("font_color_disabled");
  212. Ref<Texture> close = get_icon("close");
  213. int h = get_size().height;
  214. int w = 0;
  215. int mw = 0;
  216. for (int i = 0; i < tabs.size(); i++) {
  217. tabs[i].ofs_cache = mw;
  218. mw += get_tab_width(i);
  219. }
  220. if (tab_align == ALIGN_CENTER) {
  221. w = (get_size().width - mw) / 2;
  222. } else if (tab_align == ALIGN_RIGHT) {
  223. w = get_size().width - mw;
  224. }
  225. if (w < 0) {
  226. w = 0;
  227. }
  228. Ref<Texture> incr = get_icon("increment");
  229. Ref<Texture> decr = get_icon("decrement");
  230. Ref<Texture> incr_hl = get_icon("increment_highlight");
  231. Ref<Texture> decr_hl = get_icon("decrement_highlight");
  232. int limit = get_size().width - incr->get_size().width - decr->get_size().width;
  233. missing_right = false;
  234. for (int i = 0; i < tabs.size(); i++) {
  235. if (i < offset)
  236. continue;
  237. tabs[i].ofs_cache = w;
  238. int lsize = tabs[i].size_cache;
  239. Ref<StyleBox> sb;
  240. Color col;
  241. if (tabs[i].disabled) {
  242. sb = tab_disabled;
  243. col = color_disabled;
  244. } else if (i == current) {
  245. sb = tab_fg;
  246. col = color_fg;
  247. } else {
  248. sb = tab_bg;
  249. col = color_bg;
  250. }
  251. if (w + lsize > limit) {
  252. max_drawn_tab = i - 1;
  253. missing_right = true;
  254. break;
  255. } else {
  256. max_drawn_tab = i;
  257. }
  258. Rect2 sb_rect = Rect2(w, 0, tabs[i].size_cache, h);
  259. sb->draw(ci, sb_rect);
  260. w += sb->get_margin(MARGIN_LEFT);
  261. Size2i sb_ms = sb->get_minimum_size();
  262. Ref<Texture> icon = tabs[i].icon;
  263. if (icon.is_valid()) {
  264. icon->draw(ci, Point2i(w, sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
  265. if (tabs[i].text != "")
  266. w += icon->get_width() + get_constant("hseparation");
  267. }
  268. 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);
  269. w += tabs[i].size_text;
  270. if (tabs[i].right_button.is_valid()) {
  271. Ref<StyleBox> style = get_stylebox("button");
  272. Ref<Texture> rb = tabs[i].right_button;
  273. w += get_constant("hseparation");
  274. Rect2 rb_rect;
  275. rb_rect.size = style->get_minimum_size() + rb->get_size();
  276. rb_rect.position.x = w;
  277. rb_rect.position.y = sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
  278. if (rb_hover == i) {
  279. if (rb_pressing)
  280. get_stylebox("button_pressed")->draw(ci, rb_rect);
  281. else
  282. style->draw(ci, rb_rect);
  283. }
  284. rb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), rb_rect.position.y + style->get_margin(MARGIN_TOP)));
  285. w += rb->get_width();
  286. tabs[i].rb_rect = rb_rect;
  287. }
  288. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
  289. Ref<StyleBox> style = get_stylebox("button");
  290. Ref<Texture> cb = close;
  291. w += get_constant("hseparation");
  292. Rect2 cb_rect;
  293. cb_rect.size = style->get_minimum_size() + cb->get_size();
  294. cb_rect.position.x = w;
  295. cb_rect.position.y = sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
  296. if (!tabs[i].disabled && cb_hover == i) {
  297. if (cb_pressing)
  298. get_stylebox("button_pressed")->draw(ci, cb_rect);
  299. else
  300. style->draw(ci, cb_rect);
  301. }
  302. cb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), cb_rect.position.y + style->get_margin(MARGIN_TOP)));
  303. w += cb->get_width();
  304. tabs[i].cb_rect = cb_rect;
  305. }
  306. w += sb->get_margin(MARGIN_RIGHT);
  307. }
  308. if (offset > 0 || missing_right) {
  309. int vofs = (get_size().height - incr->get_size().height) / 2;
  310. if (offset > 0)
  311. draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit, vofs));
  312. else
  313. draw_texture(decr, Point2(limit, vofs), Color(1, 1, 1, 0.5));
  314. if (missing_right)
  315. draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit + decr->get_size().width, vofs));
  316. else
  317. draw_texture(incr, Point2(limit + decr->get_size().width, vofs), Color(1, 1, 1, 0.5));
  318. buttons_visible = true;
  319. } else {
  320. buttons_visible = false;
  321. }
  322. } break;
  323. }
  324. }
  325. int Tabs::get_tab_count() const {
  326. return tabs.size();
  327. }
  328. void Tabs::set_current_tab(int p_current) {
  329. if (current == p_current) return;
  330. ERR_FAIL_INDEX(p_current, get_tab_count());
  331. current = p_current;
  332. _change_notify("current_tab");
  333. _update_cache();
  334. update();
  335. emit_signal("tab_changed", p_current);
  336. }
  337. int Tabs::get_current_tab() const {
  338. return current;
  339. }
  340. int Tabs::get_hovered_tab() const {
  341. return hover;
  342. }
  343. void Tabs::set_tab_title(int p_tab, const String &p_title) {
  344. ERR_FAIL_INDEX(p_tab, tabs.size());
  345. tabs[p_tab].text = p_title;
  346. update();
  347. minimum_size_changed();
  348. }
  349. String Tabs::get_tab_title(int p_tab) const {
  350. ERR_FAIL_INDEX_V(p_tab, tabs.size(), "");
  351. return tabs[p_tab].text;
  352. }
  353. void Tabs::set_tab_icon(int p_tab, const Ref<Texture> &p_icon) {
  354. ERR_FAIL_INDEX(p_tab, tabs.size());
  355. tabs[p_tab].icon = p_icon;
  356. update();
  357. minimum_size_changed();
  358. }
  359. Ref<Texture> Tabs::get_tab_icon(int p_tab) const {
  360. ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture>());
  361. return tabs[p_tab].icon;
  362. }
  363. void Tabs::set_tab_disabled(int p_tab, bool p_disabled) {
  364. ERR_FAIL_INDEX(p_tab, tabs.size());
  365. tabs[p_tab].disabled = p_disabled;
  366. update();
  367. }
  368. bool Tabs::get_tab_disabled(int p_tab) const {
  369. ERR_FAIL_INDEX_V(p_tab, tabs.size(), false);
  370. return tabs[p_tab].disabled;
  371. }
  372. void Tabs::set_tab_right_button(int p_tab, const Ref<Texture> &p_right_button) {
  373. ERR_FAIL_INDEX(p_tab, tabs.size());
  374. tabs[p_tab].right_button = p_right_button;
  375. update();
  376. minimum_size_changed();
  377. }
  378. Ref<Texture> Tabs::get_tab_right_button(int p_tab) const {
  379. ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture>());
  380. return tabs[p_tab].right_button;
  381. }
  382. void Tabs::_update_cache() {
  383. Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
  384. Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
  385. Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
  386. Ref<Font> font = get_font("font");
  387. Ref<Texture> incr = get_icon("increment");
  388. Ref<Texture> decr = get_icon("decrement");
  389. int limit = get_size().width - incr->get_width() - decr->get_width();
  390. int w = 0;
  391. int mw = 0;
  392. int size_fixed = 0;
  393. int count_resize = 0;
  394. for (int i = 0; i < tabs.size(); i++) {
  395. tabs[i].ofs_cache = mw;
  396. tabs[i].size_cache = get_tab_width(i);
  397. tabs[i].size_text = font->get_string_size(tabs[i].text).width;
  398. mw += tabs[i].size_cache;
  399. if (tabs[i].size_cache <= min_width || i == current) {
  400. size_fixed += tabs[i].size_cache;
  401. } else {
  402. count_resize++;
  403. }
  404. }
  405. int m_width = min_width;
  406. if (count_resize > 0) {
  407. m_width = MAX((limit - size_fixed) / count_resize, min_width);
  408. }
  409. for (int i = 0; i < tabs.size(); i++) {
  410. if (i < offset)
  411. continue;
  412. Ref<StyleBox> sb;
  413. if (tabs[i].disabled) {
  414. sb = tab_disabled;
  415. } else if (i == current) {
  416. sb = tab_fg;
  417. } else {
  418. sb = tab_bg;
  419. }
  420. int lsize = tabs[i].size_cache;
  421. int slen = tabs[i].size_text;
  422. if (min_width > 0 && mw > limit && i != current) {
  423. if (lsize > m_width) {
  424. slen = m_width - (sb->get_margin(MARGIN_LEFT) + sb->get_margin(MARGIN_RIGHT));
  425. if (tabs[i].icon.is_valid()) {
  426. slen -= tabs[i].icon->get_width();
  427. slen -= get_constant("hseparation");
  428. }
  429. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
  430. Ref<Texture> cb = get_icon("close");
  431. slen -= cb->get_width();
  432. slen -= get_constant("hseparation");
  433. }
  434. slen = MAX(slen, 1);
  435. lsize = m_width;
  436. }
  437. }
  438. tabs[i].ofs_cache = w;
  439. tabs[i].size_cache = lsize;
  440. tabs[i].size_text = slen;
  441. w += lsize;
  442. }
  443. }
  444. void Tabs::add_tab(const String &p_str, const Ref<Texture> &p_icon) {
  445. Tab t;
  446. t.text = p_str;
  447. t.icon = p_icon;
  448. t.disabled = false;
  449. t.ofs_cache = 0;
  450. t.size_cache = 0;
  451. tabs.push_back(t);
  452. _update_cache();
  453. update();
  454. minimum_size_changed();
  455. }
  456. void Tabs::clear_tabs() {
  457. tabs.clear();
  458. current = 0;
  459. update();
  460. }
  461. void Tabs::remove_tab(int p_idx) {
  462. ERR_FAIL_INDEX(p_idx, tabs.size());
  463. tabs.remove(p_idx);
  464. if (current >= p_idx)
  465. current--;
  466. _update_cache();
  467. update();
  468. minimum_size_changed();
  469. if (current < 0)
  470. current = 0;
  471. if (current >= tabs.size())
  472. current = tabs.size() - 1;
  473. _ensure_no_over_offset();
  474. }
  475. Variant Tabs::get_drag_data(const Point2 &p_point) {
  476. return get_tab_idx_at_point(p_point);
  477. }
  478. bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
  479. return get_tab_idx_at_point(p_point) > -1;
  480. }
  481. void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) {
  482. int hover_now = get_tab_idx_at_point(p_point);
  483. ERR_FAIL_INDEX(hover_now, tabs.size());
  484. emit_signal("reposition_active_tab_request", hover_now);
  485. }
  486. int Tabs::get_tab_idx_at_point(const Point2 &p_point) const {
  487. int hover_now = -1;
  488. for (int i = 0; i < tabs.size(); i++) {
  489. if (i < offset)
  490. continue;
  491. Rect2 rect = get_tab_rect(i);
  492. if (rect.has_point(p_point)) {
  493. hover_now = i;
  494. }
  495. }
  496. return hover_now;
  497. }
  498. void Tabs::set_tab_align(TabAlign p_align) {
  499. ERR_FAIL_INDEX(p_align, ALIGN_MAX);
  500. tab_align = p_align;
  501. update();
  502. }
  503. Tabs::TabAlign Tabs::get_tab_align() const {
  504. return tab_align;
  505. }
  506. void Tabs::move_tab(int from, int to) {
  507. if (from == to)
  508. return;
  509. ERR_FAIL_INDEX(from, tabs.size());
  510. ERR_FAIL_INDEX(to, tabs.size());
  511. Tab tab_from = tabs[from];
  512. tabs.remove(from);
  513. tabs.insert(to, tab_from);
  514. _update_cache();
  515. update();
  516. }
  517. int Tabs::get_tab_width(int p_idx) const {
  518. ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0);
  519. Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
  520. Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
  521. Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
  522. Ref<Font> font = get_font("font");
  523. int x = 0;
  524. Ref<Texture> tex = tabs[p_idx].icon;
  525. if (tex.is_valid()) {
  526. x += tex->get_width();
  527. if (tabs[p_idx].text != "")
  528. x += get_constant("hseparation");
  529. }
  530. x += font->get_string_size(tabs[p_idx].text).width;
  531. if (tabs[p_idx].disabled)
  532. x += tab_disabled->get_minimum_size().width;
  533. else if (current == p_idx)
  534. x += tab_fg->get_minimum_size().width;
  535. else
  536. x += tab_bg->get_minimum_size().width;
  537. if (tabs[p_idx].right_button.is_valid()) {
  538. Ref<Texture> rb = tabs[p_idx].right_button;
  539. x += rb->get_width();
  540. x += get_constant("hseparation");
  541. }
  542. if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current)) {
  543. Ref<Texture> cb = get_icon("close");
  544. x += cb->get_width();
  545. x += get_constant("hseparation");
  546. }
  547. return x;
  548. }
  549. void Tabs::_ensure_no_over_offset() {
  550. if (!is_inside_tree())
  551. return;
  552. Ref<Texture> incr = get_icon("increment");
  553. Ref<Texture> decr = get_icon("decrement");
  554. int limit = get_size().width - incr->get_width() - decr->get_width();
  555. while (offset > 0) {
  556. int total_w = 0;
  557. for (int i = 0; i < tabs.size(); i++) {
  558. if (i < offset - 1)
  559. continue;
  560. total_w += tabs[i].size_cache;
  561. }
  562. if (total_w < limit) {
  563. offset--;
  564. update();
  565. } else {
  566. break;
  567. }
  568. }
  569. }
  570. void Tabs::ensure_tab_visible(int p_idx) {
  571. if (!is_inside_tree())
  572. return;
  573. if (tabs.size() == 0) return;
  574. ERR_FAIL_INDEX(p_idx, tabs.size());
  575. if (p_idx == offset) {
  576. return;
  577. }
  578. if (p_idx < offset) {
  579. offset = p_idx;
  580. update();
  581. return;
  582. }
  583. int prev_offset = offset;
  584. Ref<Texture> incr = get_icon("increment");
  585. Ref<Texture> decr = get_icon("decrement");
  586. int limit = get_size().width - incr->get_width() - decr->get_width();
  587. for (int i = offset; i <= p_idx; i++) {
  588. if (tabs[i].ofs_cache + tabs[i].size_cache > limit) {
  589. offset++;
  590. }
  591. }
  592. if (prev_offset != offset) {
  593. update();
  594. }
  595. }
  596. Rect2 Tabs::get_tab_rect(int p_tab) const {
  597. return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height);
  598. }
  599. void Tabs::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
  600. ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX);
  601. cb_displaypolicy = p_policy;
  602. update();
  603. }
  604. Tabs::CloseButtonDisplayPolicy Tabs::get_tab_close_display_policy() const {
  605. return cb_displaypolicy;
  606. }
  607. void Tabs::set_min_width(int p_width) {
  608. min_width = p_width;
  609. }
  610. void Tabs::_bind_methods() {
  611. ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input);
  612. ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count);
  613. ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab);
  614. ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab);
  615. ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &Tabs::set_tab_title);
  616. ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title);
  617. ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon);
  618. ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &Tabs::get_tab_icon);
  619. ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled);
  620. ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &Tabs::get_tab_disabled);
  621. ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &Tabs::remove_tab);
  622. ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &Tabs::add_tab, DEFVAL(""), DEFVAL(Ref<Texture>()));
  623. ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &Tabs::set_tab_align);
  624. ClassDB::bind_method(D_METHOD("get_tab_align"), &Tabs::get_tab_align);
  625. ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &Tabs::ensure_tab_visible);
  626. ClassDB::bind_method(D_METHOD("get_tab_rect", "tab_idx"), &Tabs::get_tab_rect);
  627. ClassDB::bind_method(D_METHOD("move_tab", "from", "to"), &Tabs::move_tab);
  628. ClassDB::bind_method(D_METHOD("set_tab_close_display_policy", "policy"), &Tabs::set_tab_close_display_policy);
  629. ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy);
  630. ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
  631. ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab")));
  632. ADD_SIGNAL(MethodInfo("tab_close", PropertyInfo(Variant::INT, "tab")));
  633. ADD_SIGNAL(MethodInfo("tab_hover", PropertyInfo(Variant::INT, "tab")));
  634. ADD_SIGNAL(MethodInfo("reposition_active_tab_request", PropertyInfo(Variant::INT, "idx_to")));
  635. ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
  636. ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
  637. ADD_PROPERTYNZ(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");
  638. BIND_ENUM_CONSTANT(ALIGN_LEFT);
  639. BIND_ENUM_CONSTANT(ALIGN_CENTER);
  640. BIND_ENUM_CONSTANT(ALIGN_RIGHT);
  641. BIND_ENUM_CONSTANT(ALIGN_MAX);
  642. BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER);
  643. BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ACTIVE_ONLY);
  644. BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ALWAYS);
  645. BIND_ENUM_CONSTANT(CLOSE_BUTTON_MAX);
  646. }
  647. Tabs::Tabs() {
  648. current = 0;
  649. tab_align = ALIGN_CENTER;
  650. rb_hover = -1;
  651. rb_pressing = false;
  652. highlight_arrow = -1;
  653. cb_hover = -1;
  654. cb_pressing = false;
  655. cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
  656. offset = 0;
  657. max_drawn_tab = 0;
  658. min_width = 0;
  659. }