diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index 49b4622aed0e..b97b4bf17d42 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -350,6 +350,13 @@ + + + + + Returns [code]true[/code] if the given [param column] is multiline editable. + + @@ -456,7 +463,7 @@ - If [code]true[/code], the given [param column] is checked. Clears column's indeterminate status. + If [param checked] is [code]true[/code], the given [param column] is checked. Clears column's indeterminate status. @@ -516,12 +523,21 @@ Sets custom font size used to draw text in the given [param column]. + + + + + + If [param multiline] is [code]true[/code], the given [param column] is multiline editable. + [b]Note:[/b] This option only affects the type of control ([LineEdit] or [TextEdit]) that appears when editing the column. You can set multiline values with [method set_text] even if the column is not multiline editable. + + - If [code]true[/code], the given [param column] is editable. + If [param enabled] is [code]true[/code], the given [param column] is editable. @@ -529,7 +545,7 @@ - If [code]true[/code], the given [param column] is expanded to the right. + If [param enable] is [code]true[/code], the given [param column] is expanded to the right. @@ -569,7 +585,7 @@ - If [code]true[/code], the given [param column] is marked [param indeterminate]. + If [param indeterminate] is [code]true[/code], the given [param column] is marked indeterminate. [b]Note:[/b] If set [code]true[/code] from [code]false[/code], then column is cleared of checked status. @@ -614,7 +630,7 @@ - If [code]true[/code], the given column is selectable. + If [param selectable] is [code]true[/code], the given [param column] is selectable. diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 434daa484f67..18d00d519cf6 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 @@ -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()); @@ -1404,6 +1417,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); @@ -1726,7 +1742,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; } @@ -1757,7 +1773,7 @@ int Tree::get_item_height(TreeItem *p_item) const { return height; } -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) { +void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Point2 &p_draw_ofs, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color) { ERR_FAIL_COND(theme_cache.font.is_null()); Rect2i rect = p_rect; @@ -1795,7 +1811,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); + draw_pos.y += Math::floor(p_draw_ofs.y) - _get_title_button_height(); p_cell.text_buf->set_width(rect.size.width); 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,7 +1831,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); + draw_pos.y += Math::floor(p_draw_ofs.y) - _get_title_button_height(); p_cell.text_buf->set_width(rect.size.width); 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); @@ -2108,12 +2124,12 @@ 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) { case TreeItem::CELL_MODE_STRING: { - draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color); + draw_item_rect(p_item->cells.write[i], item_rect, p_draw_ofs, cell_color, icon_col, outline_size, font_outline_color); } break; case TreeItem::CELL_MODE_CHECK: { Ref checked = theme_cache.checked; @@ -2137,7 +2153,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 item_rect.size.x -= check_w; item_rect.position.x += check_w; - draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color); + draw_item_rect(p_item->cells.write[i], item_rect, p_draw_ofs, cell_color, icon_col, outline_size, font_outline_color); } break; case TreeItem::CELL_MODE_RANGE: { @@ -2216,7 +2232,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } if (!p_item->cells[i].editable) { - draw_item_rect(p_item->cells.write[i], item_rect, cell_color, icon_col, outline_size, font_outline_color); + draw_item_rect(p_item->cells.write[i], item_rect, p_draw_ofs, cell_color, icon_col, outline_size, font_outline_color); break; } @@ -2244,7 +2260,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 ir.position += theme_cache.custom_button->get_offset(); } - draw_item_rect(p_item->cells.write[i], ir, cell_color, icon_col, outline_size, font_outline_color); + draw_item_rect(p_item->cells.write[i], ir, p_draw_ofs, cell_color, icon_col, outline_size, font_outline_color); downarrow->draw(ci, arrow_pos); @@ -2975,7 +2991,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 +3002,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_submit(String p_text) { +void Tree::_text_editor_gui_input(const Ref &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::_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) { @@ -3830,18 +3887,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 +3904,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 +3931,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; @@ -4144,14 +4218,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; } @@ -5386,17 +5456,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 +5489,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)); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index fcfeb26f9574..75ce6b689d2f 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -36,8 +36,9 @@ #include "scene/gui/popup_menu.h" #include "scene/gui/scroll_bar.h" #include "scene/gui/slider.h" -#include "scene/resources/text_line.h" +#include "scene/resources/text_paragraph.h" +class TextEdit; class Tree; class TreeItem : public Object { @@ -61,8 +62,9 @@ private: Ref icon; Rect2i icon_region; String text; + bool edit_multiline = false; String suffix; - Ref text_buf; + Ref text_buf; String language; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; @@ -198,6 +200,10 @@ public: void set_cell_mode(int p_column, TreeCellMode p_mode); TreeCellMode get_cell_mode(int p_column) const; + /* multiline editable */ + void set_edit_multiline(int p_column, bool p_multiline); + bool is_edit_multiline(int p_column) const; + /* check mode */ void set_checked(int p_column, bool p_checked); void set_indeterminate(int p_column, bool p_indeterminate); @@ -436,7 +442,7 @@ private: bool clip_content = false; String title; HorizontalAlignment title_alignment = HORIZONTAL_ALIGNMENT_CENTER; - Ref text_buf; + Ref text_buf; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; ColumnInfo() { @@ -449,7 +455,8 @@ private: VBoxContainer *popup_editor_vb = nullptr; Popup *popup_editor = nullptr; - LineEdit *text_editor = nullptr; + LineEdit *line_editor = nullptr; + TextEdit *text_editor = nullptr; HSlider *value_editor = nullptr; bool updating_value_editor = false; uint64_t focus_in_id = 0; @@ -469,12 +476,14 @@ private: void update_item_cell(TreeItem *p_item, int p_col); void update_item_cache(TreeItem *p_item); //void draw_item_text(String p_text,const Ref& p_icon,int p_icon_max_w,bool p_tool,Rect2i p_rect,const Color& p_color); - void 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); + void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Point2 &p_draw_ofs, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color); int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item); void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false); int 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 &p_mod); - void _text_editor_submit(String p_text); - void _text_editor_modal_close(); + void _line_editor_submit(String p_text); + void _apply_multiline_edit(); + void _text_editor_popup_modal_close(); + void _text_editor_gui_input(const Ref &p_event); void value_editor_changed(double p_value); void popup_select(int p_option); @@ -578,8 +587,6 @@ private: TreeItem *hover_item = nullptr; int hover_cell = -1; - Point2i text_editor_position; - bool rtl = false; } cache;