1 | /**************************************************************************/ |
2 | /* tab_bar.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 | |
31 | #include "tab_bar.h" |
32 | |
33 | #include "core/object/message_queue.h" |
34 | #include "core/string/translation.h" |
35 | #include "scene/gui/box_container.h" |
36 | #include "scene/gui/label.h" |
37 | #include "scene/gui/texture_rect.h" |
38 | #include "scene/main/viewport.h" |
39 | #include "scene/theme/theme_db.h" |
40 | |
41 | Size2 TabBar::get_minimum_size() const { |
42 | Size2 ms; |
43 | |
44 | if (tabs.is_empty()) { |
45 | return ms; |
46 | } |
47 | |
48 | int y_margin = MAX(MAX(MAX(theme_cache.tab_unselected_style->get_minimum_size().height, theme_cache.tab_hovered_style->get_minimum_size().height), theme_cache.tab_selected_style->get_minimum_size().height), theme_cache.tab_disabled_style->get_minimum_size().height); |
49 | |
50 | for (int i = 0; i < tabs.size(); i++) { |
51 | if (tabs[i].hidden) { |
52 | continue; |
53 | } |
54 | |
55 | int ofs = ms.width; |
56 | |
57 | Ref<StyleBox> style; |
58 | if (tabs[i].disabled) { |
59 | style = theme_cache.tab_disabled_style; |
60 | } else if (current == i) { |
61 | style = theme_cache.tab_selected_style; |
62 | } else if (hover == i) { |
63 | style = theme_cache.tab_hovered_style; |
64 | } else { |
65 | style = theme_cache.tab_unselected_style; |
66 | } |
67 | ms.width += style->get_minimum_size().width; |
68 | |
69 | if (tabs[i].icon.is_valid()) { |
70 | const Size2 icon_size = _get_tab_icon_size(i); |
71 | ms.height = MAX(ms.height, icon_size.height + y_margin); |
72 | ms.width += icon_size.width + theme_cache.h_separation; |
73 | } |
74 | |
75 | if (!tabs[i].text.is_empty()) { |
76 | ms.width += tabs[i].size_text + theme_cache.h_separation; |
77 | } |
78 | ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin); |
79 | |
80 | bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current); |
81 | |
82 | if (tabs[i].right_button.is_valid()) { |
83 | Ref<Texture2D> rb = tabs[i].right_button; |
84 | |
85 | if (close_visible) { |
86 | ms.width += theme_cache.button_hl_style->get_minimum_size().width + rb->get_width(); |
87 | } else { |
88 | ms.width += theme_cache.button_hl_style->get_margin(SIDE_LEFT) + rb->get_width() + theme_cache.h_separation; |
89 | } |
90 | |
91 | ms.height = MAX(ms.height, rb->get_height() + y_margin); |
92 | } |
93 | |
94 | if (close_visible) { |
95 | ms.width += theme_cache.button_hl_style->get_margin(SIDE_LEFT) + theme_cache.close_icon->get_width() + theme_cache.h_separation; |
96 | |
97 | ms.height = MAX(ms.height, theme_cache.close_icon->get_height() + y_margin); |
98 | } |
99 | |
100 | if (ms.width - ofs > style->get_minimum_size().width) { |
101 | ms.width -= theme_cache.h_separation; |
102 | } |
103 | } |
104 | |
105 | if (clip_tabs) { |
106 | ms.width = 0; |
107 | } |
108 | |
109 | return ms; |
110 | } |
111 | |
112 | void TabBar::gui_input(const Ref<InputEvent> &p_event) { |
113 | ERR_FAIL_COND(p_event.is_null()); |
114 | |
115 | Ref<InputEventMouseMotion> mm = p_event; |
116 | |
117 | if (mm.is_valid()) { |
118 | Point2 pos = mm->get_position(); |
119 | |
120 | if (buttons_visible) { |
121 | if (is_layout_rtl()) { |
122 | if (pos.x < theme_cache.decrement_icon->get_width()) { |
123 | if (highlight_arrow != 1) { |
124 | highlight_arrow = 1; |
125 | queue_redraw(); |
126 | } |
127 | } else if (pos.x < theme_cache.increment_icon->get_width() + theme_cache.decrement_icon->get_width()) { |
128 | if (highlight_arrow != 0) { |
129 | highlight_arrow = 0; |
130 | queue_redraw(); |
131 | } |
132 | } else if (highlight_arrow != -1) { |
133 | highlight_arrow = -1; |
134 | queue_redraw(); |
135 | } |
136 | } else { |
137 | int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); |
138 | if (pos.x > limit_minus_buttons + theme_cache.decrement_icon->get_width()) { |
139 | if (highlight_arrow != 1) { |
140 | highlight_arrow = 1; |
141 | queue_redraw(); |
142 | } |
143 | } else if (pos.x > limit_minus_buttons) { |
144 | if (highlight_arrow != 0) { |
145 | highlight_arrow = 0; |
146 | queue_redraw(); |
147 | } |
148 | } else if (highlight_arrow != -1) { |
149 | highlight_arrow = -1; |
150 | queue_redraw(); |
151 | } |
152 | } |
153 | } |
154 | |
155 | if (get_viewport()->gui_is_dragging() && can_drop_data(pos, get_viewport()->gui_get_drag_data())) { |
156 | dragging_valid_tab = true; |
157 | queue_redraw(); |
158 | } |
159 | |
160 | if (!tabs.is_empty()) { |
161 | _update_hover(); |
162 | } |
163 | |
164 | return; |
165 | } |
166 | |
167 | Ref<InputEventMouseButton> mb = p_event; |
168 | |
169 | if (mb.is_valid()) { |
170 | if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_or_control_pressed()) { |
171 | if (scrolling_enabled && buttons_visible) { |
172 | if (offset > 0) { |
173 | offset--; |
174 | _update_cache(); |
175 | queue_redraw(); |
176 | } |
177 | } |
178 | } |
179 | |
180 | if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_or_control_pressed()) { |
181 | if (scrolling_enabled && buttons_visible) { |
182 | if (missing_right && offset < tabs.size()) { |
183 | offset++; |
184 | _update_cache(); |
185 | queue_redraw(); |
186 | } |
187 | } |
188 | } |
189 | |
190 | if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { |
191 | if (rb_hover != -1) { |
192 | emit_signal(SNAME("tab_button_pressed" ), rb_hover); |
193 | } |
194 | |
195 | rb_pressing = false; |
196 | queue_redraw(); |
197 | } |
198 | |
199 | if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { |
200 | if (cb_hover != -1) { |
201 | emit_signal(SNAME("tab_close_pressed" ), cb_hover); |
202 | } |
203 | |
204 | cb_pressing = false; |
205 | queue_redraw(); |
206 | } |
207 | |
208 | if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) { |
209 | Point2 pos = mb->get_position(); |
210 | |
211 | if (buttons_visible) { |
212 | if (is_layout_rtl()) { |
213 | if (pos.x < theme_cache.decrement_icon->get_width()) { |
214 | if (missing_right) { |
215 | offset++; |
216 | _update_cache(); |
217 | queue_redraw(); |
218 | } |
219 | return; |
220 | } else if (pos.x < theme_cache.increment_icon->get_width() + theme_cache.decrement_icon->get_width()) { |
221 | if (offset > 0) { |
222 | offset--; |
223 | _update_cache(); |
224 | queue_redraw(); |
225 | } |
226 | return; |
227 | } |
228 | } else { |
229 | int limit = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); |
230 | if (pos.x > limit + theme_cache.decrement_icon->get_width()) { |
231 | if (missing_right) { |
232 | offset++; |
233 | _update_cache(); |
234 | queue_redraw(); |
235 | } |
236 | return; |
237 | } else if (pos.x > limit) { |
238 | if (offset > 0) { |
239 | offset--; |
240 | _update_cache(); |
241 | queue_redraw(); |
242 | } |
243 | return; |
244 | } |
245 | } |
246 | } |
247 | |
248 | if (tabs.is_empty()) { |
249 | // Return early if there are no actual tabs to handle input for. |
250 | return; |
251 | } |
252 | |
253 | int found = -1; |
254 | for (int i = offset; i <= max_drawn_tab; i++) { |
255 | if (tabs[i].hidden) { |
256 | continue; |
257 | } |
258 | |
259 | if (tabs[i].rb_rect.has_point(pos)) { |
260 | rb_pressing = true; |
261 | _update_hover(); |
262 | queue_redraw(); |
263 | return; |
264 | } |
265 | |
266 | if (tabs[i].cb_rect.has_point(pos) && (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current))) { |
267 | cb_pressing = true; |
268 | _update_hover(); |
269 | queue_redraw(); |
270 | return; |
271 | } |
272 | |
273 | if (pos.x >= get_tab_rect(i).position.x && pos.x < get_tab_rect(i).position.x + tabs[i].size_cache) { |
274 | if (!tabs[i].disabled) { |
275 | found = i; |
276 | } |
277 | break; |
278 | } |
279 | } |
280 | |
281 | if (found != -1) { |
282 | set_current_tab(found); |
283 | |
284 | if (mb->get_button_index() == MouseButton::RIGHT) { |
285 | // Right mouse button clicked. |
286 | emit_signal(SNAME("tab_rmb_clicked" ), found); |
287 | } |
288 | |
289 | emit_signal(SNAME("tab_clicked" ), found); |
290 | } |
291 | } |
292 | } |
293 | } |
294 | |
295 | void TabBar::_shape(int p_tab) { |
296 | tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); |
297 | tabs.write[p_tab].text_buf->clear(); |
298 | tabs.write[p_tab].text_buf->set_width(-1); |
299 | if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { |
300 | tabs.write[p_tab].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); |
301 | } else { |
302 | tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); |
303 | } |
304 | |
305 | tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, theme_cache.font, theme_cache.font_size, tabs[p_tab].language); |
306 | } |
307 | |
308 | void TabBar::_notification(int p_what) { |
309 | switch (p_what) { |
310 | case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { |
311 | queue_redraw(); |
312 | } break; |
313 | |
314 | case NOTIFICATION_THEME_CHANGED: |
315 | case NOTIFICATION_TRANSLATION_CHANGED: { |
316 | for (int i = 0; i < tabs.size(); ++i) { |
317 | _shape(i); |
318 | } |
319 | |
320 | queue_redraw(); |
321 | |
322 | [[fallthrough]]; |
323 | } |
324 | case NOTIFICATION_RESIZED: { |
325 | int ofs_old = offset; |
326 | int max_old = max_drawn_tab; |
327 | |
328 | _update_cache(); |
329 | _ensure_no_over_offset(); |
330 | |
331 | if (scroll_to_selected && (offset != ofs_old || max_drawn_tab != max_old)) { |
332 | ensure_tab_visible(current); |
333 | } |
334 | } break; |
335 | |
336 | case NOTIFICATION_DRAG_END: { |
337 | if (dragging_valid_tab) { |
338 | dragging_valid_tab = false; |
339 | queue_redraw(); |
340 | } |
341 | } break; |
342 | |
343 | case NOTIFICATION_DRAW: { |
344 | bool rtl = is_layout_rtl(); |
345 | Vector2 size = get_size(); |
346 | |
347 | if (tabs.is_empty()) { |
348 | // Draw the drop indicator where the first tab would be if there are no tabs. |
349 | if (dragging_valid_tab) { |
350 | int x = rtl ? size.x : 0; |
351 | theme_cache.drop_mark_icon->draw(get_canvas_item(), Point2(x - (theme_cache.drop_mark_icon->get_width() / 2), (size.height - theme_cache.drop_mark_icon->get_height()) / 2), theme_cache.drop_mark_color); |
352 | } |
353 | |
354 | return; |
355 | } |
356 | |
357 | int limit_minus_buttons = size.width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); |
358 | |
359 | int ofs = tabs[offset].ofs_cache; |
360 | |
361 | // Draw unselected tabs in the back. |
362 | for (int i = offset; i <= max_drawn_tab; i++) { |
363 | if (tabs[i].hidden) { |
364 | continue; |
365 | } |
366 | |
367 | if (i != current) { |
368 | Ref<StyleBox> sb; |
369 | Color col; |
370 | |
371 | if (tabs[i].disabled) { |
372 | sb = theme_cache.tab_disabled_style; |
373 | col = theme_cache.font_disabled_color; |
374 | } else if (i == hover) { |
375 | sb = theme_cache.tab_hovered_style; |
376 | col = theme_cache.font_hovered_color; |
377 | } else { |
378 | sb = theme_cache.tab_unselected_style; |
379 | col = theme_cache.font_unselected_color; |
380 | } |
381 | |
382 | _draw_tab(sb, col, i, rtl ? size.width - ofs - tabs[i].size_cache : ofs); |
383 | } |
384 | |
385 | ofs += tabs[i].size_cache; |
386 | } |
387 | |
388 | // Draw selected tab in the front, but only if it's visible. |
389 | if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) { |
390 | Ref<StyleBox> sb = tabs[current].disabled ? theme_cache.tab_disabled_style : theme_cache.tab_selected_style; |
391 | float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache; |
392 | |
393 | _draw_tab(sb, theme_cache.font_selected_color, current, x); |
394 | } |
395 | |
396 | if (buttons_visible) { |
397 | int vofs = (size.height - theme_cache.increment_icon->get_size().height) / 2; |
398 | |
399 | if (rtl) { |
400 | if (missing_right) { |
401 | draw_texture(highlight_arrow == 1 ? theme_cache.decrement_hl_icon : theme_cache.decrement_icon, Point2(0, vofs)); |
402 | } else { |
403 | draw_texture(theme_cache.decrement_icon, Point2(0, vofs), Color(1, 1, 1, 0.5)); |
404 | } |
405 | |
406 | if (offset > 0) { |
407 | draw_texture(highlight_arrow == 0 ? theme_cache.increment_hl_icon : theme_cache.increment_icon, Point2(theme_cache.increment_icon->get_size().width, vofs)); |
408 | } else { |
409 | draw_texture(theme_cache.increment_icon, Point2(theme_cache.increment_icon->get_size().width, vofs), Color(1, 1, 1, 0.5)); |
410 | } |
411 | } else { |
412 | if (offset > 0) { |
413 | draw_texture(highlight_arrow == 0 ? theme_cache.decrement_hl_icon : theme_cache.decrement_icon, Point2(limit_minus_buttons, vofs)); |
414 | } else { |
415 | draw_texture(theme_cache.decrement_icon, Point2(limit_minus_buttons, vofs), Color(1, 1, 1, 0.5)); |
416 | } |
417 | |
418 | if (missing_right) { |
419 | draw_texture(highlight_arrow == 1 ? theme_cache.increment_hl_icon : theme_cache.increment_icon, Point2(limit_minus_buttons + theme_cache.decrement_icon->get_size().width, vofs)); |
420 | } else { |
421 | draw_texture(theme_cache.increment_icon, Point2(limit_minus_buttons + theme_cache.decrement_icon->get_size().width, vofs), Color(1, 1, 1, 0.5)); |
422 | } |
423 | } |
424 | } |
425 | |
426 | if (dragging_valid_tab) { |
427 | int x; |
428 | |
429 | int tab_hover = get_hovered_tab(); |
430 | if (tab_hover != -1) { |
431 | Rect2 tab_rect = get_tab_rect(tab_hover); |
432 | |
433 | x = tab_rect.position.x; |
434 | if (get_local_mouse_position().x > x + tab_rect.size.width / 2) { |
435 | x += tab_rect.size.width; |
436 | } |
437 | } else { |
438 | if (rtl ^ (get_local_mouse_position().x < get_tab_rect(0).position.x)) { |
439 | x = get_tab_rect(0).position.x; |
440 | if (rtl) { |
441 | x += get_tab_rect(0).size.width; |
442 | } |
443 | } else { |
444 | Rect2 tab_rect = get_tab_rect(get_tab_count() - 1); |
445 | |
446 | x = tab_rect.position.x; |
447 | if (!rtl) { |
448 | x += tab_rect.size.width; |
449 | } |
450 | } |
451 | } |
452 | |
453 | theme_cache.drop_mark_icon->draw(get_canvas_item(), Point2(x - theme_cache.drop_mark_icon->get_width() / 2, (size.height - theme_cache.drop_mark_icon->get_height()) / 2), theme_cache.drop_mark_color); |
454 | } |
455 | } break; |
456 | } |
457 | } |
458 | |
459 | void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) { |
460 | RID ci = get_canvas_item(); |
461 | bool rtl = is_layout_rtl(); |
462 | |
463 | Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height); |
464 | p_tab_style->draw(ci, sb_rect); |
465 | |
466 | p_x += rtl ? tabs[p_index].size_cache - p_tab_style->get_margin(SIDE_LEFT) : p_tab_style->get_margin(SIDE_LEFT); |
467 | |
468 | Size2i sb_ms = p_tab_style->get_minimum_size(); |
469 | |
470 | // Draw the icon. |
471 | Ref<Texture2D> icon = tabs[p_index].icon; |
472 | if (icon.is_valid()) { |
473 | const Size2 icon_size = _get_tab_icon_size(p_index); |
474 | const Point2 icon_pos = Point2i(rtl ? p_x - icon_size.width : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon_size.height) / 2); |
475 | icon->draw_rect(ci, Rect2(icon_pos, icon_size)); |
476 | |
477 | p_x = rtl ? p_x - icon_size.width - theme_cache.h_separation : p_x + icon_size.width + theme_cache.h_separation; |
478 | } |
479 | |
480 | // Draw the text. |
481 | if (!tabs[p_index].text.is_empty()) { |
482 | Point2i text_pos = Point2i(rtl ? p_x - tabs[p_index].size_text : p_x, |
483 | p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[p_index].text_buf->get_size().y) / 2); |
484 | |
485 | if (theme_cache.outline_size > 0 && theme_cache.font_outline_color.a > 0) { |
486 | tabs[p_index].text_buf->draw_outline(ci, text_pos, theme_cache.outline_size, theme_cache.font_outline_color); |
487 | } |
488 | tabs[p_index].text_buf->draw(ci, text_pos, p_font_color); |
489 | |
490 | p_x = rtl ? p_x - tabs[p_index].size_text - theme_cache.h_separation : p_x + tabs[p_index].size_text + theme_cache.h_separation; |
491 | } |
492 | |
493 | // Draw and calculate rect of the right button. |
494 | if (tabs[p_index].right_button.is_valid()) { |
495 | Ref<StyleBox> style = theme_cache.button_hl_style; |
496 | Ref<Texture2D> rb = tabs[p_index].right_button; |
497 | |
498 | Rect2 rb_rect; |
499 | rb_rect.size = style->get_minimum_size() + rb->get_size(); |
500 | rb_rect.position.x = rtl ? p_x - rb_rect.size.width : p_x; |
501 | rb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2; |
502 | |
503 | tabs.write[p_index].rb_rect = rb_rect; |
504 | |
505 | if (rb_hover == p_index) { |
506 | if (rb_pressing) { |
507 | theme_cache.button_pressed_style->draw(ci, rb_rect); |
508 | } else { |
509 | style->draw(ci, rb_rect); |
510 | } |
511 | } |
512 | |
513 | rb->draw(ci, Point2i(rb_rect.position.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); |
514 | |
515 | p_x = rtl ? rb_rect.position.x : rb_rect.position.x + rb_rect.size.width; |
516 | } |
517 | |
518 | // Draw and calculate rect of the close button. |
519 | if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_index == current)) { |
520 | Ref<StyleBox> style = theme_cache.button_hl_style; |
521 | Ref<Texture2D> cb = theme_cache.close_icon; |
522 | |
523 | Rect2 cb_rect; |
524 | cb_rect.size = style->get_minimum_size() + cb->get_size(); |
525 | cb_rect.position.x = rtl ? p_x - cb_rect.size.width : p_x; |
526 | cb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2; |
527 | |
528 | tabs.write[p_index].cb_rect = cb_rect; |
529 | |
530 | if (!tabs[p_index].disabled && cb_hover == p_index) { |
531 | if (cb_pressing) { |
532 | theme_cache.button_pressed_style->draw(ci, cb_rect); |
533 | } else { |
534 | style->draw(ci, cb_rect); |
535 | } |
536 | } |
537 | |
538 | cb->draw(ci, Point2i(cb_rect.position.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); |
539 | } |
540 | } |
541 | |
542 | void TabBar::set_tab_count(int p_count) { |
543 | if (p_count == tabs.size()) { |
544 | return; |
545 | } |
546 | |
547 | ERR_FAIL_COND(p_count < 0); |
548 | tabs.resize(p_count); |
549 | |
550 | if (p_count == 0) { |
551 | offset = 0; |
552 | max_drawn_tab = 0; |
553 | current = 0; |
554 | previous = 0; |
555 | } else { |
556 | offset = MIN(offset, p_count - 1); |
557 | max_drawn_tab = MIN(max_drawn_tab, p_count - 1); |
558 | current = MIN(current, p_count - 1); |
559 | |
560 | _update_cache(); |
561 | _ensure_no_over_offset(); |
562 | if (scroll_to_selected) { |
563 | ensure_tab_visible(current); |
564 | } |
565 | } |
566 | |
567 | queue_redraw(); |
568 | update_minimum_size(); |
569 | notify_property_list_changed(); |
570 | } |
571 | |
572 | int TabBar::get_tab_count() const { |
573 | return tabs.size(); |
574 | } |
575 | |
576 | void TabBar::set_current_tab(int p_current) { |
577 | ERR_FAIL_INDEX(p_current, get_tab_count()); |
578 | |
579 | previous = current; |
580 | current = p_current; |
581 | |
582 | if (current == previous) { |
583 | emit_signal(SNAME("tab_selected" ), current); |
584 | return; |
585 | } |
586 | |
587 | emit_signal(SNAME("tab_selected" ), current); |
588 | |
589 | _update_cache(); |
590 | if (scroll_to_selected) { |
591 | ensure_tab_visible(current); |
592 | } |
593 | queue_redraw(); |
594 | |
595 | emit_signal(SNAME("tab_changed" ), p_current); |
596 | } |
597 | |
598 | int TabBar::get_current_tab() const { |
599 | return current; |
600 | } |
601 | |
602 | int TabBar::get_previous_tab() const { |
603 | return previous; |
604 | } |
605 | |
606 | int TabBar::get_hovered_tab() const { |
607 | return hover; |
608 | } |
609 | |
610 | int TabBar::get_tab_offset() const { |
611 | return offset; |
612 | } |
613 | |
614 | bool TabBar::get_offset_buttons_visible() const { |
615 | return buttons_visible; |
616 | } |
617 | |
618 | void TabBar::set_tab_title(int p_tab, const String &p_title) { |
619 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
620 | |
621 | if (tabs[p_tab].text == p_title) { |
622 | return; |
623 | } |
624 | |
625 | tabs.write[p_tab].text = p_title; |
626 | |
627 | _shape(p_tab); |
628 | _update_cache(); |
629 | _ensure_no_over_offset(); |
630 | if (scroll_to_selected) { |
631 | ensure_tab_visible(current); |
632 | } |
633 | queue_redraw(); |
634 | update_minimum_size(); |
635 | } |
636 | |
637 | String TabBar::get_tab_title(int p_tab) const { |
638 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), "" ); |
639 | return tabs[p_tab].text; |
640 | } |
641 | |
642 | void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) { |
643 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
644 | ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); |
645 | |
646 | if (tabs[p_tab].text_direction != p_text_direction) { |
647 | tabs.write[p_tab].text_direction = p_text_direction; |
648 | _shape(p_tab); |
649 | queue_redraw(); |
650 | } |
651 | } |
652 | |
653 | Control::TextDirection TabBar::get_tab_text_direction(int p_tab) const { |
654 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), Control::TEXT_DIRECTION_INHERITED); |
655 | return tabs[p_tab].text_direction; |
656 | } |
657 | |
658 | void TabBar::set_tab_language(int p_tab, const String &p_language) { |
659 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
660 | |
661 | if (tabs[p_tab].language != p_language) { |
662 | tabs.write[p_tab].language = p_language; |
663 | _shape(p_tab); |
664 | _update_cache(); |
665 | _ensure_no_over_offset(); |
666 | if (scroll_to_selected) { |
667 | ensure_tab_visible(current); |
668 | } |
669 | queue_redraw(); |
670 | update_minimum_size(); |
671 | } |
672 | } |
673 | |
674 | String TabBar::get_tab_language(int p_tab) const { |
675 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), "" ); |
676 | return tabs[p_tab].language; |
677 | } |
678 | |
679 | void TabBar::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { |
680 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
681 | |
682 | if (tabs[p_tab].icon == p_icon) { |
683 | return; |
684 | } |
685 | |
686 | tabs.write[p_tab].icon = p_icon; |
687 | |
688 | _update_cache(); |
689 | _ensure_no_over_offset(); |
690 | if (scroll_to_selected) { |
691 | ensure_tab_visible(current); |
692 | } |
693 | queue_redraw(); |
694 | update_minimum_size(); |
695 | } |
696 | |
697 | Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const { |
698 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture2D>()); |
699 | return tabs[p_tab].icon; |
700 | } |
701 | |
702 | void TabBar::set_tab_icon_max_width(int p_tab, int p_width) { |
703 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
704 | |
705 | if (tabs[p_tab].icon_max_width == p_width) { |
706 | return; |
707 | } |
708 | |
709 | tabs.write[p_tab].icon_max_width = p_width; |
710 | |
711 | _update_cache(); |
712 | _ensure_no_over_offset(); |
713 | if (scroll_to_selected) { |
714 | ensure_tab_visible(current); |
715 | } |
716 | queue_redraw(); |
717 | update_minimum_size(); |
718 | } |
719 | |
720 | int TabBar::get_tab_icon_max_width(int p_tab) const { |
721 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), 0); |
722 | return tabs[p_tab].icon_max_width; |
723 | } |
724 | |
725 | void TabBar::set_tab_disabled(int p_tab, bool p_disabled) { |
726 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
727 | |
728 | if (tabs[p_tab].disabled == p_disabled) { |
729 | return; |
730 | } |
731 | |
732 | tabs.write[p_tab].disabled = p_disabled; |
733 | |
734 | _update_cache(); |
735 | _ensure_no_over_offset(); |
736 | if (scroll_to_selected) { |
737 | ensure_tab_visible(current); |
738 | } |
739 | queue_redraw(); |
740 | update_minimum_size(); |
741 | } |
742 | |
743 | bool TabBar::is_tab_disabled(int p_tab) const { |
744 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), false); |
745 | return tabs[p_tab].disabled; |
746 | } |
747 | |
748 | void TabBar::set_tab_hidden(int p_tab, bool p_hidden) { |
749 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
750 | |
751 | if (tabs[p_tab].hidden == p_hidden) { |
752 | return; |
753 | } |
754 | |
755 | tabs.write[p_tab].hidden = p_hidden; |
756 | |
757 | _update_cache(); |
758 | _ensure_no_over_offset(); |
759 | if (scroll_to_selected) { |
760 | ensure_tab_visible(current); |
761 | } |
762 | queue_redraw(); |
763 | update_minimum_size(); |
764 | } |
765 | |
766 | bool TabBar::is_tab_hidden(int p_tab) const { |
767 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), false); |
768 | return tabs[p_tab].hidden; |
769 | } |
770 | |
771 | void TabBar::set_tab_metadata(int p_tab, const Variant &p_metadata) { |
772 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
773 | |
774 | if (tabs[p_tab].metadata == p_metadata) { |
775 | return; |
776 | } |
777 | |
778 | tabs.write[p_tab].metadata = p_metadata; |
779 | } |
780 | |
781 | Variant TabBar::get_tab_metadata(int p_tab) const { |
782 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), Variant()); |
783 | return tabs[p_tab].metadata; |
784 | } |
785 | |
786 | void TabBar::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) { |
787 | ERR_FAIL_INDEX(p_tab, tabs.size()); |
788 | |
789 | if (tabs[p_tab].right_button == p_icon) { |
790 | return; |
791 | } |
792 | |
793 | tabs.write[p_tab].right_button = p_icon; |
794 | |
795 | _update_cache(); |
796 | _ensure_no_over_offset(); |
797 | if (scroll_to_selected) { |
798 | ensure_tab_visible(current); |
799 | } |
800 | queue_redraw(); |
801 | update_minimum_size(); |
802 | } |
803 | |
804 | Ref<Texture2D> TabBar::get_tab_button_icon(int p_tab) const { |
805 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture2D>()); |
806 | return tabs[p_tab].right_button; |
807 | } |
808 | |
809 | void TabBar::_update_hover() { |
810 | if (!is_inside_tree()) { |
811 | return; |
812 | } |
813 | |
814 | ERR_FAIL_COND(tabs.is_empty()); |
815 | |
816 | const Point2 &pos = get_local_mouse_position(); |
817 | // Test hovering to display right or close button. |
818 | int hover_now = -1; |
819 | int hover_buttons = -1; |
820 | for (int i = offset; i <= max_drawn_tab; i++) { |
821 | if (tabs[i].hidden) { |
822 | continue; |
823 | } |
824 | |
825 | Rect2 rect = get_tab_rect(i); |
826 | if (rect.has_point(pos)) { |
827 | hover_now = i; |
828 | } |
829 | |
830 | if (tabs[i].rb_rect.has_point(pos)) { |
831 | rb_hover = i; |
832 | cb_hover = -1; |
833 | hover_buttons = i; |
834 | } else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) { |
835 | cb_hover = i; |
836 | rb_hover = -1; |
837 | hover_buttons = i; |
838 | } |
839 | |
840 | if (hover_buttons != -1) { |
841 | queue_redraw(); |
842 | break; |
843 | } |
844 | } |
845 | |
846 | if (hover != hover_now) { |
847 | hover = hover_now; |
848 | |
849 | if (hover != -1) { |
850 | emit_signal(SNAME("tab_hovered" ), hover); |
851 | } |
852 | |
853 | _update_cache(); |
854 | queue_redraw(); |
855 | } |
856 | |
857 | if (hover_buttons == -1) { // No hover. |
858 | int rb_hover_old = rb_hover; |
859 | int cb_hover_old = cb_hover; |
860 | |
861 | rb_hover = hover_buttons; |
862 | cb_hover = hover_buttons; |
863 | |
864 | if (rb_hover != rb_hover_old || cb_hover != cb_hover_old) { |
865 | queue_redraw(); |
866 | } |
867 | } |
868 | } |
869 | |
870 | void TabBar::_update_cache() { |
871 | if (tabs.is_empty()) { |
872 | buttons_visible = false; |
873 | return; |
874 | } |
875 | |
876 | int limit = get_size().width; |
877 | int limit_minus_buttons = limit - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); |
878 | |
879 | int w = 0; |
880 | |
881 | max_drawn_tab = tabs.size() - 1; |
882 | |
883 | for (int i = 0; i < tabs.size(); i++) { |
884 | tabs.write[i].text_buf->set_width(-1); |
885 | tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x); |
886 | tabs.write[i].size_cache = get_tab_width(i); |
887 | |
888 | if (max_width > 0 && tabs[i].size_cache > max_width) { |
889 | int size_textless = tabs[i].size_cache - tabs[i].size_text; |
890 | int mw = MAX(size_textless, max_width); |
891 | |
892 | tabs.write[i].size_text = MAX(mw - size_textless, 1); |
893 | tabs.write[i].text_buf->set_width(tabs[i].size_text); |
894 | tabs.write[i].size_cache = size_textless + tabs[i].size_text; |
895 | } |
896 | |
897 | if (i < offset || i > max_drawn_tab) { |
898 | tabs.write[i].ofs_cache = 0; |
899 | continue; |
900 | } |
901 | |
902 | tabs.write[i].ofs_cache = w; |
903 | |
904 | if (tabs[i].hidden) { |
905 | continue; |
906 | } |
907 | |
908 | w += tabs[i].size_cache; |
909 | |
910 | // Check if all tabs would fit inside the area. |
911 | if (clip_tabs && i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) { |
912 | tabs.write[i].ofs_cache = 0; |
913 | |
914 | w -= tabs[i].size_cache; |
915 | max_drawn_tab = i - 1; |
916 | |
917 | while (w > limit_minus_buttons && max_drawn_tab > offset) { |
918 | tabs.write[max_drawn_tab].ofs_cache = 0; |
919 | |
920 | if (!tabs[max_drawn_tab].hidden) { |
921 | w -= tabs[max_drawn_tab].size_cache; |
922 | } |
923 | |
924 | max_drawn_tab--; |
925 | } |
926 | } |
927 | } |
928 | |
929 | missing_right = max_drawn_tab < tabs.size() - 1; |
930 | buttons_visible = offset > 0 || missing_right; |
931 | |
932 | if (tab_alignment == ALIGNMENT_LEFT) { |
933 | _update_hover(); |
934 | return; |
935 | } |
936 | |
937 | if (tab_alignment == ALIGNMENT_CENTER) { |
938 | w = ((buttons_visible ? limit_minus_buttons : limit) - w) / 2; |
939 | } else if (tab_alignment == ALIGNMENT_RIGHT) { |
940 | w = (buttons_visible ? limit_minus_buttons : limit) - w; |
941 | } |
942 | |
943 | for (int i = offset; i <= max_drawn_tab; i++) { |
944 | tabs.write[i].ofs_cache = w; |
945 | |
946 | if (!tabs[i].hidden) { |
947 | w += tabs[i].size_cache; |
948 | } |
949 | } |
950 | |
951 | _update_hover(); |
952 | } |
953 | |
954 | void TabBar::_on_mouse_exited() { |
955 | rb_hover = -1; |
956 | cb_hover = -1; |
957 | hover = -1; |
958 | highlight_arrow = -1; |
959 | dragging_valid_tab = false; |
960 | |
961 | _update_cache(); |
962 | queue_redraw(); |
963 | } |
964 | |
965 | void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { |
966 | Tab t; |
967 | t.text = p_str; |
968 | t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); |
969 | t.icon = p_icon; |
970 | tabs.push_back(t); |
971 | |
972 | _shape(tabs.size() - 1); |
973 | _update_cache(); |
974 | if (scroll_to_selected) { |
975 | ensure_tab_visible(current); |
976 | } |
977 | queue_redraw(); |
978 | update_minimum_size(); |
979 | |
980 | if (tabs.size() == 1 && is_inside_tree()) { |
981 | emit_signal(SNAME("tab_changed" ), 0); |
982 | } |
983 | } |
984 | |
985 | void TabBar::clear_tabs() { |
986 | if (tabs.is_empty()) { |
987 | return; |
988 | } |
989 | |
990 | tabs.clear(); |
991 | offset = 0; |
992 | max_drawn_tab = 0; |
993 | current = 0; |
994 | previous = 0; |
995 | |
996 | queue_redraw(); |
997 | update_minimum_size(); |
998 | notify_property_list_changed(); |
999 | } |
1000 | |
1001 | void TabBar::remove_tab(int p_idx) { |
1002 | ERR_FAIL_INDEX(p_idx, tabs.size()); |
1003 | tabs.remove_at(p_idx); |
1004 | |
1005 | bool is_tab_changing = current == p_idx && !tabs.is_empty(); |
1006 | |
1007 | if (current >= p_idx && current > 0) { |
1008 | current--; |
1009 | } |
1010 | |
1011 | if (tabs.is_empty()) { |
1012 | offset = 0; |
1013 | max_drawn_tab = 0; |
1014 | previous = 0; |
1015 | } else { |
1016 | offset = MIN(offset, tabs.size() - 1); |
1017 | max_drawn_tab = MIN(max_drawn_tab, tabs.size() - 1); |
1018 | |
1019 | _update_cache(); |
1020 | _ensure_no_over_offset(); |
1021 | if (scroll_to_selected) { |
1022 | ensure_tab_visible(current); |
1023 | } |
1024 | } |
1025 | |
1026 | queue_redraw(); |
1027 | update_minimum_size(); |
1028 | notify_property_list_changed(); |
1029 | |
1030 | if (is_tab_changing && is_inside_tree()) { |
1031 | emit_signal(SNAME("tab_changed" ), current); |
1032 | } |
1033 | } |
1034 | |
1035 | Variant TabBar::get_drag_data(const Point2 &p_point) { |
1036 | if (!drag_to_rearrange_enabled) { |
1037 | return Control::get_drag_data(p_point); // Allow stuff like TabContainer to override it. |
1038 | } |
1039 | |
1040 | int tab_over = get_tab_idx_at_point(p_point); |
1041 | if (tab_over < 0) { |
1042 | return Variant(); |
1043 | } |
1044 | |
1045 | HBoxContainer *drag_preview = memnew(HBoxContainer); |
1046 | |
1047 | if (!tabs[tab_over].icon.is_null()) { |
1048 | const Size2 icon_size = _get_tab_icon_size(tab_over); |
1049 | |
1050 | TextureRect *tf = memnew(TextureRect); |
1051 | tf->set_texture(tabs[tab_over].icon); |
1052 | tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); |
1053 | tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE); |
1054 | tf->set_custom_minimum_size(icon_size); |
1055 | |
1056 | drag_preview->add_child(tf); |
1057 | } |
1058 | |
1059 | Label *label = memnew(Label(tabs[tab_over].xl_text)); |
1060 | drag_preview->add_child(label); |
1061 | |
1062 | set_drag_preview(drag_preview); |
1063 | |
1064 | Dictionary drag_data; |
1065 | drag_data["type" ] = "tab_element" ; |
1066 | drag_data["tab_element" ] = tab_over; |
1067 | drag_data["from_path" ] = get_path(); |
1068 | |
1069 | return drag_data; |
1070 | } |
1071 | |
1072 | bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const { |
1073 | if (!drag_to_rearrange_enabled) { |
1074 | return Control::can_drop_data(p_point, p_data); // Allow stuff like TabContainer to override it. |
1075 | } |
1076 | |
1077 | Dictionary d = p_data; |
1078 | if (!d.has("type" )) { |
1079 | return false; |
1080 | } |
1081 | |
1082 | if (String(d["type" ]) == "tab_element" ) { |
1083 | NodePath from_path = d["from_path" ]; |
1084 | NodePath to_path = get_path(); |
1085 | if (from_path == to_path) { |
1086 | return true; |
1087 | } else if (get_tabs_rearrange_group() != -1) { |
1088 | // Drag and drop between other TabBars. |
1089 | Node *from_node = get_node(from_path); |
1090 | TabBar *from_tabs = Object::cast_to<TabBar>(from_node); |
1091 | if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { |
1092 | return true; |
1093 | } |
1094 | } |
1095 | } |
1096 | |
1097 | return false; |
1098 | } |
1099 | |
1100 | void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { |
1101 | if (!drag_to_rearrange_enabled) { |
1102 | Control::drop_data(p_point, p_data); // Allow stuff like TabContainer to override it. |
1103 | return; |
1104 | } |
1105 | |
1106 | Dictionary d = p_data; |
1107 | if (!d.has("type" )) { |
1108 | return; |
1109 | } |
1110 | |
1111 | if (String(d["type" ]) == "tab_element" ) { |
1112 | int tab_from_id = d["tab_element" ]; |
1113 | int hover_now = get_tab_idx_at_point(p_point); |
1114 | NodePath from_path = d["from_path" ]; |
1115 | NodePath to_path = get_path(); |
1116 | |
1117 | if (from_path == to_path) { |
1118 | if (tab_from_id == hover_now) { |
1119 | return; |
1120 | } |
1121 | |
1122 | // Drop the new tab to the left or right depending on where the target tab is being hovered. |
1123 | if (hover_now != -1) { |
1124 | Rect2 tab_rect = get_tab_rect(hover_now); |
1125 | if (is_layout_rtl() ^ (p_point.x <= tab_rect.position.x + tab_rect.size.width / 2)) { |
1126 | if (hover_now > tab_from_id) { |
1127 | hover_now -= 1; |
1128 | } |
1129 | } else if (tab_from_id > hover_now) { |
1130 | hover_now += 1; |
1131 | } |
1132 | } else { |
1133 | int x = tabs.is_empty() ? 0 : get_tab_rect(0).position.x; |
1134 | hover_now = is_layout_rtl() ^ (p_point.x < x) ? 0 : get_tab_count() - 1; |
1135 | } |
1136 | |
1137 | move_tab(tab_from_id, hover_now); |
1138 | if (!is_tab_disabled(hover_now)) { |
1139 | emit_signal(SNAME("active_tab_rearranged" ), hover_now); |
1140 | set_current_tab(hover_now); |
1141 | } |
1142 | } else if (get_tabs_rearrange_group() != -1) { |
1143 | // Drag and drop between Tabs. |
1144 | |
1145 | Node *from_node = get_node(from_path); |
1146 | TabBar *from_tabs = Object::cast_to<TabBar>(from_node); |
1147 | |
1148 | if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { |
1149 | if (tab_from_id >= from_tabs->get_tab_count()) { |
1150 | return; |
1151 | } |
1152 | |
1153 | // Drop the new tab to the left or right depending on where the target tab is being hovered. |
1154 | if (hover_now != -1) { |
1155 | Rect2 tab_rect = get_tab_rect(hover_now); |
1156 | if (is_layout_rtl() ^ (p_point.x > tab_rect.position.x + tab_rect.size.width / 2)) { |
1157 | hover_now += 1; |
1158 | } |
1159 | } else { |
1160 | hover_now = tabs.is_empty() || (is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x)) ? 0 : get_tab_count(); |
1161 | } |
1162 | |
1163 | Tab moving_tab = from_tabs->tabs[tab_from_id]; |
1164 | from_tabs->remove_tab(tab_from_id); |
1165 | tabs.insert(hover_now, moving_tab); |
1166 | |
1167 | if (tabs.size() > 1) { |
1168 | if (current >= hover_now) { |
1169 | current++; |
1170 | } |
1171 | if (previous >= hover_now) { |
1172 | previous++; |
1173 | } |
1174 | } |
1175 | |
1176 | if (!is_tab_disabled(hover_now)) { |
1177 | set_current_tab(hover_now); |
1178 | } else { |
1179 | _update_cache(); |
1180 | queue_redraw(); |
1181 | } |
1182 | |
1183 | update_minimum_size(); |
1184 | |
1185 | if (tabs.size() == 1) { |
1186 | emit_signal(SNAME("tab_selected" ), 0); |
1187 | emit_signal(SNAME("tab_changed" ), 0); |
1188 | } |
1189 | } |
1190 | } |
1191 | } |
1192 | } |
1193 | |
1194 | int TabBar::get_tab_idx_at_point(const Point2 &p_point) const { |
1195 | int hover_now = -1; |
1196 | |
1197 | if (!tabs.is_empty()) { |
1198 | for (int i = offset; i <= max_drawn_tab; i++) { |
1199 | Rect2 rect = get_tab_rect(i); |
1200 | if (rect.has_point(p_point)) { |
1201 | hover_now = i; |
1202 | } |
1203 | } |
1204 | } |
1205 | |
1206 | return hover_now; |
1207 | } |
1208 | |
1209 | void TabBar::set_tab_alignment(AlignmentMode p_alignment) { |
1210 | ERR_FAIL_INDEX(p_alignment, ALIGNMENT_MAX); |
1211 | |
1212 | if (tab_alignment == p_alignment) { |
1213 | return; |
1214 | } |
1215 | |
1216 | tab_alignment = p_alignment; |
1217 | |
1218 | _update_cache(); |
1219 | queue_redraw(); |
1220 | } |
1221 | |
1222 | TabBar::AlignmentMode TabBar::get_tab_alignment() const { |
1223 | return tab_alignment; |
1224 | } |
1225 | |
1226 | void TabBar::set_clip_tabs(bool p_clip_tabs) { |
1227 | if (clip_tabs == p_clip_tabs) { |
1228 | return; |
1229 | } |
1230 | clip_tabs = p_clip_tabs; |
1231 | |
1232 | if (!clip_tabs) { |
1233 | offset = 0; |
1234 | max_drawn_tab = 0; |
1235 | } |
1236 | |
1237 | _update_cache(); |
1238 | if (scroll_to_selected) { |
1239 | ensure_tab_visible(current); |
1240 | } |
1241 | queue_redraw(); |
1242 | update_minimum_size(); |
1243 | } |
1244 | |
1245 | bool TabBar::get_clip_tabs() const { |
1246 | return clip_tabs; |
1247 | } |
1248 | |
1249 | void TabBar::move_tab(int p_from, int p_to) { |
1250 | if (p_from == p_to) { |
1251 | return; |
1252 | } |
1253 | |
1254 | ERR_FAIL_INDEX(p_from, tabs.size()); |
1255 | ERR_FAIL_INDEX(p_to, tabs.size()); |
1256 | |
1257 | Tab tab_from = tabs[p_from]; |
1258 | tabs.remove_at(p_from); |
1259 | tabs.insert(p_to, tab_from); |
1260 | |
1261 | if (current == p_from) { |
1262 | current = p_to; |
1263 | } else if (current > p_from && current <= p_to) { |
1264 | current--; |
1265 | } else if (current < p_from && current >= p_to) { |
1266 | current++; |
1267 | } |
1268 | |
1269 | if (previous == p_from) { |
1270 | previous = p_to; |
1271 | } else if (previous > p_from && previous >= p_to) { |
1272 | previous--; |
1273 | } else if (previous < p_from && previous <= p_to) { |
1274 | previous++; |
1275 | } |
1276 | |
1277 | _update_cache(); |
1278 | _ensure_no_over_offset(); |
1279 | if (scroll_to_selected) { |
1280 | ensure_tab_visible(current); |
1281 | } |
1282 | queue_redraw(); |
1283 | notify_property_list_changed(); |
1284 | } |
1285 | |
1286 | int TabBar::get_tab_width(int p_idx) const { |
1287 | ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0); |
1288 | |
1289 | Ref<StyleBox> style; |
1290 | |
1291 | if (tabs[p_idx].disabled) { |
1292 | style = theme_cache.tab_disabled_style; |
1293 | } else if (current == p_idx) { |
1294 | style = theme_cache.tab_selected_style; |
1295 | } else if (hover == p_idx) { |
1296 | style = theme_cache.tab_hovered_style; |
1297 | } else { |
1298 | style = theme_cache.tab_unselected_style; |
1299 | } |
1300 | int x = style->get_minimum_size().width; |
1301 | |
1302 | if (tabs[p_idx].icon.is_valid()) { |
1303 | const Size2 icon_size = _get_tab_icon_size(p_idx); |
1304 | x += icon_size.width + theme_cache.h_separation; |
1305 | } |
1306 | |
1307 | if (!tabs[p_idx].text.is_empty()) { |
1308 | x += tabs[p_idx].size_text + theme_cache.h_separation; |
1309 | } |
1310 | |
1311 | bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current); |
1312 | |
1313 | if (tabs[p_idx].right_button.is_valid()) { |
1314 | Ref<StyleBox> btn_style = theme_cache.button_hl_style; |
1315 | Ref<Texture2D> rb = tabs[p_idx].right_button; |
1316 | |
1317 | if (close_visible) { |
1318 | x += btn_style->get_minimum_size().width + rb->get_width(); |
1319 | } else { |
1320 | x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + theme_cache.h_separation; |
1321 | } |
1322 | } |
1323 | |
1324 | if (close_visible) { |
1325 | Ref<StyleBox> btn_style = theme_cache.button_hl_style; |
1326 | Ref<Texture2D> cb = theme_cache.close_icon; |
1327 | x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + theme_cache.h_separation; |
1328 | } |
1329 | |
1330 | if (x > style->get_minimum_size().width) { |
1331 | x -= theme_cache.h_separation; |
1332 | } |
1333 | |
1334 | return x; |
1335 | } |
1336 | |
1337 | Size2 TabBar::_get_tab_icon_size(int p_index) const { |
1338 | ERR_FAIL_INDEX_V(p_index, tabs.size(), Size2()); |
1339 | const TabBar::Tab &tab = tabs[p_index]; |
1340 | Size2 icon_size = tab.icon->get_size(); |
1341 | |
1342 | int icon_max_width = 0; |
1343 | if (theme_cache.icon_max_width > 0) { |
1344 | icon_max_width = theme_cache.icon_max_width; |
1345 | } |
1346 | if (tab.icon_max_width > 0 && (icon_max_width == 0 || tab.icon_max_width < icon_max_width)) { |
1347 | icon_max_width = tab.icon_max_width; |
1348 | } |
1349 | |
1350 | if (icon_max_width > 0 && icon_size.width > icon_max_width) { |
1351 | icon_size.height = icon_size.height * icon_max_width / icon_size.width; |
1352 | icon_size.width = icon_max_width; |
1353 | } |
1354 | |
1355 | return icon_size; |
1356 | } |
1357 | |
1358 | void TabBar::_ensure_no_over_offset() { |
1359 | if (!is_inside_tree() || !buttons_visible) { |
1360 | return; |
1361 | } |
1362 | |
1363 | int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); |
1364 | |
1365 | int prev_offset = offset; |
1366 | |
1367 | int total_w = tabs[max_drawn_tab].ofs_cache + tabs[max_drawn_tab].size_cache - tabs[offset].ofs_cache; |
1368 | for (int i = offset; i > 0; i--) { |
1369 | if (tabs[i - 1].hidden) { |
1370 | continue; |
1371 | } |
1372 | |
1373 | total_w += tabs[i - 1].size_cache; |
1374 | |
1375 | if (total_w < limit_minus_buttons) { |
1376 | offset--; |
1377 | } else { |
1378 | break; |
1379 | } |
1380 | } |
1381 | |
1382 | if (prev_offset != offset) { |
1383 | _update_cache(); |
1384 | queue_redraw(); |
1385 | } |
1386 | } |
1387 | |
1388 | void TabBar::ensure_tab_visible(int p_idx) { |
1389 | if (!is_inside_tree() || !buttons_visible) { |
1390 | return; |
1391 | } |
1392 | ERR_FAIL_INDEX(p_idx, tabs.size()); |
1393 | |
1394 | if (tabs[p_idx].hidden || (p_idx >= offset && p_idx <= max_drawn_tab)) { |
1395 | return; |
1396 | } |
1397 | |
1398 | if (p_idx < offset) { |
1399 | offset = p_idx; |
1400 | _update_cache(); |
1401 | queue_redraw(); |
1402 | |
1403 | return; |
1404 | } |
1405 | |
1406 | int limit_minus_buttons = get_size().width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width(); |
1407 | |
1408 | int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache; |
1409 | for (int i = max_drawn_tab; i <= p_idx; i++) { |
1410 | if (tabs[i].hidden) { |
1411 | continue; |
1412 | } |
1413 | |
1414 | total_w += tabs[i].size_cache; |
1415 | } |
1416 | |
1417 | int prev_offset = offset; |
1418 | |
1419 | for (int i = offset; i < p_idx; i++) { |
1420 | if (tabs[i].hidden) { |
1421 | continue; |
1422 | } |
1423 | |
1424 | if (total_w > limit_minus_buttons) { |
1425 | total_w -= tabs[i].size_cache; |
1426 | offset++; |
1427 | } else { |
1428 | break; |
1429 | } |
1430 | } |
1431 | |
1432 | if (prev_offset != offset) { |
1433 | _update_cache(); |
1434 | queue_redraw(); |
1435 | } |
1436 | } |
1437 | |
1438 | Rect2 TabBar::get_tab_rect(int p_tab) const { |
1439 | ERR_FAIL_INDEX_V(p_tab, tabs.size(), Rect2()); |
1440 | if (is_layout_rtl()) { |
1441 | return Rect2(get_size().width - tabs[p_tab].ofs_cache - tabs[p_tab].size_cache, 0, tabs[p_tab].size_cache, get_size().height); |
1442 | } else { |
1443 | return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height); |
1444 | } |
1445 | } |
1446 | |
1447 | void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) { |
1448 | ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX); |
1449 | |
1450 | if (cb_displaypolicy == p_policy) { |
1451 | return; |
1452 | } |
1453 | |
1454 | cb_displaypolicy = p_policy; |
1455 | |
1456 | _update_cache(); |
1457 | _ensure_no_over_offset(); |
1458 | if (scroll_to_selected) { |
1459 | ensure_tab_visible(current); |
1460 | } |
1461 | queue_redraw(); |
1462 | update_minimum_size(); |
1463 | } |
1464 | |
1465 | TabBar::CloseButtonDisplayPolicy TabBar::get_tab_close_display_policy() const { |
1466 | return cb_displaypolicy; |
1467 | } |
1468 | |
1469 | void TabBar::set_max_tab_width(int p_width) { |
1470 | ERR_FAIL_COND(p_width < 0); |
1471 | |
1472 | if (max_width == p_width) { |
1473 | return; |
1474 | } |
1475 | |
1476 | max_width = p_width; |
1477 | |
1478 | _update_cache(); |
1479 | _ensure_no_over_offset(); |
1480 | if (scroll_to_selected) { |
1481 | ensure_tab_visible(current); |
1482 | } |
1483 | queue_redraw(); |
1484 | update_minimum_size(); |
1485 | } |
1486 | |
1487 | int TabBar::get_max_tab_width() const { |
1488 | return max_width; |
1489 | } |
1490 | |
1491 | void TabBar::set_scrolling_enabled(bool p_enabled) { |
1492 | scrolling_enabled = p_enabled; |
1493 | } |
1494 | |
1495 | bool TabBar::get_scrolling_enabled() const { |
1496 | return scrolling_enabled; |
1497 | } |
1498 | |
1499 | void TabBar::set_drag_to_rearrange_enabled(bool p_enabled) { |
1500 | drag_to_rearrange_enabled = p_enabled; |
1501 | } |
1502 | |
1503 | bool TabBar::get_drag_to_rearrange_enabled() const { |
1504 | return drag_to_rearrange_enabled; |
1505 | } |
1506 | |
1507 | void TabBar::set_tabs_rearrange_group(int p_group_id) { |
1508 | tabs_rearrange_group = p_group_id; |
1509 | } |
1510 | |
1511 | int TabBar::get_tabs_rearrange_group() const { |
1512 | return tabs_rearrange_group; |
1513 | } |
1514 | |
1515 | void TabBar::set_scroll_to_selected(bool p_enabled) { |
1516 | scroll_to_selected = p_enabled; |
1517 | if (p_enabled) { |
1518 | ensure_tab_visible(current); |
1519 | } |
1520 | } |
1521 | |
1522 | bool TabBar::get_scroll_to_selected() const { |
1523 | return scroll_to_selected; |
1524 | } |
1525 | |
1526 | void TabBar::set_select_with_rmb(bool p_enabled) { |
1527 | select_with_rmb = p_enabled; |
1528 | } |
1529 | |
1530 | bool TabBar::get_select_with_rmb() const { |
1531 | return select_with_rmb; |
1532 | } |
1533 | |
1534 | bool TabBar::_set(const StringName &p_name, const Variant &p_value) { |
1535 | Vector<String> components = String(p_name).split("/" , true, 2); |
1536 | if (components.size() >= 2 && components[0].begins_with("tab_" ) && components[0].trim_prefix("tab_" ).is_valid_int()) { |
1537 | int tab_index = components[0].trim_prefix("tab_" ).to_int(); |
1538 | String property = components[1]; |
1539 | if (property == "title" ) { |
1540 | set_tab_title(tab_index, p_value); |
1541 | return true; |
1542 | } else if (property == "icon" ) { |
1543 | set_tab_icon(tab_index, p_value); |
1544 | return true; |
1545 | } else if (components[1] == "disabled" ) { |
1546 | set_tab_disabled(tab_index, p_value); |
1547 | return true; |
1548 | } |
1549 | } |
1550 | return false; |
1551 | } |
1552 | |
1553 | bool TabBar::_get(const StringName &p_name, Variant &r_ret) const { |
1554 | Vector<String> components = String(p_name).split("/" , true, 2); |
1555 | if (components.size() >= 2 && components[0].begins_with("tab_" ) && components[0].trim_prefix("tab_" ).is_valid_int()) { |
1556 | int tab_index = components[0].trim_prefix("tab_" ).to_int(); |
1557 | String property = components[1]; |
1558 | if (property == "title" ) { |
1559 | r_ret = get_tab_title(tab_index); |
1560 | return true; |
1561 | } else if (property == "icon" ) { |
1562 | r_ret = get_tab_icon(tab_index); |
1563 | return true; |
1564 | } else if (components[1] == "disabled" ) { |
1565 | r_ret = is_tab_disabled(tab_index); |
1566 | return true; |
1567 | } |
1568 | } |
1569 | return false; |
1570 | } |
1571 | |
1572 | void TabBar::_get_property_list(List<PropertyInfo> *p_list) const { |
1573 | for (int i = 0; i < tabs.size(); i++) { |
1574 | p_list->push_back(PropertyInfo(Variant::STRING, vformat("tab_%d/title" , i))); |
1575 | |
1576 | PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("tab_%d/icon" , i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D" ); |
1577 | pi.usage &= ~(get_tab_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); |
1578 | p_list->push_back(pi); |
1579 | |
1580 | pi = PropertyInfo(Variant::BOOL, vformat("tab_%d/disabled" , i)); |
1581 | pi.usage &= ~(!is_tab_disabled(i) ? PROPERTY_USAGE_STORAGE : 0); |
1582 | p_list->push_back(pi); |
1583 | } |
1584 | } |
1585 | |
1586 | void TabBar::_bind_methods() { |
1587 | ClassDB::bind_method(D_METHOD("set_tab_count" , "count" ), &TabBar::set_tab_count); |
1588 | ClassDB::bind_method(D_METHOD("get_tab_count" ), &TabBar::get_tab_count); |
1589 | ClassDB::bind_method(D_METHOD("set_current_tab" , "tab_idx" ), &TabBar::set_current_tab); |
1590 | ClassDB::bind_method(D_METHOD("get_current_tab" ), &TabBar::get_current_tab); |
1591 | ClassDB::bind_method(D_METHOD("get_previous_tab" ), &TabBar::get_previous_tab); |
1592 | ClassDB::bind_method(D_METHOD("set_tab_title" , "tab_idx" , "title" ), &TabBar::set_tab_title); |
1593 | ClassDB::bind_method(D_METHOD("get_tab_title" , "tab_idx" ), &TabBar::get_tab_title); |
1594 | ClassDB::bind_method(D_METHOD("set_tab_text_direction" , "tab_idx" , "direction" ), &TabBar::set_tab_text_direction); |
1595 | ClassDB::bind_method(D_METHOD("get_tab_text_direction" , "tab_idx" ), &TabBar::get_tab_text_direction); |
1596 | ClassDB::bind_method(D_METHOD("set_tab_language" , "tab_idx" , "language" ), &TabBar::set_tab_language); |
1597 | ClassDB::bind_method(D_METHOD("get_tab_language" , "tab_idx" ), &TabBar::get_tab_language); |
1598 | ClassDB::bind_method(D_METHOD("set_tab_icon" , "tab_idx" , "icon" ), &TabBar::set_tab_icon); |
1599 | ClassDB::bind_method(D_METHOD("get_tab_icon" , "tab_idx" ), &TabBar::get_tab_icon); |
1600 | ClassDB::bind_method(D_METHOD("set_tab_icon_max_width" , "tab_idx" , "width" ), &TabBar::set_tab_icon_max_width); |
1601 | ClassDB::bind_method(D_METHOD("get_tab_icon_max_width" , "tab_idx" ), &TabBar::get_tab_icon_max_width); |
1602 | ClassDB::bind_method(D_METHOD("set_tab_button_icon" , "tab_idx" , "icon" ), &TabBar::set_tab_button_icon); |
1603 | ClassDB::bind_method(D_METHOD("get_tab_button_icon" , "tab_idx" ), &TabBar::get_tab_button_icon); |
1604 | ClassDB::bind_method(D_METHOD("set_tab_disabled" , "tab_idx" , "disabled" ), &TabBar::set_tab_disabled); |
1605 | ClassDB::bind_method(D_METHOD("is_tab_disabled" , "tab_idx" ), &TabBar::is_tab_disabled); |
1606 | ClassDB::bind_method(D_METHOD("set_tab_hidden" , "tab_idx" , "hidden" ), &TabBar::set_tab_hidden); |
1607 | ClassDB::bind_method(D_METHOD("is_tab_hidden" , "tab_idx" ), &TabBar::is_tab_hidden); |
1608 | ClassDB::bind_method(D_METHOD("set_tab_metadata" , "tab_idx" , "metadata" ), &TabBar::set_tab_metadata); |
1609 | ClassDB::bind_method(D_METHOD("get_tab_metadata" , "tab_idx" ), &TabBar::get_tab_metadata); |
1610 | ClassDB::bind_method(D_METHOD("remove_tab" , "tab_idx" ), &TabBar::remove_tab); |
1611 | ClassDB::bind_method(D_METHOD("add_tab" , "title" , "icon" ), &TabBar::add_tab, DEFVAL("" ), DEFVAL(Ref<Texture2D>())); |
1612 | ClassDB::bind_method(D_METHOD("get_tab_idx_at_point" , "point" ), &TabBar::get_tab_idx_at_point); |
1613 | ClassDB::bind_method(D_METHOD("set_tab_alignment" , "alignment" ), &TabBar::set_tab_alignment); |
1614 | ClassDB::bind_method(D_METHOD("get_tab_alignment" ), &TabBar::get_tab_alignment); |
1615 | ClassDB::bind_method(D_METHOD("set_clip_tabs" , "clip_tabs" ), &TabBar::set_clip_tabs); |
1616 | ClassDB::bind_method(D_METHOD("get_clip_tabs" ), &TabBar::get_clip_tabs); |
1617 | ClassDB::bind_method(D_METHOD("get_tab_offset" ), &TabBar::get_tab_offset); |
1618 | ClassDB::bind_method(D_METHOD("get_offset_buttons_visible" ), &TabBar::get_offset_buttons_visible); |
1619 | ClassDB::bind_method(D_METHOD("ensure_tab_visible" , "idx" ), &TabBar::ensure_tab_visible); |
1620 | ClassDB::bind_method(D_METHOD("get_tab_rect" , "tab_idx" ), &TabBar::get_tab_rect); |
1621 | ClassDB::bind_method(D_METHOD("move_tab" , "from" , "to" ), &TabBar::move_tab); |
1622 | ClassDB::bind_method(D_METHOD("set_tab_close_display_policy" , "policy" ), &TabBar::set_tab_close_display_policy); |
1623 | ClassDB::bind_method(D_METHOD("get_tab_close_display_policy" ), &TabBar::get_tab_close_display_policy); |
1624 | ClassDB::bind_method(D_METHOD("set_max_tab_width" , "width" ), &TabBar::set_max_tab_width); |
1625 | ClassDB::bind_method(D_METHOD("get_max_tab_width" ), &TabBar::get_max_tab_width); |
1626 | ClassDB::bind_method(D_METHOD("set_scrolling_enabled" , "enabled" ), &TabBar::set_scrolling_enabled); |
1627 | ClassDB::bind_method(D_METHOD("get_scrolling_enabled" ), &TabBar::get_scrolling_enabled); |
1628 | ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled" , "enabled" ), &TabBar::set_drag_to_rearrange_enabled); |
1629 | ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled" ), &TabBar::get_drag_to_rearrange_enabled); |
1630 | ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group" , "group_id" ), &TabBar::set_tabs_rearrange_group); |
1631 | ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group" ), &TabBar::get_tabs_rearrange_group); |
1632 | ClassDB::bind_method(D_METHOD("set_scroll_to_selected" , "enabled" ), &TabBar::set_scroll_to_selected); |
1633 | ClassDB::bind_method(D_METHOD("get_scroll_to_selected" ), &TabBar::get_scroll_to_selected); |
1634 | ClassDB::bind_method(D_METHOD("set_select_with_rmb" , "enabled" ), &TabBar::set_select_with_rmb); |
1635 | ClassDB::bind_method(D_METHOD("get_select_with_rmb" ), &TabBar::get_select_with_rmb); |
1636 | ClassDB::bind_method(D_METHOD("clear_tabs" ), &TabBar::clear_tabs); |
1637 | |
1638 | ADD_SIGNAL(MethodInfo("tab_selected" , PropertyInfo(Variant::INT, "tab" ))); |
1639 | ADD_SIGNAL(MethodInfo("tab_changed" , PropertyInfo(Variant::INT, "tab" ))); |
1640 | ADD_SIGNAL(MethodInfo("tab_clicked" , PropertyInfo(Variant::INT, "tab" ))); |
1641 | ADD_SIGNAL(MethodInfo("tab_rmb_clicked" , PropertyInfo(Variant::INT, "tab" ))); |
1642 | ADD_SIGNAL(MethodInfo("tab_close_pressed" , PropertyInfo(Variant::INT, "tab" ))); |
1643 | ADD_SIGNAL(MethodInfo("tab_button_pressed" , PropertyInfo(Variant::INT, "tab" ))); |
1644 | ADD_SIGNAL(MethodInfo("tab_hovered" , PropertyInfo(Variant::INT, "tab" ))); |
1645 | ADD_SIGNAL(MethodInfo("active_tab_rearranged" , PropertyInfo(Variant::INT, "idx_to" ))); |
1646 | |
1647 | ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab" , PROPERTY_HINT_RANGE, "-1,4096,1" , PROPERTY_USAGE_EDITOR), "set_current_tab" , "get_current_tab" ); |
1648 | ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment" , PROPERTY_HINT_ENUM, "Left,Center,Right" ), "set_tab_alignment" , "get_tab_alignment" ); |
1649 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs" ), "set_clip_tabs" , "get_clip_tabs" ); |
1650 | 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" ); |
1651 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_tab_width" , PROPERTY_HINT_RANGE, "0,99999,1,suffix:px" ), "set_max_tab_width" , "get_max_tab_width" ); |
1652 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled" ), "set_scrolling_enabled" , "get_scrolling_enabled" ); |
1653 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled" ), "set_drag_to_rearrange_enabled" , "get_drag_to_rearrange_enabled" ); |
1654 | ADD_PROPERTY(PropertyInfo(Variant::INT, "tabs_rearrange_group" ), "set_tabs_rearrange_group" , "get_tabs_rearrange_group" ); |
1655 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_to_selected" ), "set_scroll_to_selected" , "get_scroll_to_selected" ); |
1656 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_with_rmb" ), "set_select_with_rmb" , "get_select_with_rmb" ); |
1657 | |
1658 | ADD_ARRAY_COUNT("Tabs" , "tab_count" , "set_tab_count" , "get_tab_count" , "tab_" ); |
1659 | |
1660 | BIND_ENUM_CONSTANT(ALIGNMENT_LEFT); |
1661 | BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); |
1662 | BIND_ENUM_CONSTANT(ALIGNMENT_RIGHT); |
1663 | BIND_ENUM_CONSTANT(ALIGNMENT_MAX); |
1664 | |
1665 | BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER); |
1666 | BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ACTIVE_ONLY); |
1667 | BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ALWAYS); |
1668 | BIND_ENUM_CONSTANT(CLOSE_BUTTON_MAX); |
1669 | |
1670 | BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabBar, h_separation); |
1671 | BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabBar, icon_max_width); |
1672 | |
1673 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, tab_unselected_style, "tab_unselected" ); |
1674 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, tab_hovered_style, "tab_hovered" ); |
1675 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, tab_selected_style, "tab_selected" ); |
1676 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, tab_disabled_style, "tab_disabled" ); |
1677 | |
1678 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabBar, increment_icon, "increment" ); |
1679 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabBar, increment_hl_icon, "increment_highlight" ); |
1680 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabBar, decrement_icon, "decrement" ); |
1681 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabBar, decrement_hl_icon, "decrement_highlight" ); |
1682 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabBar, drop_mark_icon, "drop_mark" ); |
1683 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabBar, drop_mark_color); |
1684 | |
1685 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabBar, font_selected_color); |
1686 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabBar, font_hovered_color); |
1687 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabBar, font_unselected_color); |
1688 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabBar, font_disabled_color); |
1689 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, TabBar, font_outline_color); |
1690 | |
1691 | BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, TabBar, font); |
1692 | BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, TabBar, font_size); |
1693 | BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabBar, outline_size); |
1694 | |
1695 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, TabBar, close_icon, "close" ); |
1696 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, button_pressed_style, "button_pressed" ); |
1697 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, button_hl_style, "button_highlight" ); |
1698 | } |
1699 | |
1700 | TabBar::TabBar() { |
1701 | set_size(Size2(get_size().width, get_minimum_size().height)); |
1702 | connect("mouse_exited" , callable_mp(this, &TabBar::_on_mouse_exited)); |
1703 | } |
1704 | |