Skip to content

Commit b83343f

Browse files
committed
Merge pull request godotengine#104676 from jinyangcruise/find_in_files_keep_results
Support keeping results in results of `Find in Files` and `Replace in Files`
2 parents a9b8f92 + fdd0f32 commit b83343f

File tree

4 files changed

+283
-13
lines changed

4 files changed

+283
-13
lines changed

editor/script/find_in_files.cpp

Lines changed: 224 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@
3939
#include "scene/gui/box_container.h"
4040
#include "scene/gui/button.h"
4141
#include "scene/gui/check_box.h"
42+
#include "scene/gui/check_button.h"
4243
#include "scene/gui/file_dialog.h"
4344
#include "scene/gui/grid_container.h"
4445
#include "scene/gui/label.h"
4546
#include "scene/gui/line_edit.h"
4647
#include "scene/gui/progress_bar.h"
48+
#include "scene/gui/tab_container.h"
4749
#include "scene/gui/tree.h"
4850

4951
const char *FindInFiles::SIGNAL_RESULT_FOUND = "result_found";
@@ -700,10 +702,11 @@ FindInFilesPanel::FindInFilesPanel() {
700702

701703
{
702704
HBoxContainer *hbc = memnew(HBoxContainer);
705+
hbc->set_alignment(BoxContainer::ALIGNMENT_END);
703706

704-
Label *find_label = memnew(Label);
705-
find_label->set_text(TTRC("Find:"));
706-
hbc->add_child(find_label);
707+
_find_label = memnew(Label);
708+
_find_label->set_text(TTRC("Find:"));
709+
hbc->add_child(_find_label);
707710

708711
_search_text_label = memnew(Label);
709712
_search_text_label->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
@@ -724,6 +727,12 @@ FindInFilesPanel::FindInFilesPanel() {
724727
_status_label->set_focus_mode(FOCUS_ACCESSIBILITY);
725728
hbc->add_child(_status_label);
726729

730+
_keep_results_button = memnew(CheckButton);
731+
_keep_results_button->set_text(TTRC("Keep Results"));
732+
_keep_results_button->set_tooltip_text(TTRC("Keep these results and show subsequent results in a new window"));
733+
_keep_results_button->set_pressed(false);
734+
hbc->add_child(_keep_results_button);
735+
727736
_refresh_button = memnew(Button);
728737
_refresh_button->set_text(TTRC("Refresh"));
729738
_refresh_button->connect(SceneStringName(pressed), callable_mp(this, &FindInFilesPanel::_on_refresh_button_clicked));
@@ -804,6 +813,16 @@ void FindInFilesPanel::set_replace_text(const String &text) {
804813
_replace_line_edit->set_text(text);
805814
}
806815

816+
bool FindInFilesPanel::is_keep_results() const {
817+
return _keep_results_button->is_pressed();
818+
}
819+
820+
void FindInFilesPanel::set_search_labels_visibility(bool p_visible) {
821+
_find_label->set_visible(p_visible);
822+
_search_text_label->set_visible(p_visible);
823+
_close_button->set_visible(p_visible);
824+
}
825+
807826
void FindInFilesPanel::clear() {
808827
_file_items.clear();
809828
_file_items_results_count.clear();
@@ -1244,3 +1263,205 @@ void FindInFilesPanel::_bind_methods() {
12441263

12451264
ADD_SIGNAL(MethodInfo(SIGNAL_CLOSE_BUTTON_CLICKED));
12461265
}
1266+
1267+
//-----------------------------------------------------------------------------
1268+
1269+
FindInFilesContainer::FindInFilesContainer() {
1270+
const Ref<StyleBox> bottom_panel_style = EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles));
1271+
if (bottom_panel_style.is_valid()) {
1272+
add_theme_constant_override("margin_top", -bottom_panel_style->get_margin(SIDE_TOP));
1273+
add_theme_constant_override("margin_left", -bottom_panel_style->get_margin(SIDE_LEFT));
1274+
add_theme_constant_override("margin_right", -bottom_panel_style->get_margin(SIDE_RIGHT));
1275+
add_theme_constant_override("margin_bottom", -bottom_panel_style->get_margin(SIDE_BOTTOM));
1276+
}
1277+
1278+
_tabs = memnew(TabContainer);
1279+
_tabs->set_tabs_visible(false);
1280+
add_child(_tabs);
1281+
1282+
_tabs->set_drag_to_rearrange_enabled(true);
1283+
_tabs->get_tab_bar()->set_select_with_rmb(true);
1284+
_tabs->get_tab_bar()->set_tab_close_display_policy(TabBar::CLOSE_BUTTON_SHOW_ACTIVE_ONLY);
1285+
_tabs->get_tab_bar()->connect("tab_close_pressed", callable_mp(this, &FindInFilesContainer::_on_tab_close_pressed));
1286+
_tabs->get_tab_bar()->connect(SceneStringName(gui_input), callable_mp(this, &FindInFilesContainer::_bar_input));
1287+
1288+
_tabs_context_menu = memnew(PopupMenu);
1289+
add_child(_tabs_context_menu);
1290+
_tabs_context_menu->add_item(TTRC("Close Tab"), PANEL_CLOSE);
1291+
_tabs_context_menu->add_item(TTRC("Close Other Tabs"), PANEL_CLOSE_OTHERS);
1292+
_tabs_context_menu->add_item(TTRC("Close Tabs to the Right"), PANEL_CLOSE_RIGHT);
1293+
_tabs_context_menu->add_item(TTRC("Close All Tabs"), PANEL_CLOSE_ALL);
1294+
_tabs_context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &FindInFilesContainer::_bar_menu_option));
1295+
}
1296+
1297+
FindInFilesPanel *FindInFilesContainer::_create_new_panel() {
1298+
int index = _tabs->get_current_tab();
1299+
FindInFilesPanel *panel = memnew(FindInFilesPanel);
1300+
_tabs->add_child(panel);
1301+
_tabs->move_child(panel, index + 1); // New panel is added after the current activated panel.
1302+
_tabs->set_current_tab(index + 1);
1303+
_update_bar_visibility();
1304+
1305+
panel->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, callable_mp(this, &FindInFilesContainer::_on_find_in_files_result_selected));
1306+
panel->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, callable_mp(this, &FindInFilesContainer::_on_find_in_files_modified_files));
1307+
panel->connect(FindInFilesPanel::SIGNAL_CLOSE_BUTTON_CLICKED, callable_mp(this, &FindInFilesContainer::_on_find_in_files_close_button_clicked).bind(panel));
1308+
return panel;
1309+
}
1310+
1311+
FindInFilesPanel *FindInFilesContainer::_get_current_panel() {
1312+
return Object::cast_to<FindInFilesPanel>(_tabs->get_current_tab_control());
1313+
}
1314+
1315+
FindInFilesPanel *FindInFilesContainer::get_panel_for_results(const String &p_label) {
1316+
FindInFilesPanel *panel = nullptr;
1317+
// Prefer the current panel.
1318+
if (_get_current_panel() && !_get_current_panel()->is_keep_results()) {
1319+
panel = _get_current_panel();
1320+
} else {
1321+
// Find the first panel which does not keep results.
1322+
for (int i = 0; i < _tabs->get_tab_count(); i++) {
1323+
FindInFilesPanel *p = Object::cast_to<FindInFilesPanel>(_tabs->get_tab_control(i));
1324+
if (p && !p->is_keep_results()) {
1325+
panel = p;
1326+
_tabs->set_current_tab(i);
1327+
break;
1328+
}
1329+
}
1330+
1331+
if (!panel) {
1332+
panel = _create_new_panel();
1333+
}
1334+
}
1335+
_tabs->set_tab_title(_tabs->get_current_tab(), p_label);
1336+
return panel;
1337+
}
1338+
1339+
void FindInFilesContainer::_bind_methods() {
1340+
ADD_SIGNAL(MethodInfo("result_selected",
1341+
PropertyInfo(Variant::STRING, "path"),
1342+
PropertyInfo(Variant::INT, "line_number"),
1343+
PropertyInfo(Variant::INT, "begin"),
1344+
PropertyInfo(Variant::INT, "end")));
1345+
1346+
ADD_SIGNAL(MethodInfo("files_modified", PropertyInfo(Variant::STRING, "paths")));
1347+
1348+
ADD_SIGNAL(MethodInfo("close_button_clicked"));
1349+
}
1350+
1351+
void FindInFilesContainer::_notification(int p_what) {
1352+
switch (p_what) {
1353+
case NOTIFICATION_THEME_CHANGED: {
1354+
const Ref<StyleBox> bottom_panel_style = EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles));
1355+
if (bottom_panel_style.is_valid()) {
1356+
const int margin_top = -bottom_panel_style->get_margin(SIDE_TOP);
1357+
const int margin_left = -bottom_panel_style->get_margin(SIDE_LEFT);
1358+
const int margin_right = -bottom_panel_style->get_margin(SIDE_RIGHT);
1359+
const int margin_bottom = -bottom_panel_style->get_margin(SIDE_BOTTOM);
1360+
1361+
if (get_theme_constant("margin_top") != margin_top) {
1362+
add_theme_constant_override("margin_top", margin_top);
1363+
}
1364+
if (get_theme_constant("margin_left") != margin_left) {
1365+
add_theme_constant_override("margin_left", margin_left);
1366+
}
1367+
if (get_theme_constant("margin_right") != margin_right) {
1368+
add_theme_constant_override("margin_right", margin_right);
1369+
}
1370+
if (get_theme_constant("margin_bottom") != margin_bottom) {
1371+
add_theme_constant_override("margin_bottom", margin_bottom);
1372+
}
1373+
}
1374+
} break;
1375+
}
1376+
}
1377+
1378+
void FindInFilesContainer::_on_find_in_files_result_selected(const String &p_fpath, int p_line_number, int p_begin, int p_end) {
1379+
emit_signal(SNAME("result_selected"), p_fpath, p_line_number, p_begin, p_end);
1380+
}
1381+
1382+
void FindInFilesContainer::_on_find_in_files_modified_files(const PackedStringArray &p_paths) {
1383+
emit_signal(SNAME("files_modified"), p_paths);
1384+
}
1385+
1386+
void FindInFilesContainer::_on_find_in_files_close_button_clicked(FindInFilesPanel *p_panel) {
1387+
ERR_FAIL_COND_MSG(p_panel->get_parent() != _tabs, "This panel is not a child!");
1388+
_tabs->remove_child(p_panel);
1389+
p_panel->queue_free();
1390+
_update_bar_visibility();
1391+
if (_tabs->get_tab_count() == 0) {
1392+
emit_signal(SNAME("close_button_clicked"));
1393+
}
1394+
}
1395+
1396+
void FindInFilesContainer::_on_tab_close_pressed(int p_tab) {
1397+
FindInFilesPanel *panel = Object::cast_to<FindInFilesPanel>(_tabs->get_tab_control(p_tab));
1398+
if (panel) {
1399+
_on_find_in_files_close_button_clicked(panel);
1400+
}
1401+
}
1402+
1403+
void FindInFilesContainer::_update_bar_visibility() {
1404+
if (!_update_bar) {
1405+
return;
1406+
}
1407+
1408+
// If tab count <= 1, behaves like this is not a TabContainer and the bar is hidden.
1409+
bool bar_visible = _tabs->get_tab_count() > 1;
1410+
_tabs->set_tabs_visible(bar_visible);
1411+
1412+
// Hide or show the search labels based on the visibility of the bar, as the search terms are displayed in the title of each tab.
1413+
for (int i = 0; i < _tabs->get_tab_count(); i++) {
1414+
FindInFilesPanel *panel = Object::cast_to<FindInFilesPanel>(_tabs->get_tab_control(i));
1415+
if (panel) {
1416+
panel->set_search_labels_visibility(!bar_visible);
1417+
}
1418+
}
1419+
}
1420+
1421+
void FindInFilesContainer::_bar_menu_option(int p_option) {
1422+
int tab_index = _tabs->get_current_tab();
1423+
switch (p_option) {
1424+
case PANEL_CLOSE: {
1425+
_on_tab_close_pressed(tab_index);
1426+
} break;
1427+
case PANEL_CLOSE_OTHERS: {
1428+
_update_bar = false;
1429+
FindInFilesPanel *panel = Object::cast_to<FindInFilesPanel>(_tabs->get_tab_control(tab_index));
1430+
for (int i = _tabs->get_tab_count() - 1; i >= 0; i--) {
1431+
FindInFilesPanel *p = Object::cast_to<FindInFilesPanel>(_tabs->get_tab_control(i));
1432+
if (p != panel) {
1433+
_on_find_in_files_close_button_clicked(p);
1434+
}
1435+
}
1436+
_update_bar = true;
1437+
_update_bar_visibility();
1438+
} break;
1439+
case PANEL_CLOSE_RIGHT: {
1440+
_update_bar = false;
1441+
for (int i = _tabs->get_tab_count() - 1; i > tab_index; i--) {
1442+
_on_tab_close_pressed(i);
1443+
}
1444+
_update_bar = true;
1445+
_update_bar_visibility();
1446+
} break;
1447+
case PANEL_CLOSE_ALL: {
1448+
_update_bar = false;
1449+
for (int i = _tabs->get_tab_count() - 1; i >= 0; i--) {
1450+
_on_tab_close_pressed(i);
1451+
}
1452+
_update_bar = true;
1453+
} break;
1454+
}
1455+
}
1456+
1457+
void FindInFilesContainer::_bar_input(const Ref<InputEvent> &p_input) {
1458+
int tab_id = _tabs->get_tab_bar()->get_hovered_tab();
1459+
Ref<InputEventMouseButton> mb = p_input;
1460+
1461+
if (tab_id >= 0 && mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
1462+
_tabs_context_menu->set_item_disabled(_tabs_context_menu->get_item_index(PANEL_CLOSE_RIGHT), tab_id == _tabs->get_tab_count() - 1);
1463+
_tabs_context_menu->set_position(_tabs->get_tab_bar()->get_screen_position() + mb->get_position());
1464+
_tabs_context_menu->reset_size();
1465+
_tabs_context_menu->popup();
1466+
}
1467+
}

editor/script/find_in_files.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class FindInFilesDialog : public AcceptDialog {
161161
};
162162

163163
class Button;
164+
class CheckButton;
164165
class Tree;
165166
class TreeItem;
166167
class ProgressBar;
@@ -180,6 +181,8 @@ class FindInFilesPanel : public MarginContainer {
180181

181182
void set_with_replace(bool with_replace);
182183
void set_replace_text(const String &text);
184+
bool is_keep_results() const;
185+
void set_search_labels_visibility(bool p_visible);
183186

184187
void start_search();
185188
void stop_search();
@@ -223,9 +226,11 @@ class FindInFilesPanel : public MarginContainer {
223226
void clear();
224227

225228
FindInFiles *_finder = nullptr;
229+
Label *_find_label = nullptr;
226230
Label *_search_text_label = nullptr;
227231
Tree *_results_display = nullptr;
228232
Label *_status_label = nullptr;
233+
CheckButton *_keep_results_button = nullptr;
229234
Button *_refresh_button = nullptr;
230235
Button *_cancel_button = nullptr;
231236
Button *_close_button = nullptr;
@@ -239,3 +244,46 @@ class FindInFilesPanel : public MarginContainer {
239244
LineEdit *_replace_line_edit = nullptr;
240245
Button *_replace_all_button = nullptr;
241246
};
247+
248+
class PopupMenu;
249+
class TabContainer;
250+
251+
// Contains several FindInFilesPanels. A FindInFilesPanel contains the results of a
252+
// `Find in Files` search or a `Replace in Files` search, while a
253+
// FindInFilesContainer can contain several FindInFilesPanels so that multiple search
254+
// results can remain at the same time.
255+
class FindInFilesContainer : public MarginContainer {
256+
GDCLASS(FindInFilesContainer, MarginContainer);
257+
258+
enum {
259+
PANEL_CLOSE,
260+
PANEL_CLOSE_OTHERS,
261+
PANEL_CLOSE_RIGHT,
262+
PANEL_CLOSE_ALL,
263+
};
264+
265+
void _on_tab_close_pressed(int p_tab);
266+
void _update_bar_visibility();
267+
void _bar_menu_option(int p_option);
268+
void _bar_input(const Ref<InputEvent> &p_input);
269+
270+
TabContainer *_tabs = nullptr;
271+
bool _update_bar = true;
272+
PopupMenu *_tabs_context_menu = nullptr;
273+
274+
FindInFilesPanel *_create_new_panel();
275+
FindInFilesPanel *_get_current_panel();
276+
277+
protected:
278+
static void _bind_methods();
279+
void _notification(int p_what);
280+
281+
void _on_find_in_files_result_selected(const String &p_fpath, int p_line_number, int p_begin, int p_end);
282+
void _on_find_in_files_modified_files(const PackedStringArray &p_paths);
283+
void _on_find_in_files_close_button_clicked(FindInFilesPanel *p_panel);
284+
285+
public:
286+
FindInFilesContainer();
287+
288+
FindInFilesPanel *get_panel_for_results(const String &p_label);
289+
};

editor/script/script_editor_plugin.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4079,7 +4079,8 @@ void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int li
40794079
}
40804080

40814081
void ScriptEditor::_start_find_in_files(bool with_replace) {
4082-
FindInFiles *f = find_in_files->get_finder();
4082+
FindInFilesPanel *panel = find_in_files->get_panel_for_results(with_replace ? TTR("Replace:") + " " + find_in_files_dialog->get_search_text() : TTR("Find:") + " " + find_in_files_dialog->get_search_text());
4083+
FindInFiles *f = panel->get_finder();
40834084

40844085
f->set_search_text(find_in_files_dialog->get_search_text());
40854086
f->set_match_case(find_in_files_dialog->is_match_case());
@@ -4089,9 +4090,9 @@ void ScriptEditor::_start_find_in_files(bool with_replace) {
40894090
f->set_includes(find_in_files_dialog->get_includes());
40904091
f->set_excludes(find_in_files_dialog->get_excludes());
40914092

4092-
find_in_files->set_with_replace(with_replace);
4093-
find_in_files->set_replace_text(find_in_files_dialog->get_replace_text());
4094-
find_in_files->start_search();
4093+
panel->set_with_replace(with_replace);
4094+
panel->set_replace_text(find_in_files_dialog->get_replace_text());
4095+
panel->start_search();
40954096

40964097
EditorNode::get_bottom_panel()->move_item_to_end(find_in_files);
40974098
find_in_files_button->show();
@@ -4490,12 +4491,12 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
44904491
find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, callable_mp(this, &ScriptEditor::_start_find_in_files).bind(false));
44914492
find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, callable_mp(this, &ScriptEditor::_start_find_in_files).bind(true));
44924493
add_child(find_in_files_dialog);
4493-
find_in_files = memnew(FindInFilesPanel);
4494+
find_in_files = memnew(FindInFilesContainer);
44944495
find_in_files_button = EditorNode::get_bottom_panel()->add_item(TTRC("Search Results"), find_in_files, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_search_results_bottom_panel", TTRC("Toggle Search Results Bottom Panel")));
44954496
find_in_files->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
4496-
find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, callable_mp(this, &ScriptEditor::_on_find_in_files_result_selected));
4497-
find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, callable_mp(this, &ScriptEditor::_on_find_in_files_modified_files));
4498-
find_in_files->connect(FindInFilesPanel::SIGNAL_CLOSE_BUTTON_CLICKED, callable_mp(this, &ScriptEditor::_on_find_in_files_close_button_clicked));
4497+
find_in_files->connect("result_selected", callable_mp(this, &ScriptEditor::_on_find_in_files_result_selected));
4498+
find_in_files->connect("files_modified", callable_mp(this, &ScriptEditor::_on_find_in_files_modified_files));
4499+
find_in_files->connect("close_button_clicked", callable_mp(this, &ScriptEditor::_on_find_in_files_close_button_clicked));
44994500
find_in_files->hide();
45004501
find_in_files_button->hide();
45014502

editor/script/script_editor_plugin.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ class ScriptEditorBase : public VBoxContainer {
249249
typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Resource> &p_resource);
250250

251251
class EditorScriptCodeCompletionCache;
252+
class FindInFilesContainer;
252253
class FindInFilesDialog;
253-
class FindInFilesPanel;
254254

255255
class ScriptEditor : public PanelContainer {
256256
GDCLASS(ScriptEditor, PanelContainer);
@@ -367,7 +367,7 @@ class ScriptEditor : public PanelContainer {
367367
Button *script_forward = nullptr;
368368

369369
FindInFilesDialog *find_in_files_dialog = nullptr;
370-
FindInFilesPanel *find_in_files = nullptr;
370+
FindInFilesContainer *find_in_files = nullptr;
371371
Button *find_in_files_button = nullptr;
372372

373373
WindowWrapper *window_wrapper = nullptr;

0 commit comments

Comments
 (0)