Skip to content

Commit bd2ca13

Browse files
committed
Merge pull request godotengine#112733 from KoBeWi/press_right_to_context
Add a right click menu to the project manager
2 parents 2cd268f + f51f97a commit bd2ca13

File tree

4 files changed

+194
-40
lines changed

4 files changed

+194
-40
lines changed

editor/project_manager/project_list.cpp

Lines changed: 117 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,12 @@
4444
#include "scene/gui/dialogs.h"
4545
#include "scene/gui/label.h"
4646
#include "scene/gui/line_edit.h"
47+
#include "scene/gui/popup_menu.h"
4748
#include "scene/gui/progress_bar.h"
4849
#include "scene/gui/texture_button.h"
4950
#include "scene/gui/texture_rect.h"
5051
#include "scene/resources/image_texture.h"
5152

52-
const char *ProjectList::SIGNAL_LIST_CHANGED = "list_changed";
53-
const char *ProjectList::SIGNAL_SELECTION_CHANGED = "selection_changed";
54-
const char *ProjectList::SIGNAL_PROJECT_ASK_OPEN = "project_ask_open";
55-
5653
void ProjectListItemControl::_notification(int p_what) {
5754
switch (p_what) {
5855
case NOTIFICATION_THEME_CHANGED: {
@@ -86,6 +83,10 @@ void ProjectListItemControl::_notification(int p_what) {
8683
explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load")));
8784
#endif
8885
}
86+
87+
if (touch_menu_button) {
88+
touch_menu_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
89+
}
8990
} break;
9091

9192
case NOTIFICATION_MOUSE_ENTER: {
@@ -212,6 +213,10 @@ void ProjectListItemControl::_explore_button_pressed() {
212213
emit_signal(SNAME("explore_pressed"));
213214
}
214215

216+
void ProjectListItemControl::_request_menu() {
217+
emit_signal(SNAME("request_menu"), Vector2(touch_menu_button->get_position()));
218+
}
219+
215220
void ProjectListItemControl::set_project_title(const String &p_title) {
216221
project_title->set_text(p_title);
217222
project_title->set_accessibility_name(TTRC("Project Name"));
@@ -339,6 +344,7 @@ void ProjectListItemControl::set_is_grayed(bool p_grayed) {
339344
void ProjectListItemControl::_bind_methods() {
340345
ADD_SIGNAL(MethodInfo("favorite_pressed"));
341346
ADD_SIGNAL(MethodInfo("explore_pressed"));
347+
ADD_SIGNAL(MethodInfo("request_menu"));
342348
}
343349

344350
ProjectListItemControl::ProjectListItemControl() {
@@ -442,6 +448,14 @@ ProjectListItemControl::ProjectListItemControl() {
442448
spacer->set_custom_minimum_size(Size2(10, 10));
443449
path_hb->add_child(spacer);
444450
}
451+
452+
if (DisplayServer::get_singleton()->is_touchscreen_available()) {
453+
touch_menu_button = memnew(Button);
454+
touch_menu_button->set_theme_type_variation(SceneStringName(FlatButton));
455+
touch_menu_button->set_v_size_flags(SIZE_SHRINK_CENTER);
456+
add_child(touch_menu_button);
457+
touch_menu_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectListItemControl::_request_menu));
458+
}
445459
}
446460

447461
struct ProjectListComparator {
@@ -485,6 +499,12 @@ void ProjectList::_notification(int p_what) {
485499
}
486500
} break;
487501

502+
case NOTIFICATION_THEME_CHANGED: {
503+
if (project_context_menu) {
504+
_update_menu_icons();
505+
}
506+
} break;
507+
488508
case NOTIFICATION_PROCESS: {
489509
// Load icons as a coroutine to speed up launch when you have hundreds of projects.
490510
if (_icon_load_index < _projects.size()) {
@@ -1028,6 +1048,7 @@ void ProjectList::_create_project_item_control(int p_index) {
10281048
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
10291049
hb->connect("explore_pressed", callable_mp(this, &ProjectList::_on_explore_pressed).bind(item.path));
10301050
#endif
1051+
hb->connect("request_menu", callable_mp(this, &ProjectList::_open_menu).bind(hb));
10311052

10321053
project_list_vbox->add_child(hb);
10331054
item.control = hb;
@@ -1066,38 +1087,42 @@ void ProjectList::_remove_project(int p_index, bool p_update_config) {
10661087
update_dock_menu();
10671088
}
10681089

1069-
void ProjectList::_list_item_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
1090+
void ProjectList::_list_item_input(const Ref<InputEvent> &p_ev, Control *p_hb) {
10701091
Ref<InputEventMouseButton> mb = p_ev;
10711092
int clicked_index = p_hb->get_index();
10721093
const Item &clicked_project = _projects[clicked_index];
10731094

1074-
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
1075-
if (mb->is_shift_pressed() && _selected_project_paths.size() > 0 && !_last_clicked.is_empty() && clicked_project.path != _last_clicked) {
1076-
int anchor_index = -1;
1077-
for (int i = 0; i < _projects.size(); ++i) {
1078-
const Item &p = _projects[i];
1079-
if (p.path == _last_clicked) {
1080-
anchor_index = p.control->get_index();
1081-
break;
1095+
if (mb.is_valid() && mb->is_pressed()) {
1096+
if (mb->get_button_index() == MouseButton::LEFT) {
1097+
if (mb->is_shift_pressed() && _selected_project_paths.size() > 0 && !_last_clicked.is_empty() && clicked_project.path != _last_clicked) {
1098+
int anchor_index = -1;
1099+
for (int i = 0; i < _projects.size(); ++i) {
1100+
const Item &p = _projects[i];
1101+
if (p.path == _last_clicked) {
1102+
anchor_index = p.control->get_index();
1103+
break;
1104+
}
10821105
}
1083-
}
1084-
CRASH_COND(anchor_index == -1);
1085-
_select_project_range(anchor_index, clicked_index);
1106+
CRASH_COND(anchor_index == -1);
1107+
_select_project_range(anchor_index, clicked_index);
10861108

1087-
} else if (mb->is_command_or_control_pressed()) {
1088-
_toggle_project(clicked_index);
1109+
} else if (mb->is_command_or_control_pressed()) {
1110+
_toggle_project(clicked_index);
10891111

1090-
} else {
1091-
_last_clicked = clicked_project.path;
1092-
select_project(clicked_index);
1093-
}
1112+
} else {
1113+
_last_clicked = clicked_project.path;
1114+
select_project(clicked_index);
1115+
}
10941116

1095-
emit_signal(SNAME(SIGNAL_SELECTION_CHANGED));
1117+
emit_signal(SNAME(SIGNAL_SELECTION_CHANGED));
10961118

1097-
// Do not allow opening a project more than once using a single project manager instance.
1098-
// Opening the same project in several editor instances at once can lead to various issues.
1099-
if (!mb->is_command_or_control_pressed() && mb->is_double_click() && !project_opening_initiated) {
1100-
emit_signal(SNAME(SIGNAL_PROJECT_ASK_OPEN));
1119+
// Do not allow opening a project more than once using a single project manager instance.
1120+
// Opening the same project in several editor instances at once can lead to various issues.
1121+
if (!mb->is_command_or_control_pressed() && mb->is_double_click() && !project_opening_initiated) {
1122+
emit_signal(SNAME(SIGNAL_PROJECT_ASK_OPEN));
1123+
}
1124+
} else if (mb->get_button_index() == MouseButton::RIGHT) {
1125+
_open_menu(mb->get_position(), p_hb);
11011126
}
11021127
}
11031128

@@ -1154,6 +1179,70 @@ void ProjectList::_on_explore_pressed(const String &p_path) {
11541179
OS::get_singleton()->shell_show_in_file_manager(p_path, true);
11551180
}
11561181

1182+
void ProjectList::_open_menu(const Vector2 &p_at, Control *p_hb) {
1183+
int clicked_index = p_hb->get_index();
1184+
const Item &clicked_project = _projects[clicked_index];
1185+
1186+
if (!project_context_menu) {
1187+
project_context_menu = memnew(PopupMenu);
1188+
project_context_menu->add_item(TTRC("Open in Editor"), MENU_EDIT);
1189+
project_context_menu->add_item(TTRC("Open in Editor (Verbose Mode)"), MENU_EDIT_VERBOSE);
1190+
project_context_menu->add_item(TTRC("Open in Editor (Recovery Mode)"), MENU_EDIT_RECOVERY);
1191+
project_context_menu->add_item(TTRC("Run Project"), MENU_RUN);
1192+
project_context_menu->add_separator();
1193+
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
1194+
project_context_menu->add_item(TTRC("Show in File Manager"), MENU_SHOW_IN_FILE_MANAGER);
1195+
#endif
1196+
project_context_menu->add_item(TTRC("Copy Path"), MENU_COPY_PATH);
1197+
project_context_menu->add_separator();
1198+
project_context_menu->add_item(TTRC("Rename"), MENU_RENAME);
1199+
project_context_menu->add_item(TTRC("Manage Tags"), MENU_MANAGE_TAGS);
1200+
project_context_menu->add_item(TTRC("Duplicate"), MENU_DUPLICATE);
1201+
project_context_menu->add_item(TTRC("Remove from Project List"), MENU_REMOVE);
1202+
add_child(project_context_menu);
1203+
project_context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ProjectList::_menu_option));
1204+
_update_menu_icons();
1205+
}
1206+
select_project(clicked_index);
1207+
1208+
for (int id : Vector<int>{
1209+
MENU_EDIT,
1210+
MENU_EDIT_VERBOSE,
1211+
MENU_EDIT_RECOVERY,
1212+
MENU_RUN,
1213+
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
1214+
MENU_SHOW_IN_FILE_MANAGER,
1215+
#endif
1216+
MENU_RENAME,
1217+
MENU_MANAGE_TAGS,
1218+
MENU_DUPLICATE }) {
1219+
project_context_menu->set_item_disabled(project_context_menu->get_item_index(id), clicked_project.missing);
1220+
}
1221+
1222+
project_context_menu->set_position(p_hb->get_screen_position() + p_at);
1223+
project_context_menu->reset_size();
1224+
project_context_menu->popup();
1225+
}
1226+
1227+
void ProjectList::_menu_option(int p_option) {
1228+
emit_signal(SIGNAL_MENU_OPTION_SELECTED, p_option);
1229+
}
1230+
1231+
void ProjectList::_update_menu_icons() {
1232+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_EDIT), get_editor_theme_icon("Edit"));
1233+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_EDIT_VERBOSE), get_editor_theme_icon("Notification"));
1234+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_EDIT_RECOVERY), get_editor_theme_icon("NodeWarning"));
1235+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_RUN), get_editor_theme_icon("Play"));
1236+
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
1237+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_SHOW_IN_FILE_MANAGER), get_editor_theme_icon("Load"));
1238+
#endif
1239+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_COPY_PATH), get_editor_theme_icon("ActionCopy"));
1240+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_RENAME), get_editor_theme_icon("Rename"));
1241+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_MANAGE_TAGS), get_editor_theme_icon("Script"));
1242+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_DUPLICATE), get_editor_theme_icon("Duplicate"));
1243+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_REMOVE), get_editor_theme_icon("Remove"));
1244+
}
1245+
11571246
// Project list selection.
11581247

11591248
void ProjectList::_clear_project_selection() {
@@ -1411,6 +1500,7 @@ void ProjectList::_bind_methods() {
14111500
ADD_SIGNAL(MethodInfo(SIGNAL_LIST_CHANGED));
14121501
ADD_SIGNAL(MethodInfo(SIGNAL_SELECTION_CHANGED));
14131502
ADD_SIGNAL(MethodInfo(SIGNAL_PROJECT_ASK_OPEN));
1503+
ADD_SIGNAL(MethodInfo(SIGNAL_MENU_OPTION_SELECTED));
14141504
}
14151505

14161506
ProjectList::ProjectList() {

editor/project_manager/project_list.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
class AcceptDialog;
3939
class Button;
4040
class Label;
41+
class PopupMenu;
4142
class ProjectList;
4243
class TextureButton;
4344
class TextureRect;
@@ -56,6 +57,7 @@ class ProjectListItemControl : public HBoxContainer {
5657
Label *project_version = nullptr;
5758
TextureRect *project_unsupported_features = nullptr;
5859
HBoxContainer *tag_container = nullptr;
60+
Button *touch_menu_button = nullptr;
5961

6062
Color favorite_focus_color;
6163

@@ -68,6 +70,7 @@ class ProjectListItemControl : public HBoxContainer {
6870
void _update_favorite_button_focus_color();
6971
void _favorite_button_pressed();
7072
void _explore_button_pressed();
73+
void _request_menu();
7174

7275
ProjectList *get_list() const;
7376

@@ -113,6 +116,19 @@ class ProjectList : public ScrollContainer {
113116
TAGS,
114117
};
115118

119+
enum MenuOption {
120+
MENU_EDIT,
121+
MENU_EDIT_VERBOSE,
122+
MENU_EDIT_RECOVERY,
123+
MENU_RUN,
124+
MENU_SHOW_IN_FILE_MANAGER,
125+
MENU_COPY_PATH,
126+
MENU_RENAME,
127+
MENU_MANAGE_TAGS,
128+
MENU_DUPLICATE,
129+
MENU_REMOVE,
130+
};
131+
116132
// Can often be passed by copy.
117133
struct Item {
118134
String project_name;
@@ -200,6 +216,7 @@ class ProjectList : public ScrollContainer {
200216
String _last_clicked; // Project key
201217

202218
VBoxContainer *project_list_vbox = nullptr;
219+
PopupMenu *project_context_menu = nullptr;
203220

204221
// Projects scan.
205222

@@ -233,10 +250,14 @@ class ProjectList : public ScrollContainer {
233250
void _toggle_project(int p_index);
234251
void _remove_project(int p_index, bool p_update_settings);
235252

236-
void _list_item_input(const Ref<InputEvent> &p_ev, Node *p_hb);
253+
void _list_item_input(const Ref<InputEvent> &p_ev, Control *p_hb);
237254
void _on_favorite_pressed(Node *p_hb);
238255
void _on_explore_pressed(const String &p_path);
239256

257+
void _open_menu(const Vector2 &p_at, Control *p_hb);
258+
void _menu_option(int p_option);
259+
void _update_menu_icons();
260+
240261
// Project list selection.
241262

242263
void _clear_project_selection();
@@ -254,9 +275,10 @@ class ProjectList : public ScrollContainer {
254275
static void _bind_methods();
255276

256277
public:
257-
static const char *SIGNAL_LIST_CHANGED;
258-
static const char *SIGNAL_SELECTION_CHANGED;
259-
static const char *SIGNAL_PROJECT_ASK_OPEN;
278+
static inline const char *SIGNAL_LIST_CHANGED = "list_changed";
279+
static inline const char *SIGNAL_SELECTION_CHANGED = "selection_changed";
280+
static inline const char *SIGNAL_PROJECT_ASK_OPEN = "project_ask_open";
281+
static inline const char *SIGNAL_MENU_OPTION_SELECTED = "menu_option_selected";
260282

261283
static bool project_feature_looks_like_version(const String &p_feature);
262284

0 commit comments

Comments
 (0)