Skip to content

Commit f51f97a

Browse files
committed
Add a right click menu to the project manager
1 parent 0b5ad6c commit f51f97a

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: {
@@ -80,6 +77,10 @@ void ProjectListItemControl::_notification(int p_what) {
8077
explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load")));
8178
#endif
8279
}
80+
81+
if (touch_menu_button) {
82+
touch_menu_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
83+
}
8384
} break;
8485

8586
case NOTIFICATION_MOUSE_ENTER: {
@@ -196,6 +197,10 @@ void ProjectListItemControl::_explore_button_pressed() {
196197
emit_signal(SNAME("explore_pressed"));
197198
}
198199

200+
void ProjectListItemControl::_request_menu() {
201+
emit_signal(SNAME("request_menu"), Vector2(touch_menu_button->get_position()));
202+
}
203+
199204
void ProjectListItemControl::set_project_title(const String &p_title) {
200205
project_title->set_text(p_title);
201206
project_title->set_accessibility_name(TTRC("Project Name"));
@@ -318,6 +323,7 @@ void ProjectListItemControl::set_is_grayed(bool p_grayed) {
318323
void ProjectListItemControl::_bind_methods() {
319324
ADD_SIGNAL(MethodInfo("favorite_pressed"));
320325
ADD_SIGNAL(MethodInfo("explore_pressed"));
326+
ADD_SIGNAL(MethodInfo("request_menu"));
321327
}
322328

323329
ProjectListItemControl::ProjectListItemControl() {
@@ -419,6 +425,14 @@ ProjectListItemControl::ProjectListItemControl() {
419425
spacer->set_custom_minimum_size(Size2(10, 10));
420426
path_hb->add_child(spacer);
421427
}
428+
429+
if (DisplayServer::get_singleton()->is_touchscreen_available()) {
430+
touch_menu_button = memnew(Button);
431+
touch_menu_button->set_theme_type_variation(SceneStringName(FlatButton));
432+
touch_menu_button->set_v_size_flags(SIZE_SHRINK_CENTER);
433+
add_child(touch_menu_button);
434+
touch_menu_button->connect(SceneStringName(pressed), callable_mp(this, &ProjectListItemControl::_request_menu));
435+
}
422436
}
423437

424438
struct ProjectListComparator {
@@ -462,6 +476,12 @@ void ProjectList::_notification(int p_what) {
462476
}
463477
} break;
464478

479+
case NOTIFICATION_THEME_CHANGED: {
480+
if (project_context_menu) {
481+
_update_menu_icons();
482+
}
483+
} break;
484+
465485
case NOTIFICATION_PROCESS: {
466486
// Load icons as a coroutine to speed up launch when you have hundreds of projects.
467487
if (_icon_load_index < _projects.size()) {
@@ -1005,6 +1025,7 @@ void ProjectList::_create_project_item_control(int p_index) {
10051025
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
10061026
hb->connect("explore_pressed", callable_mp(this, &ProjectList::_on_explore_pressed).bind(item.path));
10071027
#endif
1028+
hb->connect("request_menu", callable_mp(this, &ProjectList::_open_menu).bind(hb));
10081029

10091030
project_list_vbox->add_child(hb);
10101031
item.control = hb;
@@ -1043,38 +1064,42 @@ void ProjectList::_remove_project(int p_index, bool p_update_config) {
10431064
update_dock_menu();
10441065
}
10451066

1046-
void ProjectList::_list_item_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
1067+
void ProjectList::_list_item_input(const Ref<InputEvent> &p_ev, Control *p_hb) {
10471068
Ref<InputEventMouseButton> mb = p_ev;
10481069
int clicked_index = p_hb->get_index();
10491070
const Item &clicked_project = _projects[clicked_index];
10501071

1051-
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
1052-
if (mb->is_shift_pressed() && _selected_project_paths.size() > 0 && !_last_clicked.is_empty() && clicked_project.path != _last_clicked) {
1053-
int anchor_index = -1;
1054-
for (int i = 0; i < _projects.size(); ++i) {
1055-
const Item &p = _projects[i];
1056-
if (p.path == _last_clicked) {
1057-
anchor_index = p.control->get_index();
1058-
break;
1072+
if (mb.is_valid() && mb->is_pressed()) {
1073+
if (mb->get_button_index() == MouseButton::LEFT) {
1074+
if (mb->is_shift_pressed() && _selected_project_paths.size() > 0 && !_last_clicked.is_empty() && clicked_project.path != _last_clicked) {
1075+
int anchor_index = -1;
1076+
for (int i = 0; i < _projects.size(); ++i) {
1077+
const Item &p = _projects[i];
1078+
if (p.path == _last_clicked) {
1079+
anchor_index = p.control->get_index();
1080+
break;
1081+
}
10591082
}
1060-
}
1061-
CRASH_COND(anchor_index == -1);
1062-
_select_project_range(anchor_index, clicked_index);
1083+
CRASH_COND(anchor_index == -1);
1084+
_select_project_range(anchor_index, clicked_index);
10631085

1064-
} else if (mb->is_command_or_control_pressed()) {
1065-
_toggle_project(clicked_index);
1086+
} else if (mb->is_command_or_control_pressed()) {
1087+
_toggle_project(clicked_index);
10661088

1067-
} else {
1068-
_last_clicked = clicked_project.path;
1069-
select_project(clicked_index);
1070-
}
1089+
} else {
1090+
_last_clicked = clicked_project.path;
1091+
select_project(clicked_index);
1092+
}
10711093

1072-
emit_signal(SNAME(SIGNAL_SELECTION_CHANGED));
1094+
emit_signal(SNAME(SIGNAL_SELECTION_CHANGED));
10731095

1074-
// Do not allow opening a project more than once using a single project manager instance.
1075-
// Opening the same project in several editor instances at once can lead to various issues.
1076-
if (!mb->is_command_or_control_pressed() && mb->is_double_click() && !project_opening_initiated) {
1077-
emit_signal(SNAME(SIGNAL_PROJECT_ASK_OPEN));
1096+
// Do not allow opening a project more than once using a single project manager instance.
1097+
// Opening the same project in several editor instances at once can lead to various issues.
1098+
if (!mb->is_command_or_control_pressed() && mb->is_double_click() && !project_opening_initiated) {
1099+
emit_signal(SNAME(SIGNAL_PROJECT_ASK_OPEN));
1100+
}
1101+
} else if (mb->get_button_index() == MouseButton::RIGHT) {
1102+
_open_menu(mb->get_position(), p_hb);
10781103
}
10791104
}
10801105
}
@@ -1112,6 +1137,70 @@ void ProjectList::_on_explore_pressed(const String &p_path) {
11121137
OS::get_singleton()->shell_show_in_file_manager(p_path, true);
11131138
}
11141139

1140+
void ProjectList::_open_menu(const Vector2 &p_at, Control *p_hb) {
1141+
int clicked_index = p_hb->get_index();
1142+
const Item &clicked_project = _projects[clicked_index];
1143+
1144+
if (!project_context_menu) {
1145+
project_context_menu = memnew(PopupMenu);
1146+
project_context_menu->add_item(TTRC("Open in Editor"), MENU_EDIT);
1147+
project_context_menu->add_item(TTRC("Open in Editor (Verbose Mode)"), MENU_EDIT_VERBOSE);
1148+
project_context_menu->add_item(TTRC("Open in Editor (Recovery Mode)"), MENU_EDIT_RECOVERY);
1149+
project_context_menu->add_item(TTRC("Run Project"), MENU_RUN);
1150+
project_context_menu->add_separator();
1151+
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
1152+
project_context_menu->add_item(TTRC("Show in File Manager"), MENU_SHOW_IN_FILE_MANAGER);
1153+
#endif
1154+
project_context_menu->add_item(TTRC("Copy Path"), MENU_COPY_PATH);
1155+
project_context_menu->add_separator();
1156+
project_context_menu->add_item(TTRC("Rename"), MENU_RENAME);
1157+
project_context_menu->add_item(TTRC("Manage Tags"), MENU_MANAGE_TAGS);
1158+
project_context_menu->add_item(TTRC("Duplicate"), MENU_DUPLICATE);
1159+
project_context_menu->add_item(TTRC("Remove from Project List"), MENU_REMOVE);
1160+
add_child(project_context_menu);
1161+
project_context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &ProjectList::_menu_option));
1162+
_update_menu_icons();
1163+
}
1164+
select_project(clicked_index);
1165+
1166+
for (int id : Vector<int>{
1167+
MENU_EDIT,
1168+
MENU_EDIT_VERBOSE,
1169+
MENU_EDIT_RECOVERY,
1170+
MENU_RUN,
1171+
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
1172+
MENU_SHOW_IN_FILE_MANAGER,
1173+
#endif
1174+
MENU_RENAME,
1175+
MENU_MANAGE_TAGS,
1176+
MENU_DUPLICATE }) {
1177+
project_context_menu->set_item_disabled(project_context_menu->get_item_index(id), clicked_project.missing);
1178+
}
1179+
1180+
project_context_menu->set_position(p_hb->get_screen_position() + p_at);
1181+
project_context_menu->reset_size();
1182+
project_context_menu->popup();
1183+
}
1184+
1185+
void ProjectList::_menu_option(int p_option) {
1186+
emit_signal(SIGNAL_MENU_OPTION_SELECTED, p_option);
1187+
}
1188+
1189+
void ProjectList::_update_menu_icons() {
1190+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_EDIT), get_editor_theme_icon("Edit"));
1191+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_EDIT_VERBOSE), get_editor_theme_icon("Notification"));
1192+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_EDIT_RECOVERY), get_editor_theme_icon("NodeWarning"));
1193+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_RUN), get_editor_theme_icon("Play"));
1194+
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
1195+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_SHOW_IN_FILE_MANAGER), get_editor_theme_icon("Load"));
1196+
#endif
1197+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_COPY_PATH), get_editor_theme_icon("ActionCopy"));
1198+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_RENAME), get_editor_theme_icon("Rename"));
1199+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_MANAGE_TAGS), get_editor_theme_icon("Script"));
1200+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_DUPLICATE), get_editor_theme_icon("Duplicate"));
1201+
project_context_menu->set_item_icon(project_context_menu->get_item_index(MENU_REMOVE), get_editor_theme_icon("Remove"));
1202+
}
1203+
11151204
// Project list selection.
11161205

11171206
void ProjectList::_clear_project_selection() {
@@ -1369,6 +1458,7 @@ void ProjectList::_bind_methods() {
13691458
ADD_SIGNAL(MethodInfo(SIGNAL_LIST_CHANGED));
13701459
ADD_SIGNAL(MethodInfo(SIGNAL_SELECTION_CHANGED));
13711460
ADD_SIGNAL(MethodInfo(SIGNAL_PROJECT_ASK_OPEN));
1461+
ADD_SIGNAL(MethodInfo(SIGNAL_MENU_OPTION_SELECTED));
13721462
}
13731463

13741464
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
bool project_is_missing = false;
6163
bool icon_needs_reload = true;
@@ -64,6 +66,7 @@ class ProjectListItemControl : public HBoxContainer {
6466

6567
void _favorite_button_pressed();
6668
void _explore_button_pressed();
69+
void _request_menu();
6770

6871
ProjectList *get_list() const;
6972

@@ -109,6 +112,19 @@ class ProjectList : public ScrollContainer {
109112
TAGS,
110113
};
111114

115+
enum MenuOption {
116+
MENU_EDIT,
117+
MENU_EDIT_VERBOSE,
118+
MENU_EDIT_RECOVERY,
119+
MENU_RUN,
120+
MENU_SHOW_IN_FILE_MANAGER,
121+
MENU_COPY_PATH,
122+
MENU_RENAME,
123+
MENU_MANAGE_TAGS,
124+
MENU_DUPLICATE,
125+
MENU_REMOVE,
126+
};
127+
112128
// Can often be passed by copy.
113129
struct Item {
114130
String project_name;
@@ -196,6 +212,7 @@ class ProjectList : public ScrollContainer {
196212
String _last_clicked; // Project key
197213

198214
VBoxContainer *project_list_vbox = nullptr;
215+
PopupMenu *project_context_menu = nullptr;
199216

200217
// Projects scan.
201218

@@ -229,10 +246,14 @@ class ProjectList : public ScrollContainer {
229246
void _toggle_project(int p_index);
230247
void _remove_project(int p_index, bool p_update_settings);
231248

232-
void _list_item_input(const Ref<InputEvent> &p_ev, Node *p_hb);
249+
void _list_item_input(const Ref<InputEvent> &p_ev, Control *p_hb);
233250
void _on_favorite_pressed(Node *p_hb);
234251
void _on_explore_pressed(const String &p_path);
235252

253+
void _open_menu(const Vector2 &p_at, Control *p_hb);
254+
void _menu_option(int p_option);
255+
void _update_menu_icons();
256+
236257
// Project list selection.
237258

238259
void _clear_project_selection();
@@ -250,9 +271,10 @@ class ProjectList : public ScrollContainer {
250271
static void _bind_methods();
251272

252273
public:
253-
static const char *SIGNAL_LIST_CHANGED;
254-
static const char *SIGNAL_SELECTION_CHANGED;
255-
static const char *SIGNAL_PROJECT_ASK_OPEN;
274+
static inline const char *SIGNAL_LIST_CHANGED = "list_changed";
275+
static inline const char *SIGNAL_SELECTION_CHANGED = "selection_changed";
276+
static inline const char *SIGNAL_PROJECT_ASK_OPEN = "project_ask_open";
277+
static inline const char *SIGNAL_MENU_OPTION_SELECTED = "menu_option_selected";
256278

257279
static bool project_feature_looks_like_version(const String &p_feature);
258280

0 commit comments

Comments
 (0)