|
44 | 44 | #include "scene/gui/dialogs.h" |
45 | 45 | #include "scene/gui/label.h" |
46 | 46 | #include "scene/gui/line_edit.h" |
| 47 | +#include "scene/gui/popup_menu.h" |
47 | 48 | #include "scene/gui/progress_bar.h" |
48 | 49 | #include "scene/gui/texture_button.h" |
49 | 50 | #include "scene/gui/texture_rect.h" |
50 | 51 | #include "scene/resources/image_texture.h" |
51 | 52 |
|
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 | | - |
56 | 53 | void ProjectListItemControl::_notification(int p_what) { |
57 | 54 | switch (p_what) { |
58 | 55 | case NOTIFICATION_THEME_CHANGED: { |
@@ -86,6 +83,10 @@ void ProjectListItemControl::_notification(int p_what) { |
86 | 83 | explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load"))); |
87 | 84 | #endif |
88 | 85 | } |
| 86 | + |
| 87 | + if (touch_menu_button) { |
| 88 | + touch_menu_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); |
| 89 | + } |
89 | 90 | } break; |
90 | 91 |
|
91 | 92 | case NOTIFICATION_MOUSE_ENTER: { |
@@ -212,6 +213,10 @@ void ProjectListItemControl::_explore_button_pressed() { |
212 | 213 | emit_signal(SNAME("explore_pressed")); |
213 | 214 | } |
214 | 215 |
|
| 216 | +void ProjectListItemControl::_request_menu() { |
| 217 | + emit_signal(SNAME("request_menu"), Vector2(touch_menu_button->get_position())); |
| 218 | +} |
| 219 | + |
215 | 220 | void ProjectListItemControl::set_project_title(const String &p_title) { |
216 | 221 | project_title->set_text(p_title); |
217 | 222 | project_title->set_accessibility_name(TTRC("Project Name")); |
@@ -339,6 +344,7 @@ void ProjectListItemControl::set_is_grayed(bool p_grayed) { |
339 | 344 | void ProjectListItemControl::_bind_methods() { |
340 | 345 | ADD_SIGNAL(MethodInfo("favorite_pressed")); |
341 | 346 | ADD_SIGNAL(MethodInfo("explore_pressed")); |
| 347 | + ADD_SIGNAL(MethodInfo("request_menu")); |
342 | 348 | } |
343 | 349 |
|
344 | 350 | ProjectListItemControl::ProjectListItemControl() { |
@@ -442,6 +448,14 @@ ProjectListItemControl::ProjectListItemControl() { |
442 | 448 | spacer->set_custom_minimum_size(Size2(10, 10)); |
443 | 449 | path_hb->add_child(spacer); |
444 | 450 | } |
| 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 | + } |
445 | 459 | } |
446 | 460 |
|
447 | 461 | struct ProjectListComparator { |
@@ -485,6 +499,12 @@ void ProjectList::_notification(int p_what) { |
485 | 499 | } |
486 | 500 | } break; |
487 | 501 |
|
| 502 | + case NOTIFICATION_THEME_CHANGED: { |
| 503 | + if (project_context_menu) { |
| 504 | + _update_menu_icons(); |
| 505 | + } |
| 506 | + } break; |
| 507 | + |
488 | 508 | case NOTIFICATION_PROCESS: { |
489 | 509 | // Load icons as a coroutine to speed up launch when you have hundreds of projects. |
490 | 510 | if (_icon_load_index < _projects.size()) { |
@@ -1028,6 +1048,7 @@ void ProjectList::_create_project_item_control(int p_index) { |
1028 | 1048 | #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) |
1029 | 1049 | hb->connect("explore_pressed", callable_mp(this, &ProjectList::_on_explore_pressed).bind(item.path)); |
1030 | 1050 | #endif |
| 1051 | + hb->connect("request_menu", callable_mp(this, &ProjectList::_open_menu).bind(hb)); |
1031 | 1052 |
|
1032 | 1053 | project_list_vbox->add_child(hb); |
1033 | 1054 | item.control = hb; |
@@ -1066,38 +1087,42 @@ void ProjectList::_remove_project(int p_index, bool p_update_config) { |
1066 | 1087 | update_dock_menu(); |
1067 | 1088 | } |
1068 | 1089 |
|
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) { |
1070 | 1091 | Ref<InputEventMouseButton> mb = p_ev; |
1071 | 1092 | int clicked_index = p_hb->get_index(); |
1072 | 1093 | const Item &clicked_project = _projects[clicked_index]; |
1073 | 1094 |
|
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 | + } |
1082 | 1105 | } |
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); |
1086 | 1108 |
|
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); |
1089 | 1111 |
|
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 | + } |
1094 | 1116 |
|
1095 | | - emit_signal(SNAME(SIGNAL_SELECTION_CHANGED)); |
| 1117 | + emit_signal(SNAME(SIGNAL_SELECTION_CHANGED)); |
1096 | 1118 |
|
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); |
1101 | 1126 | } |
1102 | 1127 | } |
1103 | 1128 |
|
@@ -1154,6 +1179,70 @@ void ProjectList::_on_explore_pressed(const String &p_path) { |
1154 | 1179 | OS::get_singleton()->shell_show_in_file_manager(p_path, true); |
1155 | 1180 | } |
1156 | 1181 |
|
| 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 | + |
1157 | 1246 | // Project list selection. |
1158 | 1247 |
|
1159 | 1248 | void ProjectList::_clear_project_selection() { |
@@ -1411,6 +1500,7 @@ void ProjectList::_bind_methods() { |
1411 | 1500 | ADD_SIGNAL(MethodInfo(SIGNAL_LIST_CHANGED)); |
1412 | 1501 | ADD_SIGNAL(MethodInfo(SIGNAL_SELECTION_CHANGED)); |
1413 | 1502 | ADD_SIGNAL(MethodInfo(SIGNAL_PROJECT_ASK_OPEN)); |
| 1503 | + ADD_SIGNAL(MethodInfo(SIGNAL_MENU_OPTION_SELECTED)); |
1414 | 1504 | } |
1415 | 1505 |
|
1416 | 1506 | ProjectList::ProjectList() { |
|
0 commit comments