|
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: { |
@@ -80,6 +77,10 @@ void ProjectListItemControl::_notification(int p_what) { |
80 | 77 | explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load"))); |
81 | 78 | #endif |
82 | 79 | } |
| 80 | + |
| 81 | + if (touch_menu_button) { |
| 82 | + touch_menu_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); |
| 83 | + } |
83 | 84 | } break; |
84 | 85 |
|
85 | 86 | case NOTIFICATION_MOUSE_ENTER: { |
@@ -196,6 +197,10 @@ void ProjectListItemControl::_explore_button_pressed() { |
196 | 197 | emit_signal(SNAME("explore_pressed")); |
197 | 198 | } |
198 | 199 |
|
| 200 | +void ProjectListItemControl::_request_menu() { |
| 201 | + emit_signal(SNAME("request_menu"), Vector2(touch_menu_button->get_position())); |
| 202 | +} |
| 203 | + |
199 | 204 | void ProjectListItemControl::set_project_title(const String &p_title) { |
200 | 205 | project_title->set_text(p_title); |
201 | 206 | project_title->set_accessibility_name(TTRC("Project Name")); |
@@ -318,6 +323,7 @@ void ProjectListItemControl::set_is_grayed(bool p_grayed) { |
318 | 323 | void ProjectListItemControl::_bind_methods() { |
319 | 324 | ADD_SIGNAL(MethodInfo("favorite_pressed")); |
320 | 325 | ADD_SIGNAL(MethodInfo("explore_pressed")); |
| 326 | + ADD_SIGNAL(MethodInfo("request_menu")); |
321 | 327 | } |
322 | 328 |
|
323 | 329 | ProjectListItemControl::ProjectListItemControl() { |
@@ -419,6 +425,14 @@ ProjectListItemControl::ProjectListItemControl() { |
419 | 425 | spacer->set_custom_minimum_size(Size2(10, 10)); |
420 | 426 | path_hb->add_child(spacer); |
421 | 427 | } |
| 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 | + } |
422 | 436 | } |
423 | 437 |
|
424 | 438 | struct ProjectListComparator { |
@@ -462,6 +476,12 @@ void ProjectList::_notification(int p_what) { |
462 | 476 | } |
463 | 477 | } break; |
464 | 478 |
|
| 479 | + case NOTIFICATION_THEME_CHANGED: { |
| 480 | + if (project_context_menu) { |
| 481 | + _update_menu_icons(); |
| 482 | + } |
| 483 | + } break; |
| 484 | + |
465 | 485 | case NOTIFICATION_PROCESS: { |
466 | 486 | // Load icons as a coroutine to speed up launch when you have hundreds of projects. |
467 | 487 | if (_icon_load_index < _projects.size()) { |
@@ -1005,6 +1025,7 @@ void ProjectList::_create_project_item_control(int p_index) { |
1005 | 1025 | #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) |
1006 | 1026 | hb->connect("explore_pressed", callable_mp(this, &ProjectList::_on_explore_pressed).bind(item.path)); |
1007 | 1027 | #endif |
| 1028 | + hb->connect("request_menu", callable_mp(this, &ProjectList::_open_menu).bind(hb)); |
1008 | 1029 |
|
1009 | 1030 | project_list_vbox->add_child(hb); |
1010 | 1031 | item.control = hb; |
@@ -1043,38 +1064,42 @@ void ProjectList::_remove_project(int p_index, bool p_update_config) { |
1043 | 1064 | update_dock_menu(); |
1044 | 1065 | } |
1045 | 1066 |
|
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) { |
1047 | 1068 | Ref<InputEventMouseButton> mb = p_ev; |
1048 | 1069 | int clicked_index = p_hb->get_index(); |
1049 | 1070 | const Item &clicked_project = _projects[clicked_index]; |
1050 | 1071 |
|
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 | + } |
1059 | 1082 | } |
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); |
1063 | 1085 |
|
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); |
1066 | 1088 |
|
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 | + } |
1071 | 1093 |
|
1072 | | - emit_signal(SNAME(SIGNAL_SELECTION_CHANGED)); |
| 1094 | + emit_signal(SNAME(SIGNAL_SELECTION_CHANGED)); |
1073 | 1095 |
|
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); |
1078 | 1103 | } |
1079 | 1104 | } |
1080 | 1105 | } |
@@ -1112,6 +1137,70 @@ void ProjectList::_on_explore_pressed(const String &p_path) { |
1112 | 1137 | OS::get_singleton()->shell_show_in_file_manager(p_path, true); |
1113 | 1138 | } |
1114 | 1139 |
|
| 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 | + |
1115 | 1204 | // Project list selection. |
1116 | 1205 |
|
1117 | 1206 | void ProjectList::_clear_project_selection() { |
@@ -1369,6 +1458,7 @@ void ProjectList::_bind_methods() { |
1369 | 1458 | ADD_SIGNAL(MethodInfo(SIGNAL_LIST_CHANGED)); |
1370 | 1459 | ADD_SIGNAL(MethodInfo(SIGNAL_SELECTION_CHANGED)); |
1371 | 1460 | ADD_SIGNAL(MethodInfo(SIGNAL_PROJECT_ASK_OPEN)); |
| 1461 | + ADD_SIGNAL(MethodInfo(SIGNAL_MENU_OPTION_SELECTED)); |
1372 | 1462 | } |
1373 | 1463 |
|
1374 | 1464 | ProjectList::ProjectList() { |
|
0 commit comments