diff options
Diffstat (limited to 'scene/gui/tree.cpp')
-rw-r--r-- | scene/gui/tree.cpp | 344 |
1 files changed, 215 insertions, 129 deletions
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 7842fc5fc0..fc5b942918 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -35,8 +35,6 @@ #include "core/math/math_funcs.h" #include "core/os/keyboard.h" #include "core/os/os.h" -#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" @@ -381,8 +379,8 @@ void TreeItem::set_text_overrun_behavior(int p_column, TextServer::OverrunBehavi cells.write[p_column].text_buf->set_text_overrun_behavior(p_behavior); cells.write[p_column].dirty = true; - _changed_notify(p_column); cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); } TextServer::OverrunBehavior TreeItem::get_text_overrun_behavior(int p_column) const { @@ -1514,7 +1512,9 @@ Size2 TreeItem::get_minimum_size(int p_column) { const TreeItem::Cell &cell = cells[p_column]; if (cell.cached_minimum_size_dirty) { - Size2 size; + Size2 size = Size2( + parent_tree->theme_cache.inner_item_margin_left + parent_tree->theme_cache.inner_item_margin_right, + parent_tree->theme_cache.inner_item_margin_top + parent_tree->theme_cache.inner_item_margin_bottom); // Text. if (!cell.text.is_empty()) { @@ -1522,7 +1522,9 @@ Size2 TreeItem::get_minimum_size(int p_column) { parent_tree->update_item_cell(this, p_column); } Size2 text_size = cell.text_buf->get_size(); - size.width += text_size.width; + if (get_text_overrun_behavior(p_column) == TextServer::OVERRUN_NO_TRIMMING) { + size.width += text_size.width; + } size.height = MAX(size.height, text_size.height); } @@ -1541,13 +1543,10 @@ Size2 TreeItem::get_minimum_size(int p_column) { Ref<Texture2D> texture = cell.buttons[i].texture; if (texture.is_valid()) { Size2 button_size = texture->get_size() + parent_tree->theme_cache.button_pressed->get_minimum_size(); - size.width += button_size.width; + size.width += button_size.width + parent_tree->theme_cache.button_margin; size.height = MAX(size.height, button_size.height); } } - if (cell.buttons.size() >= 2) { - size.width += (cell.buttons.size() - 1) * parent_tree->theme_cache.button_margin; - } cells.write[p_column].cached_minimum_size = size; cells.write[p_column].cached_minimum_size_dirty = false; @@ -2195,9 +2194,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected || !p_item->has_meta("__focus_rect")) { Rect2i r = cell_rect; - p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); - if (select_mode != SELECT_ROW) { + p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); if (rtl) { r.position.x = get_size().width - r.position.x - r.size.x; } @@ -2208,6 +2206,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 theme_cache.selected->draw(ci, r); } } + } else { + p_item->set_meta("__focus_col_" + itos(i), Rect2(r.position, r.size)); } } @@ -2458,7 +2458,6 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (rtl) { button_ofs.x = get_size().width - button_ofs.x - button_texture->get_width(); } - p_item->cells.write[i].buttons.write[j].rect = Rect2i(button_ofs, button_size); 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); item_width_with_buttons -= button_size.width + theme_cache.button_margin; } @@ -2693,7 +2692,6 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c if (p_selected == p_current && (!c.selected || allow_reselect)) { c.selected = true; selected_item = p_selected; - selected_col = 0; if (!emitted_row) { emit_signal(SNAME("item_selected")); emitted_row = true; @@ -2704,6 +2702,9 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c c.selected = false; } } + if (&selected_cell == &c) { + selected_col = i; + } } else if (select_mode == SELECT_SINGLE || select_mode == SELECT_MULTI) { if (!r_in_range && &selected_cell == &c) { if (!selected_cell.selected || allow_reselect) { @@ -2827,7 +2828,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int return -1; } - 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))) { + if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x < (x_ofs + theme_cache.item_margin))) { if (enable_recursive_folding && p_mod->is_shift_pressed()) { p_item->set_collapsed_recursive(!p_item->is_collapsed()); } else { @@ -3151,10 +3152,12 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int } 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)) { - return; + if (popup_edit_commited) { + return; // Already processed by LineEdit/TextEdit commit. + } + + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { + return; // ESC pressed, app focus lost, or forced close from code. } if (value_editor->has_point(value_editor->get_local_mouse_position())) { @@ -3173,9 +3176,18 @@ void Tree::_text_editor_popup_modal_close() { } void Tree::_text_editor_gui_input(const Ref<InputEvent> &p_event) { + if (popup_edit_commited) { + return; // Already processed by _text_editor_popup_modal_close + } + + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { + return; // ESC pressed, app focus lost, or forced close from code. + } + if (p_event->is_action_pressed("ui_text_newline_blank", true)) { accept_event(); } else if (p_event->is_action_pressed("ui_text_newline")) { + popup_edit_commited = true; // End edit popup processing. popup_editor->hide(); _apply_multiline_edit(); accept_event(); @@ -3206,6 +3218,15 @@ void Tree::_apply_multiline_edit() { } void Tree::_line_editor_submit(String p_text) { + if (popup_edit_commited) { + return; // Already processed by _text_editor_popup_modal_close + } + + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { + return; // ESC pressed, app focus lost, or forced close from code. + } + + popup_edit_commited = true; // End edit popup processing. popup_editor->hide(); if (!popup_edited_item) { @@ -3428,7 +3449,7 @@ Rect2 Tree::_get_content_rect() const { const real_t v_size = v_scroll->is_visible() ? (v_scroll->get_combined_minimum_size().x + theme_cache.scrollbar_h_separation) : 0; const real_t h_size = h_scroll->is_visible() ? (h_scroll->get_combined_minimum_size().y + theme_cache.scrollbar_v_separation) : 0; const Point2 scroll_begin = _get_scrollbar_layout_rect().get_end() - Vector2(v_size, h_size); - const Size2 offset = (content_rect.get_end() - scroll_begin).max(Vector2(0, 0)); + const Size2 offset = (content_rect.get_end() - scroll_begin).maxf(0); return content_rect.grow_individual(0, 0, -offset.x, -offset.y); } @@ -3778,7 +3799,12 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); warp_mouse(range_drag_capture_pos); } else { - Rect2 rect = get_selected()->get_meta("__focus_rect"); + Rect2 rect; + if (select_mode == SELECT_ROW) { + rect = get_selected()->get_meta("__focus_col_" + itos(selected_col)); + } else { + rect = get_selected()->get_meta("__focus_rect"); + } Point2 mpos = mb->get_position(); int icon_size_x = 0; Ref<Texture2D> icon = get_selected()->get_icon(selected_col); @@ -3868,6 +3894,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } if (!root || (!root->get_first_child() && hide_root)) { + emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index()); break; } @@ -3986,7 +4013,14 @@ bool Tree::edit_selected(bool p_force_edit) { return false; } - Rect2 rect = s->get_meta("__focus_rect"); + float popup_scale = popup_editor->is_embedded() ? 1.0 : popup_editor->get_parent_visible_window()->get_content_scale_factor(); + Rect2 rect; + if (select_mode == SELECT_ROW) { + rect = s->get_meta("__focus_col_" + itos(selected_col)); + } else { + rect = s->get_meta("__focus_rect"); + } + rect.position *= popup_scale; popup_edited_item = s; popup_edited_item_col = col; @@ -4029,7 +4063,7 @@ bool Tree::edit_selected(bool p_force_edit) { popup_rect.size = rect.size; // Account for icon. - Size2 icon_size = _get_cell_icon_size(c); + Size2 icon_size = _get_cell_icon_size(c) * popup_scale; popup_rect.position.x += icon_size.x; popup_rect.size.x -= icon_size.x; @@ -4056,7 +4090,11 @@ bool Tree::edit_selected(bool p_force_edit) { } popup_editor->set_position(popup_rect.position); - popup_editor->set_size(popup_rect.size); + popup_editor->set_size(popup_rect.size * popup_scale); + if (!popup_editor->is_embedded()) { + popup_editor->set_content_scale_factor(popup_scale); + } + popup_edit_commited = false; // Start edit popup processing. popup_editor->popup(); popup_editor->child_controls_changed(); @@ -4072,7 +4110,11 @@ bool Tree::edit_selected(bool p_force_edit) { text_editor->show(); popup_editor->set_position(get_screen_position() + rect.position); - popup_editor->set_size(rect.size); + popup_editor->set_size(rect.size * popup_scale); + if (!popup_editor->is_embedded()) { + popup_editor->set_content_scale_factor(popup_scale); + } + popup_edit_commited = false; // Start edit popup processing. popup_editor->popup(); popup_editor->child_controls_changed(); @@ -4325,7 +4367,7 @@ void Tree::_notification(int p_what) { } default: { - text_pos.x += sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2; + text_pos.x += (tbrect.size.width - columns[i].text_buf->get_size().x) / 2; break; } } @@ -4375,17 +4417,23 @@ void Tree::_update_all() { } Size2 Tree::get_minimum_size() const { - if (h_scroll_enabled && v_scroll_enabled) { - return Size2(); - } else { - Vector2 min_size = get_internal_min_size(); - Ref<StyleBox> bg = theme_cache.panel_style; - if (bg.is_valid()) { - min_size.x += bg->get_margin(SIDE_LEFT) + bg->get_margin(SIDE_RIGHT); - min_size.y += bg->get_margin(SIDE_TOP) + bg->get_margin(SIDE_BOTTOM); - } - return Vector2(h_scroll_enabled ? 0 : min_size.x, v_scroll_enabled ? 0 : min_size.y); + Vector2 min_size = Vector2(0, _get_title_button_height()); + + if (theme_cache.panel_style.is_valid()) { + min_size += theme_cache.panel_style->get_minimum_size(); } + + Vector2 content_min_size = get_internal_min_size(); + if (h_scroll_enabled) { + content_min_size.x = 0; + min_size.y += h_scroll->get_combined_minimum_size().height; + } + if (v_scroll_enabled) { + min_size.x += v_scroll->get_combined_minimum_size().width; + content_min_size.y = 0; + } + + return min_size + content_min_size; } TreeItem *Tree::create_item(TreeItem *p_parent, int p_index) { @@ -4563,6 +4611,7 @@ void Tree::set_hide_root(bool p_enabled) { hide_root = p_enabled; queue_redraw(); + update_minimum_size(); } bool Tree::is_root_hidden() const { @@ -4707,34 +4756,37 @@ int Tree::get_column_minimum_width(int p_column) const { // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(theme_cache.font->get_string_size(columns[p_column].xl_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); - } - - if (!columns[p_column].clip_content) { - int depth = 0; - TreeItem *next; - for (TreeItem *item = get_root(); item; item = next) { - next = item->get_next_visible(); - // Compute the depth in tree. - if (next && p_column == 0) { - if (next->get_parent() == item) { - depth += 1; - } else { - TreeItem *common_parent = item->get_parent(); - while (common_parent != next->get_parent() && common_parent) { - common_parent = common_parent->get_parent(); - depth -= 1; + const float padding = theme_cache.title_button->get_margin(SIDE_LEFT) + theme_cache.title_button->get_margin(SIDE_RIGHT); + min_width = MAX(theme_cache.font->get_string_size(columns[p_column].xl_title, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + padding, min_width); + } + + if (root && !columns[p_column].clip_content) { + int depth = 1; + + TreeItem *last = nullptr; + TreeItem *first = hide_root ? root->get_next_visible() : root; + for (TreeItem *item = first; item; last = item, item = item->get_next_visible()) { + // Get column indentation. + int indent; + if (p_column == 0) { + if (last) { + if (item->parent == last) { + depth += 1; + } else if (item->parent != last->parent) { + depth = hide_root ? 0 : 1; + for (TreeItem *iter = item->parent; iter; iter = iter->parent) { + depth += 1; + } } } + indent = theme_cache.item_margin * depth; + } else { + indent = theme_cache.h_separation; } // Get the item minimum size. Size2 item_size = item->get_minimum_size(p_column); - if (p_column == 0) { - item_size.width += theme_cache.item_margin * depth; - } else { - item_size.width += theme_cache.h_separation; - } + item_size.width += indent; // Check if the item is wider. min_width = MAX(min_width, item_size.width); @@ -4948,6 +5000,7 @@ void Tree::set_column_titles_visible(bool p_show) { show_column_titles = p_show; queue_redraw(); + update_minimum_size(); } bool Tree::are_column_titles_visible() const { @@ -5246,6 +5299,86 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_ return nullptr; } +// When on a button, r_index is valid. +// When on an item, both r_item and r_column are valid. +// Otherwise, all output arguments are invalid. +void Tree::_find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_column, int &r_index) const { + r_item = nullptr; + r_column = -1; + r_index = -1; + + if (!root) { + return; + } + + Point2 pos = p_pos - theme_cache.panel_style->get_offset(); + pos.y -= _get_title_button_height(); + if (pos.y < 0) { + return; + } + + if (cache.rtl) { + pos.x = get_size().width - pos.x; + } + pos += theme_cache.offset; // Scrolling. + + int col, h, section; + TreeItem *it = _find_item_at_pos(root, pos, col, h, section); + if (!it) { + return; + } + + r_item = it; + r_column = col; + + const TreeItem::Cell &c = it->cells[col]; + if (c.buttons.is_empty()) { + return; + } + + int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width + theme_cache.offset.x; + if (v_scroll->is_visible_in_tree()) { + x_limit -= v_scroll->get_minimum_size().width; + } + + for (int i = 0; i < col; i++) { + const int col_w = get_column_width(i) + theme_cache.h_separation; + pos.x -= col_w; + x_limit -= col_w; + } + + int x_check; + if (cache.rtl) { + x_check = get_column_width(col); + } else { + // Right edge of the buttons area, relative to the start of the column. + int buttons_area_min = 0; + if (col == 0) { + // Content of column 0 should take indentation into account. + for (TreeItem *current = it; current && (current != root || !hide_root); current = current->parent) { + buttons_area_min += theme_cache.item_margin; + } + } + for (int i = c.buttons.size() - 1; i >= 0; i--) { + Ref<Texture2D> b = c.buttons[i].texture; + buttons_area_min += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin; + } + + x_check = MAX(buttons_area_min, MIN(get_column_width(col), x_limit)); + } + + for (int i = c.buttons.size() - 1; i >= 0; i--) { + Ref<Texture2D> b = c.buttons[i].texture; + Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size(); + if (pos.x > x_check - size.width) { + x_limit -= theme_cache.item_margin; + r_index = i; + return; + } + x_check -= size.width + theme_cache.button_margin; + } +} + int Tree::get_column_at_position(const Point2 &p_pos) const { if (root) { Point2 pos = p_pos; @@ -5337,84 +5470,37 @@ TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const { } int Tree::get_button_id_at_position(const Point2 &p_pos) const { - if (root) { - Point2 pos = p_pos; - pos -= theme_cache.panel_style->get_offset(); - pos.y -= _get_title_button_height(); - if (pos.y < 0) { - return -1; - } - - if (h_scroll->is_visible_in_tree()) { - pos.x += h_scroll->get_value(); - } - if (v_scroll->is_visible_in_tree()) { - pos.y += v_scroll->get_value(); - } - - int col, h, section; - TreeItem *it = _find_item_at_pos(root, pos, col, h, section); - - if (it) { - const TreeItem::Cell &c = it->cells[col]; - int col_width = get_column_width(col); + TreeItem *it; + int col, index; + _find_button_at_pos(p_pos, it, col, index); - for (int i = 0; i < col; i++) { - pos.x -= get_column_width(i); - } - - for (int j = c.buttons.size() - 1; j >= 0; j--) { - Ref<Texture2D> b = c.buttons[j].texture; - Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size(); - if (pos.x > col_width - size.width) { - return c.buttons[j].id; - } - col_width -= size.width; - } - } + if (index == -1) { + return -1; } - - return -1; + return it->cells[col].buttons[index].id; } String Tree::get_tooltip(const Point2 &p_pos) const { - if (root) { - Point2 pos = p_pos; - pos -= theme_cache.panel_style->get_offset(); - pos.y -= _get_title_button_height(); - if (pos.y < 0) { - return Control::get_tooltip(p_pos); - } + Point2 pos = p_pos - theme_cache.panel_style->get_offset(); + pos.y -= _get_title_button_height(); + if (pos.y < 0) { + return Control::get_tooltip(p_pos); + } - Point2 button_pos = pos; - if (h_scroll->is_visible_in_tree()) { - pos.x += h_scroll->get_value(); - } - if (v_scroll->is_visible_in_tree()) { - pos.y += v_scroll->get_value(); - } + TreeItem *it; + int col, index; + _find_button_at_pos(p_pos, it, col, index); - int col, h, section; - TreeItem *it = _find_item_at_pos(root, pos, col, h, section); + if (index != -1) { + return it->cells[col].buttons[index].tooltip; + } - if (it) { - const TreeItem::Cell &c = it->cells[col]; - for (int j = c.buttons.size() - 1; j >= 0; j--) { - if (c.buttons[j].rect.has_point(button_pos)) { - String tooltip = c.buttons[j].tooltip; - if (!tooltip.is_empty()) { - return tooltip; - } - } - } - String ret; - if (it->get_tooltip_text(col) == "") { - ret = it->get_text(col); - } else { - ret = it->get_tooltip_text(col); - } - return ret; + if (it) { + const String item_tooltip = it->get_tooltip_text(col); + if (item_tooltip.is_empty()) { + return it->get_text(col); } + return item_tooltip; } return Control::get_tooltip(p_pos); @@ -5592,8 +5678,8 @@ void Tree::_bind_methods() { ADD_SIGNAL(MethodInfo("item_selected")); ADD_SIGNAL(MethodInfo("cell_selected")); ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::BOOL, "selected"))); - ADD_SIGNAL(MethodInfo("item_mouse_selected", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::INT, "mouse_button_index"))); - ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::INT, "mouse_button_index"))); + ADD_SIGNAL(MethodInfo("item_mouse_selected", PropertyInfo(Variant::VECTOR2, "mouse_position"), PropertyInfo(Variant::INT, "mouse_button_index"))); + ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "click_position"), PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("item_edited")); ADD_SIGNAL(MethodInfo("custom_item_clicked", PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("item_icon_double_clicked")); @@ -5733,7 +5819,7 @@ Tree::Tree() { h_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved)); v_scroll->connect("value_changed", callable_mp(this, &Tree::_scroll_moved)); 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)); + text_editor->connect(SceneStringName(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)); |