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 | |
47 | Size2 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 | |
58 | void 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 | |
72 | void TreeItem::_changed_notify(int p_cell) { |
73 | tree->item_changed(p_cell, this); |
74 | } |
75 | |
76 | void TreeItem::_changed_notify() { |
77 | tree->item_changed(-1, this); |
78 | } |
79 | |
80 | void TreeItem::_cell_selected(int p_cell) { |
81 | tree->item_selected(p_cell, this); |
82 | } |
83 | |
84 | void TreeItem::_cell_deselected(int p_cell) { |
85 | tree->item_deselected(p_cell, this); |
86 | } |
87 | |
88 | void 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 */ |
143 | void 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 | |
166 | TreeItem::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 */ |
172 | void 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 | |
178 | bool 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 */ |
184 | void 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 | |
198 | void 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 | |
213 | bool 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 | |
218 | bool 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 | |
223 | void 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 | |
233 | void 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 | |
245 | void 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 | |
283 | void 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 | |
313 | String TreeItem::get_text(int p_column) const { |
314 | ERR_FAIL_INDEX_V(p_column, cells.size(), "" ); |
315 | return cells[p_column].text; |
316 | } |
317 | |
318 | void 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 | |
332 | Control::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 | |
337 | void 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 | |
351 | TextServer::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 | |
356 | void 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 | |
369 | TextServer::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 | |
374 | void 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 | |
386 | TextServer::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 | |
391 | void 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 | |
405 | Array 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 | |
410 | void 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 | |
422 | String TreeItem::get_language(int p_column) const { |
423 | ERR_FAIL_INDEX_V(p_column, cells.size(), "" ); |
424 | return cells[p_column].language; |
425 | } |
426 | |
427 | void 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 | |
440 | String TreeItem::get_suffix(int p_column) const { |
441 | ERR_FAIL_INDEX_V(p_column, cells.size(), "" ); |
442 | return cells[p_column].suffix; |
443 | } |
444 | |
445 | void 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 | |
458 | Ref<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 | |
463 | void 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 | |
476 | Rect2 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 | |
481 | void 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 | |
492 | Color 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 | |
497 | void 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 | |
510 | int 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 */ |
516 | void 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 | |
537 | double 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 | |
542 | bool 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 | |
547 | void 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 | |
561 | void 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 | |
568 | void 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 | |
573 | Variant 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 | |
579 | void 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 | |
587 | void 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 | |
614 | bool TreeItem::is_collapsed() { |
615 | return collapsed; |
616 | } |
617 | |
618 | void 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 | |
632 | bool 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 | |
656 | bool 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 | |
669 | void 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 | |
680 | bool TreeItem::is_visible() { |
681 | return visible; |
682 | } |
683 | |
684 | void TreeItem::uncollapse_tree() { |
685 | TreeItem *t = this; |
686 | while (t) { |
687 | t->set_collapsed(false); |
688 | t = t->parent; |
689 | } |
690 | } |
691 | |
692 | void 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 | |
706 | int TreeItem::get_custom_minimum_height() const { |
707 | return custom_min_height; |
708 | } |
709 | |
710 | /* Item manipulation */ |
711 | |
712 | TreeItem *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 | |
758 | void 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 | |
785 | void 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 | |
798 | Tree *TreeItem::get_tree() const { |
799 | return tree; |
800 | } |
801 | |
802 | TreeItem *TreeItem::get_next() const { |
803 | return next; |
804 | } |
805 | |
806 | TreeItem *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 | |
825 | TreeItem *TreeItem::get_parent() const { |
826 | return parent; |
827 | } |
828 | |
829 | TreeItem *TreeItem::get_first_child() const { |
830 | return first_child; |
831 | } |
832 | |
833 | TreeItem *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 | |
869 | TreeItem *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 | |
883 | TreeItem *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 | |
910 | TreeItem *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 | |
924 | TreeItem *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 | |
929 | TreeItem *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 | |
934 | TreeItem *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 | |
945 | int 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 | |
956 | int TreeItem::get_child_count() { |
957 | _create_children_cache(); |
958 | return children_cache.size(); |
959 | } |
960 | |
961 | TypedArray<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 | |
973 | void 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 | |
985 | int 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 |
997 | void 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 | |
1012 | void 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 | |
1057 | void 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 | |
1100 | void 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 | |
1105 | bool 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 | |
1110 | bool 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 | |
1115 | void 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 | |
1131 | void TreeItem::select(int p_column) { |
1132 | ERR_FAIL_INDEX(p_column, cells.size()); |
1133 | _cell_selected(p_column); |
1134 | } |
1135 | |
1136 | void TreeItem::deselect(int p_column) { |
1137 | ERR_FAIL_INDEX(p_column, cells.size()); |
1138 | _cell_deselected(p_column); |
1139 | } |
1140 | |
1141 | void 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 | |
1158 | int 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 | |
1163 | Ref<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 | |
1169 | String 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 | |
1175 | int 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 | |
1181 | void 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 | |
1188 | int 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 | |
1199 | void 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 | |
1205 | void 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 | |
1220 | void 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 | |
1232 | void 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 | |
1246 | bool 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 | |
1253 | void 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 | |
1266 | bool TreeItem::is_editable(int p_column) { |
1267 | ERR_FAIL_INDEX_V(p_column, cells.size(), false); |
1268 | return cells[p_column].editable; |
1269 | } |
1270 | |
1271 | void 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 | |
1283 | Color 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 | |
1291 | void 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 | |
1298 | void 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 | |
1305 | Ref<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 | |
1310 | void 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 | |
1317 | int 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 | |
1322 | void 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 | |
1327 | String 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 | |
1332 | void 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 | |
1345 | void 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 | |
1352 | Color 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 | |
1360 | void 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 | |
1367 | bool 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 | |
1372 | void 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 | |
1385 | HorizontalAlignment 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 | |
1390 | void 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 | |
1403 | bool 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 | |
1408 | void 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 | |
1422 | bool TreeItem::is_folding_disabled() const { |
1423 | return disable_folding; |
1424 | } |
1425 | |
1426 | Size2 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 | |
1476 | void 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 | |
1495 | void 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 | |
1507 | void 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 | |
1511 | void 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 | |
1681 | TreeItem::TreeItem(Tree *p_tree) { |
1682 | tree = p_tree; |
1683 | } |
1684 | |
1685 | TreeItem::~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 | |
1701 | void Tree::_update_theme_item_cache() { |
1702 | Control::_update_theme_item_cache(); |
1703 | |
1704 | theme_cache.base_scale = get_theme_default_base_scale(); |
1705 | } |
1706 | |
1707 | Size2 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 | |
1726 | int 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 | |
1784 | int 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 | |
1805 | void 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 | |
1869 | void 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 | |
1881 | void 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 | |
1959 | void 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 | |
1971 | int 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 | |
2511 | int 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 | |
2530 | bool 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 | |
2548 | bool 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 | |
2560 | void 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 | |
2645 | Rect2 Tree::search_item_rect(TreeItem *p_from, TreeItem *p_item) { |
2646 | return Rect2(); |
2647 | } |
2648 | |
2649 | void 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 | |
2701 | int 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 | |
3041 | void Tree::() { |
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 | |
3063 | void 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 | |
3073 | void 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 | |
3096 | void 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 | |
3134 | void 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 | |
3151 | void Tree::(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 | |
3166 | void 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 | |
3194 | void 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 | |
3215 | void 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 | |
3246 | void 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 | |
3280 | bool 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 | |
3289 | Rect2 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 | |
3307 | Rect2 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 | |
3325 | void 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 | |
3877 | bool 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 ; |
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 | |
3986 | bool Tree::is_editing() { |
3987 | return popup_editor->is_visible(); |
3988 | } |
3989 | |
3990 | void 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 | |
3998 | Size2 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 | |
4010 | void 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 | |
4067 | int 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 | |
4078 | void 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 | |
4267 | void 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 | |
4276 | Size2 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 | |
4290 | TreeItem *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 | |
4315 | TreeItem *Tree::get_root() const { |
4316 | return root; |
4317 | } |
4318 | |
4319 | TreeItem *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 | |
4335 | void 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 | |
4347 | void 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 | |
4355 | void 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 | |
4372 | void 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 | |
4398 | void Tree::set_select_mode(SelectMode p_mode) { |
4399 | select_mode = p_mode; |
4400 | } |
4401 | |
4402 | Tree::SelectMode Tree::get_select_mode() const { |
4403 | return select_mode; |
4404 | } |
4405 | |
4406 | void 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 | |
4429 | bool Tree::is_anything_selected() { |
4430 | return (selected_item != nullptr); |
4431 | } |
4432 | |
4433 | void 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 | |
4458 | void 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 | |
4467 | bool Tree::is_root_hidden() const { |
4468 | return hide_root; |
4469 | } |
4470 | |
4471 | void 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 | |
4486 | void 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 | |
4498 | void 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 | |
4510 | void 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 | |
4522 | bool 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 | |
4528 | int 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 | |
4534 | bool 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 | |
4540 | TreeItem *Tree::get_selected() const { |
4541 | return selected_item; |
4542 | } |
4543 | |
4544 | void 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 | |
4550 | int Tree::get_selected_column() const { |
4551 | return selected_col; |
4552 | } |
4553 | |
4554 | TreeItem *Tree::get_edited() const { |
4555 | return edited_item; |
4556 | } |
4557 | |
4558 | int Tree::get_edited_column() const { |
4559 | return edited_col; |
4560 | } |
4561 | |
4562 | TreeItem *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 | |
4598 | int 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 | |
4648 | int 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 | |
4672 | void 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 | |
4682 | void 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 | |
4696 | int Tree::get_columns() const { |
4697 | return columns.size(); |
4698 | } |
4699 | |
4700 | void Tree::_scroll_moved(float) { |
4701 | queue_redraw(); |
4702 | } |
4703 | |
4704 | Rect2 Tree::() const { |
4705 | return custom_popup_rect; |
4706 | } |
4707 | |
4708 | int 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 | |
4745 | void 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 | |
4792 | int Tree::get_pressed_button() const { |
4793 | return pressed_button; |
4794 | } |
4795 | |
4796 | Rect2 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 | |
4841 | void 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 | |
4850 | bool Tree::are_column_titles_visible() const { |
4851 | return show_column_titles; |
4852 | } |
4853 | |
4854 | void 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 | |
4866 | String 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 | |
4871 | void 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 | |
4887 | HorizontalAlignment 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 | |
4892 | void 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 | |
4902 | Control::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 | |
4907 | void 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 | |
4916 | String 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 | |
4921 | Point2 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 | |
4932 | void 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 | |
4962 | void 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 | |
4971 | bool Tree::is_h_scroll_enabled() const { |
4972 | return h_scroll_enabled; |
4973 | } |
4974 | |
4975 | void 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 | |
4984 | bool Tree::is_v_scroll_enabled() const { |
4985 | return v_scroll_enabled; |
4986 | } |
4987 | |
4988 | TreeItem *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 | |
5022 | TreeItem *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 | |
5035 | TreeItem *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 | |
5046 | TreeItem *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 | |
5066 | void 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 | |
5090 | TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_column, int &h, int §ion) 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 | |
5145 | int 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 | |
5175 | int 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 | |
5205 | TreeItem *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 | |
5235 | int 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 | |
5276 | String 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 | |
5319 | void Tree::set_cursor_can_exit_tree(bool p_enable) { |
5320 | cursor_can_exit_tree = p_enable; |
5321 | } |
5322 | |
5323 | void 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 | |
5332 | bool Tree::is_folding_hidden() const { |
5333 | return hide_folding; |
5334 | } |
5335 | |
5336 | void Tree::set_enable_recursive_folding(bool p_enable) { |
5337 | enable_recursive_folding = p_enable; |
5338 | } |
5339 | |
5340 | bool Tree::is_recursive_folding_enabled() const { |
5341 | return enable_recursive_folding; |
5342 | } |
5343 | |
5344 | void 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 | |
5356 | int Tree::get_drop_mode_flags() const { |
5357 | return drop_mode_flags; |
5358 | } |
5359 | |
5360 | void Tree::set_edit_checkbox_cell_only_when_checkbox_is_pressed(bool p_enable) { |
5361 | force_edit_checkbox_only_on_checkbox = p_enable; |
5362 | } |
5363 | |
5364 | bool Tree::get_edit_checkbox_cell_only_when_checkbox_is_pressed() const { |
5365 | return force_edit_checkbox_only_on_checkbox; |
5366 | } |
5367 | |
5368 | void Tree::set_allow_rmb_select(bool p_allow) { |
5369 | allow_rmb_select = p_allow; |
5370 | } |
5371 | |
5372 | bool Tree::get_allow_rmb_select() const { |
5373 | return allow_rmb_select; |
5374 | } |
5375 | |
5376 | void Tree::set_allow_reselect(bool p_allow) { |
5377 | allow_reselect = p_allow; |
5378 | } |
5379 | |
5380 | bool Tree::get_allow_reselect() const { |
5381 | return allow_reselect; |
5382 | } |
5383 | |
5384 | void Tree::set_allow_search(bool p_allow) { |
5385 | allow_search = p_allow; |
5386 | } |
5387 | |
5388 | bool Tree::get_allow_search() const { |
5389 | return allow_search; |
5390 | } |
5391 | |
5392 | void 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 | |
5583 | Tree::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 | |
5640 | Tree::~Tree() { |
5641 | if (root) { |
5642 | memdelete(root); |
5643 | } |
5644 | } |
5645 | |