summaryrefslogtreecommitdiffstats
path: root/scene/gui/tree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/tree.cpp')
-rw-r--r--scene/gui/tree.cpp228
1 files changed, 177 insertions, 51 deletions
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 434daa484f..b8fc8004c9 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -38,6 +38,7 @@
#include "core/string/print_string.h"
#include "core/string/translation.h"
#include "scene/gui/box_container.h"
+#include "scene/gui/text_edit.h"
#include "scene/main/window.h"
#include <limits.h>
@@ -166,6 +167,18 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const {
return cells[p_column].mode;
}
+/* multiline editable */
+void TreeItem::set_edit_multiline(int p_column, bool p_multiline) {
+ ERR_FAIL_INDEX(p_column, cells.size());
+ cells.write[p_column].edit_multiline = p_multiline;
+ _changed_notify(p_column);
+}
+
+bool TreeItem::is_edit_multiline(int p_column) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), false);
+ return cells[p_column].edit_multiline;
+}
+
/* check mode */
void TreeItem::set_checked(int p_column, bool p_checked) {
ERR_FAIL_INDEX(p_column, cells.size());
@@ -735,7 +748,7 @@ TreeItem *TreeItem::get_first_child() const {
return first_child;
}
-TreeItem *TreeItem::_get_prev_visible(bool p_wrap) {
+TreeItem *TreeItem::_get_prev_in_tree(bool p_wrap, bool p_include_invisible) {
TreeItem *current = this;
TreeItem *prev_item = current->get_prev();
@@ -758,7 +771,7 @@ TreeItem *TreeItem::_get_prev_visible(bool p_wrap) {
}
} else {
current = prev_item;
- while (!current->collapsed && current->first_child) {
+ while ((!current->collapsed || p_include_invisible) && current->first_child) {
//go to the very end
current = current->first_child;
@@ -773,9 +786,9 @@ TreeItem *TreeItem::_get_prev_visible(bool p_wrap) {
TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
TreeItem *loop = this;
- TreeItem *prev_item = this->_get_prev_visible(p_wrap);
+ TreeItem *prev_item = this->_get_prev_in_tree(p_wrap);
while (prev_item && !prev_item->is_visible()) {
- prev_item = prev_item->_get_prev_visible(p_wrap);
+ prev_item = prev_item->_get_prev_in_tree(p_wrap);
if (prev_item == loop) {
// Check that we haven't looped all the way around to the start.
prev_item = nullptr;
@@ -785,10 +798,10 @@ TreeItem *TreeItem::get_prev_visible(bool p_wrap) {
return prev_item;
}
-TreeItem *TreeItem::_get_next_visible(bool p_wrap) {
+TreeItem *TreeItem::_get_next_in_tree(bool p_wrap, bool p_include_invisible) {
TreeItem *current = this;
- if (!current->collapsed && current->first_child) {
+ if ((!current->collapsed || p_include_invisible) && current->first_child) {
current = current->first_child;
} else if (current->next) {
@@ -814,9 +827,9 @@ TreeItem *TreeItem::_get_next_visible(bool p_wrap) {
TreeItem *TreeItem::get_next_visible(bool p_wrap) {
TreeItem *loop = this;
- TreeItem *next_item = this->_get_next_visible(p_wrap);
+ TreeItem *next_item = this->_get_next_in_tree(p_wrap);
while (next_item && !next_item->is_visible()) {
- next_item = next_item->_get_next_visible(p_wrap);
+ next_item = next_item->_get_next_in_tree(p_wrap);
if (next_item == loop) {
// Check that we haven't looped all the way around to the start.
next_item = nullptr;
@@ -826,6 +839,16 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) {
return next_item;
}
+TreeItem *TreeItem::get_prev_in_tree(bool p_wrap) {
+ TreeItem *prev_item = this->_get_prev_in_tree(p_wrap, true);
+ return prev_item;
+}
+
+TreeItem *TreeItem::get_next_in_tree(bool p_wrap) {
+ TreeItem *next_item = this->_get_next_in_tree(p_wrap, true);
+ return next_item;
+}
+
TreeItem *TreeItem::get_child(int p_index) {
_create_children_cache();
@@ -1404,6 +1427,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_cell_mode", "column", "mode"), &TreeItem::set_cell_mode);
ClassDB::bind_method(D_METHOD("get_cell_mode", "column"), &TreeItem::get_cell_mode);
+ ClassDB::bind_method(D_METHOD("set_edit_multiline", "column", "multiline"), &TreeItem::set_edit_multiline);
+ ClassDB::bind_method(D_METHOD("is_edit_multiline", "column"), &TreeItem::is_edit_multiline);
+
ClassDB::bind_method(D_METHOD("set_checked", "column", "checked"), &TreeItem::set_checked);
ClassDB::bind_method(D_METHOD("set_indeterminate", "column", "indeterminate"), &TreeItem::set_indeterminate);
ClassDB::bind_method(D_METHOD("is_checked", "column"), &TreeItem::is_checked);
@@ -1523,6 +1549,9 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent);
ClassDB::bind_method(D_METHOD("get_first_child"), &TreeItem::get_first_child);
+ ClassDB::bind_method(D_METHOD("get_next_in_tree", "wrap"), &TreeItem::get_next_in_tree, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_prev_in_tree", "wrap"), &TreeItem::get_prev_in_tree, DEFVAL(false));
+
ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false));
@@ -1726,7 +1755,7 @@ int Tree::compute_item_height(TreeItem *p_item) const {
}
}
}
- int item_min_height = p_item->get_custom_minimum_height();
+ int item_min_height = MAX(theme_cache.font->get_height(theme_cache.font_size), p_item->get_custom_minimum_height());
if (height < item_min_height) {
height = item_min_height;
}
@@ -1795,8 +1824,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
if (rtl && rect.size.width > 0) {
Point2 draw_pos = rect.position;
- draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0);
- p_cell.text_buf->set_width(rect.size.width);
+ draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) * 0.5);
if (p_ol_size > 0 && p_ol_color.a > 0) {
p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
}
@@ -1815,8 +1843,7 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
if (!rtl && rect.size.width > 0) {
Point2 draw_pos = rect.position;
- draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0);
- p_cell.text_buf->set_width(rect.size.width);
+ draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) * 0.5);
if (p_ol_size > 0 && p_ol_color.a > 0) {
p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
}
@@ -1891,6 +1918,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
font_size = theme_cache.font_size;
}
p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language);
+ p_item->cells.write[p_col].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE);
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));
p_item->cells.write[p_col].dirty = false;
}
@@ -1907,7 +1935,7 @@ void Tree::update_item_cache(TreeItem *p_item) {
}
}
-int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item) {
+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) {
if (p_pos.y - theme_cache.offset.y > (p_draw_size.height)) {
return -1; //draw no more!
}
@@ -1920,17 +1948,14 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
int htotal = 0;
- int label_h = compute_item_height(p_item);
+ int label_h = 0;
bool rtl = cache.rtl;
- /* Calculate height of the label part */
- label_h += theme_cache.v_separation;
-
/* Draw label, if height fits */
bool skip = (p_item == root && hide_root);
- if (!skip && (p_pos.y + label_h - theme_cache.offset.y) > 0) {
+ if (!skip) {
// Draw separation.
ERR_FAIL_COND_V(theme_cache.font.is_null(), -1);
@@ -1990,6 +2015,14 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
buttons_width += button_size.width + theme_cache.button_margin;
}
+ p_item->cells.write[i].text_buf->set_width(item_width);
+
+ label_h = compute_item_height(p_item);
+ if (r_self_height != nullptr) {
+ *r_self_height = label_h;
+ }
+ label_h += theme_cache.v_separation;
+
Rect2i item_rect = Rect2i(Point2i(ofs, p_pos.y) - theme_cache.offset + p_draw_ofs, Size2i(item_width, label_h));
Rect2i cell_rect = item_rect;
if (i != 0) {
@@ -2108,7 +2141,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
}
Point2i text_pos = item_rect.position;
- text_pos.y += Math::floor((item_rect.size.y - p_item->cells[i].text_buf->get_size().y) / 2);
+ text_pos.y += Math::floor(p_draw_ofs.y) - _get_title_button_height();
int text_width = p_item->cells[i].text_buf->get_size().x;
switch (p_item->cells[i].mode) {
@@ -2149,7 +2182,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
Ref<Texture2D> downarrow = theme_cache.select_arrow;
int cell_width = item_rect.size.x - downarrow->get_width();
- p_item->cells.write[i].text_buf->set_width(cell_width);
if (rtl) {
if (outline_size > 0 && font_outline_color.a > 0) {
p_item->cells[i].text_buf->draw_outline(ci, text_pos + Vector2(cell_width - text_width, 0), outline_size, font_outline_color);
@@ -2336,15 +2368,17 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
while (c) {
int child_h = -1;
+ int child_self_height = 0;
if (htotal >= 0) {
- child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c);
+ child_h = draw_item(children_pos, p_draw_ofs, p_draw_size, c, &child_self_height);
+ child_self_height += theme_cache.v_separation;
}
// Draw relationship lines.
if (theme_cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root) && c->is_visible()) {
int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? theme_cache.h_separation : theme_cache.item_margin);
int parent_ofs = p_pos.x + theme_cache.item_margin;
- Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - theme_cache.offset + p_draw_ofs;
+ Point2i root_pos = Point2i(root_ofs, children_pos.y + child_self_height / 2) - theme_cache.offset + p_draw_ofs;
if (c->get_visible_child_count() > 0) {
root_pos -= Point2i(theme_cache.arrow->get_width(), 0);
@@ -2975,7 +3009,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
return item_h; // nothing found
}
-void Tree::_text_editor_modal_close() {
+void Tree::_text_editor_popup_modal_close() {
if (Input::get_singleton()->is_key_pressed(Key::ESCAPE) ||
Input::get_singleton()->is_key_pressed(Key::KP_ENTER) ||
Input::get_singleton()->is_key_pressed(Key::ENTER)) {
@@ -2986,10 +3020,51 @@ void Tree::_text_editor_modal_close() {
return;
}
- _text_editor_submit(text_editor->get_text());
+ if (!popup_edited_item) {
+ return;
+ }
+
+ 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) {
+ _apply_multiline_edit();
+ } else {
+ _line_editor_submit(line_editor->get_text());
+ }
+}
+
+void Tree::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
+ if (p_event->is_action_pressed("ui_text_newline_blank", true)) {
+ accept_event();
+ } else if (p_event->is_action_pressed("ui_text_newline")) {
+ popup_editor->hide();
+ _apply_multiline_edit();
+ accept_event();
+ }
}
-void Tree::_text_editor_submit(String p_text) {
+void Tree::_apply_multiline_edit() {
+ if (!popup_edited_item) {
+ return;
+ }
+
+ if (popup_edited_item_col < 0 || popup_edited_item_col > columns.size()) {
+ return;
+ }
+
+ TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col];
+ switch (c.mode) {
+ case TreeItem::CELL_MODE_STRING: {
+ c.text = text_editor->get_text();
+ } break;
+ default: {
+ ERR_FAIL();
+ }
+ }
+
+ item_edited(popup_edited_item_col, popup_edited_item);
+ queue_redraw();
+}
+
+void Tree::_line_editor_submit(String p_text) {
popup_editor->hide();
if (!popup_edited_item) {
@@ -3789,14 +3864,14 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
}
}
-bool Tree::edit_selected() {
+bool Tree::edit_selected(bool p_force_edit) {
TreeItem *s = get_selected();
ERR_FAIL_COND_V_MSG(!s, false, "No item selected.");
ensure_cursor_is_visible();
int col = get_selected_column();
ERR_FAIL_INDEX_V_MSG(col, columns.size(), false, "No item column selected.");
- if (!s->cells[col].editable) {
+ if (!s->cells[col].editable && !p_force_edit) {
return false;
}
@@ -3830,18 +3905,16 @@ bool Tree::edit_selected() {
popup_menu->popup();
popup_edited_item = s;
popup_edited_item_col = col;
- return true;
- } else if (c.mode == TreeItem::CELL_MODE_STRING || c.mode == TreeItem::CELL_MODE_RANGE) {
+ return true;
+ } else if ((c.mode == TreeItem::CELL_MODE_STRING && !c.edit_multiline) || c.mode == TreeItem::CELL_MODE_RANGE) {
Rect2 popup_rect;
int value_editor_height = c.mode == TreeItem::CELL_MODE_RANGE ? value_editor->get_minimum_size().height : 0;
// "floor()" centers vertically.
- Vector2 ofs(0, Math::floor((MAX(text_editor->get_minimum_size().height, rect.size.height - value_editor_height) - rect.size.height) / 2));
+ Vector2 ofs(0, Math::floor((MAX(line_editor->get_minimum_size().height, rect.size.height - value_editor_height) - rect.size.height) / 2));
- Point2i textedpos = get_screen_position() + rect.position - ofs;
- cache.text_editor_position = textedpos;
- popup_rect.position = textedpos;
+ popup_rect.position = get_screen_position() + rect.position - ofs;
popup_rect.size = rect.size;
// Account for icon.
@@ -3849,9 +3922,12 @@ bool Tree::edit_selected() {
popup_rect.position.x += icon_size.x;
popup_rect.size.x -= icon_size.x;
- text_editor->clear();
- text_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
- text_editor->select_all();
+ line_editor->clear();
+ line_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
+ line_editor->select_all();
+ line_editor->show();
+
+ text_editor->hide();
if (c.mode == TreeItem::CELL_MODE_RANGE) {
popup_rect.size.y += value_editor_height;
@@ -3873,6 +3949,22 @@ bool Tree::edit_selected() {
popup_editor->popup();
popup_editor->child_controls_changed();
+ line_editor->grab_focus();
+
+ return true;
+ } else if (c.mode == TreeItem::CELL_MODE_STRING && c.edit_multiline) {
+ line_editor->hide();
+
+ text_editor->clear();
+ text_editor->set_text(c.text);
+ text_editor->select_all();
+ text_editor->show();
+
+ popup_editor->set_position(get_screen_position() + rect.position);
+ popup_editor->set_size(rect.size);
+ popup_editor->popup();
+ popup_editor->child_controls_changed();
+
text_editor->grab_focus();
return true;
@@ -3885,6 +3977,14 @@ bool Tree::is_editing() {
return popup_editor->is_visible();
}
+void Tree::set_editor_selection(int p_from_line, int p_to_line, int p_from_column, int p_to_column, int p_caret) {
+ if (p_from_column == -1 || p_to_column == -1) {
+ line_editor->select(p_from_line, p_to_line);
+ } else {
+ text_editor->select(p_from_line, p_from_column, p_to_line, p_to_column, p_caret);
+ }
+}
+
Size2 Tree::get_internal_min_size() const {
Size2i size;
if (root) {
@@ -4144,14 +4244,10 @@ void Tree::_notification(int p_what) {
case NOTIFICATION_TRANSFORM_CHANGED: {
if (popup_edited_item != nullptr) {
Rect2 rect = popup_edited_item->get_meta("__focus_rect");
- Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2);
- Point2i textedpos = get_global_position() + rect.position - ofs;
- if (cache.text_editor_position != textedpos) {
- cache.text_editor_position = textedpos;
- text_editor->set_position(textedpos);
- value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height));
- }
+ popup_editor->set_position(get_global_position() + rect.position);
+ popup_editor->set_size(rect.size);
+ popup_editor->child_controls_changed();
}
} break;
}
@@ -4920,6 +5016,26 @@ TreeItem *Tree::get_item_with_text(const String &p_find) const {
return nullptr;
}
+TreeItem *Tree::get_item_with_metadata(const Variant &p_find, int p_column) const {
+ if (p_column < 0) {
+ for (TreeItem *current = root; current; current = current->get_next_in_tree()) {
+ for (int i = 0; i < columns.size(); i++) {
+ if (current->get_metadata(i) == p_find) {
+ return current;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ for (TreeItem *current = root; current; current = current->get_next_in_tree()) {
+ if (current->get_metadata(p_column) == p_find) {
+ return current;
+ }
+ }
+ return nullptr;
+}
+
void Tree::_do_incr_search(const String &p_add) {
uint64_t time = OS::get_singleton()->get_ticks_usec() / 1000; // convert to msec
uint64_t diff = time - last_keypress;
@@ -5285,7 +5401,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_edited"), &Tree::get_edited);
ClassDB::bind_method(D_METHOD("get_edited_column"), &Tree::get_edited_column);
- ClassDB::bind_method(D_METHOD("edit_selected"), &Tree::edit_selected);
+ ClassDB::bind_method(D_METHOD("edit_selected", "force_edit"), &Tree::edit_selected, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_custom_popup_rect"), &Tree::get_custom_popup_rect);
ClassDB::bind_method(D_METHOD("get_item_area_rect", "item", "column", "button_index"), &Tree::get_item_rect, DEFVAL(-1), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position);
@@ -5386,17 +5502,26 @@ Tree::Tree() {
popup_editor = memnew(Popup);
add_child(popup_editor, false, INTERNAL_MODE_FRONT);
+
popup_editor_vb = memnew(VBoxContainer);
- popup_editor->add_child(popup_editor_vb);
popup_editor_vb->add_theme_constant_override("separation", 0);
popup_editor_vb->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
- text_editor = memnew(LineEdit);
- popup_editor_vb->add_child(text_editor);
+ popup_editor->add_child(popup_editor_vb);
+
+ line_editor = memnew(LineEdit);
+ line_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ line_editor->hide();
+ popup_editor_vb->add_child(line_editor);
+
+ text_editor = memnew(TextEdit);
text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ text_editor->hide();
+ popup_editor_vb->add_child(text_editor);
+
value_editor = memnew(HSlider);
- popup_editor_vb->add_child(value_editor);
value_editor->set_v_size_flags(SIZE_EXPAND_FILL);
value_editor->hide();
+ popup_editor_vb->add_child(value_editor);
h_scroll = memnew(HScrollBar);
v_scroll = memnew(VScrollBar);
@@ -5410,8 +5535,9 @@ Tree::Tree() {
h_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
v_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved));
- text_editor->connect("text_submitted", callable_mp(this, &Tree::_text_editor_submit));
- popup_editor->connect("popup_hide", callable_mp(this, &Tree::_text_editor_modal_close));
+ line_editor->connect("text_submitted", callable_mp(this, &Tree::_line_editor_submit));
+ text_editor->connect("gui_input", callable_mp(this, &Tree::_text_editor_gui_input));
+ popup_editor->connect("popup_hide", callable_mp(this, &Tree::_text_editor_popup_modal_close));
popup_menu->connect("id_pressed", callable_mp(this, &Tree::popup_select));
value_editor->connect("value_changed", callable_mp(this, &Tree::value_editor_changed));