From 28038feb04b7b638e76a6c34bcbb02a706868b3e Mon Sep 17 00:00:00 2001 From: Wiwip Date: Fri, 26 May 2023 17:04:14 -0400 Subject: [PATCH] Permits the renaming of files and folders inline for items in the FileSystemDock. This is a continuation of: https://github.com/godotengine/godot/pull/76794 which didn't consider DISPLAY_MODE_SPLIT. Fixes: https://github.com/godotengine/godot/issues/77527 and https://github.com/godotengine/godot-proposals/issues/4933 --- editor/filesystem_dock.cpp | 133 ++++++++++++++++++++++++++++++++----- editor/filesystem_dock.h | 19 +++++- 2 files changed, 134 insertions(+), 18 deletions(-) diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 1c43c29c33c..cf8c91acc2a 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -76,6 +76,94 @@ Control *FileSystemList::make_custom_tooltip(const String &p_text) const { return FileSystemDock::get_singleton()->create_tooltip_for_path(get_item_metadata(idx)); } +void FileSystemList::_line_editor_submit(String p_text) { + popup_editor->hide(); + + emit_signal(SNAME("item_edited")); + queue_redraw(); +} + +bool FileSystemList::edit_selected() { + ERR_FAIL_COND_V_MSG(!is_anything_selected(), false, "No item selected."); + int s = get_current(); + ensure_current_is_visible(); + + Rect2 rect; + Rect2 popup_rect; + Vector2 ofs; + + Vector2 icon_size = get_item_icon(s)->get_size(); + + // Handles the different icon modes (TOP/LEFT). + switch (get_icon_mode()) { + case ItemList::ICON_MODE_LEFT: + rect = get_item_rect(s, true); + ofs = Vector2(0, Math::floor((MAX(line_editor->get_minimum_size().height, rect.size.height) - rect.size.height) / 2)); + popup_rect.position = get_screen_position() + rect.position - ofs; + popup_rect.size = rect.size; + + // Adjust for icon position and size. + popup_rect.size.x -= icon_size.x; + popup_rect.position.x += icon_size.x; + break; + case ItemList::ICON_MODE_TOP: + rect = get_item_rect(s, false); + popup_rect.position = get_screen_position() + rect.position; + popup_rect.size = rect.size; + + // Adjust for icon position and size. + popup_rect.size.y -= icon_size.y; + popup_rect.position.y += icon_size.y; + break; + } + + popup_editor->set_position(popup_rect.position); + popup_editor->set_size(popup_rect.size); + + String name = get_item_text(s); + line_editor->set_text(name); + line_editor->select(0, name.rfind(".")); + + popup_editor->popup(); + popup_editor->child_controls_changed(); + line_editor->grab_focus(); + return true; +} + +String FileSystemList::get_edit_text() { + return line_editor->get_text(); +} + +void FileSystemList::_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; + } + + _line_editor_submit(line_editor->get_text()); +} + +void FileSystemList::_bind_methods() { + ADD_SIGNAL(MethodInfo("item_edited")); +} + +FileSystemList::FileSystemList() { + popup_editor = memnew(Popup); + add_child(popup_editor); + + popup_editor_vb = memnew(VBoxContainer); + popup_editor_vb->add_theme_constant_override("separation", 0); + popup_editor_vb->set_anchors_and_offsets_preset(PRESET_FULL_RECT); + popup_editor->add_child(popup_editor_vb); + + line_editor = memnew(LineEdit); + line_editor->set_v_size_flags(SIZE_EXPAND_FILL); + popup_editor_vb->add_child(line_editor); + line_editor->connect("text_submitted", callable_mp(this, &FileSystemList::_line_editor_submit)); + popup_editor->connect("popup_hide", callable_mp(this, &FileSystemList::_text_editor_popup_modal_close)); +} + FileSystemDock *FileSystemDock::singleton = nullptr; Ref FileSystemDock::_get_tree_item_icon(bool p_is_valid, String p_file_type) { @@ -1581,13 +1669,15 @@ void FileSystemDock::_folder_removed(String p_folder) { } void FileSystemDock::_rename_operation_confirm() { - if (!tree->is_anything_selected()) { - return; - } + String new_name; TreeItem *s = tree->get_selected(); int col_index = tree->get_selected_column(); - String new_name = s->get_text(col_index); - new_name = new_name.strip_edges(); + + if (tree->has_focus()) { + new_name = s->get_text(col_index).strip_edges(); + } else if (files->has_focus()) { + new_name = files->get_edit_text().strip_edges(); + } String old_name = to_rename.is_file ? to_rename.path.get_file() : to_rename.path.left(-1).get_file(); bool rename_error = false; @@ -1607,10 +1697,12 @@ void FileSystemDock::_rename_operation_confirm() { } } - // Restores Tree to restore original names. - if (rename_error) { + // Restore original name. + if (rename_error && tree->has_focus()) { s->set_text(col_index, old_name); return; + } else if (rename_error && files->has_focus()) { + return; } String old_path = to_rename.path.ends_with("/") ? to_rename.path.left(-1) : to_rename.path; @@ -2032,21 +2124,27 @@ void FileSystemDock::_file_option(int p_option, const Vector &p_selected } break; case FILE_RENAME: { - if (tree->is_anything_selected() && !p_selected.is_empty()) { + if (!p_selected.is_empty()) { // Set to_rename variable for callback execution. to_rename.path = p_selected[0]; to_rename.is_file = !to_rename.path.ends_with("/"); + if (to_rename.path == "res://") { + break; + } - // Edit node in Tree. - tree->grab_focus(); - tree->edit_selected(true); + if (tree->has_focus()) { + // Edit node in Tree. + tree->edit_selected(true); - if (to_rename.is_file) { - String name = to_rename.path.get_file(); - tree->set_editor_selection(0, name.rfind(".")); - } else { - String name = to_rename.path.left(-1).get_file(); // Removes the "/" suffix for folders. - tree->set_editor_selection(0, name.length()); + if (to_rename.is_file) { + String name = to_rename.path.get_file(); + tree->set_editor_selection(0, name.rfind(".")); + } else { + String name = to_rename.path.left(-1).get_file(); // Removes the "/" suffix for folders. + tree->set_editor_selection(0, name.length()); + } + } else if (files->has_focus()) { + files->edit_selected(); } } } break; @@ -3337,6 +3435,7 @@ FileSystemDock::FileSystemDock() { files->connect("gui_input", callable_mp(this, &FileSystemDock::_file_list_gui_input)); files->connect("multi_selected", callable_mp(this, &FileSystemDock::_file_multi_selected)); files->connect("empty_clicked", callable_mp(this, &FileSystemDock::_file_list_empty_clicked)); + files->connect("item_edited", callable_mp(this, &FileSystemDock::_rename_operation_confirm)); files->set_custom_minimum_size(Size2(0, 15 * EDSCALE)); files->set_allow_rmb_select(true); file_list_vb->add_child(files); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 480e0db84d0..0af3193aa5a 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -57,7 +57,24 @@ class FileSystemTree : public Tree { }; class FileSystemList : public ItemList { - virtual Control *make_custom_tooltip(const String &p_text) const; + GDCLASS(FileSystemList, ItemList); + + VBoxContainer *popup_editor_vb = nullptr; + Popup *popup_editor = nullptr; + LineEdit *line_editor = nullptr; + + virtual Control *make_custom_tooltip(const String &p_text) const override; + void _line_editor_submit(String p_text); + void _text_editor_popup_modal_close(); + +protected: + static void _bind_methods(); + +public: + bool edit_selected(); + String get_edit_text(); + + FileSystemList(); }; class FileSystemDock : public VBoxContainer {