1/**************************************************************************/
2/* tree.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 "tree.h"
32
33#include "core/config/project_settings.h"
34#include "core/input/input.h"
35#include "core/math/math_funcs.h"
36#include "core/os/keyboard.h"
37#include "core/os/os.h"
38#include "core/string/print_string.h"
39#include "core/string/translation.h"
40#include "scene/gui/box_container.h"
41#include "scene/gui/text_edit.h"
42#include "scene/main/window.h"
43#include "scene/theme/theme_db.h"
44
45#include <limits.h>
46
47Size2 TreeItem::Cell::get_icon_size() const {
48 if (icon.is_null()) {
49 return Size2();
50 }
51 if (icon_region == Rect2i()) {
52 return icon->get_size();
53 } else {
54 return icon_region.size;
55 }
56}
57
58void TreeItem::Cell::draw_icon(const RID &p_where, const Point2 &p_pos, const Size2 &p_size, const Color &p_color) const {
59 if (icon.is_null()) {
60 return;
61 }
62
63 Size2i dsize = (p_size == Size2()) ? icon->get_size() : p_size;
64
65 if (icon_region == Rect2i()) {
66 icon->draw_rect_region(p_where, Rect2(p_pos, dsize), Rect2(Point2(), icon->get_size()), p_color);
67 } else {
68 icon->draw_rect_region(p_where, Rect2(p_pos, dsize), icon_region, p_color);
69 }
70}
71
72void TreeItem::_changed_notify(int p_cell) {
73 tree->item_changed(p_cell, this);
74}
75
76void TreeItem::_changed_notify() {
77 tree->item_changed(-1, this);
78}
79
80void TreeItem::_cell_selected(int p_cell) {
81 tree->item_selected(p_cell, this);
82}
83
84void TreeItem::_cell_deselected(int p_cell) {
85 tree->item_deselected(p_cell, this);
86}
87
88void TreeItem::_change_tree(Tree *p_tree) {
89 if (p_tree == tree) {
90 return;
91 }
92
93 TreeItem *c = first_child;
94 while (c) {
95 c->_change_tree(p_tree);
96 c = c->next;
97 }
98
99 if (tree) {
100 if (tree->root == this) {
101 tree->root = nullptr;
102 }
103
104 if (tree->popup_edited_item == this) {
105 tree->popup_edited_item = nullptr;
106 tree->popup_pressing_edited_item = nullptr;
107 tree->pressing_for_editor = false;
108 }
109
110 if (tree->cache.hover_item == this) {
111 tree->cache.hover_item = nullptr;
112 }
113
114 if (tree->selected_item == this) {
115 tree->selected_item = nullptr;
116 }
117
118 if (tree->drop_mode_over == this) {
119 tree->drop_mode_over = nullptr;
120 }
121
122 if (tree->single_select_defer == this) {
123 tree->single_select_defer = nullptr;
124 }
125
126 if (tree->edited_item == this) {
127 tree->edited_item = nullptr;
128 tree->pressing_for_editor = false;
129 }
130
131 tree->queue_redraw();
132 }
133
134 tree = p_tree;
135
136 if (tree) {
137 tree->queue_redraw();
138 cells.resize(tree->columns.size());
139 }
140}
141
142/* cell mode */
143void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) {
144 ERR_FAIL_INDEX(p_column, cells.size());
145
146 if (cells[p_column].mode == p_mode) {
147 return;
148 }
149
150 Cell &c = cells.write[p_column];
151 c.mode = p_mode;
152 c.min = 0;
153 c.max = 100;
154 c.step = 1;
155 c.val = 0;
156 c.checked = false;
157 c.icon = Ref<Texture2D>();
158 c.text = "";
159 c.dirty = true;
160 c.icon_max_w = 0;
161 c.cached_minimum_size_dirty = true;
162
163 _changed_notify(p_column);
164}
165
166TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const {
167 ERR_FAIL_INDEX_V(p_column, cells.size(), TreeItem::CELL_MODE_STRING);
168 return cells[p_column].mode;
169}
170
171/* multiline editable */
172void TreeItem::set_edit_multiline(int p_column, bool p_multiline) {
173 ERR_FAIL_INDEX(p_column, cells.size());
174 cells.write[p_column].edit_multiline = p_multiline;
175 _changed_notify(p_column);
176}
177
178bool TreeItem::is_edit_multiline(int p_column) const {
179 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
180 return cells[p_column].edit_multiline;
181}
182
183/* check mode */
184void TreeItem::set_checked(int p_column, bool p_checked) {
185 ERR_FAIL_INDEX(p_column, cells.size());
186
187 if (cells[p_column].checked == p_checked) {
188 return;
189 }
190
191 cells.write[p_column].checked = p_checked;
192 cells.write[p_column].indeterminate = false;
193 cells.write[p_column].cached_minimum_size_dirty = true;
194
195 _changed_notify(p_column);
196}
197
198void TreeItem::set_indeterminate(int p_column, bool p_indeterminate) {
199 ERR_FAIL_INDEX(p_column, cells.size());
200
201 // Prevent uncheck if indeterminate set to false twice
202 if (p_indeterminate == cells[p_column].indeterminate) {
203 return;
204 }
205
206 cells.write[p_column].indeterminate = p_indeterminate;
207 cells.write[p_column].checked = false;
208 cells.write[p_column].cached_minimum_size_dirty = true;
209
210 _changed_notify(p_column);
211}
212
213bool TreeItem::is_checked(int p_column) const {
214 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
215 return cells[p_column].checked;
216}
217
218bool TreeItem::is_indeterminate(int p_column) const {
219 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
220 return cells[p_column].indeterminate;
221}
222
223void TreeItem::propagate_check(int p_column, bool p_emit_signal) {
224 bool ch = cells[p_column].checked;
225
226 if (p_emit_signal) {
227 tree->emit_signal(SNAME("check_propagated_to_item"), this, p_column);
228 }
229 _propagate_check_through_children(p_column, ch, p_emit_signal);
230 _propagate_check_through_parents(p_column, p_emit_signal);
231}
232
233void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal) {
234 TreeItem *current = get_first_child();
235 while (current) {
236 current->set_checked(p_column, p_checked);
237 if (p_emit_signal) {
238 current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column);
239 }
240 current->_propagate_check_through_children(p_column, p_checked, p_emit_signal);
241 current = current->get_next();
242 }
243}
244
245void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal) {
246 TreeItem *current = get_parent();
247 if (!current) {
248 return;
249 }
250
251 bool any_checked = false;
252 bool any_unchecked = false;
253 bool any_indeterminate = false;
254
255 TreeItem *child_item = current->get_first_child();
256 while (child_item) {
257 if (!child_item->is_checked(p_column)) {
258 any_unchecked = true;
259 if (child_item->is_indeterminate(p_column)) {
260 any_indeterminate = true;
261 break;
262 }
263 } else {
264 any_checked = true;
265 }
266 child_item = child_item->get_next();
267 }
268
269 if (any_indeterminate || (any_checked && any_unchecked)) {
270 current->set_indeterminate(p_column, true);
271 } else if (current->is_indeterminate(p_column) && !any_checked) {
272 current->set_indeterminate(p_column, false);
273 } else {
274 current->set_checked(p_column, any_checked);
275 }
276
277 if (p_emit_signal) {
278 current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column);
279 }
280 current->_propagate_check_through_parents(p_column, p_emit_signal);
281}
282
283void TreeItem::set_text(int p_column, String p_text) {
284 ERR_FAIL_INDEX(p_column, cells.size());
285
286 if (cells[p_column].text == p_text) {
287 return;
288 }
289
290 cells.write[p_column].text = p_text;
291 cells.write[p_column].dirty = true;
292
293 if (cells[p_column].mode == TreeItem::CELL_MODE_RANGE) {
294 Vector<String> strings = p_text.split(",");
295 cells.write[p_column].min = INT_MAX;
296 cells.write[p_column].max = INT_MIN;
297 for (int i = 0; i < strings.size(); i++) {
298 int value = i;
299 if (!strings[i].get_slicec(':', 1).is_empty()) {
300 value = strings[i].get_slicec(':', 1).to_int();
301 }
302 cells.write[p_column].min = MIN(cells[p_column].min, value);
303 cells.write[p_column].max = MAX(cells[p_column].max, value);
304 }
305 cells.write[p_column].step = 0;
306 }
307
308 cells.write[p_column].cached_minimum_size_dirty = true;
309
310 _changed_notify(p_column);
311}
312
313String TreeItem::get_text(int p_column) const {
314 ERR_FAIL_INDEX_V(p_column, cells.size(), "");
315 return cells[p_column].text;
316}
317
318void TreeItem::set_text_direction(int p_column, Control::TextDirection p_text_direction) {
319 ERR_FAIL_INDEX(p_column, cells.size());
320 ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
321
322 if (cells[p_column].text_direction == p_text_direction) {
323 return;
324 }
325
326 cells.write[p_column].text_direction = p_text_direction;
327 cells.write[p_column].dirty = true;
328 _changed_notify(p_column);
329 cells.write[p_column].cached_minimum_size_dirty = true;
330}
331
332Control::TextDirection TreeItem::get_text_direction(int p_column) const {
333 ERR_FAIL_INDEX_V(p_column, cells.size(), Control::TEXT_DIRECTION_INHERITED);
334 return cells[p_column].text_direction;
335}
336
337void TreeItem::set_autowrap_mode(int p_column, TextServer::AutowrapMode p_mode) {
338 ERR_FAIL_INDEX(p_column, cells.size());
339 ERR_FAIL_COND(p_mode < TextServer::AUTOWRAP_OFF || p_mode > TextServer::AUTOWRAP_WORD_SMART);
340
341 if (cells[p_column].autowrap_mode == p_mode) {
342 return;
343 }
344
345 cells.write[p_column].autowrap_mode = p_mode;
346 cells.write[p_column].dirty = true;
347 _changed_notify(p_column);
348 cells.write[p_column].cached_minimum_size_dirty = true;
349}
350
351TextServer::AutowrapMode TreeItem::get_autowrap_mode(int p_column) const {
352 ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::AUTOWRAP_OFF);
353 return cells[p_column].autowrap_mode;
354}
355
356void TreeItem::set_text_overrun_behavior(int p_column, TextServer::OverrunBehavior p_behavior) {
357 ERR_FAIL_INDEX(p_column, cells.size());
358
359 if (cells[p_column].text_buf->get_text_overrun_behavior() == p_behavior) {
360 return;
361 }
362
363 cells.write[p_column].text_buf->set_text_overrun_behavior(p_behavior);
364 cells.write[p_column].dirty = true;
365 _changed_notify(p_column);
366 cells.write[p_column].cached_minimum_size_dirty = true;
367}
368
369TextServer::OverrunBehavior TreeItem::get_text_overrun_behavior(int p_column) const {
370 ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::OVERRUN_TRIM_ELLIPSIS);
371 return cells[p_column].text_buf->get_text_overrun_behavior();
372}
373
374void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser) {
375 ERR_FAIL_INDEX(p_column, cells.size());
376
377 if (cells[p_column].st_parser != p_parser) {
378 cells.write[p_column].st_parser = p_parser;
379 cells.write[p_column].dirty = true;
380 cells.write[p_column].cached_minimum_size_dirty = true;
381
382 _changed_notify(p_column);
383 }
384}
385
386TextServer::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const {
387 ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_DEFAULT);
388 return cells[p_column].st_parser;
389}
390
391void TreeItem::set_structured_text_bidi_override_options(int p_column, Array p_args) {
392 ERR_FAIL_INDEX(p_column, cells.size());
393
394 if (cells[p_column].st_args == p_args) {
395 return;
396 }
397
398 cells.write[p_column].st_args = p_args;
399 cells.write[p_column].dirty = true;
400 cells.write[p_column].cached_minimum_size_dirty = true;
401
402 _changed_notify(p_column);
403}
404
405Array TreeItem::get_structured_text_bidi_override_options(int p_column) const {
406 ERR_FAIL_INDEX_V(p_column, cells.size(), Array());
407 return cells[p_column].st_args;
408}
409
410void TreeItem::set_language(int p_column, const String &p_language) {
411 ERR_FAIL_INDEX(p_column, cells.size());
412
413 if (cells[p_column].language != p_language) {
414 cells.write[p_column].language = p_language;
415 cells.write[p_column].dirty = true;
416 cells.write[p_column].cached_minimum_size_dirty = true;
417
418 _changed_notify(p_column);
419 }
420}
421
422String TreeItem::get_language(int p_column) const {
423 ERR_FAIL_INDEX_V(p_column, cells.size(), "");
424 return cells[p_column].language;
425}
426
427void TreeItem::set_suffix(int p_column, String p_suffix) {
428 ERR_FAIL_INDEX(p_column, cells.size());
429
430 if (cells[p_column].suffix == p_suffix) {
431 return;
432 }
433
434 cells.write[p_column].suffix = p_suffix;
435 cells.write[p_column].cached_minimum_size_dirty = true;
436
437 _changed_notify(p_column);
438}
439
440String TreeItem::get_suffix(int p_column) const {
441 ERR_FAIL_INDEX_V(p_column, cells.size(), "");
442 return cells[p_column].suffix;
443}
444
445void TreeItem::set_icon(int p_column, const Ref<Texture2D> &p_icon) {
446 ERR_FAIL_INDEX(p_column, cells.size());
447
448 if (cells[p_column].icon == p_icon) {
449 return;
450 }
451
452 cells.write[p_column].icon = p_icon;
453 cells.write[p_column].cached_minimum_size_dirty = true;
454
455 _changed_notify(p_column);
456}
457
458Ref<Texture2D> TreeItem::get_icon(int p_column) const {
459 ERR_FAIL_INDEX_V(p_column, cells.size(), Ref<Texture2D>());
460 return cells[p_column].icon;
461}
462
463void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) {
464 ERR_FAIL_INDEX(p_column, cells.size());
465
466 if (cells[p_column].icon_region == p_icon_region) {
467 return;
468 }
469
470 cells.write[p_column].icon_region = p_icon_region;
471 cells.write[p_column].cached_minimum_size_dirty = true;
472
473 _changed_notify(p_column);
474}
475
476Rect2 TreeItem::get_icon_region(int p_column) const {
477 ERR_FAIL_INDEX_V(p_column, cells.size(), Rect2());
478 return cells[p_column].icon_region;
479}
480
481void TreeItem::set_icon_modulate(int p_column, const Color &p_modulate) {
482 ERR_FAIL_INDEX(p_column, cells.size());
483
484 if (cells[p_column].icon_color == p_modulate) {
485 return;
486 }
487
488 cells.write[p_column].icon_color = p_modulate;
489 _changed_notify(p_column);
490}
491
492Color TreeItem::get_icon_modulate(int p_column) const {
493 ERR_FAIL_INDEX_V(p_column, cells.size(), Color());
494 return cells[p_column].icon_color;
495}
496
497void TreeItem::set_icon_max_width(int p_column, int p_max) {
498 ERR_FAIL_INDEX(p_column, cells.size());
499
500 if (cells[p_column].icon_max_w == p_max) {
501 return;
502 }
503
504 cells.write[p_column].icon_max_w = p_max;
505 cells.write[p_column].cached_minimum_size_dirty = true;
506
507 _changed_notify(p_column);
508}
509
510int TreeItem::get_icon_max_width(int p_column) const {
511 ERR_FAIL_INDEX_V(p_column, cells.size(), 0);
512 return cells[p_column].icon_max_w;
513}
514
515/* range works for mode number or mode combo */
516void TreeItem::set_range(int p_column, double p_value) {
517 ERR_FAIL_INDEX(p_column, cells.size());
518 if (cells[p_column].step > 0) {
519 p_value = Math::snapped(p_value, cells[p_column].step);
520 }
521 if (p_value < cells[p_column].min) {
522 p_value = cells[p_column].min;
523 }
524 if (p_value > cells[p_column].max) {
525 p_value = cells[p_column].max;
526 }
527
528 if (cells[p_column].val == p_value) {
529 return;
530 }
531
532 cells.write[p_column].val = p_value;
533 cells.write[p_column].dirty = true;
534 _changed_notify(p_column);
535}
536
537double TreeItem::get_range(int p_column) const {
538 ERR_FAIL_INDEX_V(p_column, cells.size(), 0);
539 return cells[p_column].val;
540}
541
542bool TreeItem::is_range_exponential(int p_column) const {
543 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
544 return cells[p_column].expr;
545}
546
547void TreeItem::set_range_config(int p_column, double p_min, double p_max, double p_step, bool p_exp) {
548 ERR_FAIL_INDEX(p_column, cells.size());
549
550 if (cells[p_column].min == p_min && cells[p_column].max == p_max && cells[p_column].step == p_step && cells[p_column].expr == p_exp) {
551 return;
552 }
553
554 cells.write[p_column].min = p_min;
555 cells.write[p_column].max = p_max;
556 cells.write[p_column].step = p_step;
557 cells.write[p_column].expr = p_exp;
558 _changed_notify(p_column);
559}
560
561void TreeItem::get_range_config(int p_column, double &r_min, double &r_max, double &r_step) const {
562 ERR_FAIL_INDEX(p_column, cells.size());
563 r_min = cells[p_column].min;
564 r_max = cells[p_column].max;
565 r_step = cells[p_column].step;
566}
567
568void TreeItem::set_metadata(int p_column, const Variant &p_meta) {
569 ERR_FAIL_INDEX(p_column, cells.size());
570 cells.write[p_column].meta = p_meta;
571}
572
573Variant TreeItem::get_metadata(int p_column) const {
574 ERR_FAIL_INDEX_V(p_column, cells.size(), Variant());
575
576 return cells[p_column].meta;
577}
578
579void TreeItem::set_custom_draw(int p_column, Object *p_object, const StringName &p_callback) {
580 ERR_FAIL_INDEX(p_column, cells.size());
581 ERR_FAIL_NULL(p_object);
582
583 cells.write[p_column].custom_draw_obj = p_object->get_instance_id();
584 cells.write[p_column].custom_draw_callback = p_callback;
585}
586
587void TreeItem::set_collapsed(bool p_collapsed) {
588 if (collapsed == p_collapsed || !tree) {
589 return;
590 }
591 collapsed = p_collapsed;
592 TreeItem *ci = tree->selected_item;
593 if (ci) {
594 while (ci && ci != this) {
595 ci = ci->parent;
596 }
597 if (ci) { // collapsing cursor/selected, move it!
598
599 if (tree->select_mode == Tree::SELECT_MULTI) {
600 tree->selected_item = this;
601 emit_signal(SNAME("cell_selected"));
602 } else {
603 select(tree->selected_col);
604 }
605
606 tree->queue_redraw();
607 }
608 }
609
610 _changed_notify();
611 tree->emit_signal(SNAME("item_collapsed"), this);
612}
613
614bool TreeItem::is_collapsed() {
615 return collapsed;
616}
617
618void TreeItem::set_collapsed_recursive(bool p_collapsed) {
619 if (!tree) {
620 return;
621 }
622
623 set_collapsed(p_collapsed);
624
625 TreeItem *child = get_first_child();
626 while (child) {
627 child->set_collapsed_recursive(p_collapsed);
628 child = child->get_next();
629 }
630}
631
632bool TreeItem::_is_any_collapsed(bool p_only_visible) {
633 TreeItem *child = get_first_child();
634
635 // Check on children directly first (avoid recursing if possible).
636 while (child) {
637 if (child->get_first_child() && child->is_collapsed() && (!p_only_visible || (child->is_visible() && child->get_visible_child_count()))) {
638 return true;
639 }
640 child = child->get_next();
641 }
642
643 child = get_first_child();
644
645 // Otherwise recurse on children.
646 while (child) {
647 if (child->get_first_child() && (!p_only_visible || (child->is_visible() && child->get_visible_child_count())) && child->_is_any_collapsed(p_only_visible)) {
648 return true;
649 }
650 child = child->get_next();
651 }
652
653 return false;
654}
655
656bool TreeItem::is_any_collapsed(bool p_only_visible) {
657 if (p_only_visible && !is_visible()) {
658 return false;
659 }
660
661 // Collapsed if this is collapsed and it has children (only considers visible if only visible is set).
662 if (is_collapsed() && get_first_child() && (!p_only_visible || get_visible_child_count())) {
663 return true;
664 }
665
666 return _is_any_collapsed(p_only_visible);
667}
668
669void TreeItem::set_visible(bool p_visible) {
670 if (visible == p_visible) {
671 return;
672 }
673 visible = p_visible;
674 if (tree) {
675 tree->queue_redraw();
676 _changed_notify();
677 }
678}
679
680bool TreeItem::is_visible() {
681 return visible;
682}
683
684void TreeItem::uncollapse_tree() {
685 TreeItem *t = this;
686 while (t) {
687 t->set_collapsed(false);
688 t = t->parent;
689 }
690}
691
692void TreeItem::set_custom_minimum_height(int p_height) {
693 if (custom_min_height == p_height) {
694 return;
695 }
696
697 custom_min_height = p_height;
698
699 for (Cell &c : cells) {
700 c.cached_minimum_size_dirty = true;
701 }
702
703 _changed_notify();
704}
705
706int TreeItem::get_custom_minimum_height() const {
707 return custom_min_height;
708}
709
710/* Item manipulation */
711
712TreeItem *TreeItem::create_child(int p_index) {
713 TreeItem *ti = memnew(TreeItem(tree));
714 if (tree) {
715 ti->cells.resize(tree->columns.size());
716 tree->queue_redraw();
717 }
718
719 TreeItem *item_prev = nullptr;
720 TreeItem *item_next = first_child;
721
722 int idx = 0;
723 while (item_next) {
724 if (idx == p_index) {
725 item_next->prev = ti;
726 ti->next = item_next;
727 break;
728 }
729
730 item_prev = item_next;
731 item_next = item_next->next;
732 idx++;
733 }
734
735 if (item_prev) {
736 item_prev->next = ti;
737 ti->prev = item_prev;
738
739 if (!children_cache.is_empty()) {
740 if (ti->next) {
741 children_cache.insert(p_index, ti);
742 } else {
743 children_cache.append(ti);
744 }
745 }
746 } else {
747 first_child = ti;
748 if (!children_cache.is_empty()) {
749 children_cache.insert(0, ti);
750 }
751 }
752
753 ti->parent = this;
754
755 return ti;
756}
757
758void TreeItem::add_child(TreeItem *p_item) {
759 ERR_FAIL_NULL(p_item);
760 ERR_FAIL_COND(p_item->tree);
761 ERR_FAIL_COND(p_item->parent);
762
763 p_item->_change_tree(tree);
764 p_item->parent = this;
765
766 TreeItem *item_prev = first_child;
767 while (item_prev && item_prev->next) {
768 item_prev = item_prev->next;
769 }
770
771 if (item_prev) {
772 item_prev->next = p_item;
773 p_item->prev = item_prev;
774 } else {
775 first_child = p_item;
776 }
777
778 if (!children_cache.is_empty()) {
779 children_cache.append(p_item);
780 }
781
782 validate_cache();
783}
784
785void TreeItem::remove_child(TreeItem *p_item) {
786 ERR_FAIL_NULL(p_item);
787 ERR_FAIL_COND(p_item->parent != this);
788
789 p_item->_unlink_from_tree();
790 p_item->_change_tree(nullptr);
791 p_item->prev = nullptr;
792 p_item->next = nullptr;
793 p_item->parent = nullptr;
794
795 validate_cache();
796}
797
798Tree *TreeItem::get_tree() const {
799 return tree;
800}
801
802TreeItem *TreeItem::get_next() const {
803 return next;
804}
805
806TreeItem *TreeItem::get_prev() {
807 if (prev) {
808 return prev;
809 }
810
811 if (!parent || parent->first_child == this) {
812 return nullptr;
813 }
814 // This is an edge case
815 TreeItem *l_prev = parent->first_child;
816 while (l_prev && l_prev->next != this) {
817 l_prev = l_prev->next;
818 }
819
820 prev = l_prev;
821
822 return prev;
823}
824
825TreeItem *TreeItem::get_parent() const {
826 return parent;
827}
828
829TreeItem *TreeItem::get_first_child() const {
830 return first_child;
831}
832
833TreeItem *TreeItem::_get_prev_in_tree(bool p_wrap, bool p_include_invisible) {
834 TreeItem *current = this;
835
836 TreeItem *prev_item = current->get_prev();
837
838 if (!prev_item) {
839 current = current->parent;
840 if (current == tree->root && tree->hide_root) {
841 return nullptr;
842 } else if (!current) {
843 if (p_wrap) {
844 current = this;
845 TreeItem *temp = this->get_next_visible();
846 while (temp) {
847 current = temp;
848 temp = temp->get_next_visible();
849 }
850 } else {
851 return nullptr;
852 }
853 }
854 } else {
855 current = prev_item;
856 while ((!current->collapsed || p_include_invisible) && current->first_child) {
857 //go to the very end
858
859 current = current->first_child;
860 while (current->next) {
861 current = current->next;
862 }
863 }
864 }
865
866 return current;
867}
868
869TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
870 TreeItem *loop = this;
871 TreeItem *prev_item = this->_get_prev_in_tree(p_wrap);
872 while (prev_item && !prev_item->is_visible()) {
873 prev_item = prev_item->_get_prev_in_tree(p_wrap);
874 if (prev_item == loop) {
875 // Check that we haven't looped all the way around to the start.
876 prev_item = nullptr;
877 break;
878 }
879 }
880 return prev_item;
881}
882
883TreeItem *TreeItem::_get_next_in_tree(bool p_wrap, bool p_include_invisible) {
884 TreeItem *current = this;
885
886 if ((!current->collapsed || p_include_invisible) && current->first_child) {
887 current = current->first_child;
888
889 } else if (current->next) {
890 current = current->next;
891 } else {
892 while (current && !current->next) {
893 current = current->parent;
894 }
895
896 if (!current) {
897 if (p_wrap) {
898 return tree->root;
899 } else {
900 return nullptr;
901 }
902 } else {
903 current = current->next;
904 }
905 }
906
907 return current;
908}
909
910TreeItem *TreeItem::get_next_visible(bool p_wrap) {
911 TreeItem *loop = this;
912 TreeItem *next_item = this->_get_next_in_tree(p_wrap);
913 while (next_item && !next_item->is_visible()) {
914 next_item = next_item->_get_next_in_tree(p_wrap);
915 if (next_item == loop) {
916 // Check that we haven't looped all the way around to the start.
917 next_item = nullptr;
918 break;
919 }
920 }
921 return next_item;
922}
923
924TreeItem *TreeItem::get_prev_in_tree(bool p_wrap) {
925 TreeItem *prev_item = this->_get_prev_in_tree(p_wrap, true);
926 return prev_item;
927}
928
929TreeItem *TreeItem::get_next_in_tree(bool p_wrap) {
930 TreeItem *next_item = this->_get_next_in_tree(p_wrap, true);
931 return next_item;
932}
933
934TreeItem *TreeItem::get_child(int p_index) {
935 _create_children_cache();
936
937 if (p_index < 0) {
938 p_index += children_cache.size();
939 }
940 ERR_FAIL_INDEX_V(p_index, children_cache.size(), nullptr);
941
942 return children_cache.get(p_index);
943}
944
945int TreeItem::get_visible_child_count() {
946 _create_children_cache();
947 int visible_count = 0;
948 for (int i = 0; i < children_cache.size(); i++) {
949 if (children_cache[i]->is_visible()) {
950 visible_count += 1;
951 }
952 }
953 return visible_count;
954}
955
956int TreeItem::get_child_count() {
957 _create_children_cache();
958 return children_cache.size();
959}
960
961TypedArray<TreeItem> TreeItem::get_children() {
962 // Don't need to explicitly create children cache, because get_child_count creates it.
963 int size = get_child_count();
964 TypedArray<TreeItem> arr;
965 arr.resize(size);
966 for (int i = 0; i < size; i++) {
967 arr[i] = children_cache[i];
968 }
969
970 return arr;
971}
972
973void TreeItem::clear_children() {
974 TreeItem *c = first_child;
975 while (c) {
976 TreeItem *aux = c;
977 c = c->get_next();
978 aux->parent = nullptr; // So it won't try to recursively auto-remove from me in here.
979 memdelete(aux);
980 }
981
982 first_child = nullptr;
983};
984
985int TreeItem::get_index() {
986 int idx = 0;
987 TreeItem *c = this;
988
989 while (c) {
990 c = c->get_prev();
991 idx++;
992 }
993 return idx - 1;
994}
995
996#ifdef DEV_ENABLED
997void TreeItem::validate_cache() const {
998 if (!parent || parent->children_cache.is_empty()) {
999 return;
1000 }
1001 TreeItem *scan = parent->first_child;
1002 int index = 0;
1003 while (scan) {
1004 DEV_ASSERT(parent->children_cache[index] == scan);
1005 ++index;
1006 scan = scan->get_next();
1007 }
1008 DEV_ASSERT(index == parent->children_cache.size());
1009}
1010#endif
1011
1012void TreeItem::move_before(TreeItem *p_item) {
1013 ERR_FAIL_NULL(p_item);
1014 ERR_FAIL_COND(is_root);
1015 ERR_FAIL_NULL(p_item->parent);
1016
1017 if (p_item == this) {
1018 return;
1019 }
1020
1021 TreeItem *p = p_item->parent;
1022 while (p) {
1023 ERR_FAIL_COND_MSG(p == this, "Can't move to a descendant");
1024 p = p->parent;
1025 }
1026
1027 Tree *old_tree = tree;
1028 _unlink_from_tree();
1029 _change_tree(p_item->tree);
1030
1031 parent = p_item->parent;
1032
1033 TreeItem *item_prev = p_item->get_prev();
1034 if (item_prev) {
1035 item_prev->next = this;
1036 parent->children_cache.clear();
1037 } else {
1038 parent->first_child = this;
1039 // If the cache is empty, it has not been built but there
1040 // are items in the tree (note p_item != nullptr) so we cannot update it.
1041 if (!parent->children_cache.is_empty()) {
1042 parent->children_cache.insert(0, this);
1043 }
1044 }
1045
1046 prev = item_prev;
1047 next = p_item;
1048 p_item->prev = this;
1049
1050 if (tree && old_tree == tree) {
1051 tree->queue_redraw();
1052 }
1053
1054 validate_cache();
1055}
1056
1057void TreeItem::move_after(TreeItem *p_item) {
1058 ERR_FAIL_NULL(p_item);
1059 ERR_FAIL_COND(is_root);
1060 ERR_FAIL_NULL(p_item->parent);
1061
1062 if (p_item == this) {
1063 return;
1064 }
1065
1066 TreeItem *p = p_item->parent;
1067 while (p) {
1068 ERR_FAIL_COND_MSG(p == this, "Can't move to a descendant");
1069 p = p->parent;
1070 }
1071
1072 Tree *old_tree = tree;
1073 _unlink_from_tree();
1074 _change_tree(p_item->tree);
1075
1076 if (p_item->next) {
1077 p_item->next->prev = this;
1078 }
1079 parent = p_item->parent;
1080 prev = p_item;
1081 next = p_item->next;
1082 p_item->next = this;
1083
1084 if (next) {
1085 parent->children_cache.clear();
1086 } else {
1087 // If the cache is empty, it has not been built but there
1088 // are items in the tree (note p_item != nullptr,) so we cannot update it.
1089 if (!parent->children_cache.is_empty()) {
1090 parent->children_cache.append(this);
1091 }
1092 }
1093
1094 if (tree && old_tree == tree) {
1095 tree->queue_redraw();
1096 }
1097 validate_cache();
1098}
1099
1100void TreeItem::set_selectable(int p_column, bool p_selectable) {
1101 ERR_FAIL_INDEX(p_column, cells.size());
1102 cells.write[p_column].selectable = p_selectable;
1103}
1104
1105bool TreeItem::is_selectable(int p_column) const {
1106 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
1107 return cells[p_column].selectable;
1108}
1109
1110bool TreeItem::is_selected(int p_column) {
1111 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
1112 return cells[p_column].selectable && cells[p_column].selected;
1113}
1114
1115void TreeItem::set_as_cursor(int p_column) {
1116 ERR_FAIL_INDEX(p_column, cells.size());
1117 if (!tree) {
1118 return;
1119 }
1120 if (tree->select_mode != Tree::SELECT_MULTI) {
1121 return;
1122 }
1123 if (tree->selected_item == this && tree->selected_col == p_column) {
1124 return;
1125 }
1126 tree->selected_item = this;
1127 tree->selected_col = p_column;
1128 tree->queue_redraw();
1129}
1130
1131void TreeItem::select(int p_column) {
1132 ERR_FAIL_INDEX(p_column, cells.size());
1133 _cell_selected(p_column);
1134}
1135
1136void TreeItem::deselect(int p_column) {
1137 ERR_FAIL_INDEX(p_column, cells.size());
1138 _cell_deselected(p_column);
1139}
1140
1141void TreeItem::add_button(int p_column, const Ref<Texture2D> &p_button, int p_id, bool p_disabled, const String &p_tooltip) {
1142 ERR_FAIL_INDEX(p_column, cells.size());
1143 ERR_FAIL_COND(!p_button.is_valid());
1144 TreeItem::Cell::Button button;
1145 button.texture = p_button;
1146 if (p_id < 0) {
1147 p_id = cells[p_column].buttons.size();
1148 }
1149 button.id = p_id;
1150 button.disabled = p_disabled;
1151 button.tooltip = p_tooltip;
1152 cells.write[p_column].buttons.push_back(button);
1153 cells.write[p_column].cached_minimum_size_dirty = true;
1154
1155 _changed_notify(p_column);
1156}
1157
1158int TreeItem::get_button_count(int p_column) const {
1159 ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
1160 return cells[p_column].buttons.size();
1161}
1162
1163Ref<Texture2D> TreeItem::get_button(int p_column, int p_index) const {
1164 ERR_FAIL_INDEX_V(p_column, cells.size(), Ref<Texture2D>());
1165 ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), Ref<Texture2D>());
1166 return cells[p_column].buttons[p_index].texture;
1167}
1168
1169String TreeItem::get_button_tooltip_text(int p_column, int p_index) const {
1170 ERR_FAIL_INDEX_V(p_column, cells.size(), String());
1171 ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), String());
1172 return cells[p_column].buttons[p_index].tooltip;
1173}
1174
1175int TreeItem::get_button_id(int p_column, int p_index) const {
1176 ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
1177 ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), -1);
1178 return cells[p_column].buttons[p_index].id;
1179}
1180
1181void TreeItem::erase_button(int p_column, int p_index) {
1182 ERR_FAIL_INDEX(p_column, cells.size());
1183 ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
1184 cells.write[p_column].buttons.remove_at(p_index);
1185 _changed_notify(p_column);
1186}
1187
1188int TreeItem::get_button_by_id(int p_column, int p_id) const {
1189 ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
1190 for (int i = 0; i < cells[p_column].buttons.size(); i++) {
1191 if (cells[p_column].buttons[i].id == p_id) {
1192 return i;
1193 }
1194 }
1195
1196 return -1;
1197}
1198
1199void TreeItem::set_button_tooltip_text(int p_column, int p_index, const String &p_tooltip) {
1200 ERR_FAIL_INDEX(p_column, cells.size());
1201 ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
1202 cells.write[p_column].buttons.write[p_index].tooltip = p_tooltip;
1203}
1204
1205void TreeItem::set_button(int p_column, int p_index, const Ref<Texture2D> &p_button) {
1206 ERR_FAIL_COND(p_button.is_null());
1207 ERR_FAIL_INDEX(p_column, cells.size());
1208 ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
1209
1210 if (cells[p_column].buttons[p_index].texture == p_button) {
1211 return;
1212 }
1213
1214 cells.write[p_column].buttons.write[p_index].texture = p_button;
1215 cells.write[p_column].cached_minimum_size_dirty = true;
1216
1217 _changed_notify(p_column);
1218}
1219
1220void TreeItem::set_button_color(int p_column, int p_index, const Color &p_color) {
1221 ERR_FAIL_INDEX(p_column, cells.size());
1222 ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
1223
1224 if (cells[p_column].buttons[p_index].color == p_color) {
1225 return;
1226 }
1227
1228 cells.write[p_column].buttons.write[p_index].color = p_color;
1229 _changed_notify(p_column);
1230}
1231
1232void TreeItem::set_button_disabled(int p_column, int p_index, bool p_disabled) {
1233 ERR_FAIL_INDEX(p_column, cells.size());
1234 ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size());
1235
1236 if (cells[p_column].buttons[p_index].disabled == p_disabled) {
1237 return;
1238 }
1239
1240 cells.write[p_column].buttons.write[p_index].disabled = p_disabled;
1241 cells.write[p_column].cached_minimum_size_dirty = true;
1242
1243 _changed_notify(p_column);
1244}
1245
1246bool TreeItem::is_button_disabled(int p_column, int p_index) const {
1247 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
1248 ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), false);
1249
1250 return cells[p_column].buttons[p_index].disabled;
1251}
1252
1253void TreeItem::set_editable(int p_column, bool p_editable) {
1254 ERR_FAIL_INDEX(p_column, cells.size());
1255
1256 if (cells[p_column].editable == p_editable) {
1257 return;
1258 }
1259
1260 cells.write[p_column].editable = p_editable;
1261 cells.write[p_column].cached_minimum_size_dirty = true;
1262
1263 _changed_notify(p_column);
1264}
1265
1266bool TreeItem::is_editable(int p_column) {
1267 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
1268 return cells[p_column].editable;
1269}
1270
1271void TreeItem::set_custom_color(int p_column, const Color &p_color) {
1272 ERR_FAIL_INDEX(p_column, cells.size());
1273
1274 if (cells[p_column].custom_color && cells[p_column].color == p_color) {
1275 return;
1276 }
1277
1278 cells.write[p_column].custom_color = true;
1279 cells.write[p_column].color = p_color;
1280 _changed_notify(p_column);
1281}
1282
1283Color TreeItem::get_custom_color(int p_column) const {
1284 ERR_FAIL_INDEX_V(p_column, cells.size(), Color());
1285 if (!cells[p_column].custom_color) {
1286 return Color();
1287 }
1288 return cells[p_column].color;
1289}
1290
1291void TreeItem::clear_custom_color(int p_column) {
1292 ERR_FAIL_INDEX(p_column, cells.size());
1293 cells.write[p_column].custom_color = false;
1294 cells.write[p_column].color = Color();
1295 _changed_notify(p_column);
1296}
1297
1298void TreeItem::set_custom_font(int p_column, const Ref<Font> &p_font) {
1299 ERR_FAIL_INDEX(p_column, cells.size());
1300
1301 cells.write[p_column].custom_font = p_font;
1302 cells.write[p_column].cached_minimum_size_dirty = true;
1303}
1304
1305Ref<Font> TreeItem::get_custom_font(int p_column) const {
1306 ERR_FAIL_INDEX_V(p_column, cells.size(), Ref<Font>());
1307 return cells[p_column].custom_font;
1308}
1309
1310void TreeItem::set_custom_font_size(int p_column, int p_font_size) {
1311 ERR_FAIL_INDEX(p_column, cells.size());
1312
1313 cells.write[p_column].custom_font_size = p_font_size;
1314 cells.write[p_column].cached_minimum_size_dirty = true;
1315}
1316
1317int TreeItem::get_custom_font_size(int p_column) const {
1318 ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
1319 return cells[p_column].custom_font_size;
1320}
1321
1322void TreeItem::set_tooltip_text(int p_column, const String &p_tooltip) {
1323 ERR_FAIL_INDEX(p_column, cells.size());
1324 cells.write[p_column].tooltip = p_tooltip;
1325}
1326
1327String TreeItem::get_tooltip_text(int p_column) const {
1328 ERR_FAIL_INDEX_V(p_column, cells.size(), "");
1329 return cells[p_column].tooltip;
1330}
1331
1332void TreeItem::set_custom_bg_color(int p_column, const Color &p_color, bool p_bg_outline) {
1333 ERR_FAIL_INDEX(p_column, cells.size());
1334
1335 if (cells[p_column].custom_bg_color && cells[p_column].custom_bg_outline == p_bg_outline && cells[p_column].bg_color == p_color) {
1336 return;
1337 }
1338
1339 cells.write[p_column].custom_bg_color = true;
1340 cells.write[p_column].custom_bg_outline = p_bg_outline;
1341 cells.write[p_column].bg_color = p_color;
1342 _changed_notify(p_column);
1343}
1344
1345void TreeItem::clear_custom_bg_color(int p_column) {
1346 ERR_FAIL_INDEX(p_column, cells.size());
1347 cells.write[p_column].custom_bg_color = false;
1348 cells.write[p_column].bg_color = Color();
1349 _changed_notify(p_column);
1350}
1351
1352Color TreeItem::get_custom_bg_color(int p_column) const {
1353 ERR_FAIL_INDEX_V(p_column, cells.size(), Color());
1354 if (!cells[p_column].custom_bg_color) {
1355 return Color();
1356 }
1357 return cells[p_column].bg_color;
1358}
1359
1360void TreeItem::set_custom_as_button(int p_column, bool p_button) {
1361 ERR_FAIL_INDEX(p_column, cells.size());
1362
1363 cells.write[p_column].custom_button = p_button;
1364 cells.write[p_column].cached_minimum_size_dirty = true;
1365}
1366
1367bool TreeItem::is_custom_set_as_button(int p_column) const {
1368 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
1369 return cells[p_column].custom_button;
1370}
1371
1372void TreeItem::set_text_alignment(int p_column, HorizontalAlignment p_alignment) {
1373 ERR_FAIL_INDEX(p_column, cells.size());
1374
1375 if (cells[p_column].text_alignment == p_alignment) {
1376 return;
1377 }
1378
1379 cells.write[p_column].text_alignment = p_alignment;
1380 cells.write[p_column].cached_minimum_size_dirty = true;
1381
1382 _changed_notify(p_column);
1383}
1384
1385HorizontalAlignment TreeItem::get_text_alignment(int p_column) const {
1386 ERR_FAIL_INDEX_V(p_column, cells.size(), HORIZONTAL_ALIGNMENT_LEFT);
1387 return cells[p_column].text_alignment;
1388}
1389
1390void TreeItem::set_expand_right(int p_column, bool p_enable) {
1391 ERR_FAIL_INDEX(p_column, cells.size());
1392
1393 if (cells[p_column].expand_right == p_enable) {
1394 return;
1395 }
1396
1397 cells.write[p_column].expand_right = p_enable;
1398 cells.write[p_column].cached_minimum_size_dirty = true;
1399
1400 _changed_notify(p_column);
1401}
1402
1403bool TreeItem::get_expand_right(int p_column) const {
1404 ERR_FAIL_INDEX_V(p_column, cells.size(), false);
1405 return cells[p_column].expand_right;
1406}
1407
1408void TreeItem::set_disable_folding(bool p_disable) {
1409 if (disable_folding == p_disable) {
1410 return;
1411 }
1412
1413 disable_folding = p_disable;
1414
1415 for (Cell &c : cells) {
1416 c.cached_minimum_size_dirty = true;
1417 }
1418
1419 _changed_notify(0);
1420}
1421
1422bool TreeItem::is_folding_disabled() const {
1423 return disable_folding;
1424}
1425
1426Size2 TreeItem::get_minimum_size(int p_column) {
1427 ERR_FAIL_INDEX_V(p_column, cells.size(), Size2());
1428 Tree *parent_tree = get_tree();
1429 ERR_FAIL_NULL_V(parent_tree, Size2());
1430
1431 const TreeItem::Cell &cell = cells[p_column];
1432
1433 if (cell.cached_minimum_size_dirty) {
1434 Size2 size;
1435
1436 // Text.
1437 if (!cell.text.is_empty()) {
1438 if (cell.dirty) {
1439 parent_tree->update_item_cell(this, p_column);
1440 }
1441 Size2 text_size = cell.text_buf->get_size();
1442 size.width += text_size.width;
1443 size.height = MAX(size.height, text_size.height);
1444 }
1445
1446 // Icon.
1447 if (cell.mode == CELL_MODE_CHECK) {
1448 size.width += parent_tree->theme_cache.checked->get_width() + parent_tree->theme_cache.h_separation;
1449 }
1450 if (cell.icon.is_valid()) {
1451 Size2i icon_size = parent_tree->_get_cell_icon_size(cell);
1452 size.width += icon_size.width + parent_tree->theme_cache.h_separation;
1453 size.height = MAX(size.height, icon_size.height);
1454 }
1455
1456 // Buttons.
1457 for (int i = 0; i < cell.buttons.size(); i++) {
1458 Ref<Texture2D> texture = cell.buttons[i].texture;
1459 if (texture.is_valid()) {
1460 Size2 button_size = texture->get_size() + parent_tree->theme_cache.button_pressed->get_minimum_size();
1461 size.width += button_size.width;
1462 size.height = MAX(size.height, button_size.height);
1463 }
1464 }
1465 if (cell.buttons.size() >= 2) {
1466 size.width += (cell.buttons.size() - 1) * parent_tree->theme_cache.button_margin;
1467 }
1468
1469 cells.write[p_column].cached_minimum_size = size;
1470 cells.write[p_column].cached_minimum_size_dirty = false;
1471 }
1472
1473 return cell.cached_minimum_size;
1474}
1475
1476void TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
1477 if (p_argcount < 1) {
1478 r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
1479 r_error.argument = 0;
1480 return;
1481 }
1482
1483 if (p_args[0]->get_type() != Variant::STRING && p_args[0]->get_type() != Variant::STRING_NAME) {
1484 r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
1485 r_error.argument = 0;
1486 r_error.expected = Variant::STRING_NAME;
1487 return;
1488 }
1489
1490 StringName method = *p_args[0];
1491
1492 call_recursive(method, &p_args[1], p_argcount - 1, r_error);
1493}
1494
1495void recursive_call_aux(TreeItem *p_item, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
1496 if (!p_item) {
1497 return;
1498 }
1499 p_item->callp(p_method, p_args, p_argcount, r_error);
1500 TreeItem *c = p_item->get_first_child();
1501 while (c) {
1502 recursive_call_aux(c, p_method, p_args, p_argcount, r_error);
1503 c = c->get_next();
1504 }
1505}
1506
1507void TreeItem::call_recursive(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
1508 recursive_call_aux(this, p_method, p_args, p_argcount, r_error);
1509}
1510
1511void TreeItem::_bind_methods() {
1512 ClassDB::bind_method(D_METHOD("set_cell_mode", "column", "mode"), &TreeItem::set_cell_mode);
1513 ClassDB::bind_method(D_METHOD("get_cell_mode", "column"), &TreeItem::get_cell_mode);
1514
1515 ClassDB::bind_method(D_METHOD("set_edit_multiline", "column", "multiline"), &TreeItem::set_edit_multiline);
1516 ClassDB::bind_method(D_METHOD("is_edit_multiline", "column"), &TreeItem::is_edit_multiline);
1517
1518 ClassDB::bind_method(D_METHOD("set_checked", "column", "checked"), &TreeItem::set_checked);
1519 ClassDB::bind_method(D_METHOD("set_indeterminate", "column", "indeterminate"), &TreeItem::set_indeterminate);
1520 ClassDB::bind_method(D_METHOD("is_checked", "column"), &TreeItem::is_checked);
1521 ClassDB::bind_method(D_METHOD("is_indeterminate", "column"), &TreeItem::is_indeterminate);
1522
1523 ClassDB::bind_method(D_METHOD("propagate_check", "column", "emit_signal"), &TreeItem::propagate_check, DEFVAL(true));
1524
1525 ClassDB::bind_method(D_METHOD("set_text", "column", "text"), &TreeItem::set_text);
1526 ClassDB::bind_method(D_METHOD("get_text", "column"), &TreeItem::get_text);
1527
1528 ClassDB::bind_method(D_METHOD("set_text_direction", "column", "direction"), &TreeItem::set_text_direction);
1529 ClassDB::bind_method(D_METHOD("get_text_direction", "column"), &TreeItem::get_text_direction);
1530
1531 ClassDB::bind_method(D_METHOD("set_autowrap_mode", "column", "autowrap_mode"), &TreeItem::set_autowrap_mode);
1532 ClassDB::bind_method(D_METHOD("get_autowrap_mode", "column"), &TreeItem::get_autowrap_mode);
1533
1534 ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "column", "overrun_behavior"), &TreeItem::set_text_overrun_behavior);
1535 ClassDB::bind_method(D_METHOD("get_text_overrun_behavior", "column"), &TreeItem::get_text_overrun_behavior);
1536
1537 ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "column", "parser"), &TreeItem::set_structured_text_bidi_override);
1538 ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override", "column"), &TreeItem::get_structured_text_bidi_override);
1539
1540 ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "column", "args"), &TreeItem::set_structured_text_bidi_override_options);
1541 ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options", "column"), &TreeItem::get_structured_text_bidi_override_options);
1542
1543 ClassDB::bind_method(D_METHOD("set_language", "column", "language"), &TreeItem::set_language);
1544 ClassDB::bind_method(D_METHOD("get_language", "column"), &TreeItem::get_language);
1545
1546 ClassDB::bind_method(D_METHOD("set_suffix", "column", "text"), &TreeItem::set_suffix);
1547 ClassDB::bind_method(D_METHOD("get_suffix", "column"), &TreeItem::get_suffix);
1548
1549 ClassDB::bind_method(D_METHOD("set_icon", "column", "texture"), &TreeItem::set_icon);
1550 ClassDB::bind_method(D_METHOD("get_icon", "column"), &TreeItem::get_icon);
1551
1552 ClassDB::bind_method(D_METHOD("set_icon_region", "column", "region"), &TreeItem::set_icon_region);
1553 ClassDB::bind_method(D_METHOD("get_icon_region", "column"), &TreeItem::get_icon_region);
1554
1555 ClassDB::bind_method(D_METHOD("set_icon_max_width", "column", "width"), &TreeItem::set_icon_max_width);
1556 ClassDB::bind_method(D_METHOD("get_icon_max_width", "column"), &TreeItem::get_icon_max_width);
1557
1558 ClassDB::bind_method(D_METHOD("set_icon_modulate", "column", "modulate"), &TreeItem::set_icon_modulate);
1559 ClassDB::bind_method(D_METHOD("get_icon_modulate", "column"), &TreeItem::get_icon_modulate);
1560
1561 ClassDB::bind_method(D_METHOD("set_range", "column", "value"), &TreeItem::set_range);
1562 ClassDB::bind_method(D_METHOD("get_range", "column"), &TreeItem::get_range);
1563 ClassDB::bind_method(D_METHOD("set_range_config", "column", "min", "max", "step", "expr"), &TreeItem::set_range_config, DEFVAL(false));
1564 ClassDB::bind_method(D_METHOD("get_range_config", "column"), &TreeItem::_get_range_config);
1565
1566 ClassDB::bind_method(D_METHOD("set_metadata", "column", "meta"), &TreeItem::set_metadata);
1567 ClassDB::bind_method(D_METHOD("get_metadata", "column"), &TreeItem::get_metadata);
1568
1569 ClassDB::bind_method(D_METHOD("set_custom_draw", "column", "object", "callback"), &TreeItem::set_custom_draw);
1570
1571 ClassDB::bind_method(D_METHOD("set_collapsed", "enable"), &TreeItem::set_collapsed);
1572 ClassDB::bind_method(D_METHOD("is_collapsed"), &TreeItem::is_collapsed);
1573
1574 ClassDB::bind_method(D_METHOD("set_collapsed_recursive", "enable"), &TreeItem::set_collapsed_recursive);
1575 ClassDB::bind_method(D_METHOD("is_any_collapsed", "only_visible"), &TreeItem::is_any_collapsed, DEFVAL(false));
1576
1577 ClassDB::bind_method(D_METHOD("set_visible", "enable"), &TreeItem::set_visible);
1578 ClassDB::bind_method(D_METHOD("is_visible"), &TreeItem::is_visible);
1579
1580 ClassDB::bind_method(D_METHOD("uncollapse_tree"), &TreeItem::uncollapse_tree);
1581
1582 ClassDB::bind_method(D_METHOD("set_custom_minimum_height", "height"), &TreeItem::set_custom_minimum_height);
1583 ClassDB::bind_method(D_METHOD("get_custom_minimum_height"), &TreeItem::get_custom_minimum_height);
1584
1585 ClassDB::bind_method(D_METHOD("set_selectable", "column", "selectable"), &TreeItem::set_selectable);
1586 ClassDB::bind_method(D_METHOD("is_selectable", "column"), &TreeItem::is_selectable);
1587
1588 ClassDB::bind_method(D_METHOD("is_selected", "column"), &TreeItem::is_selected);
1589 ClassDB::bind_method(D_METHOD("select", "column"), &TreeItem::select);
1590 ClassDB::bind_method(D_METHOD("deselect", "column"), &TreeItem::deselect);
1591
1592 ClassDB::bind_method(D_METHOD("set_editable", "column", "enabled"), &TreeItem::set_editable);
1593 ClassDB::bind_method(D_METHOD("is_editable", "column"), &TreeItem::is_editable);
1594
1595 ClassDB::bind_method(D_METHOD("set_custom_color", "column", "color"), &TreeItem::set_custom_color);
1596 ClassDB::bind_method(D_METHOD("get_custom_color", "column"), &TreeItem::get_custom_color);
1597 ClassDB::bind_method(D_METHOD("clear_custom_color", "column"), &TreeItem::clear_custom_color);
1598
1599 ClassDB::bind_method(D_METHOD("set_custom_font", "column", "font"), &TreeItem::set_custom_font);
1600 ClassDB::bind_method(D_METHOD("get_custom_font", "column"), &TreeItem::get_custom_font);
1601
1602 ClassDB::bind_method(D_METHOD("set_custom_font_size", "column", "font_size"), &TreeItem::set_custom_font_size);
1603 ClassDB::bind_method(D_METHOD("get_custom_font_size", "column"), &TreeItem::get_custom_font_size);
1604
1605 ClassDB::bind_method(D_METHOD("set_custom_bg_color", "column", "color", "just_outline"), &TreeItem::set_custom_bg_color, DEFVAL(false));
1606 ClassDB::bind_method(D_METHOD("clear_custom_bg_color", "column"), &TreeItem::clear_custom_bg_color);
1607 ClassDB::bind_method(D_METHOD("get_custom_bg_color", "column"), &TreeItem::get_custom_bg_color);
1608
1609 ClassDB::bind_method(D_METHOD("set_custom_as_button", "column", "enable"), &TreeItem::set_custom_as_button);
1610 ClassDB::bind_method(D_METHOD("is_custom_set_as_button", "column"), &TreeItem::is_custom_set_as_button);
1611
1612 ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip_text"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
1613 ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count);
1614 ClassDB::bind_method(D_METHOD("get_button_tooltip_text", "column", "button_index"), &TreeItem::get_button_tooltip_text);
1615 ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_index"), &TreeItem::get_button_id);
1616 ClassDB::bind_method(D_METHOD("get_button_by_id", "column", "id"), &TreeItem::get_button_by_id);
1617 ClassDB::bind_method(D_METHOD("get_button", "column", "button_index"), &TreeItem::get_button);
1618 ClassDB::bind_method(D_METHOD("set_button_tooltip_text", "column", "button_index", "tooltip"), &TreeItem::set_button_tooltip_text);
1619 ClassDB::bind_method(D_METHOD("set_button", "column", "button_index", "button"), &TreeItem::set_button);
1620 ClassDB::bind_method(D_METHOD("erase_button", "column", "button_index"), &TreeItem::erase_button);
1621 ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_index", "disabled"), &TreeItem::set_button_disabled);
1622 ClassDB::bind_method(D_METHOD("set_button_color", "column", "button_index", "color"), &TreeItem::set_button_color);
1623 ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_index"), &TreeItem::is_button_disabled);
1624
1625 ClassDB::bind_method(D_METHOD("set_tooltip_text", "column", "tooltip"), &TreeItem::set_tooltip_text);
1626 ClassDB::bind_method(D_METHOD("get_tooltip_text", "column"), &TreeItem::get_tooltip_text);
1627 ClassDB::bind_method(D_METHOD("set_text_alignment", "column", "text_alignment"), &TreeItem::set_text_alignment);
1628 ClassDB::bind_method(D_METHOD("get_text_alignment", "column"), &TreeItem::get_text_alignment);
1629
1630 ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right);
1631 ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right);
1632
1633 ClassDB::bind_method(D_METHOD("set_disable_folding", "disable"), &TreeItem::set_disable_folding);
1634 ClassDB::bind_method(D_METHOD("is_folding_disabled"), &TreeItem::is_folding_disabled);
1635
1636 ClassDB::bind_method(D_METHOD("create_child", "index"), &TreeItem::create_child, DEFVAL(-1));
1637 ClassDB::bind_method(D_METHOD("add_child", "child"), &TreeItem::add_child);
1638 ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::remove_child);
1639
1640 ClassDB::bind_method(D_METHOD("get_tree"), &TreeItem::get_tree);
1641
1642 ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next);
1643 ClassDB::bind_method(D_METHOD("get_prev"), &TreeItem::get_prev);
1644 ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent);
1645 ClassDB::bind_method(D_METHOD("get_first_child"), &TreeItem::get_first_child);
1646
1647 ClassDB::bind_method(D_METHOD("get_next_in_tree", "wrap"), &TreeItem::get_next_in_tree, DEFVAL(false));
1648 ClassDB::bind_method(D_METHOD("get_prev_in_tree", "wrap"), &TreeItem::get_prev_in_tree, DEFVAL(false));
1649
1650 ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false));
1651 ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false));
1652
1653 ClassDB::bind_method(D_METHOD("get_child", "index"), &TreeItem::get_child);
1654 ClassDB::bind_method(D_METHOD("get_child_count"), &TreeItem::get_child_count);
1655 ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
1656 ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index);
1657
1658 ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::move_before);
1659 ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::move_after);
1660
1661 {
1662 MethodInfo mi;
1663 mi.name = "call_recursive";
1664 mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method"));
1665
1666 ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_recursive", &TreeItem::_call_recursive_bind, mi);
1667 }
1668
1669 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed");
1670 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
1671 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_folding"), "set_disable_folding", "is_folding_disabled");
1672 ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_minimum_height", PROPERTY_HINT_RANGE, "0,1000,1"), "set_custom_minimum_height", "get_custom_minimum_height");
1673
1674 BIND_ENUM_CONSTANT(CELL_MODE_STRING);
1675 BIND_ENUM_CONSTANT(CELL_MODE_CHECK);
1676 BIND_ENUM_CONSTANT(CELL_MODE_RANGE);
1677 BIND_ENUM_CONSTANT(CELL_MODE_ICON);
1678 BIND_ENUM_CONSTANT(CELL_MODE_CUSTOM);
1679}
1680
1681TreeItem::TreeItem(Tree *p_tree) {
1682 tree = p_tree;
1683}
1684
1685TreeItem::~TreeItem() {
1686 _unlink_from_tree();
1687 _change_tree(nullptr);
1688
1689 validate_cache();
1690 prev = nullptr;
1691 clear_children();
1692}
1693
1694/**********************************************/
1695/**********************************************/
1696/**********************************************/
1697/**********************************************/
1698/**********************************************/
1699/**********************************************/
1700
1701void Tree::_update_theme_item_cache() {
1702 Control::_update_theme_item_cache();
1703
1704 theme_cache.base_scale = get_theme_default_base_scale();
1705}
1706
1707Size2 Tree::_get_cell_icon_size(const TreeItem::Cell &p_cell) const {
1708 Size2i icon_size = p_cell.get_icon_size();
1709
1710 int max_width = 0;
1711 if (theme_cache.icon_max_width > 0) {
1712 max_width = theme_cache.icon_max_width;
1713 }
1714 if (p_cell.icon_max_w > 0 && (max_width == 0 || p_cell.icon_max_w < max_width)) {
1715 max_width = p_cell.icon_max_w;
1716 }
1717
1718 if (max_width > 0 && icon_size.width > max_width) {
1719 icon_size.height = icon_size.height * max_width / icon_size.width;
1720 icon_size.width = max_width;
1721 }
1722
1723 return icon_size;
1724}
1725
1726int Tree::compute_item_height(TreeItem *p_item) const {
1727 if ((p_item == root && hide_root) || !p_item->is_visible()) {
1728 return 0;
1729 }
1730
1731 ERR_FAIL_COND_V(theme_cache.font.is_null(), 0);
1732 int height = 0;
1733
1734 for (int i = 0; i < columns.size(); i++) {
1735 if (p_item->cells[i].dirty) {
1736 const_cast<Tree *>(this)->update_item_cell(p_item, i);
1737 }
1738 height = MAX(height, p_item->cells[i].text_buf->get_size().y);
1739 for (int j = 0; j < p_item->cells[i].buttons.size(); j++) {
1740 Size2i s; // = cache.button_pressed->get_minimum_size();
1741 s += p_item->cells[i].buttons[j].texture->get_size();
1742 if (s.height > height) {
1743 height = s.height;
1744 }
1745 }
1746
1747 switch (p_item->cells[i].mode) {
1748 case TreeItem::CELL_MODE_CHECK: {
1749 int check_icon_h = theme_cache.checked->get_height();
1750 if (height < check_icon_h) {
1751 height = check_icon_h;
1752 }
1753 [[fallthrough]];
1754 }
1755 case TreeItem::CELL_MODE_STRING:
1756 case TreeItem::CELL_MODE_CUSTOM:
1757 case TreeItem::CELL_MODE_ICON: {
1758 Ref<Texture2D> icon = p_item->cells[i].icon;
1759 if (!icon.is_null()) {
1760 Size2i s = _get_cell_icon_size(p_item->cells[i]);
1761 if (s.height > height) {
1762 height = s.height;
1763 }
1764 }
1765 if (p_item->cells[i].mode == TreeItem::CELL_MODE_CUSTOM && p_item->cells[i].custom_button) {
1766 height += theme_cache.custom_button->get_minimum_size().height;
1767 }
1768
1769 } break;
1770 default: {
1771 }
1772 }
1773 }
1774 int item_min_height = MAX(theme_cache.font->get_height(theme_cache.font_size), p_item->get_custom_minimum_height());
1775 if (height < item_min_height) {
1776 height = item_min_height;
1777 }
1778
1779 height += theme_cache.v_separation;
1780
1781 return height;
1782}
1783
1784int Tree::get_item_height(TreeItem *p_item) const {
1785 if (!p_item->is_visible()) {
1786 return 0;
1787 }
1788 int height = compute_item_height(p_item);
1789 height += theme_cache.v_separation;
1790
1791 if (!p_item->collapsed) { /* if not collapsed, check the children */
1792
1793 TreeItem *c = p_item->first_child;
1794
1795 while (c) {
1796 height += get_item_height(c);
1797
1798 c = c->next;
1799 }
1800 }
1801
1802 return height;
1803}
1804
1805void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color) {
1806 ERR_FAIL_COND(theme_cache.font.is_null());
1807
1808 Rect2i rect = p_rect.grow_individual(-theme_cache.inner_item_margin_left, -theme_cache.inner_item_margin_top, -theme_cache.inner_item_margin_right, -theme_cache.inner_item_margin_bottom);
1809 Size2 ts = p_cell.text_buf->get_size();
1810 bool rtl = is_layout_rtl();
1811
1812 int w = 0;
1813 Size2i bmsize;
1814 if (!p_cell.icon.is_null()) {
1815 bmsize = _get_cell_icon_size(p_cell);
1816 w += bmsize.width + theme_cache.h_separation;
1817 if (rect.size.width > 0 && (w + ts.width) > rect.size.width) {
1818 ts.width = rect.size.width - w;
1819 }
1820 }
1821 w += ts.width;
1822
1823 switch (p_cell.text_alignment) {
1824 case HORIZONTAL_ALIGNMENT_FILL:
1825 case HORIZONTAL_ALIGNMENT_LEFT: {
1826 if (rtl) {
1827 rect.position.x += MAX(0, (rect.size.width - w));
1828 }
1829 } break;
1830 case HORIZONTAL_ALIGNMENT_CENTER:
1831 rect.position.x += MAX(0, (rect.size.width - w) / 2);
1832 break;
1833 case HORIZONTAL_ALIGNMENT_RIGHT:
1834 if (!rtl) {
1835 rect.position.x += MAX(0, (rect.size.width - w));
1836 }
1837 break;
1838 }
1839
1840 RID ci = get_canvas_item();
1841
1842 if (rtl && rect.size.width > 0) {
1843 Point2 draw_pos = rect.position;
1844 draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) * 0.5);
1845 if (p_ol_size > 0 && p_ol_color.a > 0) {
1846 p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
1847 }
1848 p_cell.text_buf->draw(ci, draw_pos, p_color);
1849 rect.position.x += ts.width + theme_cache.h_separation;
1850 rect.size.x -= ts.width + theme_cache.h_separation;
1851 }
1852
1853 if (!p_cell.icon.is_null()) {
1854 p_cell.draw_icon(ci, rect.position + Size2i(0, Math::floor((real_t)(rect.size.y - bmsize.y) / 2)), bmsize, p_icon_color);
1855 rect.position.x += bmsize.x + theme_cache.h_separation;
1856 rect.size.x -= bmsize.x + theme_cache.h_separation;
1857 }
1858
1859 if (!rtl && rect.size.width > 0) {
1860 Point2 draw_pos = rect.position;
1861 draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) * 0.5);
1862 if (p_ol_size > 0 && p_ol_color.a > 0) {
1863 p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
1864 }
1865 p_cell.text_buf->draw(ci, draw_pos, p_color);
1866 }
1867}
1868
1869void Tree::update_column(int p_col) {
1870 columns.write[p_col].text_buf->clear();
1871 if (columns[p_col].text_direction == Control::TEXT_DIRECTION_INHERITED) {
1872 columns.write[p_col].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
1873 } else {
1874 columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction);
1875 }
1876
1877 columns.write[p_col].text_buf->add_string(columns[p_col].title, theme_cache.tb_font, theme_cache.tb_font_size, columns[p_col].language);
1878 columns.write[p_col].cached_minimum_width_dirty = true;
1879}
1880
1881void Tree::update_item_cell(TreeItem *p_item, int p_col) {
1882 String valtext;
1883
1884 p_item->cells.write[p_col].text_buf->clear();
1885 if (p_item->cells[p_col].mode == TreeItem::CELL_MODE_RANGE) {
1886 if (!p_item->cells[p_col].text.is_empty()) {
1887 if (!p_item->cells[p_col].editable) {
1888 return;
1889 }
1890
1891 int option = (int)p_item->cells[p_col].val;
1892
1893 valtext = RTR("(Other)");
1894 Vector<String> strings = p_item->cells[p_col].text.split(",");
1895 for (int j = 0; j < strings.size(); j++) {
1896 int value = j;
1897 if (!strings[j].get_slicec(':', 1).is_empty()) {
1898 value = strings[j].get_slicec(':', 1).to_int();
1899 }
1900 if (option == value) {
1901 valtext = strings[j].get_slicec(':', 0);
1902 break;
1903 }
1904 }
1905
1906 } else {
1907 valtext = String::num(p_item->cells[p_col].val, Math::range_step_decimals(p_item->cells[p_col].step));
1908 }
1909 } else {
1910 valtext = p_item->cells[p_col].text;
1911 }
1912
1913 if (!p_item->cells[p_col].suffix.is_empty()) {
1914 valtext += " " + p_item->cells[p_col].suffix;
1915 }
1916
1917 if (p_item->cells[p_col].text_direction == Control::TEXT_DIRECTION_INHERITED) {
1918 p_item->cells.write[p_col].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
1919 } else {
1920 p_item->cells.write[p_col].text_buf->set_direction((TextServer::Direction)p_item->cells[p_col].text_direction);
1921 }
1922
1923 Ref<Font> font;
1924 if (p_item->cells[p_col].custom_font.is_valid()) {
1925 font = p_item->cells[p_col].custom_font;
1926 } else {
1927 font = theme_cache.font;
1928 }
1929
1930 int font_size;
1931 if (p_item->cells[p_col].custom_font_size > 0) {
1932 font_size = p_item->cells[p_col].custom_font_size;
1933 } else {
1934 font_size = theme_cache.font_size;
1935 }
1936 p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language);
1937
1938 BitField<TextServer::LineBreakFlag> break_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES;
1939 switch (p_item->cells.write[p_col].autowrap_mode) {
1940 case TextServer::AUTOWRAP_OFF:
1941 break;
1942 case TextServer::AUTOWRAP_ARBITRARY:
1943 break_flags.set_flag(TextServer::BREAK_GRAPHEME_BOUND);
1944 break;
1945 case TextServer::AUTOWRAP_WORD:
1946 break_flags.set_flag(TextServer::BREAK_WORD_BOUND);
1947 break;
1948 case TextServer::AUTOWRAP_WORD_SMART:
1949 break_flags.set_flag(TextServer::BREAK_WORD_BOUND);
1950 break_flags.set_flag(TextServer::BREAK_ADAPTIVE);
1951 break;
1952 }
1953 p_item->cells.write[p_col].text_buf->set_break_flags(break_flags);
1954
1955 TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext));
1956 p_item->cells.write[p_col].dirty = false;
1957}
1958
1959void Tree::update_item_cache(TreeItem *p_item) {
1960 for (int i = 0; i < p_item->cells.size(); i++) {
1961 update_item_cell(p_item, i);
1962 }
1963
1964 TreeItem *c = p_item->first_child;
1965 while (c) {
1966 update_item_cache(c);
1967 c = c->next;
1968 }
1969}
1970
1971int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int &r_self_height) {
1972 if (p_pos.y - theme_cache.offset.y > (p_draw_size.height)) {
1973 return -1; //draw no more!
1974 }
1975
1976 if (!p_item->is_visible()) {
1977 return 0;
1978 }
1979
1980 RID ci = get_canvas_item();
1981
1982 int htotal = 0;
1983
1984 int label_h = 0;
1985 bool rtl = cache.rtl;
1986
1987 /* Draw label, if height fits */
1988
1989 bool skip = (p_item == root && hide_root);
1990
1991 if (!skip) {
1992 // Draw separation.
1993
1994 ERR_FAIL_COND_V(theme_cache.font.is_null(), -1);
1995
1996 int ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.h_separation : theme_cache.item_margin);
1997 int skip2 = 0;
1998 for (int i = 0; i < columns.size(); i++) {
1999 if (skip2) {
2000 skip2--;
2001 continue;
2002 }
2003
2004 int item_width = get_column_width(i);
2005
2006 if (i == 0) {
2007 item_width -= ofs;
2008
2009 if (item_width <= 0) {
2010 ofs = get_column_width(0);
2011 continue;
2012 }
2013 } else {
2014 ofs += theme_cache.h_separation;
2015 item_width -= theme_cache.h_separation;
2016 }
2017
2018 if (p_item->cells[i].expand_right) {
2019 int plus = 1;
2020 while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
2021 item_width += get_column_width(i + plus);
2022 plus++;
2023 skip2++;
2024 }
2025 }
2026
2027 if (!rtl && p_item->cells[i].buttons.size()) {
2028 int buttons_width = 0;
2029 for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
2030 Ref<Texture2D> button_texture = p_item->cells[i].buttons[j].texture;
2031 buttons_width += button_texture->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin;
2032 }
2033
2034 int total_ofs = ofs - theme_cache.offset.x;
2035
2036 if (total_ofs + item_width > p_draw_size.width) {
2037 item_width = MAX(buttons_width, p_draw_size.width - total_ofs);
2038 }
2039 }
2040
2041 int item_width_with_buttons = item_width; // used later for drawing buttons
2042 int buttons_width = 0;
2043 for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
2044 Ref<Texture2D> button_texture = p_item->cells[i].buttons[j].texture;
2045 Size2 button_size = button_texture->get_size() + theme_cache.button_pressed->get_minimum_size();
2046
2047 item_width -= button_size.width + theme_cache.button_margin;
2048 buttons_width += button_size.width + theme_cache.button_margin;
2049 }
2050
2051 int text_width = item_width - theme_cache.inner_item_margin_left - theme_cache.inner_item_margin_right;
2052 if (p_item->cells[i].icon.is_valid()) {
2053 text_width -= p_item->cells[i].get_icon_size().x + theme_cache.h_separation;
2054 }
2055
2056 p_item->cells.write[i].text_buf->set_width(text_width);
2057
2058 r_self_height = compute_item_height(p_item);
2059 label_h = r_self_height + theme_cache.v_separation;
2060
2061 if (p_pos.y + label_h - theme_cache.offset.y < 0) {
2062 continue; // No need to draw.
2063 }
2064
2065 Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - theme_cache.offset + p_draw_ofs, Size2i(item_width, label_h));
2066 Rect2i cell_rect = item_rect;
2067 if (i != 0) {
2068 cell_rect.position.x -= theme_cache.h_separation;
2069 cell_rect.size.x += theme_cache.h_separation;
2070 }
2071
2072 if (theme_cache.draw_guides) {
2073 Rect2 r = cell_rect;
2074 if (rtl) {
2075 r.position.x = get_size().width - r.position.x - r.size.x;
2076 }
2077 RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(r.position.x, r.position.y + r.size.height), r.position + r.size, theme_cache.guide_color, 1);
2078 }
2079
2080 if (i == 0) {
2081 if (p_item->cells[0].selected && select_mode == SELECT_ROW) {
2082 const Rect2 content_rect = _get_content_rect();
2083 Rect2i row_rect = Rect2i(Point2i(content_rect.position.x, item_rect.position.y), Size2i(content_rect.size.x, item_rect.size.y));
2084 if (rtl) {
2085 row_rect.position.x = get_size().width - row_rect.position.x - row_rect.size.x;
2086 }
2087 if (has_focus()) {
2088 theme_cache.selected_focus->draw(ci, row_rect);
2089 } else {
2090 theme_cache.selected->draw(ci, row_rect);
2091 }
2092 }
2093 }
2094
2095 if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected || !p_item->has_meta("__focus_rect")) {
2096 Rect2i r = cell_rect;
2097
2098 p_item->set_meta("__focus_rect", Rect2(r.position, r.size));
2099
2100 if (select_mode != SELECT_ROW) {
2101 if (rtl) {
2102 r.position.x = get_size().width - r.position.x - r.size.x;
2103 }
2104 if (p_item->cells[i].selected) {
2105 if (has_focus()) {
2106 theme_cache.selected_focus->draw(ci, r);
2107 } else {
2108 theme_cache.selected->draw(ci, r);
2109 }
2110 }
2111 }
2112 }
2113
2114 if (p_item->cells[i].custom_bg_color) {
2115 Rect2 r = cell_rect;
2116 if (i == 0) {
2117 r.position.x = p_draw_ofs.x;
2118 r.size.x = item_width + ofs;
2119 } else {
2120 r.position.x -= theme_cache.h_separation;
2121 r.size.x += theme_cache.h_separation;
2122 }
2123 if (rtl) {
2124 r.position.x = get_size().width - r.position.x - r.size.x;
2125 }
2126 if (p_item->cells[i].custom_bg_outline) {
2127 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), p_item->cells[i].bg_color);
2128 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y - 1, r.size.x, 1), p_item->cells[i].bg_color);
2129 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, 1, r.size.y), p_item->cells[i].bg_color);
2130 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), p_item->cells[i].bg_color);
2131 } else {
2132 RenderingServer::get_singleton()->canvas_item_add_rect(ci, r, p_item->cells[i].bg_color);
2133 }
2134 }
2135
2136 if (drop_mode_flags && drop_mode_over) {
2137 Rect2 r = cell_rect;
2138 if (rtl) {
2139 r.position.x = get_size().width - r.position.x - r.size.x;
2140 }
2141 if (drop_mode_over == p_item) {
2142 if (drop_mode_section == 0 || drop_mode_section == -1) {
2143 // Line above.
2144 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), theme_cache.drop_position_color);
2145 }
2146 if (drop_mode_section == 0) {
2147 // Side lines.
2148 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, 1, r.size.y), theme_cache.drop_position_color);
2149 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), theme_cache.drop_position_color);
2150 }
2151 if (drop_mode_section == 0 || (drop_mode_section == 1 && (!p_item->get_first_child() || p_item->is_collapsed()))) {
2152 // Line below.
2153 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y, r.size.x, 1), theme_cache.drop_position_color);
2154 }
2155 } else if (drop_mode_over == p_item->get_parent()) {
2156 if (drop_mode_section == 1 && !p_item->get_prev() /* && !drop_mode_over->is_collapsed() */) { // The drop_mode_over shouldn't ever be collapsed in here, otherwise we would be drawing a child of a collapsed item.
2157 // Line above.
2158 RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), theme_cache.drop_position_color);
2159 }
2160 }
2161 }
2162
2163 Color cell_color;
2164 if (p_item->cells[i].custom_color) {
2165 cell_color = p_item->cells[i].color;
2166 } else {
2167 cell_color = p_item->cells[i].selected ? theme_cache.font_selected_color : theme_cache.font_color;
2168 }
2169
2170 Color font_outline_color = theme_cache.font_outline_color;
2171 int outline_size = theme_cache.font_outline_size;
2172 Color icon_col = p_item->cells[i].icon_color;
2173
2174 if (p_item->cells[i].dirty) {
2175 const_cast<Tree *>(this)->update_item_cell(p_item, i);
2176 }
2177
2178 if (rtl) {
2179 item_rect.position.x = get_size().width - item_rect.position.x - item_rect.size.x;
2180 }
2181
2182 Point2i text_pos = item_rect.position;
2183 text_pos.y += Math::floor(p_draw_ofs.y) - _get_title_button_height();
2184
2185 switch (p_item->cells[i].mode) {
2186 case TreeItem::CELL_MODE_STRING: {
2187 draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color);
2188 } break;
2189 case TreeItem::CELL_MODE_CHECK: {
2190 Ref<Texture2D> checked = theme_cache.checked;
2191 Ref<Texture2D> unchecked = theme_cache.unchecked;
2192 Ref<Texture2D> indeterminate = theme_cache.indeterminate;
2193 Point2i check_ofs = item_rect.position;
2194 check_ofs.y += Math::floor((real_t)(item_rect.size.y - checked->get_height()) / 2);
2195
2196 if (p_item->cells[i].indeterminate) {
2197 indeterminate->draw(ci, check_ofs);
2198 } else if (p_item->cells[i].checked) {
2199 checked->draw(ci, check_ofs);
2200 } else {
2201 unchecked->draw(ci, check_ofs);
2202 }
2203
2204 int check_w = checked->get_width() + theme_cache.h_separation;
2205
2206 text_pos.x += check_w;
2207
2208 item_rect.size.x -= check_w;
2209 item_rect.position.x += check_w;
2210
2211 draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color);
2212
2213 } break;
2214 case TreeItem::CELL_MODE_RANGE: {
2215 if (!p_item->cells[i].text.is_empty()) {
2216 if (!p_item->cells[i].editable) {
2217 break;
2218 }
2219
2220 Ref<Texture2D> downarrow = theme_cache.select_arrow;
2221 int cell_width = item_rect.size.x - downarrow->get_width();
2222
2223 if (rtl) {
2224 if (outline_size > 0 && font_outline_color.a > 0) {
2225 p_item->cells[i].text_buf->draw_outline(ci, text_pos + Vector2(cell_width - text_width, 0), outline_size, font_outline_color);
2226 }
2227 p_item->cells[i].text_buf->draw(ci, text_pos + Vector2(cell_width - text_width, 0), cell_color);
2228 } else {
2229 if (outline_size > 0 && font_outline_color.a > 0) {
2230 p_item->cells[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
2231 }
2232 p_item->cells[i].text_buf->draw(ci, text_pos, cell_color);
2233 }
2234
2235 Point2i arrow_pos = item_rect.position;
2236 arrow_pos.x += item_rect.size.x - downarrow->get_width();
2237 arrow_pos.y += Math::floor(((item_rect.size.y - downarrow->get_height())) / 2.0);
2238
2239 downarrow->draw(ci, arrow_pos);
2240 } else {
2241 Ref<Texture2D> updown = theme_cache.updown;
2242
2243 int cell_width = item_rect.size.x - updown->get_width();
2244
2245 if (rtl) {
2246 if (outline_size > 0 && font_outline_color.a > 0) {
2247 p_item->cells[i].text_buf->draw_outline(ci, text_pos + Vector2(cell_width - text_width, 0), outline_size, font_outline_color);
2248 }
2249 p_item->cells[i].text_buf->draw(ci, text_pos + Vector2(cell_width - text_width, 0), cell_color);
2250 } else {
2251 if (outline_size > 0 && font_outline_color.a > 0) {
2252 p_item->cells[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
2253 }
2254 p_item->cells[i].text_buf->draw(ci, text_pos, cell_color);
2255 }
2256
2257 if (!p_item->cells[i].editable) {
2258 break;
2259 }
2260
2261 Point2i updown_pos = item_rect.position;
2262 updown_pos.x += item_rect.size.x - updown->get_width();
2263 updown_pos.y += Math::floor(((item_rect.size.y - updown->get_height())) / 2.0);
2264
2265 updown->draw(ci, updown_pos);
2266 }
2267
2268 } break;
2269 case TreeItem::CELL_MODE_ICON: {
2270 if (p_item->cells[i].icon.is_null()) {
2271 break;
2272 }
2273 Size2i icon_size = _get_cell_icon_size(p_item->cells[i]);
2274 Point2i icon_ofs = (item_rect.size - icon_size) / 2;
2275 icon_ofs += item_rect.position;
2276
2277 draw_texture_rect(p_item->cells[i].icon, Rect2(icon_ofs, icon_size), false, icon_col);
2278
2279 } break;
2280 case TreeItem::CELL_MODE_CUSTOM: {
2281 if (p_item->cells[i].custom_draw_obj.is_valid()) {
2282 Object *cdo = ObjectDB::get_instance(p_item->cells[i].custom_draw_obj);
2283 if (cdo) {
2284 cdo->call(p_item->cells[i].custom_draw_callback, p_item, Rect2(item_rect));
2285 }
2286 }
2287
2288 if (!p_item->cells[i].editable) {
2289 draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color);
2290 break;
2291 }
2292
2293 Ref<Texture2D> downarrow = theme_cache.select_arrow;
2294
2295 Rect2i ir = item_rect;
2296
2297 Point2i arrow_pos = item_rect.position;
2298 arrow_pos.x += item_rect.size.x - downarrow->get_width();
2299 arrow_pos.y += Math::floor(((item_rect.size.y - downarrow->get_height())) / 2.0);
2300 ir.size.width -= downarrow->get_width();
2301
2302 if (p_item->cells[i].custom_button) {
2303 if (cache.hover_item == p_item && cache.hover_cell == i) {
2304 if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
2305 draw_style_box(theme_cache.custom_button_pressed, ir);
2306 } else {
2307 draw_style_box(theme_cache.custom_button_hover, ir);
2308 cell_color = theme_cache.custom_button_font_highlight;
2309 }
2310 } else {
2311 draw_style_box(theme_cache.custom_button, ir);
2312 }
2313 ir.size -= theme_cache.custom_button->get_minimum_size();
2314 ir.position += theme_cache.custom_button->get_offset();
2315 }
2316
2317 draw_item_rect(p_item->cells.write[i], ir, cell_color, icon_col, outline_size, font_outline_color);
2318
2319 downarrow->draw(ci, arrow_pos);
2320
2321 } break;
2322 }
2323
2324 for (int j = p_item->cells[i].buttons.size() - 1; j >= 0; j--) {
2325 Ref<Texture2D> button_texture = p_item->cells[i].buttons[j].texture;
2326 Size2 button_size = button_texture->get_size() + theme_cache.button_pressed->get_minimum_size();
2327
2328 Point2i button_ofs = Point2i(ofs + item_width_with_buttons - button_size.width, p_pos.y) - theme_cache.offset + p_draw_ofs;
2329
2330 if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item == p_item && cache.click_column == i && cache.click_index == j && !p_item->cells[i].buttons[j].disabled) {
2331 // Being pressed.
2332 Point2 od = button_ofs;
2333 if (rtl) {
2334 od.x = get_size().width - od.x - button_size.x;
2335 }
2336 theme_cache.button_pressed->draw(get_canvas_item(), Rect2(od.x, od.y, button_size.width, MAX(button_size.height, label_h)));
2337 }
2338
2339 button_ofs.y += (label_h - button_size.height) / 2;
2340 button_ofs += theme_cache.button_pressed->get_offset();
2341
2342 if (rtl) {
2343 button_ofs.x = get_size().width - button_ofs.x - button_texture->get_width();
2344 }
2345 p_item->cells.write[i].buttons.write[j].rect = Rect2i(button_ofs, button_size);
2346 button_texture->draw(ci, button_ofs, p_item->cells[i].buttons[j].disabled ? Color(1, 1, 1, 0.5) : p_item->cells[i].buttons[j].color);
2347 item_width_with_buttons -= button_size.width + theme_cache.button_margin;
2348 }
2349
2350 if (i == 0) {
2351 ofs = get_column_width(0);
2352 } else {
2353 ofs += item_width + buttons_width;
2354 }
2355
2356 if (select_mode == SELECT_MULTI && selected_item == p_item && selected_col == i) {
2357 if (is_layout_rtl()) {
2358 cell_rect.position.x = get_size().width - cell_rect.position.x - cell_rect.size.x;
2359 }
2360 if (has_focus()) {
2361 theme_cache.cursor->draw(ci, cell_rect);
2362 } else {
2363 theme_cache.cursor_unfocus->draw(ci, cell_rect);
2364 }
2365 }
2366 }
2367
2368 if (!p_item->disable_folding && !hide_folding && p_item->first_child && p_item->get_visible_child_count() != 0) { //has visible children, draw the guide box
2369
2370 Ref<Texture2D> arrow;
2371
2372 if (p_item->collapsed) {
2373 if (is_layout_rtl()) {
2374 arrow = theme_cache.arrow_collapsed_mirrored;
2375 } else {
2376 arrow = theme_cache.arrow_collapsed;
2377 }
2378 } else {
2379 arrow = theme_cache.arrow;
2380 }
2381
2382 Point2 apos = p_pos + Point2i(0, (label_h - arrow->get_height()) / 2) - theme_cache.offset + p_draw_ofs;
2383 apos.x += theme_cache.item_margin - arrow->get_width();
2384
2385 if (rtl) {
2386 apos.x = get_size().width - apos.x - arrow->get_width();
2387 }
2388
2389 arrow->draw(ci, apos);
2390 }
2391 }
2392
2393 Point2 children_pos = p_pos;
2394
2395 if (!skip) {
2396 children_pos.x += theme_cache.item_margin;
2397 htotal += label_h;
2398 children_pos.y += htotal;
2399 }
2400
2401 if (!p_item->collapsed) { /* if not collapsed, check the children */
2402 TreeItem *c = p_item->first_child;
2403
2404 int base_ofs = children_pos.y - theme_cache.offset.y + p_draw_ofs.y;
2405 int prev_ofs = base_ofs;
2406 int prev_hl_ofs = base_ofs;
2407
2408 while (c) {
2409 int child_h = -1;
2410 int child_self_height = 0;
2411 if (htotal >= 0) {
2412 child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c, child_self_height);
2413 child_self_height += theme_cache.v_separation;
2414 }
2415
2416 // Draw relationship lines.
2417 if (theme_cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root) && c->is_visible()) {
2418 int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.h_separation : theme_cache.item_margin);
2419 int parent_ofs = p_pos.x + theme_cache.item_margin;
2420 Point2i root_pos = Point2i(root_ofs, children_pos.y + child_self_height / 2) - theme_cache.offset + p_draw_ofs;
2421
2422 if (c->get_visible_child_count() > 0) {
2423 root_pos -= Point2i(theme_cache.arrow->get_width(), 0);
2424 }
2425
2426 float line_width = theme_cache.relationship_line_width * Math::round(theme_cache.base_scale);
2427 float parent_line_width = theme_cache.parent_hl_line_width * Math::round(theme_cache.base_scale);
2428 float children_line_width = theme_cache.children_hl_line_width * Math::round(theme_cache.base_scale);
2429
2430 Point2i parent_pos = Point2i(parent_ofs - theme_cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + theme_cache.arrow->get_height() / 2) - theme_cache.offset + p_draw_ofs;
2431
2432 int more_prev_ofs = 0;
2433
2434 if (root_pos.y + line_width >= 0) {
2435 if (rtl) {
2436 root_pos.x = get_size().width - root_pos.x;
2437 parent_pos.x = get_size().width - parent_pos.x;
2438 }
2439
2440 // Order of parts on this bend: the horizontal line first, then the vertical line.
2441 if (_is_branch_selected(c)) {
2442 // If this item or one of its children is selected, we draw the line using parent highlight style.
2443 if (htotal >= 0) {
2444 RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), theme_cache.parent_hl_line_color, parent_line_width);
2445 }
2446 RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width);
2447
2448 more_prev_ofs = theme_cache.parent_hl_line_margin;
2449 prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
2450 } else if (p_item->is_selected(0)) {
2451 // If parent item is selected (but this item is not), we draw the line using children highlight style.
2452 // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted.
2453 if (_is_sibling_branch_selected(c)) {
2454 if (htotal >= 0) {
2455 RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), theme_cache.children_hl_line_color, children_line_width);
2456 }
2457 RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width);
2458
2459 prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
2460 } else {
2461 if (htotal >= 0) {
2462 RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), theme_cache.children_hl_line_color, children_line_width);
2463 }
2464 RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), theme_cache.children_hl_line_color, children_line_width);
2465 }
2466 } else {
2467 // If nothing of the above is true, we draw the line using normal style.
2468 // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted.
2469 if (_is_sibling_branch_selected(c)) {
2470 if (htotal >= 0) {
2471 RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + theme_cache.parent_hl_line_margin, root_pos.y), theme_cache.relationship_line_color, line_width);
2472 }
2473 RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), theme_cache.parent_hl_line_color, parent_line_width);
2474
2475 prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
2476 } else {
2477 if (htotal >= 0) {
2478 RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), theme_cache.relationship_line_color, line_width);
2479 }
2480 RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), theme_cache.relationship_line_color, line_width);
2481 }
2482 }
2483 }
2484
2485 prev_ofs = root_pos.y + more_prev_ofs;
2486 }
2487
2488 if (child_h < 0) {
2489 if (htotal == -1) {
2490 break; // Last loop done, stop.
2491 }
2492
2493 if (theme_cache.draw_relationship_lines == 0) {
2494 return -1; // No need to draw anymore, full stop.
2495 }
2496
2497 htotal = -1;
2498 children_pos.y = theme_cache.offset.y + p_draw_size.height;
2499 } else {
2500 htotal += child_h;
2501 children_pos.y += child_h;
2502 }
2503
2504 c = c->next;
2505 }
2506 }
2507
2508 return htotal;
2509}
2510
2511int Tree::_count_selected_items(TreeItem *p_from) const {
2512 int count = 0;
2513 for (int i = 0; i < columns.size(); i++) {
2514 if (p_from->is_selected(i)) {
2515 count++;
2516 }
2517 }
2518
2519 if (p_from->get_first_child()) {
2520 count += _count_selected_items(p_from->get_first_child());
2521 }
2522
2523 if (p_from->get_next()) {
2524 count += _count_selected_items(p_from->get_next());
2525 }
2526
2527 return count;
2528}
2529
2530bool Tree::_is_branch_selected(TreeItem *p_from) const {
2531 for (int i = 0; i < columns.size(); i++) {
2532 if (p_from->is_selected(i)) {
2533 return true;
2534 }
2535 }
2536
2537 TreeItem *child_item = p_from->get_first_child();
2538 while (child_item) {
2539 if (_is_branch_selected(child_item)) {
2540 return true;
2541 }
2542 child_item = child_item->get_next();
2543 }
2544
2545 return false;
2546}
2547
2548bool Tree::_is_sibling_branch_selected(TreeItem *p_from) const {
2549 TreeItem *sibling_item = p_from->get_next();
2550 while (sibling_item) {
2551 if (_is_branch_selected(sibling_item)) {
2552 return true;
2553 }
2554 sibling_item = sibling_item->get_next();
2555 }
2556
2557 return false;
2558}
2559
2560void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev, bool *r_in_range, bool p_force_deselect) {
2561 popup_editor->hide();
2562
2563 TreeItem::Cell &selected_cell = p_selected->cells.write[p_col];
2564
2565 bool switched = false;
2566 if (r_in_range && !*r_in_range && (p_current == p_selected || p_current == p_prev)) {
2567 *r_in_range = true;
2568 switched = true;
2569 }
2570
2571 bool emitted_row = false;
2572
2573 for (int i = 0; i < columns.size(); i++) {
2574 TreeItem::Cell &c = p_current->cells.write[i];
2575
2576 if (!c.selectable) {
2577 continue;
2578 }
2579
2580 if (select_mode == SELECT_ROW) {
2581 if (p_selected == p_current && (!c.selected || allow_reselect)) {
2582 c.selected = true;
2583 selected_item = p_selected;
2584 selected_col = 0;
2585 if (!emitted_row) {
2586 emit_signal(SNAME("item_selected"));
2587 emitted_row = true;
2588 }
2589 } else if (c.selected) {
2590 if (p_selected != p_current) {
2591 // Deselect other rows.
2592 c.selected = false;
2593 }
2594 }
2595 } else if (select_mode == SELECT_SINGLE || select_mode == SELECT_MULTI) {
2596 if (!r_in_range && &selected_cell == &c) {
2597 if (!selected_cell.selected || allow_reselect) {
2598 selected_cell.selected = true;
2599
2600 selected_item = p_selected;
2601 selected_col = i;
2602
2603 emit_signal(SNAME("cell_selected"));
2604 if (select_mode == SELECT_MULTI) {
2605 emit_signal(SNAME("multi_selected"), p_current, i, true);
2606 } else if (select_mode == SELECT_SINGLE) {
2607 emit_signal(SNAME("item_selected"));
2608 }
2609
2610 } else if (select_mode == SELECT_MULTI && (selected_item != p_selected || selected_col != i)) {
2611 selected_item = p_selected;
2612 selected_col = i;
2613 emit_signal(SNAME("cell_selected"));
2614 }
2615 } else {
2616 if (r_in_range && *r_in_range && !p_force_deselect) {
2617 if (!c.selected && c.selectable) {
2618 c.selected = true;
2619 emit_signal(SNAME("multi_selected"), p_current, i, true);
2620 }
2621
2622 } else if (!r_in_range || p_force_deselect) {
2623 if (select_mode == SELECT_MULTI && c.selected) {
2624 emit_signal(SNAME("multi_selected"), p_current, i, false);
2625 }
2626 c.selected = false;
2627 }
2628 //p_current->deselected_signal.call(p_col);
2629 }
2630 }
2631 }
2632
2633 if (!switched && r_in_range && *r_in_range && (p_current == p_selected || p_current == p_prev)) {
2634 *r_in_range = false;
2635 }
2636
2637 TreeItem *c = p_current->first_child;
2638
2639 while (c) {
2640 select_single_item(p_selected, c, p_col, p_prev, r_in_range, p_current->is_collapsed() || p_force_deselect);
2641 c = c->next;
2642 }
2643}
2644
2645Rect2 Tree::search_item_rect(TreeItem *p_from, TreeItem *p_item) {
2646 return Rect2();
2647}
2648
2649void Tree::_range_click_timeout() {
2650 if (range_item_last && !range_drag_enabled && Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
2651 Point2 pos = get_local_mouse_position() - theme_cache.panel_style->get_offset();
2652 if (show_column_titles) {
2653 pos.y -= _get_title_button_height();
2654
2655 if (pos.y < 0) {
2656 range_click_timer->stop();
2657 return;
2658 }
2659 }
2660
2661 if (!root) {
2662 return;
2663 }
2664
2665 click_handled = false;
2666 Ref<InputEventMouseButton> mb;
2667 mb.instantiate();
2668
2669 int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width;
2670 if (v_scroll->is_visible()) {
2671 x_limit -= v_scroll->get_minimum_size().width;
2672 }
2673
2674 cache.rtl = is_layout_rtl();
2675
2676 propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case)
2677 blocked++;
2678 propagate_mouse_event(pos + theme_cache.offset, 0, 0, x_limit + theme_cache.offset.width, false, root, MouseButton::LEFT, mb);
2679 blocked--;
2680
2681 if (range_click_timer->is_one_shot()) {
2682 range_click_timer->set_wait_time(0.05);
2683 range_click_timer->set_one_shot(false);
2684 range_click_timer->start();
2685 }
2686
2687 if (!click_handled) {
2688 range_click_timer->stop();
2689 }
2690
2691 if (propagate_mouse_activated) {
2692 emit_signal(SNAME("item_activated"));
2693 propagate_mouse_activated = false;
2694 }
2695
2696 } else {
2697 range_click_timer->stop();
2698 }
2699}
2700
2701int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x_limit, bool p_double_click, TreeItem *p_item, MouseButton p_button, const Ref<InputEventWithModifiers> &p_mod) {
2702 if (p_item && !p_item->is_visible()) {
2703 // Skip any processing of invisible items.
2704 return 0;
2705 }
2706
2707 int item_h = compute_item_height(p_item) + theme_cache.v_separation;
2708
2709 bool skip = (p_item == root && hide_root);
2710
2711 if (!skip && p_pos.y < item_h) {
2712 // check event!
2713
2714 if (range_click_timer->get_time_left() > 0 && p_item != range_item_last) {
2715 return -1;
2716 }
2717
2718 if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + theme_cache.item_margin))) {
2719 if (enable_recursive_folding && p_mod->is_shift_pressed()) {
2720 p_item->set_collapsed_recursive(!p_item->is_collapsed());
2721 } else {
2722 p_item->set_collapsed(!p_item->is_collapsed());
2723 }
2724 return -1;
2725 }
2726
2727 int x = p_pos.x;
2728 /* find clicked column */
2729 int col = -1;
2730 int col_ofs = 0;
2731 int col_width = 0;
2732
2733 int limit_w = x_limit;
2734
2735 for (int i = 0; i < columns.size(); i++) {
2736 col_width = get_column_width(i);
2737
2738 if (p_item->cells[i].expand_right) {
2739 int plus = 1;
2740 while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
2741 col_width += theme_cache.h_separation;
2742 col_width += get_column_width(i + plus);
2743 plus++;
2744 }
2745 }
2746
2747 if (x > col_width) {
2748 col_ofs += col_width;
2749 x -= col_width;
2750 limit_w -= col_width;
2751 continue;
2752 }
2753
2754 col = i;
2755 break;
2756 }
2757
2758 if (col == -1) {
2759 return -1;
2760 } else if (col == 0) {
2761 int margin = x_ofs + theme_cache.item_margin; //-theme_cache.h_separation;
2762 //int lm = theme_cache.panel_style->get_margin(SIDE_LEFT);
2763 col_width -= margin;
2764 limit_w -= margin;
2765 col_ofs += margin;
2766 x -= margin;
2767 } else {
2768 col_width -= theme_cache.h_separation;
2769 limit_w -= theme_cache.h_separation;
2770 x -= theme_cache.h_separation;
2771 }
2772
2773 if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) {
2774 if (enable_recursive_folding && p_mod->is_shift_pressed()) {
2775 p_item->set_collapsed_recursive(!p_item->is_collapsed());
2776 } else {
2777 p_item->set_collapsed(!p_item->is_collapsed());
2778 }
2779 return -1; //collapse/uncollapse because nothing can be done with item
2780 }
2781
2782 const TreeItem::Cell &c = p_item->cells[col];
2783
2784 bool already_selected = c.selected;
2785 bool already_cursor = (p_item == selected_item) && col == selected_col;
2786
2787 if (!cache.rtl && p_item->cells[col].buttons.size()) {
2788 int button_w = 0;
2789 for (int j = p_item->cells[col].buttons.size() - 1; j >= 0; j--) {
2790 Ref<Texture2D> b = p_item->cells[col].buttons[j].texture;
2791 button_w += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin;
2792 }
2793
2794 col_width = MAX(button_w, MIN(limit_w, col_width));
2795 }
2796
2797 for (int j = c.buttons.size() - 1; j >= 0; j--) {
2798 Ref<Texture2D> b = c.buttons[j].texture;
2799 int w = b->get_size().width + theme_cache.button_pressed->get_minimum_size().width;
2800
2801 if (x > col_width - w) {
2802 if (c.buttons[j].disabled) {
2803 pressed_button = -1;
2804 cache.click_type = Cache::CLICK_NONE;
2805 return -1;
2806 }
2807
2808 // Make sure the click is correct.
2809 Point2 click_pos = get_global_mouse_position() - get_global_position();
2810 if (!get_item_at_position(click_pos)) {
2811 pressed_button = -1;
2812 cache.click_type = Cache::CLICK_NONE;
2813 return -1;
2814 }
2815
2816 pressed_button = j;
2817 cache.click_type = Cache::CLICK_BUTTON;
2818 cache.click_index = j;
2819 cache.click_id = c.buttons[j].id;
2820 cache.click_item = p_item;
2821 cache.click_column = col;
2822 cache.click_pos = click_pos;
2823 queue_redraw();
2824 return -1;
2825 }
2826
2827 col_width -= w + theme_cache.button_margin;
2828 }
2829
2830 if (p_button == MouseButton::LEFT || (p_button == MouseButton::RIGHT && allow_rmb_select)) {
2831 /* process selection */
2832
2833 if (p_double_click && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check
2834 // Emits the "item_activated" signal.
2835 propagate_mouse_activated = true;
2836
2837 incr_search.clear();
2838 return -1;
2839 }
2840
2841 if (c.selectable) {
2842 if (select_mode == SELECT_MULTI && p_mod->is_command_or_control_pressed()) {
2843 if (c.selected && p_button == MouseButton::LEFT) {
2844 p_item->deselect(col);
2845 emit_signal(SNAME("multi_selected"), p_item, col, false);
2846 } else {
2847 p_item->select(col);
2848 emit_signal(SNAME("multi_selected"), p_item, col, true);
2849 emit_signal(SNAME("item_mouse_selected"), get_local_mouse_position(), p_button);
2850 }
2851 } else {
2852 if (select_mode == SELECT_MULTI && p_mod->is_shift_pressed() && selected_item && selected_item != p_item) {
2853 bool inrange = false;
2854
2855 select_single_item(p_item, root, col, selected_item, &inrange);
2856 emit_signal(SNAME("item_mouse_selected"), get_local_mouse_position(), p_button);
2857 } else {
2858 int icount = _count_selected_items(root);
2859
2860 if (select_mode == SELECT_MULTI && icount > 1 && p_button != MouseButton::RIGHT) {
2861 single_select_defer = p_item;
2862 single_select_defer_column = col;
2863 } else {
2864 if (p_button != MouseButton::RIGHT || !c.selected) {
2865 select_single_item(p_item, root, col);
2866 }
2867
2868 emit_signal(SNAME("item_mouse_selected"), get_local_mouse_position(), p_button);
2869 }
2870 }
2871 queue_redraw();
2872 }
2873 }
2874 }
2875
2876 if (!c.editable) {
2877 return -1; // if cell is not editable, don't bother
2878 }
2879
2880 /* editing */
2881
2882 bool bring_up_editor = allow_reselect ? (c.selected && already_selected) : c.selected;
2883 String editor_text = c.text;
2884
2885 switch (c.mode) {
2886 case TreeItem::CELL_MODE_STRING: {
2887 //nothing in particular
2888
2889 if (select_mode == SELECT_MULTI && (get_viewport()->get_processed_events_count() == focus_in_id || !already_cursor)) {
2890 bring_up_editor = false;
2891 }
2892
2893 } break;
2894 case TreeItem::CELL_MODE_CHECK: {
2895 bring_up_editor = false; //checkboxes are not edited with editor
2896 if (force_edit_checkbox_only_on_checkbox) {
2897 if (x < theme_cache.checked->get_width()) {
2898 p_item->set_checked(col, !c.checked);
2899 item_edited(col, p_item, p_button);
2900 }
2901 } else {
2902 p_item->set_checked(col, !c.checked);
2903 item_edited(col, p_item, p_button);
2904 }
2905 click_handled = true;
2906 //p_item->edited_signal.call(col);
2907
2908 } break;
2909 case TreeItem::CELL_MODE_RANGE: {
2910 if (!c.text.is_empty()) {
2911 //if (x >= (get_column_width(col)-item_h/2)) {
2912 popup_menu->clear();
2913 for (int i = 0; i < c.text.get_slice_count(","); i++) {
2914 String s = c.text.get_slicec(',', i);
2915 popup_menu->add_item(s.get_slicec(':', 0), s.get_slicec(':', 1).is_empty() ? i : s.get_slicec(':', 1).to_int());
2916 }
2917
2918 popup_menu->set_size(Size2(col_width, 0));
2919 popup_menu->set_position(get_screen_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - theme_cache.offset);
2920 popup_menu->popup();
2921 popup_edited_item = p_item;
2922 popup_edited_item_col = col;
2923 //}
2924 bring_up_editor = false;
2925 } else {
2926 if (x >= (col_width - item_h / 2)) {
2927 /* touching the combo */
2928 bool up = p_pos.y < (item_h / 2);
2929
2930 if (p_button == MouseButton::LEFT) {
2931 if (range_click_timer->get_time_left() == 0) {
2932 range_item_last = p_item;
2933 range_up_last = up;
2934
2935 range_click_timer->set_wait_time(0.6);
2936 range_click_timer->set_one_shot(true);
2937 range_click_timer->start();
2938
2939 } else if (up != range_up_last) {
2940 return -1; // break. avoid changing direction on mouse held
2941 }
2942
2943 p_item->set_range(col, c.val + (up ? 1.0 : -1.0) * c.step);
2944
2945 item_edited(col, p_item, p_button);
2946
2947 } else if (p_button == MouseButton::RIGHT) {
2948 p_item->set_range(col, (up ? c.max : c.min));
2949 item_edited(col, p_item, p_button);
2950 } else if (p_button == MouseButton::WHEEL_UP) {
2951 p_item->set_range(col, c.val + c.step);
2952 item_edited(col, p_item, p_button);
2953 } else if (p_button == MouseButton::WHEEL_DOWN) {
2954 p_item->set_range(col, c.val - c.step);
2955 item_edited(col, p_item, p_button);
2956 }
2957
2958 //p_item->edited_signal.call(col);
2959 bring_up_editor = false;
2960
2961 } else {
2962 editor_text = String::num(p_item->cells[col].val, Math::range_step_decimals(p_item->cells[col].step));
2963 if (select_mode == SELECT_MULTI && get_viewport()->get_processed_events_count() == focus_in_id) {
2964 bring_up_editor = false;
2965 }
2966 }
2967 }
2968 click_handled = true;
2969
2970 } break;
2971 case TreeItem::CELL_MODE_ICON: {
2972 bring_up_editor = false;
2973 } break;
2974 case TreeItem::CELL_MODE_CUSTOM: {
2975 edited_item = p_item;
2976 edited_col = col;
2977 bool on_arrow = x > col_width - theme_cache.select_arrow->get_width();
2978
2979 custom_popup_rect = Rect2i(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h - theme_cache.offset.y), Size2(get_column_width(col), item_h));
2980
2981 if (on_arrow || !p_item->cells[col].custom_button) {
2982 emit_signal(SNAME("custom_popup_edited"), ((bool)(x >= (col_width - item_h / 2))));
2983 }
2984
2985 if (!p_item->cells[col].custom_button || !on_arrow) {
2986 item_edited(col, p_item, p_button);
2987 }
2988 click_handled = true;
2989 return -1;
2990 } break;
2991 };
2992
2993 if (!bring_up_editor || p_button != MouseButton::LEFT) {
2994 return -1;
2995 }
2996
2997 click_handled = true;
2998 popup_pressing_edited_item = p_item;
2999 popup_pressing_edited_item_column = col;
3000
3001 pressing_item_rect = Rect2(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs) - theme_cache.offset, Size2(col_width, item_h));
3002 pressing_for_editor_text = editor_text;
3003 pressing_for_editor = true;
3004
3005 return -1; //select
3006 } else {
3007 Point2i new_pos = p_pos;
3008
3009 if (!skip) {
3010 x_ofs += theme_cache.item_margin;
3011 //new_pos.x-=theme_cache.item_margin;
3012 y_ofs += item_h;
3013 new_pos.y -= item_h;
3014 }
3015
3016 if (!p_item->collapsed) { /* if not collapsed, check the children */
3017
3018 TreeItem *c = p_item->first_child;
3019
3020 while (c) {
3021 int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, x_limit, p_double_click, c, p_button, p_mod);
3022
3023 if (child_h < 0) {
3024 return -1; // break, stop propagating, no need to anymore
3025 }
3026
3027 new_pos.y -= child_h;
3028 y_ofs += child_h;
3029 c = c->next;
3030 item_h += child_h;
3031 }
3032 }
3033 if (p_item == root) {
3034 emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), p_button);
3035 }
3036 }
3037
3038 return item_h; // nothing found
3039}
3040
3041void Tree::_text_editor_popup_modal_close() {
3042 if (Input::get_singleton()->is_key_pressed(Key::ESCAPE) ||
3043 Input::get_singleton()->is_key_pressed(Key::KP_ENTER) ||
3044 Input::get_singleton()->is_key_pressed(Key::ENTER)) {
3045 return;
3046 }
3047
3048 if (value_editor->has_point(value_editor->get_local_mouse_position())) {
3049 return;
3050 }
3051
3052 if (!popup_edited_item) {
3053 return;
3054 }
3055
3056 if (popup_edited_item->is_edit_multiline(popup_edited_item_col) && popup_edited_item->get_cell_mode(popup_edited_item_col) == TreeItem::CELL_MODE_STRING) {
3057 _apply_multiline_edit();
3058 } else {
3059 _line_editor_submit(line_editor->get_text());
3060 }
3061}
3062
3063void Tree::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
3064 if (p_event->is_action_pressed("ui_text_newline_blank", true)) {
3065 accept_event();
3066 } else if (p_event->is_action_pressed("ui_text_newline")) {
3067 popup_editor->hide();
3068 _apply_multiline_edit();
3069 accept_event();
3070 }
3071}
3072
3073void Tree::_apply_multiline_edit() {
3074 if (!popup_edited_item) {
3075 return;
3076 }
3077
3078 if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) {
3079 return;
3080 }
3081
3082 TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col];
3083 switch (c.mode) {
3084 case TreeItem::CELL_MODE_STRING: {
3085 c.text = text_editor->get_text();
3086 } break;
3087 default: {
3088 ERR_FAIL();
3089 }
3090 }
3091
3092 item_edited(popup_edited_item_col, popup_edited_item);
3093 queue_redraw();
3094}
3095
3096void Tree::_line_editor_submit(String p_text) {
3097 popup_editor->hide();
3098
3099 if (!popup_edited_item) {
3100 return;
3101 }
3102
3103 if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) {
3104 return;
3105 }
3106
3107 TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col];
3108 switch (c.mode) {
3109 case TreeItem::CELL_MODE_STRING: {
3110 c.text = p_text;
3111 //popup_edited_item->edited_signal.call( popup_edited_item_col );
3112 } break;
3113 case TreeItem::CELL_MODE_RANGE: {
3114 c.val = p_text.to_float();
3115 if (c.step > 0) {
3116 c.val = Math::snapped(c.val, c.step);
3117 }
3118 if (c.val < c.min) {
3119 c.val = c.min;
3120 } else if (c.val > c.max) {
3121 c.val = c.max;
3122 }
3123 //popup_edited_item->edited_signal.call( popup_edited_item_col );
3124 } break;
3125 default: {
3126 ERR_FAIL();
3127 }
3128 }
3129
3130 item_edited(popup_edited_item_col, popup_edited_item);
3131 queue_redraw();
3132}
3133
3134void Tree::value_editor_changed(double p_value) {
3135 if (updating_value_editor) {
3136 return;
3137 }
3138 if (!popup_edited_item) {
3139 return;
3140 }
3141
3142 TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col];
3143 c.val = p_value;
3144
3145 text_editor->set_text(String::num(c.val, Math::range_step_decimals(c.step)));
3146
3147 item_edited(popup_edited_item_col, popup_edited_item);
3148 queue_redraw();
3149}
3150
3151void Tree::popup_select(int p_option) {
3152 if (!popup_edited_item) {
3153 return;
3154 }
3155
3156 if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) {
3157 return;
3158 }
3159
3160 popup_edited_item->cells.write[popup_edited_item_col].val = p_option;
3161 //popup_edited_item->edited_signal.call( popup_edited_item_col );
3162 queue_redraw();
3163 item_edited(popup_edited_item_col, popup_edited_item);
3164}
3165
3166void Tree::_go_left() {
3167 if (selected_col == 0) {
3168 if (selected_item->get_first_child() != nullptr && !selected_item->is_collapsed()) {
3169 selected_item->set_collapsed(true);
3170 } else {
3171 if (columns.size() == 1) { // goto parent with one column
3172 TreeItem *parent = selected_item->get_parent();
3173 if (selected_item != get_root() && parent && parent->is_selectable(selected_col) && !(hide_root && parent == get_root())) {
3174 select_single_item(parent, get_root(), selected_col);
3175 }
3176 } else if (selected_item->get_prev_visible()) {
3177 selected_col = columns.size() - 1;
3178 _go_up(); // go to upper column if possible
3179 }
3180 }
3181 } else {
3182 if (select_mode == SELECT_MULTI) {
3183 selected_col--;
3184 emit_signal(SNAME("cell_selected"));
3185 } else {
3186 selected_item->select(selected_col - 1);
3187 }
3188 }
3189 queue_redraw();
3190 accept_event();
3191 ensure_cursor_is_visible();
3192}
3193
3194void Tree::_go_right() {
3195 if (selected_col == (columns.size() - 1)) {
3196 if (selected_item->get_first_child() != nullptr && selected_item->is_collapsed()) {
3197 selected_item->set_collapsed(false);
3198 } else if (selected_item->get_next_visible()) {
3199 selected_col = 0;
3200 _go_down();
3201 }
3202 } else {
3203 if (select_mode == SELECT_MULTI) {
3204 selected_col++;
3205 emit_signal(SNAME("cell_selected"));
3206 } else {
3207 selected_item->select(selected_col + 1);
3208 }
3209 }
3210 queue_redraw();
3211 ensure_cursor_is_visible();
3212 accept_event();
3213}
3214
3215void Tree::_go_up() {
3216 TreeItem *prev = nullptr;
3217 if (!selected_item) {
3218 prev = get_last_item();
3219 selected_col = 0;
3220 } else {
3221 prev = selected_item->get_prev_visible();
3222 }
3223
3224 if (select_mode == SELECT_MULTI) {
3225 if (!prev) {
3226 return;
3227 }
3228 selected_item = prev;
3229 emit_signal(SNAME("cell_selected"));
3230 queue_redraw();
3231 } else {
3232 int col = selected_col < 0 ? 0 : selected_col;
3233 while (prev && !prev->cells[col].selectable) {
3234 prev = prev->get_prev_visible();
3235 }
3236 if (!prev) {
3237 return; // do nothing..
3238 }
3239 prev->select(col);
3240 }
3241
3242 ensure_cursor_is_visible();
3243 accept_event();
3244}
3245
3246void Tree::_go_down() {
3247 TreeItem *next = nullptr;
3248 if (!selected_item) {
3249 if (root) {
3250 next = hide_root ? root->get_next_visible() : root;
3251 }
3252 } else {
3253 next = selected_item->get_next_visible();
3254 }
3255
3256 if (select_mode == SELECT_MULTI) {
3257 if (!next) {
3258 return;
3259 }
3260
3261 selected_item = next;
3262 emit_signal(SNAME("cell_selected"));
3263 queue_redraw();
3264 } else {
3265 int col = selected_col < 0 ? 0 : selected_col;
3266
3267 while (next && !next->cells[col].selectable) {
3268 next = next->get_next_visible();
3269 }
3270 if (!next) {
3271 return; // do nothing..
3272 }
3273 next->select(col);
3274 }
3275
3276 ensure_cursor_is_visible();
3277 accept_event();
3278}
3279
3280bool Tree::_scroll(bool p_horizontal, float p_pages) {
3281 ScrollBar *scroll = p_horizontal ? (ScrollBar *)h_scroll : (ScrollBar *)v_scroll;
3282
3283 double prev_value = scroll->get_value();
3284 scroll->set_value(scroll->get_value() + scroll->get_page() * p_pages);
3285
3286 return scroll->get_value() != prev_value;
3287}
3288
3289Rect2 Tree::_get_scrollbar_layout_rect() const {
3290 const Size2 control_size = get_size();
3291 const Ref<StyleBox> background = theme_cache.panel_style;
3292
3293 // This is the background stylebox's content rect.
3294 const real_t width = control_size.x - background->get_margin(SIDE_LEFT) - background->get_margin(SIDE_RIGHT);
3295 const real_t height = control_size.y - background->get_margin(SIDE_TOP) - background->get_margin(SIDE_BOTTOM);
3296 const Rect2 content_rect = Rect2(background->get_offset(), Size2(width, height));
3297
3298 // Use the stylebox's margins by default. Can be overridden by `scrollbar_margin_*`.
3299 const real_t top = theme_cache.scrollbar_margin_top < 0 ? content_rect.get_position().y : theme_cache.scrollbar_margin_top;
3300 const real_t right = theme_cache.scrollbar_margin_right < 0 ? content_rect.get_end().x : (control_size.x - theme_cache.scrollbar_margin_right);
3301 const real_t bottom = theme_cache.scrollbar_margin_bottom < 0 ? content_rect.get_end().y : (control_size.y - theme_cache.scrollbar_margin_bottom);
3302 const real_t left = theme_cache.scrollbar_margin_left < 0 ? content_rect.get_position().x : theme_cache.scrollbar_margin_left;
3303
3304 return Rect2(left, top, right - left, bottom - top);
3305}
3306
3307Rect2 Tree::_get_content_rect() const {
3308 const Size2 control_size = get_size();
3309 const Ref<StyleBox> background = theme_cache.panel_style;
3310
3311 // This is the background stylebox's content rect.
3312 const real_t width = control_size.x - background->get_margin(SIDE_LEFT) - background->get_margin(SIDE_RIGHT);
3313 const real_t height = control_size.y - background->get_margin(SIDE_TOP) - background->get_margin(SIDE_BOTTOM);
3314 const Rect2 content_rect = Rect2(background->get_offset(), Size2(width, height));
3315
3316 // Scrollbars won't affect Tree's content rect if they're not visible or placed inside the stylebox margin area.
3317 const real_t v_size = v_scroll->is_visible() ? (v_scroll->get_combined_minimum_size().x + theme_cache.scrollbar_h_separation) : 0;
3318 const real_t h_size = h_scroll->is_visible() ? (h_scroll->get_combined_minimum_size().y + theme_cache.scrollbar_v_separation) : 0;
3319 const Point2 scroll_begin = _get_scrollbar_layout_rect().get_end() - Vector2(v_size, h_size);
3320 const Size2 offset = (content_rect.get_end() - scroll_begin).max(Vector2(0, 0));
3321
3322 return content_rect.grow_individual(0, 0, -offset.x, -offset.y);
3323}
3324
3325void Tree::gui_input(const Ref<InputEvent> &p_event) {
3326 ERR_FAIL_COND(p_event.is_null());
3327
3328 Ref<InputEventKey> k = p_event;
3329
3330 bool is_command = k.is_valid() && k->is_command_or_control_pressed();
3331 if (p_event->is_action("ui_right", true) && p_event->is_pressed()) {
3332 if (!cursor_can_exit_tree) {
3333 accept_event();
3334 }
3335
3336 if (!selected_item || select_mode == SELECT_ROW || selected_col > (columns.size() - 1)) {
3337 return;
3338 }
3339 if (k.is_valid() && k->is_alt_pressed()) {
3340 selected_item->set_collapsed(false);
3341 TreeItem *next = selected_item->get_first_child();
3342 while (next && next != selected_item->next) {
3343 next->set_collapsed(false);
3344 next = next->get_next_visible();
3345 }
3346 } else {
3347 _go_right();
3348 }
3349 } else if (p_event->is_action("ui_left", true) && p_event->is_pressed()) {
3350 if (!cursor_can_exit_tree) {
3351 accept_event();
3352 }
3353
3354 if (!selected_item || select_mode == SELECT_ROW || selected_col < 0) {
3355 return;
3356 }
3357
3358 if (k.is_valid() && k->is_alt_pressed()) {
3359 selected_item->set_collapsed(true);
3360 TreeItem *next = selected_item->get_first_child();
3361 while (next && next != selected_item->next) {
3362 next->set_collapsed(true);
3363 next = next->get_next_visible();
3364 }
3365 } else {
3366 _go_left();
3367 }
3368
3369 } else if (p_event->is_action("ui_up", true) && p_event->is_pressed() && !is_command) {
3370 if (!cursor_can_exit_tree) {
3371 accept_event();
3372 }
3373
3374 _go_up();
3375
3376 } else if (p_event->is_action("ui_down", true) && p_event->is_pressed() && !is_command) {
3377 if (!cursor_can_exit_tree) {
3378 accept_event();
3379 }
3380
3381 _go_down();
3382
3383 } else if (p_event->is_action("ui_page_down", true) && p_event->is_pressed()) {
3384 if (!cursor_can_exit_tree) {
3385 accept_event();
3386 }
3387
3388 TreeItem *next = nullptr;
3389 if (!selected_item) {
3390 return;
3391 }
3392 next = selected_item;
3393
3394 for (int i = 0; i < 10; i++) {
3395 TreeItem *_n = next->get_next_visible();
3396 if (_n) {
3397 next = _n;
3398 } else {
3399 break;
3400 }
3401 }
3402 if (next == selected_item) {
3403 return;
3404 }
3405
3406 if (select_mode == SELECT_MULTI) {
3407 selected_item = next;
3408 emit_signal(SNAME("cell_selected"));
3409 queue_redraw();
3410 } else {
3411 while (next && !next->cells[selected_col].selectable) {
3412 next = next->get_next_visible();
3413 }
3414 if (!next) {
3415 return; // do nothing..
3416 }
3417 next->select(selected_col);
3418 }
3419
3420 ensure_cursor_is_visible();
3421 } else if (p_event->is_action("ui_page_up", true) && p_event->is_pressed()) {
3422 if (!cursor_can_exit_tree) {
3423 accept_event();
3424 }
3425
3426 TreeItem *prev = nullptr;
3427 if (!selected_item) {
3428 return;
3429 }
3430 prev = selected_item;
3431
3432 for (int i = 0; i < 10; i++) {
3433 TreeItem *_n = prev->get_prev_visible();
3434 if (_n) {
3435 prev = _n;
3436 } else {
3437 break;
3438 }
3439 }
3440 if (prev == selected_item) {
3441 return;
3442 }
3443
3444 if (select_mode == SELECT_MULTI) {
3445 selected_item = prev;
3446 emit_signal(SNAME("cell_selected"));
3447 queue_redraw();
3448 } else {
3449 while (prev && !prev->cells[selected_col].selectable) {
3450 prev = prev->get_prev_visible();
3451 }
3452 if (!prev) {
3453 return; // do nothing..
3454 }
3455 prev->select(selected_col);
3456 }
3457 ensure_cursor_is_visible();
3458 } else if (p_event->is_action("ui_accept", true) && p_event->is_pressed()) {
3459 if (selected_item) {
3460 //bring up editor if possible
3461 if (!edit_selected()) {
3462 emit_signal(SNAME("item_activated"));
3463 incr_search.clear();
3464 }
3465 }
3466 accept_event();
3467 } else if (p_event->is_action("ui_select", true) && p_event->is_pressed()) {
3468 if (select_mode == SELECT_MULTI) {
3469 if (!selected_item) {
3470 return;
3471 }
3472 if (selected_item->is_selected(selected_col)) {
3473 selected_item->deselect(selected_col);
3474 emit_signal(SNAME("multi_selected"), selected_item, selected_col, false);
3475 } else if (selected_item->is_selectable(selected_col)) {
3476 selected_item->select(selected_col);
3477 emit_signal(SNAME("multi_selected"), selected_item, selected_col, true);
3478 }
3479 }
3480 accept_event();
3481 }
3482
3483 if (allow_search && k.is_valid()) { // Incremental search
3484
3485 if (!k->is_pressed()) {
3486 return;
3487 }
3488 if (k->is_command_or_control_pressed() || (k->is_shift_pressed() && k->get_unicode() == 0) || k->is_meta_pressed()) {
3489 return;
3490 }
3491 if (!root) {
3492 return;
3493 }
3494
3495 if (hide_root && !root->get_next_visible()) {
3496 return;
3497 }
3498
3499 if (k->get_unicode() > 0) {
3500 _do_incr_search(String::chr(k->get_unicode()));
3501 accept_event();
3502
3503 return;
3504 } else {
3505 if (k->get_keycode() != Key::SHIFT) {
3506 last_keypress = 0;
3507 }
3508 }
3509 }
3510
3511 Ref<InputEventMouseMotion> mm = p_event;
3512 if (mm.is_valid()) {
3513 Ref<StyleBox> bg = theme_cache.panel_style;
3514 bool rtl = is_layout_rtl();
3515
3516 Point2 pos = mm->get_position();
3517 if (rtl) {
3518 pos.x = get_size().width - pos.x;
3519 }
3520 pos -= theme_cache.panel_style->get_offset();
3521
3522 Cache::ClickType old_hover = cache.hover_type;
3523 int old_index = cache.hover_index;
3524
3525 cache.hover_type = Cache::CLICK_NONE;
3526 cache.hover_index = 0;
3527 if (show_column_titles) {
3528 pos.y -= _get_title_button_height();
3529 if (pos.y < 0) {
3530 pos.x += theme_cache.offset.x;
3531 int len = 0;
3532 for (int i = 0; i < columns.size(); i++) {
3533 len += get_column_width(i);
3534 if (pos.x < len) {
3535 cache.hover_type = Cache::CLICK_TITLE;
3536 cache.hover_index = i;
3537 break;
3538 }
3539 }
3540 }
3541 }
3542
3543 if (root) {
3544 Point2 mpos = mm->get_position();
3545 if (rtl) {
3546 mpos.x = get_size().width - mpos.x;
3547 }
3548 mpos -= theme_cache.panel_style->get_offset();
3549 mpos.y -= _get_title_button_height();
3550 if (mpos.y >= 0) {
3551 if (h_scroll->is_visible_in_tree()) {
3552 mpos.x += h_scroll->get_value();
3553 }
3554 if (v_scroll->is_visible_in_tree()) {
3555 mpos.y += v_scroll->get_value();
3556 }
3557
3558 TreeItem *old_it = cache.hover_item;
3559 int old_col = cache.hover_cell;
3560
3561 int col, h, section;
3562 TreeItem *it = _find_item_at_pos(root, mpos, col, h, section);
3563
3564 if (drop_mode_flags) {
3565 if (it != drop_mode_over) {
3566 drop_mode_over = it;
3567 queue_redraw();
3568 }
3569 if (it && section != drop_mode_section) {
3570 drop_mode_section = section;
3571 queue_redraw();
3572 }
3573 }
3574
3575 cache.hover_item = it;
3576 cache.hover_cell = col;
3577
3578 if (it != old_it || col != old_col) {
3579 if (old_it && old_col >= old_it->cells.size()) {
3580 // Columns may have changed since last redraw().
3581 queue_redraw();
3582 } else {
3583 // Only need to update if mouse enters/exits a button
3584 bool was_over_button = old_it && old_it->cells[old_col].custom_button;
3585 bool is_over_button = it && it->cells[col].custom_button;
3586 if (was_over_button || is_over_button) {
3587 queue_redraw();
3588 }
3589 }
3590 }
3591 }
3592 }
3593
3594 // Update if mouse enters/exits columns
3595 if (cache.hover_type != old_hover || cache.hover_index != old_index) {
3596 queue_redraw();
3597 }
3598
3599 if (pressing_for_editor && popup_pressing_edited_item && (popup_pressing_edited_item->get_cell_mode(popup_pressing_edited_item_column) == TreeItem::CELL_MODE_RANGE)) {
3600 /* This needs to happen now, because the popup can be closed when pressing another item, and must remain the popup edited item until it actually closes */
3601 popup_edited_item = popup_pressing_edited_item;
3602 popup_edited_item_col = popup_pressing_edited_item_column;
3603
3604 popup_pressing_edited_item = nullptr;
3605 popup_pressing_edited_item_column = -1;
3606
3607 if (!range_drag_enabled) {
3608 //range drag
3609 Vector2 cpos = mm->get_position();
3610 if (rtl) {
3611 cpos.x = get_size().width - cpos.x;
3612 }
3613 if (cpos.distance_to(pressing_pos) > 2) {
3614 range_drag_enabled = true;
3615 range_drag_capture_pos = cpos;
3616 range_drag_base = popup_edited_item->get_range(popup_edited_item_col);
3617 Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
3618 }
3619 } else {
3620 const TreeItem::Cell &c = popup_edited_item->cells[popup_edited_item_col];
3621 float diff_y = -mm->get_relative().y;
3622 diff_y = Math::pow(ABS(diff_y), 1.8f) * SIGN(diff_y);
3623 diff_y *= 0.1;
3624 range_drag_base = CLAMP(range_drag_base + c.step * diff_y, c.min, c.max);
3625 popup_edited_item->set_range(popup_edited_item_col, range_drag_base);
3626 item_edited(popup_edited_item_col, popup_edited_item);
3627 }
3628 }
3629
3630 if (drag_touching && !drag_touching_deaccel) {
3631 drag_accum -= mm->get_relative().y;
3632 v_scroll->set_value(drag_from + drag_accum);
3633 drag_speed = -mm->get_velocity().y;
3634 }
3635 }
3636
3637 Ref<InputEventMouseButton> mb = p_event;
3638 if (mb.is_valid()) {
3639 bool rtl = is_layout_rtl();
3640
3641 if (!mb->is_pressed()) {
3642 if (mb->get_button_index() == MouseButton::LEFT ||
3643 mb->get_button_index() == MouseButton::RIGHT) {
3644 Point2 pos = mb->get_position();
3645 if (rtl) {
3646 pos.x = get_size().width - pos.x;
3647 }
3648 pos -= theme_cache.panel_style->get_offset();
3649 if (show_column_titles) {
3650 pos.y -= _get_title_button_height();
3651
3652 if (pos.y < 0) {
3653 pos.x += theme_cache.offset.x;
3654 int len = 0;
3655 for (int i = 0; i < columns.size(); i++) {
3656 len += get_column_width(i);
3657 if (pos.x < static_cast<real_t>(len)) {
3658 emit_signal(SNAME("column_title_clicked"), i, mb->get_button_index());
3659 break;
3660 }
3661 }
3662 }
3663 }
3664 }
3665
3666 if (mb->get_button_index() == MouseButton::LEFT) {
3667 if (single_select_defer) {
3668 select_single_item(single_select_defer, root, single_select_defer_column);
3669 single_select_defer = nullptr;
3670 }
3671
3672 range_click_timer->stop();
3673
3674 if (pressing_for_editor) {
3675 if (range_drag_enabled) {
3676 range_drag_enabled = false;
3677 Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
3678 warp_mouse(range_drag_capture_pos);
3679 } else {
3680 Rect2 rect = get_selected()->get_meta("__focus_rect");
3681 Point2 mpos = mb->get_position();
3682 int icon_size_x = 0;
3683 Ref<Texture2D> icon = get_selected()->get_icon(selected_col);
3684 if (icon.is_valid()) {
3685 Rect2i icon_region = get_selected()->get_icon_region(selected_col);
3686 if (icon_region == Rect2i()) {
3687 icon_size_x = icon->get_width();
3688 } else {
3689 icon_size_x = icon_region.size.width;
3690 }
3691 }
3692 // Icon is treated as if it is outside of the rect so that double clicking on it will emit the item_icon_double_clicked signal.
3693 if (rtl) {
3694 mpos.x = get_size().width - (mpos.x + icon_size_x);
3695 } else {
3696 mpos.x -= icon_size_x;
3697 }
3698 if (rect.has_point(mpos)) {
3699 if (!edit_selected()) {
3700 emit_signal(SNAME("item_icon_double_clicked"));
3701 }
3702 } else {
3703 emit_signal(SNAME("item_icon_double_clicked"));
3704 }
3705 }
3706 pressing_for_editor = false;
3707 }
3708
3709 if (drag_touching) {
3710 if (drag_speed == 0) {
3711 drag_touching_deaccel = false;
3712 drag_touching = false;
3713 set_physics_process_internal(false);
3714 } else {
3715 drag_touching_deaccel = true;
3716 }
3717 }
3718 }
3719
3720 if (cache.click_type == Cache::CLICK_BUTTON && cache.click_item != nullptr) {
3721 // make sure in case of wrong reference after reconstructing whole TreeItems
3722 cache.click_item = get_item_at_position(cache.click_pos);
3723 emit_signal("button_clicked", cache.click_item, cache.click_column, cache.click_id, mb->get_button_index());
3724 }
3725
3726 cache.click_type = Cache::CLICK_NONE;
3727 cache.click_index = -1;
3728 cache.click_id = -1;
3729 cache.click_item = nullptr;
3730 cache.click_column = 0;
3731 queue_redraw();
3732 return;
3733 }
3734
3735 if (range_drag_enabled) {
3736 return;
3737 }
3738
3739 switch (mb->get_button_index()) {
3740 case MouseButton::RIGHT:
3741 case MouseButton::LEFT: {
3742 Ref<StyleBox> bg = theme_cache.panel_style;
3743
3744 Point2 pos = mb->get_position();
3745 if (rtl) {
3746 pos.x = get_size().width - pos.x;
3747 }
3748 pos -= bg->get_offset();
3749 cache.click_type = Cache::CLICK_NONE;
3750 if (show_column_titles) {
3751 pos.y -= _get_title_button_height();
3752
3753 if (pos.y < 0) {
3754 pos.x += theme_cache.offset.x;
3755 int len = 0;
3756 for (int i = 0; i < columns.size(); i++) {
3757 len += get_column_width(i);
3758 if (pos.x < static_cast<real_t>(len)) {
3759 cache.click_type = Cache::CLICK_TITLE;
3760 cache.click_index = i;
3761 queue_redraw();
3762 break;
3763 }
3764 }
3765 break;
3766 }
3767 }
3768
3769 if (!root || (!root->get_first_child() && hide_root)) {
3770 break;
3771 }
3772
3773 click_handled = false;
3774 pressing_for_editor = false;
3775 propagate_mouse_activated = false;
3776
3777 int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width;
3778 if (v_scroll->is_visible()) {
3779 x_limit -= v_scroll->get_minimum_size().width;
3780 }
3781
3782 cache.rtl = is_layout_rtl();
3783 blocked++;
3784 propagate_mouse_event(pos + theme_cache.offset, 0, 0, x_limit + theme_cache.offset.width, mb->is_double_click(), root, mb->get_button_index(), mb);
3785 blocked--;
3786
3787 if (pressing_for_editor) {
3788 pressing_pos = mb->get_position();
3789 if (rtl) {
3790 pressing_pos.x = get_size().width - pressing_pos.x;
3791 }
3792 }
3793
3794 if (mb->get_button_index() == MouseButton::RIGHT) {
3795 break;
3796 }
3797
3798 if (drag_touching) {
3799 set_physics_process_internal(false);
3800 drag_touching_deaccel = false;
3801 drag_touching = false;
3802 drag_speed = 0;
3803 drag_from = 0;
3804 }
3805
3806 if (!click_handled) {
3807 drag_speed = 0;
3808 drag_accum = 0;
3809 //last_drag_accum=0;
3810 drag_from = v_scroll->get_value();
3811 drag_touching = DisplayServer::get_singleton()->is_touchscreen_available();
3812 drag_touching_deaccel = false;
3813 if (drag_touching) {
3814 set_physics_process_internal(true);
3815 }
3816
3817 if (mb->get_button_index() == MouseButton::LEFT) {
3818 if (get_item_at_position(mb->get_position()) == nullptr && !mb->is_shift_pressed() && !mb->is_ctrl_pressed() && !mb->is_command_or_control_pressed()) {
3819 emit_signal(SNAME("nothing_selected"));
3820 }
3821 }
3822 }
3823
3824 if (propagate_mouse_activated) {
3825 emit_signal(SNAME("item_activated"));
3826 propagate_mouse_activated = false;
3827 }
3828
3829 } break;
3830 case MouseButton::WHEEL_UP: {
3831 if (_scroll(false, -mb->get_factor() / 8)) {
3832 accept_event();
3833 }
3834
3835 } break;
3836 case MouseButton::WHEEL_DOWN: {
3837 if (_scroll(false, mb->get_factor() / 8)) {
3838 accept_event();
3839 }
3840
3841 } break;
3842 case MouseButton::WHEEL_LEFT: {
3843 if (_scroll(true, -mb->get_factor() / 8)) {
3844 accept_event();
3845 }
3846
3847 } break;
3848 case MouseButton::WHEEL_RIGHT: {
3849 if (_scroll(true, mb->get_factor() / 8)) {
3850 accept_event();
3851 }
3852
3853 } break;
3854 default:
3855 break;
3856 }
3857 }
3858
3859 Ref<InputEventPanGesture> pan_gesture = p_event;
3860 if (pan_gesture.is_valid()) {
3861 double prev_v = v_scroll->get_value();
3862 v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8);
3863
3864 double prev_h = h_scroll->get_value();
3865 if (is_layout_rtl()) {
3866 h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * -pan_gesture->get_delta().x / 8);
3867 } else {
3868 h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8);
3869 }
3870
3871 if (v_scroll->get_value() != prev_v || h_scroll->get_value() != prev_h) {
3872 accept_event();
3873 }
3874 }
3875}
3876
3877bool Tree::edit_selected(bool p_force_edit) {
3878 TreeItem *s = get_selected();
3879 ERR_FAIL_NULL_V_MSG(s, false, "No item selected.");
3880 ensure_cursor_is_visible();
3881 int col = get_selected_column();
3882 ERR_FAIL_INDEX_V_MSG(col, columns.size(), false, "No item column selected.");
3883
3884 if (!s->cells[col].editable && !p_force_edit) {
3885 return false;
3886 }
3887
3888 Rect2 rect = s->get_meta("__focus_rect");
3889 popup_edited_item = s;
3890 popup_edited_item_col = col;
3891
3892 const TreeItem::Cell &c = s->cells[col];
3893
3894 if (c.mode == TreeItem::CELL_MODE_CHECK) {
3895 s->set_checked(col, !c.checked);
3896 item_edited(col, s);
3897 return true;
3898 } else if (c.mode == TreeItem::CELL_MODE_CUSTOM) {
3899 edited_item = s;
3900 edited_col = col;
3901 custom_popup_rect = Rect2i(get_global_position() + rect.position, rect.size);
3902 emit_signal(SNAME("custom_popup_edited"), false);
3903 item_edited(col, s);
3904
3905 return true;
3906 } else if (c.mode == TreeItem::CELL_MODE_RANGE && !c.text.is_empty()) {
3907 popup_menu->clear();
3908 for (int i = 0; i < c.text.get_slice_count(","); i++) {
3909 String s2 = c.text.get_slicec(',', i);
3910 popup_menu->add_item(s2.get_slicec(':', 0), s2.get_slicec(':', 1).is_empty() ? i : s2.get_slicec(':', 1).to_int());
3911 }
3912
3913 popup_menu->set_size(Size2(rect.size.width, 0));
3914 popup_menu->set_position(get_screen_position() + rect.position + Point2i(0, rect.size.height));
3915 popup_menu->popup();
3916 popup_edited_item = s;
3917 popup_edited_item_col = col;
3918
3919 return true;
3920 } else if ((c.mode == TreeItem::CELL_MODE_STRING && !c.edit_multiline) || c.mode == TreeItem::CELL_MODE_RANGE) {
3921 Rect2 popup_rect;
3922
3923 int value_editor_height = c.mode == TreeItem::CELL_MODE_RANGE ? value_editor->get_minimum_size().height : 0;
3924 // "floor()" centers vertically.
3925 Vector2 ofs(0, Math::floor((MAX(line_editor->get_minimum_size().height, rect.size.height - value_editor_height) - rect.size.height) / 2));
3926
3927 popup_rect.position = get_screen_position() + rect.position - ofs;
3928 popup_rect.size = rect.size;
3929
3930 // Account for icon.
3931 Size2 icon_size = _get_cell_icon_size(c);
3932 popup_rect.position.x += icon_size.x;
3933 popup_rect.size.x -= icon_size.x;
3934
3935 line_editor->clear();
3936 line_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
3937 line_editor->select_all();
3938 line_editor->show();
3939
3940 text_editor->hide();
3941
3942 if (c.mode == TreeItem::CELL_MODE_RANGE) {
3943 popup_rect.size.y += value_editor_height;
3944
3945 value_editor->show();
3946 updating_value_editor = true;
3947 value_editor->set_min(c.min);
3948 value_editor->set_max(c.max);
3949 value_editor->set_step(c.step);
3950 value_editor->set_value(c.val);
3951 value_editor->set_exp_ratio(c.expr);
3952 updating_value_editor = false;
3953 } else {
3954 value_editor->hide();
3955 }
3956
3957 popup_editor->set_position(popup_rect.position);
3958 popup_editor->set_size(popup_rect.size);
3959 popup_editor->popup();
3960 popup_editor->child_controls_changed();
3961
3962 line_editor->grab_focus();
3963
3964 return true;
3965 } else if (c.mode == TreeItem::CELL_MODE_STRING && c.edit_multiline) {
3966 line_editor->hide();
3967
3968 text_editor->clear();
3969 text_editor->set_text(c.text);
3970 text_editor->select_all();
3971 text_editor->show();
3972
3973 popup_editor->set_position(get_screen_position() + rect.position);
3974 popup_editor->set_size(rect.size);
3975 popup_editor->popup();
3976 popup_editor->child_controls_changed();
3977
3978 text_editor->grab_focus();
3979
3980 return true;
3981 }
3982
3983 return false;
3984}
3985
3986bool Tree::is_editing() {
3987 return popup_editor->is_visible();
3988}
3989
3990void Tree::set_editor_selection(int p_from_line, int p_to_line, int p_from_column, int p_to_column, int p_caret) {
3991 if (p_from_column == -1 || p_to_column == -1) {
3992 line_editor->select(p_from_line, p_to_line);
3993 } else {
3994 text_editor->select(p_from_line, p_from_column, p_to_line, p_to_column, p_caret);
3995 }
3996}
3997
3998Size2 Tree::get_internal_min_size() const {
3999 Size2i size;
4000 if (root) {
4001 size.height += get_item_height(root);
4002 }
4003 for (int i = 0; i < columns.size(); i++) {
4004 size.width += get_column_minimum_width(i);
4005 }
4006
4007 return size;
4008}
4009
4010void Tree::update_scrollbars() {
4011 const Size2 control_size = get_size();
4012 const Ref<StyleBox> background = theme_cache.panel_style;
4013
4014 // This is the background stylebox's content rect.
4015 const real_t width = control_size.x - background->get_margin(SIDE_LEFT) - background->get_margin(SIDE_RIGHT);
4016 const real_t height = control_size.y - background->get_margin(SIDE_TOP) - background->get_margin(SIDE_BOTTOM);
4017 const Rect2 content_rect = Rect2(background->get_offset(), Size2(width, height));
4018
4019 const Size2 hmin = h_scroll->get_combined_minimum_size();
4020 const Size2 vmin = v_scroll->get_combined_minimum_size();
4021
4022 const Size2 internal_min_size = get_internal_min_size();
4023 const int title_button_height = _get_title_button_height();
4024
4025 Size2 tree_content_size = content_rect.get_size() - Vector2(0, title_button_height);
4026 bool display_vscroll = internal_min_size.height > tree_content_size.height;
4027 bool display_hscroll = internal_min_size.width > tree_content_size.width;
4028 for (int i = 0; i < 2; i++) {
4029 // Check twice, as both values are dependent on each other.
4030 if (display_hscroll) {
4031 tree_content_size.height = content_rect.get_size().height - title_button_height - hmin.height;
4032 display_vscroll = internal_min_size.height > tree_content_size.height;
4033 }
4034 if (display_vscroll) {
4035 tree_content_size.width = content_rect.get_size().width - vmin.width;
4036 display_hscroll = internal_min_size.width > tree_content_size.width;
4037 }
4038 }
4039
4040 if (display_vscroll) {
4041 v_scroll->show();
4042 v_scroll->set_max(internal_min_size.height);
4043 v_scroll->set_page(tree_content_size.height);
4044 theme_cache.offset.y = v_scroll->get_value();
4045 } else {
4046 v_scroll->hide();
4047 theme_cache.offset.y = 0;
4048 }
4049
4050 if (display_hscroll) {
4051 h_scroll->show();
4052 h_scroll->set_max(internal_min_size.width);
4053 h_scroll->set_page(tree_content_size.width);
4054 theme_cache.offset.x = h_scroll->get_value();
4055 } else {
4056 h_scroll->hide();
4057 theme_cache.offset.x = 0;
4058 }
4059
4060 const Rect2 scroll_rect = _get_scrollbar_layout_rect();
4061 v_scroll->set_begin(scroll_rect.get_position() + Vector2(scroll_rect.get_size().x - vmin.width, 0));
4062 v_scroll->set_end(scroll_rect.get_end() - Vector2(0, display_hscroll ? hmin.height : 0));
4063 h_scroll->set_begin(scroll_rect.get_position() + Vector2(0, scroll_rect.get_size().y - hmin.height));
4064 h_scroll->set_end(scroll_rect.get_end() - Vector2(display_vscroll ? vmin.width : 0, 0));
4065}
4066
4067int Tree::_get_title_button_height() const {
4068 ERR_FAIL_COND_V(theme_cache.tb_font.is_null() || theme_cache.title_button.is_null(), 0);
4069 int h = 0;
4070 if (show_column_titles) {
4071 for (int i = 0; i < columns.size(); i++) {
4072 h = MAX(h, columns[i].text_buf->get_size().y + theme_cache.title_button->get_minimum_size().height);
4073 }
4074 }
4075 return h;
4076}
4077
4078void Tree::_notification(int p_what) {
4079 switch (p_what) {
4080 case NOTIFICATION_FOCUS_ENTER: {
4081 if (get_viewport()) {
4082 focus_in_id = get_viewport()->get_processed_events_count();
4083 }
4084 } break;
4085
4086 case NOTIFICATION_MOUSE_EXIT: {
4087 if (cache.hover_type != Cache::CLICK_NONE) {
4088 cache.hover_type = Cache::CLICK_NONE;
4089 queue_redraw();
4090 }
4091 } break;
4092
4093 case NOTIFICATION_VISIBILITY_CHANGED: {
4094 drag_touching = false;
4095 } break;
4096
4097 case NOTIFICATION_DRAG_END: {
4098 drop_mode_flags = 0;
4099 scrolling = false;
4100 set_physics_process_internal(false);
4101 queue_redraw();
4102 } break;
4103
4104 case NOTIFICATION_DRAG_BEGIN: {
4105 single_select_defer = nullptr;
4106 if (theme_cache.scroll_speed > 0) {
4107 scrolling = true;
4108 set_physics_process_internal(true);
4109 }
4110 } break;
4111
4112 case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
4113 if (drag_touching) {
4114 if (drag_touching_deaccel) {
4115 float pos = v_scroll->get_value();
4116 pos += drag_speed * get_physics_process_delta_time();
4117
4118 bool turnoff = false;
4119 if (pos < 0) {
4120 pos = 0;
4121 turnoff = true;
4122 set_physics_process_internal(false);
4123 drag_touching = false;
4124 drag_touching_deaccel = false;
4125 }
4126 if (pos > (v_scroll->get_max() - v_scroll->get_page())) {
4127 pos = v_scroll->get_max() - v_scroll->get_page();
4128 turnoff = true;
4129 }
4130
4131 v_scroll->set_value(pos);
4132 float sgn = drag_speed < 0 ? -1 : 1;
4133 float val = Math::abs(drag_speed);
4134 val -= 1000 * get_physics_process_delta_time();
4135
4136 if (val < 0) {
4137 turnoff = true;
4138 }
4139 drag_speed = sgn * val;
4140
4141 if (turnoff) {
4142 set_physics_process_internal(false);
4143 drag_touching = false;
4144 drag_touching_deaccel = false;
4145 }
4146 }
4147 }
4148
4149 Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
4150 if (scrolling && get_rect().grow(theme_cache.scroll_border).has_point(mouse_position)) {
4151 Point2 point;
4152
4153 if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < theme_cache.scroll_border)) {
4154 point.x = mouse_position.x - theme_cache.scroll_border;
4155 } else if (ABS(mouse_position.x - get_size().width) < theme_cache.scroll_border) {
4156 point.x = mouse_position.x - (get_size().width - theme_cache.scroll_border);
4157 }
4158
4159 if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < theme_cache.scroll_border)) {
4160 point.y = mouse_position.y - theme_cache.scroll_border;
4161 } else if (ABS(mouse_position.y - get_size().height) < theme_cache.scroll_border) {
4162 point.y = mouse_position.y - (get_size().height - theme_cache.scroll_border);
4163 }
4164
4165 point *= theme_cache.scroll_speed * get_physics_process_delta_time();
4166 point += get_scroll();
4167 h_scroll->set_value(point.x);
4168 v_scroll->set_value(point.y);
4169 }
4170 } break;
4171
4172 case NOTIFICATION_DRAW: {
4173 v_scroll->set_custom_step(theme_cache.font->get_height(theme_cache.font_size));
4174
4175 update_scrollbars();
4176 RID ci = get_canvas_item();
4177
4178 Ref<StyleBox> bg = theme_cache.panel_style;
4179 const Rect2 content_rect = _get_content_rect();
4180
4181 Point2 draw_ofs = content_rect.position;
4182 Size2 draw_size = content_rect.size;
4183
4184 bg->draw(ci, Rect2(Point2(), get_size()));
4185
4186 int tbh = _get_title_button_height();
4187
4188 draw_ofs.y += tbh;
4189 draw_size.y -= tbh;
4190
4191 cache.rtl = is_layout_rtl();
4192
4193 if (root && get_size().x > 0 && get_size().y > 0) {
4194 int self_height = 0; // Just to pass a reference, we don't need the root's `self_height`.
4195 draw_item(Point2(), draw_ofs, draw_size, root, self_height);
4196 }
4197
4198 if (show_column_titles) {
4199 //title buttons
4200 int ofs2 = theme_cache.panel_style->get_margin(SIDE_LEFT);
4201 for (int i = 0; i < columns.size(); i++) {
4202 Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? theme_cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? theme_cache.title_button_hover : theme_cache.title_button);
4203 Rect2 tbrect = Rect2(ofs2 - theme_cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
4204 if (cache.rtl) {
4205 tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
4206 }
4207 sb->draw(ci, tbrect);
4208 ofs2 += tbrect.size.width;
4209 //text
4210 int clip_w = tbrect.size.width - sb->get_minimum_size().width;
4211 columns.write[i].text_buf->set_width(clip_w);
4212 columns.write[i].cached_minimum_width_dirty = true;
4213
4214 Vector2 text_pos = Point2i(tbrect.position.x, tbrect.position.y + (tbrect.size.height - columns[i].text_buf->get_size().y) / 2);
4215 switch (columns[i].title_alignment) {
4216 case HorizontalAlignment::HORIZONTAL_ALIGNMENT_LEFT: {
4217 text_pos.x += cache.rtl ? tbrect.size.width - (sb->get_offset().x + columns[i].text_buf->get_size().x) : sb->get_offset().x;
4218 break;
4219 }
4220
4221 case HorizontalAlignment::HORIZONTAL_ALIGNMENT_RIGHT: {
4222 text_pos.x += cache.rtl ? sb->get_offset().x : tbrect.size.width - (sb->get_offset().x + columns[i].text_buf->get_size().x);
4223 break;
4224 }
4225
4226 default: {
4227 text_pos.x += sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2;
4228 break;
4229 }
4230 }
4231
4232 if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
4233 columns[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_outline_size, theme_cache.font_outline_color);
4234 }
4235 columns[i].text_buf->draw(ci, text_pos, theme_cache.title_button_color);
4236 }
4237 }
4238
4239 // Draw the focus outline last, so that it is drawn in front of the section headings.
4240 // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling.
4241 if (has_focus()) {
4242 RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
4243 theme_cache.focus_style->draw(ci, Rect2(Point2(), get_size()));
4244 RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
4245 }
4246 } break;
4247
4248 case NOTIFICATION_THEME_CHANGED:
4249 case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
4250 case NOTIFICATION_TRANSLATION_CHANGED: {
4251 _update_all();
4252 } break;
4253
4254 case NOTIFICATION_RESIZED:
4255 case NOTIFICATION_TRANSFORM_CHANGED: {
4256 if (popup_edited_item != nullptr) {
4257 Rect2 rect = popup_edited_item->get_meta("__focus_rect");
4258
4259 popup_editor->set_position(get_global_position() + rect.position);
4260 popup_editor->set_size(rect.size);
4261 popup_editor->child_controls_changed();
4262 }
4263 } break;
4264 }
4265}
4266
4267void Tree::_update_all() {
4268 for (int i = 0; i < columns.size(); i++) {
4269 update_column(i);
4270 }
4271 if (root) {
4272 update_item_cache(root);
4273 }
4274}
4275
4276Size2 Tree::get_minimum_size() const {
4277 if (h_scroll_enabled && v_scroll_enabled) {
4278 return Size2();
4279 } else {
4280 Vector2 min_size = get_internal_min_size();
4281 Ref<StyleBox> bg = theme_cache.panel_style;
4282 if (bg.is_valid()) {
4283 min_size.x += bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT);
4284 min_size.y += bg->get_margin(SIDE_TOP) + bg->get_margin(SIDE_BOTTOM);
4285 }
4286 return Vector2(h_scroll_enabled ? 0 : min_size.x, v_scroll_enabled ? 0 : min_size.y);
4287 }
4288}
4289
4290TreeItem *Tree::create_item(TreeItem *p_parent, int p_index) {
4291 ERR_FAIL_COND_V(blocked > 0, nullptr);
4292
4293 TreeItem *ti = nullptr;
4294
4295 if (p_parent) {
4296 ERR_FAIL_COND_V_MSG(p_parent->tree != this, nullptr, "A different tree owns the given parent");
4297 ti = p_parent->create_child(p_index);
4298 } else {
4299 if (!root) {
4300 // No root exists, make the given item the new root.
4301 ti = memnew(TreeItem(this));
4302 ERR_FAIL_NULL_V(ti, nullptr);
4303 ti->cells.resize(columns.size());
4304 ti->is_root = true;
4305 root = ti;
4306 } else {
4307 // Root exists, append or insert to root.
4308 ti = create_item(root, p_index);
4309 }
4310 }
4311
4312 return ti;
4313}
4314
4315TreeItem *Tree::get_root() const {
4316 return root;
4317}
4318
4319TreeItem *Tree::get_last_item() const {
4320 TreeItem *last = root;
4321
4322 while (last) {
4323 if (last->next) {
4324 last = last->next;
4325 } else if (last->first_child) {
4326 last = last->first_child;
4327 } else {
4328 break;
4329 }
4330 }
4331
4332 return last;
4333}
4334
4335void Tree::item_edited(int p_column, TreeItem *p_item, MouseButton p_custom_mouse_index) {
4336 edited_item = p_item;
4337 edited_col = p_column;
4338 if (p_item != nullptr && p_column >= 0 && p_column < p_item->cells.size()) {
4339 edited_item->cells.write[p_column].dirty = true;
4340 }
4341 emit_signal(SNAME("item_edited"));
4342 if (p_custom_mouse_index != MouseButton::NONE) {
4343 emit_signal(SNAME("custom_item_clicked"), p_custom_mouse_index);
4344 }
4345}
4346
4347void Tree::item_changed(int p_column, TreeItem *p_item) {
4348 if (p_item != nullptr && p_column >= 0 && p_column < p_item->cells.size()) {
4349 p_item->cells.write[p_column].dirty = true;
4350 columns.write[p_column].cached_minimum_width_dirty = true;
4351 }
4352 queue_redraw();
4353}
4354
4355void Tree::item_selected(int p_column, TreeItem *p_item) {
4356 if (select_mode == SELECT_MULTI) {
4357 if (!p_item->cells[p_column].selectable) {
4358 return;
4359 }
4360
4361 p_item->cells.write[p_column].selected = true;
4362 //emit_signal(SNAME("multi_selected"),p_item,p_column,true); - NO this is for TreeItem::select
4363
4364 selected_col = p_column;
4365 selected_item = p_item;
4366 } else {
4367 select_single_item(p_item, root, p_column);
4368 }
4369 queue_redraw();
4370}
4371
4372void Tree::item_deselected(int p_column, TreeItem *p_item) {
4373 if (select_mode == SELECT_SINGLE && selected_item == p_item && selected_col == p_column) {
4374 selected_item = nullptr;
4375 selected_col = -1;
4376 } else {
4377 if (select_mode == SELECT_ROW && selected_item == p_item) {
4378 selected_item = nullptr;
4379 selected_col = -1;
4380 } else {
4381 if (select_mode == SELECT_MULTI) {
4382 selected_item = p_item;
4383 selected_col = p_column;
4384 }
4385 }
4386 }
4387
4388 if (select_mode == SELECT_MULTI || select_mode == SELECT_SINGLE) {
4389 p_item->cells.write[p_column].selected = false;
4390 } else if (select_mode == SELECT_ROW) {
4391 for (int i = 0; i < p_item->cells.size(); i++) {
4392 p_item->cells.write[i].selected = false;
4393 }
4394 }
4395 queue_redraw();
4396}
4397
4398void Tree::set_select_mode(SelectMode p_mode) {
4399 select_mode = p_mode;
4400}
4401
4402Tree::SelectMode Tree::get_select_mode() const {
4403 return select_mode;
4404}
4405
4406void Tree::deselect_all() {
4407 if (root) {
4408 TreeItem *item = root;
4409 while (item) {
4410 if (select_mode == SELECT_ROW) {
4411 item->deselect(0);
4412 } else {
4413 for (int i = 0; i < columns.size(); i++) {
4414 item->deselect(i);
4415 }
4416 }
4417 TreeItem *prev_item = item;
4418 item = get_next_selected(root);
4419 ERR_FAIL_COND(item == prev_item);
4420 }
4421 }
4422
4423 selected_item = nullptr;
4424 selected_col = -1;
4425
4426 queue_redraw();
4427}
4428
4429bool Tree::is_anything_selected() {
4430 return (selected_item != nullptr);
4431}
4432
4433void Tree::clear() {
4434 ERR_FAIL_COND(blocked > 0);
4435
4436 if (pressing_for_editor) {
4437 if (range_drag_enabled) {
4438 range_drag_enabled = false;
4439 Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
4440 warp_mouse(range_drag_capture_pos);
4441 }
4442 pressing_for_editor = false;
4443 }
4444
4445 if (root) {
4446 memdelete(root);
4447 root = nullptr;
4448 };
4449
4450 selected_item = nullptr;
4451 edited_item = nullptr;
4452 popup_edited_item = nullptr;
4453 popup_pressing_edited_item = nullptr;
4454
4455 queue_redraw();
4456};
4457
4458void Tree::set_hide_root(bool p_enabled) {
4459 if (hide_root == p_enabled) {
4460 return;
4461 }
4462
4463 hide_root = p_enabled;
4464 queue_redraw();
4465}
4466
4467bool Tree::is_root_hidden() const {
4468 return hide_root;
4469}
4470
4471void Tree::set_column_custom_minimum_width(int p_column, int p_min_width) {
4472 ERR_FAIL_INDEX(p_column, columns.size());
4473
4474 if (columns[p_column].custom_min_width == p_min_width) {
4475 return;
4476 }
4477
4478 if (p_min_width < 0) {
4479 return;
4480 }
4481 columns.write[p_column].custom_min_width = p_min_width;
4482 columns.write[p_column].cached_minimum_width_dirty = true;
4483 queue_redraw();
4484}
4485
4486void Tree::set_column_expand(int p_column, bool p_expand) {
4487 ERR_FAIL_INDEX(p_column, columns.size());
4488
4489 if (columns[p_column].expand == p_expand) {
4490 return;
4491 }
4492
4493 columns.write[p_column].expand = p_expand;
4494 columns.write[p_column].cached_minimum_width_dirty = true;
4495 queue_redraw();
4496}
4497
4498void Tree::set_column_expand_ratio(int p_column, int p_ratio) {
4499 ERR_FAIL_INDEX(p_column, columns.size());
4500
4501 if (columns[p_column].expand_ratio == p_ratio) {
4502 return;
4503 }
4504
4505 columns.write[p_column].expand_ratio = p_ratio;
4506 columns.write[p_column].cached_minimum_width_dirty = true;
4507 queue_redraw();
4508}
4509
4510void Tree::set_column_clip_content(int p_column, bool p_fit) {
4511 ERR_FAIL_INDEX(p_column, columns.size());
4512
4513 if (columns[p_column].clip_content == p_fit) {
4514 return;
4515 }
4516
4517 columns.write[p_column].clip_content = p_fit;
4518 columns.write[p_column].cached_minimum_width_dirty = true;
4519 queue_redraw();
4520}
4521
4522bool Tree::is_column_expanding(int p_column) const {
4523 ERR_FAIL_INDEX_V(p_column, columns.size(), false);
4524
4525 return columns[p_column].expand;
4526}
4527
4528int Tree::get_column_expand_ratio(int p_column) const {
4529 ERR_FAIL_INDEX_V(p_column, columns.size(), 1);
4530
4531 return columns[p_column].expand_ratio;
4532}
4533
4534bool Tree::is_column_clipping_content(int p_column) const {
4535 ERR_FAIL_INDEX_V(p_column, columns.size(), false);
4536
4537 return columns[p_column].clip_content;
4538}
4539
4540TreeItem *Tree::get_selected() const {
4541 return selected_item;
4542}
4543
4544void Tree::set_selected(TreeItem *p_item, int p_column) {
4545 ERR_FAIL_INDEX(p_column, columns.size());
4546 ERR_FAIL_NULL(p_item);
4547 select_single_item(p_item, get_root(), p_column);
4548}
4549
4550int Tree::get_selected_column() const {
4551 return selected_col;
4552}
4553
4554TreeItem *Tree::get_edited() const {
4555 return edited_item;
4556}
4557
4558int Tree::get_edited_column() const {
4559 return edited_col;
4560}
4561
4562TreeItem *Tree::get_next_selected(TreeItem *p_item) {
4563 if (!root) {
4564 return nullptr;
4565 }
4566
4567 while (true) {
4568 if (!p_item) {
4569 p_item = root;
4570 } else {
4571 if (p_item->first_child) {
4572 p_item = p_item->first_child;
4573
4574 } else if (p_item->next) {
4575 p_item = p_item->next;
4576 } else {
4577 while (!p_item->next) {
4578 p_item = p_item->parent;
4579 if (p_item == nullptr) {
4580 return nullptr;
4581 }
4582 }
4583
4584 p_item = p_item->next;
4585 }
4586 }
4587
4588 for (int i = 0; i < columns.size(); i++) {
4589 if (p_item->cells[i].selected) {
4590 return p_item;
4591 }
4592 }
4593 }
4594
4595 return nullptr;
4596}
4597
4598int Tree::get_column_minimum_width(int p_column) const {
4599 ERR_FAIL_INDEX_V(p_column, columns.size(), -1);
4600
4601 if (columns[p_column].cached_minimum_width_dirty) {
4602 // Use the custom minimum width.
4603 int min_width = columns[p_column].custom_min_width;
4604
4605 // Check if the visible title of the column is wider.
4606 if (show_column_titles) {
4607 min_width = MAX(theme_cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT), min_width);
4608 }
4609
4610 if (!columns[p_column].clip_content) {
4611 int depth = 0;
4612 TreeItem *next;
4613 for (TreeItem *item = get_root(); item; item = next) {
4614 next = item->get_next_visible();
4615 // Compute the depth in tree.
4616 if (next && p_column == 0) {
4617 if (next->get_parent() == item) {
4618 depth += 1;
4619 } else {
4620 TreeItem *common_parent = item->get_parent();
4621 while (common_parent != next->get_parent() && common_parent) {
4622 common_parent = common_parent->get_parent();
4623 depth -= 1;
4624 }
4625 }
4626 }
4627
4628 // Get the item minimum size.
4629 Size2 item_size = item->get_minimum_size(p_column);
4630 if (p_column == 0) {
4631 item_size.width += theme_cache.item_margin * depth;
4632 } else {
4633 item_size.width += theme_cache.h_separation;
4634 }
4635
4636 // Check if the item is wider.
4637 min_width = MAX(min_width, item_size.width);
4638 }
4639 }
4640
4641 columns.get(p_column).cached_minimum_width = min_width;
4642 columns.get(p_column).cached_minimum_width_dirty = false;
4643 }
4644
4645 return columns[p_column].cached_minimum_width;
4646}
4647
4648int Tree::get_column_width(int p_column) const {
4649 ERR_FAIL_INDEX_V(p_column, columns.size(), -1);
4650
4651 int column_width = get_column_minimum_width(p_column);
4652
4653 if (columns[p_column].expand) {
4654 int expand_area = _get_content_rect().size.width;
4655 int expanding_total = 0;
4656
4657 for (int i = 0; i < columns.size(); i++) {
4658 expand_area -= get_column_minimum_width(i);
4659 if (columns[i].expand) {
4660 expanding_total += columns[i].expand_ratio;
4661 }
4662 }
4663
4664 if (expand_area >= expanding_total && expanding_total > 0) {
4665 column_width += expand_area * columns[p_column].expand_ratio / expanding_total;
4666 }
4667 }
4668
4669 return column_width;
4670}
4671
4672void Tree::propagate_set_columns(TreeItem *p_item) {
4673 p_item->cells.resize(columns.size());
4674
4675 TreeItem *c = p_item->get_first_child();
4676 while (c) {
4677 propagate_set_columns(c);
4678 c = c->next;
4679 }
4680}
4681
4682void Tree::set_columns(int p_columns) {
4683 ERR_FAIL_COND(p_columns < 1);
4684 ERR_FAIL_COND(blocked > 0);
4685 columns.resize(p_columns);
4686
4687 if (root) {
4688 propagate_set_columns(root);
4689 }
4690 if (selected_col >= p_columns) {
4691 selected_col = p_columns - 1;
4692 }
4693 queue_redraw();
4694}
4695
4696int Tree::get_columns() const {
4697 return columns.size();
4698}
4699
4700void Tree::_scroll_moved(float) {
4701 queue_redraw();
4702}
4703
4704Rect2 Tree::get_custom_popup_rect() const {
4705 return custom_popup_rect;
4706}
4707
4708int Tree::get_item_offset(TreeItem *p_item) const {
4709 TreeItem *it = root;
4710 int ofs = _get_title_button_height();
4711 if (!it) {
4712 return 0;
4713 }
4714
4715 while (true) {
4716 if (it == p_item) {
4717 return ofs;
4718 }
4719
4720 if ((it != root || !hide_root) && it->is_visible()) {
4721 ofs += compute_item_height(it);
4722 ofs += theme_cache.v_separation;
4723 }
4724
4725 if (it->first_child && !it->collapsed) {
4726 it = it->first_child;
4727
4728 } else if (it->next) {
4729 it = it->next;
4730 } else {
4731 while (!it->next) {
4732 it = it->parent;
4733 if (it == nullptr) {
4734 return 0;
4735 }
4736 }
4737
4738 it = it->next;
4739 }
4740 }
4741
4742 return -1; //not found
4743}
4744
4745void Tree::ensure_cursor_is_visible() {
4746 if (!is_inside_tree()) {
4747 return;
4748 }
4749 if (!selected_item || (selected_col == -1)) {
4750 return; // Nothing under cursor.
4751 }
4752
4753 // Note: Code below similar to Tree::scroll_to_item(), in case of bug fix both.
4754 const Size2 area_size = _get_content_rect().size;
4755
4756 int y_offset = get_item_offset(selected_item);
4757 if (y_offset != -1) {
4758 const int tbh = _get_title_button_height();
4759 y_offset -= tbh;
4760
4761 const int cell_h = compute_item_height(selected_item) + theme_cache.v_separation;
4762 int screen_h = area_size.height - tbh;
4763
4764 if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet.
4765 v_scroll->set_value(y_offset);
4766 } else if (y_offset + cell_h > v_scroll->get_value() + screen_h) {
4767 v_scroll->call_deferred(SNAME("set_value"), y_offset - screen_h + cell_h);
4768 } else if (y_offset < v_scroll->get_value()) {
4769 v_scroll->set_value(y_offset);
4770 }
4771 }
4772
4773 if (select_mode != SELECT_ROW) { // Cursor always at col 0 in this mode.
4774 int x_offset = 0;
4775 for (int i = 0; i < selected_col; i++) {
4776 x_offset += get_column_width(i);
4777 }
4778
4779 const int cell_w = get_column_width(selected_col);
4780 const int screen_w = area_size.width;
4781
4782 if (cell_w > screen_w) {
4783 h_scroll->set_value(x_offset);
4784 } else if (x_offset + cell_w > h_scroll->get_value() + screen_w) {
4785 h_scroll->call_deferred(SNAME("set_value"), x_offset - screen_w + cell_w);
4786 } else if (x_offset < h_scroll->get_value()) {
4787 h_scroll->set_value(x_offset);
4788 }
4789 }
4790}
4791
4792int Tree::get_pressed_button() const {
4793 return pressed_button;
4794}
4795
4796Rect2 Tree::get_item_rect(TreeItem *p_item, int p_column, int p_button) const {
4797 ERR_FAIL_NULL_V(p_item, Rect2());
4798 ERR_FAIL_COND_V(p_item->tree != this, Rect2());
4799 if (p_column != -1) {
4800 ERR_FAIL_INDEX_V(p_column, columns.size(), Rect2());
4801 }
4802 if (p_button != -1) {
4803 ERR_FAIL_COND_V(p_column == -1, Rect2()); // pass a column if you want to pass a button
4804 ERR_FAIL_INDEX_V(p_button, p_item->cells[p_column].buttons.size(), Rect2());
4805 }
4806
4807 int ofs = get_item_offset(p_item);
4808 int height = compute_item_height(p_item);
4809 Rect2 r;
4810 r.position.y = ofs;
4811 r.size.height = height;
4812
4813 if (p_column == -1) {
4814 r.position.x = 0;
4815 r.size.x = get_size().width;
4816 } else {
4817 int accum = 0;
4818 for (int i = 0; i < p_column; i++) {
4819 accum += get_column_width(i);
4820 }
4821 r.position.x = accum;
4822 r.size.x = get_column_width(p_column);
4823 if (p_button != -1) {
4824 const TreeItem::Cell &c = p_item->cells[p_column];
4825 Vector2 ofst = Vector2(r.position.x + r.size.x, r.position.y);
4826 for (int j = c.buttons.size() - 1; j >= 0; j--) {
4827 Ref<Texture2D> b = c.buttons[j].texture;
4828 Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size();
4829 ofst.x -= size.x;
4830
4831 if (j == p_button) {
4832 return Rect2(ofst, size);
4833 }
4834 }
4835 }
4836 }
4837
4838 return r;
4839}
4840
4841void Tree::set_column_titles_visible(bool p_show) {
4842 if (show_column_titles == p_show) {
4843 return;
4844 }
4845
4846 show_column_titles = p_show;
4847 queue_redraw();
4848}
4849
4850bool Tree::are_column_titles_visible() const {
4851 return show_column_titles;
4852}
4853
4854void Tree::set_column_title(int p_column, const String &p_title) {
4855 ERR_FAIL_INDEX(p_column, columns.size());
4856
4857 if (columns[p_column].title == p_title) {
4858 return;
4859 }
4860
4861 columns.write[p_column].title = p_title;
4862 update_column(p_column);
4863 queue_redraw();
4864}
4865
4866String Tree::get_column_title(int p_column) const {
4867 ERR_FAIL_INDEX_V(p_column, columns.size(), "");
4868 return columns[p_column].title;
4869}
4870
4871void Tree::set_column_title_alignment(int p_column, HorizontalAlignment p_alignment) {
4872 ERR_FAIL_INDEX(p_column, columns.size());
4873
4874 if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
4875 WARN_PRINT("HORIZONTAL_ALIGNMENT_FILL is not supported for column titles.");
4876 }
4877
4878 if (columns[p_column].title_alignment == p_alignment) {
4879 return;
4880 }
4881
4882 columns.write[p_column].title_alignment = p_alignment;
4883 update_column(p_column);
4884 queue_redraw();
4885}
4886
4887HorizontalAlignment Tree::get_column_title_alignment(int p_column) const {
4888 ERR_FAIL_INDEX_V(p_column, columns.size(), HorizontalAlignment::HORIZONTAL_ALIGNMENT_CENTER);
4889 return columns[p_column].title_alignment;
4890}
4891
4892void Tree::set_column_title_direction(int p_column, Control::TextDirection p_text_direction) {
4893 ERR_FAIL_INDEX(p_column, columns.size());
4894 ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
4895 if (columns[p_column].text_direction != p_text_direction) {
4896 columns.write[p_column].text_direction = p_text_direction;
4897 update_column(p_column);
4898 queue_redraw();
4899 }
4900}
4901
4902Control::TextDirection Tree::get_column_title_direction(int p_column) const {
4903 ERR_FAIL_INDEX_V(p_column, columns.size(), TEXT_DIRECTION_INHERITED);
4904 return columns[p_column].text_direction;
4905}
4906
4907void Tree::set_column_title_language(int p_column, const String &p_language) {
4908 ERR_FAIL_INDEX(p_column, columns.size());
4909 if (columns[p_column].language != p_language) {
4910 columns.write[p_column].language = p_language;
4911 update_column(p_column);
4912 queue_redraw();
4913 }
4914}
4915
4916String Tree::get_column_title_language(int p_column) const {
4917 ERR_FAIL_INDEX_V(p_column, columns.size(), "");
4918 return columns[p_column].language;
4919}
4920
4921Point2 Tree::get_scroll() const {
4922 Point2 ofs;
4923 if (h_scroll->is_visible_in_tree()) {
4924 ofs.x = h_scroll->get_value();
4925 }
4926 if (v_scroll->is_visible_in_tree()) {
4927 ofs.y = v_scroll->get_value();
4928 }
4929 return ofs;
4930}
4931
4932void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) {
4933 ERR_FAIL_NULL(p_item);
4934
4935 update_scrollbars();
4936
4937 // Note: Code below similar to Tree::ensure_cursor_is_visible(), in case of bug fix both.
4938 const Size2 area_size = _get_content_rect().size;
4939
4940 int y_offset = get_item_offset(p_item);
4941 if (y_offset != -1) {
4942 const int tbh = _get_title_button_height();
4943 y_offset -= tbh;
4944
4945 const int cell_h = compute_item_height(p_item) + theme_cache.v_separation;
4946 int screen_h = area_size.height - tbh;
4947
4948 if (p_center_on_item) {
4949 v_scroll->set_value(y_offset - (screen_h - cell_h) / 2.0f);
4950 } else {
4951 if (cell_h > screen_h) { // Screen size is too small, maybe it was not resized yet.
4952 v_scroll->set_value(y_offset);
4953 } else if (y_offset + cell_h > v_scroll->get_value() + screen_h) {
4954 v_scroll->set_value(y_offset - screen_h + cell_h);
4955 } else if (y_offset < v_scroll->get_value()) {
4956 v_scroll->set_value(y_offset);
4957 }
4958 }
4959 }
4960}
4961
4962void Tree::set_h_scroll_enabled(bool p_enable) {
4963 if (h_scroll_enabled == p_enable) {
4964 return;
4965 }
4966
4967 h_scroll_enabled = p_enable;
4968 update_minimum_size();
4969}
4970
4971bool Tree::is_h_scroll_enabled() const {
4972 return h_scroll_enabled;
4973}
4974
4975void Tree::set_v_scroll_enabled(bool p_enable) {
4976 if (v_scroll_enabled == p_enable) {
4977 return;
4978 }
4979
4980 v_scroll_enabled = p_enable;
4981 update_minimum_size();
4982}
4983
4984bool Tree::is_v_scroll_enabled() const {
4985 return v_scroll_enabled;
4986}
4987
4988TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) {
4989 TreeItem *from = p_at;
4990 TreeItem *loop = nullptr; // Safe-guard against infinite loop.
4991
4992 while (p_at) {
4993 for (int i = 0; i < columns.size(); i++) {
4994 if (p_at->get_text(i).findn(p_find) == 0 && (!p_selectable || p_at->is_selectable(i))) {
4995 if (r_col) {
4996 *r_col = i;
4997 }
4998 return p_at;
4999 }
5000 }
5001
5002 if (p_backwards) {
5003 p_at = p_at->get_prev_visible(true);
5004 } else {
5005 p_at = p_at->get_next_visible(true);
5006 }
5007
5008 if ((p_at) == from) {
5009 break;
5010 }
5011
5012 if (!loop) {
5013 loop = p_at;
5014 } else if (loop == p_at) {
5015 break;
5016 }
5017 }
5018
5019 return nullptr;
5020}
5021
5022TreeItem *Tree::search_item_text(const String &p_find, int *r_col, bool p_selectable) {
5023 TreeItem *from = get_selected();
5024
5025 if (!from) {
5026 from = root;
5027 }
5028 if (!from) {
5029 return nullptr;
5030 }
5031
5032 return _search_item_text(from->get_next_visible(true), p_find, r_col, p_selectable);
5033}
5034
5035TreeItem *Tree::get_item_with_text(const String &p_find) const {
5036 for (TreeItem *current = root; current; current = current->get_next_visible()) {
5037 for (int i = 0; i < columns.size(); i++) {
5038 if (current->get_text(i) == p_find) {
5039 return current;
5040 }
5041 }
5042 }
5043 return nullptr;
5044}
5045
5046TreeItem *Tree::get_item_with_metadata(const Variant &p_find, int p_column) const {
5047 if (p_column < 0) {
5048 for (TreeItem *current = root; current; current = current->get_next_in_tree()) {
5049 for (int i = 0; i < columns.size(); i++) {
5050 if (current->get_metadata(i) == p_find) {
5051 return current;
5052 }
5053 }
5054 }
5055 return nullptr;
5056 }
5057
5058 for (TreeItem *current = root; current; current = current->get_next_in_tree()) {
5059 if (current->get_metadata(p_column) == p_find) {
5060 return current;
5061 }
5062 }
5063 return nullptr;
5064}
5065
5066void Tree::_do_incr_search(const String &p_add) {
5067 uint64_t time = OS::get_singleton()->get_ticks_usec() / 1000; // convert to msec
5068 uint64_t diff = time - last_keypress;
5069 if (diff > uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec"))) {
5070 incr_search = p_add;
5071 } else if (incr_search != p_add) {
5072 incr_search += p_add;
5073 }
5074
5075 last_keypress = time;
5076 int col;
5077 TreeItem *item = search_item_text(incr_search, &col, true);
5078 if (!item) {
5079 return;
5080 }
5081
5082 if (select_mode == SELECT_MULTI) {
5083 item->set_as_cursor(col);
5084 } else {
5085 item->select(col);
5086 }
5087 ensure_cursor_is_visible();
5088}
5089
5090TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_column, int &h, int &section) const {
5091 Point2 pos = p_pos;
5092
5093 if ((root != p_item || !hide_root) && p_item->is_visible()) {
5094 h = compute_item_height(p_item) + theme_cache.v_separation;
5095 if (pos.y < h) {
5096 if (drop_mode_flags == DROP_MODE_ON_ITEM) {
5097 section = 0;
5098 } else if (drop_mode_flags == DROP_MODE_INBETWEEN) {
5099 section = pos.y < h / 2 ? -1 : 1;
5100 } else if (pos.y < h / 4) {
5101 section = -1;
5102 } else if (pos.y >= (h * 3 / 4)) {
5103 section = 1;
5104 } else {
5105 section = 0;
5106 }
5107
5108 for (int i = 0; i < columns.size(); i++) {
5109 int w = get_column_width(i);
5110 if (pos.x < w) {
5111 r_column = i;
5112
5113 return p_item;
5114 }
5115 pos.x -= w;
5116 }
5117
5118 return nullptr;
5119 } else {
5120 pos.y -= h;
5121 }
5122 } else {
5123 h = 0;
5124 }
5125
5126 if (p_item->is_collapsed() || !p_item->is_visible()) {
5127 return nullptr; // do not try children, it's collapsed
5128 }
5129
5130 TreeItem *n = p_item->get_first_child();
5131 while (n) {
5132 int ch;
5133 TreeItem *r = _find_item_at_pos(n, pos, r_column, ch, section);
5134 pos.y -= ch;
5135 h += ch;
5136 if (r) {
5137 return r;
5138 }
5139 n = n->get_next();
5140 }
5141
5142 return nullptr;
5143}
5144
5145int Tree::get_column_at_position(const Point2 &p_pos) const {
5146 if (root) {
5147 Point2 pos = p_pos;
5148 if (is_layout_rtl()) {
5149 pos.x = get_size().width - pos.x;
5150 }
5151 pos -= theme_cache.panel_style->get_offset();
5152 pos.y -= _get_title_button_height();
5153 if (pos.y < 0) {
5154 return -1;
5155 }
5156
5157 if (h_scroll->is_visible_in_tree()) {
5158 pos.x += h_scroll->get_value();
5159 }
5160 if (v_scroll->is_visible_in_tree()) {
5161 pos.y += v_scroll->get_value();
5162 }
5163
5164 int col, h, section;
5165 TreeItem *it = _find_item_at_pos(root, pos, col, h, section);
5166
5167 if (it) {
5168 return col;
5169 }
5170 }
5171
5172 return -1;
5173}
5174
5175int Tree::get_drop_section_at_position(const Point2 &p_pos) const {
5176 if (root) {
5177 Point2 pos = p_pos;
5178 if (is_layout_rtl()) {
5179 pos.x = get_size().width - pos.x;
5180 }
5181 pos -= theme_cache.panel_style->get_offset();
5182 pos.y -= _get_title_button_height();
5183 if (pos.y < 0) {
5184 return -100;
5185 }
5186
5187 if (h_scroll->is_visible_in_tree()) {
5188 pos.x += h_scroll->get_value();
5189 }
5190 if (v_scroll->is_visible_in_tree()) {
5191 pos.y += v_scroll->get_value();
5192 }
5193
5194 int col, h, section;
5195 TreeItem *it = _find_item_at_pos(root, pos, col, h, section);
5196
5197 if (it) {
5198 return section;
5199 }
5200 }
5201
5202 return -100;
5203}
5204
5205TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const {
5206 if (root) {
5207 Point2 pos = p_pos;
5208 if (is_layout_rtl()) {
5209 pos.x = get_size().width - pos.x;
5210 }
5211 pos -= theme_cache.panel_style->get_offset();
5212 pos.y -= _get_title_button_height();
5213 if (pos.y < 0) {
5214 return nullptr;
5215 }
5216
5217 if (h_scroll->is_visible_in_tree()) {
5218 pos.x += h_scroll->get_value();
5219 }
5220 if (v_scroll->is_visible_in_tree()) {
5221 pos.y += v_scroll->get_value();
5222 }
5223
5224 int col, h, section;
5225 TreeItem *it = _find_item_at_pos(root, pos, col, h, section);
5226
5227 if (it) {
5228 return it;
5229 }
5230 }
5231
5232 return nullptr;
5233}
5234
5235int Tree::get_button_id_at_position(const Point2 &p_pos) const {
5236 if (root) {
5237 Point2 pos = p_pos;
5238 pos -= theme_cache.panel_style->get_offset();
5239 pos.y -= _get_title_button_height();
5240 if (pos.y < 0) {
5241 return -1;
5242 }
5243
5244 if (h_scroll->is_visible_in_tree()) {
5245 pos.x += h_scroll->get_value();
5246 }
5247 if (v_scroll->is_visible_in_tree()) {
5248 pos.y += v_scroll->get_value();
5249 }
5250
5251 int col, h, section;
5252 TreeItem *it = _find_item_at_pos(root, pos, col, h, section);
5253
5254 if (it) {
5255 const TreeItem::Cell &c = it->cells[col];
5256 int col_width = get_column_width(col);
5257
5258 for (int i = 0; i < col; i++) {
5259 pos.x -= get_column_width(i);
5260 }
5261
5262 for (int j = c.buttons.size() - 1; j >= 0; j--) {
5263 Ref<Texture2D> b = c.buttons[j].texture;
5264 Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size();
5265 if (pos.x > col_width - size.width) {
5266 return c.buttons[j].id;
5267 }
5268 col_width -= size.width;
5269 }
5270 }
5271 }
5272
5273 return -1;
5274}
5275
5276String Tree::get_tooltip(const Point2 &p_pos) const {
5277 if (root) {
5278 Point2 pos = p_pos;
5279 pos -= theme_cache.panel_style->get_offset();
5280 pos.y -= _get_title_button_height();
5281 if (pos.y < 0) {
5282 return Control::get_tooltip(p_pos);
5283 }
5284
5285 Point2 button_pos = pos;
5286 if (h_scroll->is_visible_in_tree()) {
5287 pos.x += h_scroll->get_value();
5288 }
5289 if (v_scroll->is_visible_in_tree()) {
5290 pos.y += v_scroll->get_value();
5291 }
5292
5293 int col, h, section;
5294 TreeItem *it = _find_item_at_pos(root, pos, col, h, section);
5295
5296 if (it) {
5297 const TreeItem::Cell &c = it->cells[col];
5298 for (int j = c.buttons.size() - 1; j >= 0; j--) {
5299 if (c.buttons[j].rect.has_point(button_pos)) {
5300 String tooltip = c.buttons[j].tooltip;
5301 if (!tooltip.is_empty()) {
5302 return tooltip;
5303 }
5304 }
5305 }
5306 String ret;
5307 if (it->get_tooltip_text(col) == "") {
5308 ret = it->get_text(col);
5309 } else {
5310 ret = it->get_tooltip_text(col);
5311 }
5312 return ret;
5313 }
5314 }
5315
5316 return Control::get_tooltip(p_pos);
5317}
5318
5319void Tree::set_cursor_can_exit_tree(bool p_enable) {
5320 cursor_can_exit_tree = p_enable;
5321}
5322
5323void Tree::set_hide_folding(bool p_hide) {
5324 if (hide_folding == p_hide) {
5325 return;
5326 }
5327
5328 hide_folding = p_hide;
5329 queue_redraw();
5330}
5331
5332bool Tree::is_folding_hidden() const {
5333 return hide_folding;
5334}
5335
5336void Tree::set_enable_recursive_folding(bool p_enable) {
5337 enable_recursive_folding = p_enable;
5338}
5339
5340bool Tree::is_recursive_folding_enabled() const {
5341 return enable_recursive_folding;
5342}
5343
5344void Tree::set_drop_mode_flags(int p_flags) {
5345 if (drop_mode_flags == p_flags) {
5346 return;
5347 }
5348 drop_mode_flags = p_flags;
5349 if (drop_mode_flags == 0) {
5350 drop_mode_over = nullptr;
5351 }
5352
5353 queue_redraw();
5354}
5355
5356int Tree::get_drop_mode_flags() const {
5357 return drop_mode_flags;
5358}
5359
5360void Tree::set_edit_checkbox_cell_only_when_checkbox_is_pressed(bool p_enable) {
5361 force_edit_checkbox_only_on_checkbox = p_enable;
5362}
5363
5364bool Tree::get_edit_checkbox_cell_only_when_checkbox_is_pressed() const {
5365 return force_edit_checkbox_only_on_checkbox;
5366}
5367
5368void Tree::set_allow_rmb_select(bool p_allow) {
5369 allow_rmb_select = p_allow;
5370}
5371
5372bool Tree::get_allow_rmb_select() const {
5373 return allow_rmb_select;
5374}
5375
5376void Tree::set_allow_reselect(bool p_allow) {
5377 allow_reselect = p_allow;
5378}
5379
5380bool Tree::get_allow_reselect() const {
5381 return allow_reselect;
5382}
5383
5384void Tree::set_allow_search(bool p_allow) {
5385 allow_search = p_allow;
5386}
5387
5388bool Tree::get_allow_search() const {
5389 return allow_search;
5390}
5391
5392void Tree::_bind_methods() {
5393 ClassDB::bind_method(D_METHOD("clear"), &Tree::clear);
5394 ClassDB::bind_method(D_METHOD("create_item", "parent", "index"), &Tree::create_item, DEFVAL(Variant()), DEFVAL(-1));
5395
5396 ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root);
5397 ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width);
5398 ClassDB::bind_method(D_METHOD("set_column_expand", "column", "expand"), &Tree::set_column_expand);
5399 ClassDB::bind_method(D_METHOD("set_column_expand_ratio", "column", "ratio"), &Tree::set_column_expand_ratio);
5400 ClassDB::bind_method(D_METHOD("set_column_clip_content", "column", "enable"), &Tree::set_column_clip_content);
5401 ClassDB::bind_method(D_METHOD("is_column_expanding", "column"), &Tree::is_column_expanding);
5402 ClassDB::bind_method(D_METHOD("is_column_clipping_content", "column"), &Tree::is_column_clipping_content);
5403 ClassDB::bind_method(D_METHOD("get_column_expand_ratio", "column"), &Tree::get_column_expand_ratio);
5404
5405 ClassDB::bind_method(D_METHOD("get_column_width", "column"), &Tree::get_column_width);
5406
5407 ClassDB::bind_method(D_METHOD("set_hide_root", "enable"), &Tree::set_hide_root);
5408 ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden);
5409 ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::get_next_selected);
5410 ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected);
5411 ClassDB::bind_method(D_METHOD("set_selected", "item", "column"), &Tree::set_selected);
5412 ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column);
5413 ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button);
5414 ClassDB::bind_method(D_METHOD("set_select_mode", "mode"), &Tree::set_select_mode);
5415 ClassDB::bind_method(D_METHOD("get_select_mode"), &Tree::get_select_mode);
5416 ClassDB::bind_method(D_METHOD("deselect_all"), &Tree::deselect_all);
5417
5418 ClassDB::bind_method(D_METHOD("set_columns", "amount"), &Tree::set_columns);
5419 ClassDB::bind_method(D_METHOD("get_columns"), &Tree::get_columns);
5420
5421 ClassDB::bind_method(D_METHOD("get_edited"), &Tree::get_edited);
5422 ClassDB::bind_method(D_METHOD("get_edited_column"), &Tree::get_edited_column);
5423 ClassDB::bind_method(D_METHOD("edit_selected", "force_edit"), &Tree::edit_selected, DEFVAL(false));
5424 ClassDB::bind_method(D_METHOD("get_custom_popup_rect"), &Tree::get_custom_popup_rect);
5425 ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column", "button_index"), &Tree::get_item_rect, DEFVAL(-1), DEFVAL(-1));
5426 ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position);
5427 ClassDB::bind_method(D_METHOD("get_column_at_position", "position"), &Tree::get_column_at_position);
5428 ClassDB::bind_method(D_METHOD("get_drop_section_at_position", "position"), &Tree::get_drop_section_at_position);
5429 ClassDB::bind_method(D_METHOD("get_button_id_at_position", "position"), &Tree::get_button_id_at_position);
5430
5431 ClassDB::bind_method(D_METHOD("ensure_cursor_is_visible"), &Tree::ensure_cursor_is_visible);
5432
5433 ClassDB::bind_method(D_METHOD("set_column_titles_visible", "visible"), &Tree::set_column_titles_visible);
5434 ClassDB::bind_method(D_METHOD("are_column_titles_visible"), &Tree::are_column_titles_visible);
5435
5436 ClassDB::bind_method(D_METHOD("set_column_title", "column", "title"), &Tree::set_column_title);
5437 ClassDB::bind_method(D_METHOD("get_column_title", "column"), &Tree::get_column_title);
5438
5439 ClassDB::bind_method(D_METHOD("set_column_title_alignment", "column", "title_alignment"), &Tree::set_column_title_alignment);
5440 ClassDB::bind_method(D_METHOD("get_column_title_alignment", "column"), &Tree::get_column_title_alignment);
5441
5442 ClassDB::bind_method(D_METHOD("set_column_title_direction", "column", "direction"), &Tree::set_column_title_direction);
5443 ClassDB::bind_method(D_METHOD("get_column_title_direction", "column"), &Tree::get_column_title_direction);
5444
5445 ClassDB::bind_method(D_METHOD("set_column_title_language", "column", "language"), &Tree::set_column_title_language);
5446 ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language);
5447
5448 ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll);
5449 ClassDB::bind_method(D_METHOD("scroll_to_item", "item", "center_on_item"), &Tree::scroll_to_item, DEFVAL(false));
5450
5451 ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled);
5452 ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled);
5453
5454 ClassDB::bind_method(D_METHOD("set_v_scroll_enabled", "h_scroll"), &Tree::set_v_scroll_enabled);
5455 ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &Tree::is_v_scroll_enabled);
5456
5457 ClassDB::bind_method(D_METHOD("set_hide_folding", "hide"), &Tree::set_hide_folding);
5458 ClassDB::bind_method(D_METHOD("is_folding_hidden"), &Tree::is_folding_hidden);
5459
5460 ClassDB::bind_method(D_METHOD("set_enable_recursive_folding", "enable"), &Tree::set_enable_recursive_folding);
5461 ClassDB::bind_method(D_METHOD("is_recursive_folding_enabled"), &Tree::is_recursive_folding_enabled);
5462
5463 ClassDB::bind_method(D_METHOD("set_drop_mode_flags", "flags"), &Tree::set_drop_mode_flags);
5464 ClassDB::bind_method(D_METHOD("get_drop_mode_flags"), &Tree::get_drop_mode_flags);
5465
5466 ClassDB::bind_method(D_METHOD("set_allow_rmb_select", "allow"), &Tree::set_allow_rmb_select);
5467 ClassDB::bind_method(D_METHOD("get_allow_rmb_select"), &Tree::get_allow_rmb_select);
5468
5469 ClassDB::bind_method(D_METHOD("set_allow_reselect", "allow"), &Tree::set_allow_reselect);
5470 ClassDB::bind_method(D_METHOD("get_allow_reselect"), &Tree::get_allow_reselect);
5471
5472 ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &Tree::set_allow_search);
5473 ClassDB::bind_method(D_METHOD("get_allow_search"), &Tree::get_allow_search);
5474
5475 ADD_PROPERTY(PropertyInfo(Variant::INT, "columns"), "set_columns", "get_columns");
5476 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "column_titles_visible"), "set_column_titles_visible", "are_column_titles_visible");
5477 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
5478 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
5479 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
5480 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_folding"), "set_hide_folding", "is_folding_hidden");
5481 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_recursive_folding"), "set_enable_recursive_folding", "is_recursive_folding_enabled");
5482 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_root"), "set_hide_root", "is_root_hidden");
5483 ADD_PROPERTY(PropertyInfo(Variant::INT, "drop_mode_flags", PROPERTY_HINT_FLAGS, "On Item,In Between"), "set_drop_mode_flags", "get_drop_mode_flags");
5484 ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Row,Multi"), "set_select_mode", "get_select_mode");
5485 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_h_scroll_enabled", "is_h_scroll_enabled");
5486 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_v_scroll_enabled", "is_v_scroll_enabled");
5487
5488 ADD_SIGNAL(MethodInfo("item_selected"));
5489 ADD_SIGNAL(MethodInfo("cell_selected"));
5490 ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected")));
5491 ADD_SIGNAL(MethodInfo("item_mouse_selected", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::INT, "mouse_button_index")));
5492 ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::INT, "mouse_button_index")));
5493 ADD_SIGNAL(MethodInfo("item_edited"));
5494 ADD_SIGNAL(MethodInfo("custom_item_clicked", PropertyInfo(Variant::INT, "mouse_button_index")));
5495 ADD_SIGNAL(MethodInfo("item_icon_double_clicked"));
5496 ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem")));
5497 ADD_SIGNAL(MethodInfo("check_propagated_to_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column")));
5498 ADD_SIGNAL(MethodInfo("button_clicked", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "mouse_button_index")));
5499 ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked")));
5500 ADD_SIGNAL(MethodInfo("item_activated"));
5501 ADD_SIGNAL(MethodInfo("column_title_clicked", PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "mouse_button_index")));
5502 ADD_SIGNAL(MethodInfo("nothing_selected"));
5503
5504 BIND_ENUM_CONSTANT(SELECT_SINGLE);
5505 BIND_ENUM_CONSTANT(SELECT_ROW);
5506 BIND_ENUM_CONSTANT(SELECT_MULTI);
5507
5508 BIND_ENUM_CONSTANT(DROP_MODE_DISABLED);
5509 BIND_ENUM_CONSTANT(DROP_MODE_ON_ITEM);
5510 BIND_ENUM_CONSTANT(DROP_MODE_INBETWEEN);
5511
5512 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Tree, panel_style, "panel");
5513 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Tree, focus_style, "focus");
5514
5515 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, Tree, font);
5516 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, Tree, font_size);
5517 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT, Tree, tb_font, "title_button_font");
5518 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT_SIZE, Tree, tb_font_size, "title_button_font_size");
5519
5520 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, selected);
5521 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, selected_focus);
5522 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, cursor);
5523 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Tree, cursor_unfocus, "cursor_unfocused");
5524 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, button_pressed);
5525
5526 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, checked);
5527 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, unchecked);
5528 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, indeterminate);
5529 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow);
5530 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow_collapsed);
5531 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, arrow_collapsed_mirrored);
5532 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, select_arrow);
5533 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Tree, updown);
5534
5535 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, custom_button);
5536 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, custom_button_hover);
5537 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, custom_button_pressed);
5538 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, custom_button_font_highlight);
5539
5540 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_color);
5541 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_selected_color);
5542 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, drop_position_color);
5543 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, h_separation);
5544 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, v_separation);
5545 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, inner_item_margin_bottom);
5546 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, inner_item_margin_left);
5547 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, inner_item_margin_right);
5548 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, inner_item_margin_top);
5549 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, item_margin);
5550 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, button_margin);
5551 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, icon_max_width);
5552
5553 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, font_outline_color);
5554 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, Tree, font_outline_size, "outline_size");
5555
5556 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, draw_guides);
5557 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, guide_color);
5558 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, draw_relationship_lines);
5559 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, relationship_line_width);
5560 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, parent_hl_line_width);
5561 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, children_hl_line_width);
5562 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, parent_hl_line_margin);
5563 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, relationship_line_color);
5564 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, parent_hl_line_color);
5565 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, children_hl_line_color);
5566
5567 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, scroll_border);
5568 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, scroll_speed);
5569
5570 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, scrollbar_margin_top);
5571 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, scrollbar_margin_right);
5572 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, scrollbar_margin_bottom);
5573 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, scrollbar_margin_left);
5574 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, scrollbar_h_separation);
5575 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Tree, scrollbar_v_separation);
5576
5577 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Tree, title_button, "title_button_normal");
5578 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, title_button_pressed);
5579 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Tree, title_button_hover);
5580 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Tree, title_button_color);
5581}
5582
5583Tree::Tree() {
5584 columns.resize(1);
5585
5586 set_focus_mode(FOCUS_ALL);
5587
5588 popup_menu = memnew(PopupMenu);
5589 popup_menu->hide();
5590 add_child(popup_menu, false, INTERNAL_MODE_FRONT);
5591
5592 popup_editor = memnew(Popup);
5593 add_child(popup_editor, false, INTERNAL_MODE_FRONT);
5594
5595 popup_editor_vb = memnew(VBoxContainer);
5596 popup_editor_vb->add_theme_constant_override("separation", 0);
5597 popup_editor_vb->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
5598 popup_editor->add_child(popup_editor_vb);
5599
5600 line_editor = memnew(LineEdit);
5601 line_editor->set_v_size_flags(SIZE_EXPAND_FILL);
5602 line_editor->hide();
5603 popup_editor_vb->add_child(line_editor);
5604
5605 text_editor = memnew(TextEdit);
5606 text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
5607 text_editor->hide();
5608 popup_editor_vb->add_child(text_editor);
5609
5610 value_editor = memnew(HSlider);
5611 value_editor->set_v_size_flags(SIZE_EXPAND_FILL);
5612 value_editor->hide();
5613 popup_editor_vb->add_child(value_editor);
5614
5615 h_scroll = memnew(HScrollBar);
5616 v_scroll = memnew(VScrollBar);
5617
5618 add_child(h_scroll, false, INTERNAL_MODE_FRONT);
5619 add_child(v_scroll, false, INTERNAL_MODE_FRONT);
5620
5621 range_click_timer = memnew(Timer);
5622 range_click_timer->connect("timeout", callable_mp(this, &Tree::_range_click_timeout));
5623 add_child(range_click_timer, false, INTERNAL_MODE_FRONT);
5624
5625 h_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
5626 v_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
5627 line_editor->connect("text_submitted", callable_mp(this, &Tree::_line_editor_submit));
5628 text_editor->connect("gui_input", callable_mp(this, &Tree::_text_editor_gui_input));
5629 popup_editor->connect("popup_hide", callable_mp(this, &Tree::_text_editor_popup_modal_close));
5630 popup_menu->connect("id_pressed", callable_mp(this, &Tree::popup_select));
5631 value_editor->connect("value_changed", callable_mp(this, &Tree::value_editor_changed));
5632
5633 set_notify_transform(true);
5634
5635 set_mouse_filter(MOUSE_FILTER_STOP);
5636
5637 set_clip_contents(true);
5638}
5639
5640Tree::~Tree() {
5641 if (root) {
5642 memdelete(root);
5643 }
5644}
5645