1/**************************************************************************/
2/* project_manager.h */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#ifndef PROJECT_MANAGER_H
32#define PROJECT_MANAGER_H
33
34#include "core/io/config_file.h"
35#include "editor/editor_about.h"
36#include "scene/gui/dialogs.h"
37#include "scene/gui/file_dialog.h"
38#include "scene/gui/scroll_container.h"
39
40class CheckBox;
41class EditorAssetLibrary;
42class EditorFileDialog;
43class HFlowContainer;
44class PanelContainer;
45class ProjectList;
46
47class ProjectDialog : public ConfirmationDialog {
48 GDCLASS(ProjectDialog, ConfirmationDialog);
49
50public:
51 enum Mode {
52 MODE_NEW,
53 MODE_IMPORT,
54 MODE_INSTALL,
55 MODE_RENAME,
56 };
57
58private:
59 enum MessageType {
60 MESSAGE_ERROR,
61 MESSAGE_WARNING,
62 MESSAGE_SUCCESS,
63 };
64
65 enum InputType {
66 PROJECT_PATH,
67 INSTALL_PATH,
68 };
69
70 Mode mode = MODE_NEW;
71 bool is_folder_empty = true;
72
73 Button *browse = nullptr;
74 Button *install_browse = nullptr;
75 Button *create_dir = nullptr;
76 Container *name_container = nullptr;
77 Container *path_container = nullptr;
78 Container *install_path_container = nullptr;
79
80 Container *renderer_container = nullptr;
81 Label *renderer_info = nullptr;
82 HBoxContainer *default_files_container = nullptr;
83 Ref<ButtonGroup> renderer_button_group;
84
85 Label *msg = nullptr;
86 LineEdit *project_path = nullptr;
87 LineEdit *project_name = nullptr;
88 LineEdit *install_path = nullptr;
89 TextureRect *status_rect = nullptr;
90 TextureRect *install_status_rect = nullptr;
91
92 OptionButton *vcs_metadata_selection = nullptr;
93
94 EditorFileDialog *fdialog = nullptr;
95 EditorFileDialog *fdialog_install = nullptr;
96 AcceptDialog *dialog_error = nullptr;
97
98 String zip_path;
99 String zip_title;
100 String fav_dir;
101
102 String created_folder_path;
103
104 void _set_message(const String &p_msg, MessageType p_type = MESSAGE_SUCCESS, InputType input_type = PROJECT_PATH);
105
106 String _test_path();
107 void _path_text_changed(const String &p_path);
108 void _path_selected(const String &p_path);
109 void _file_selected(const String &p_path);
110 void _install_path_selected(const String &p_path);
111
112 void _browse_path();
113 void _browse_install_path();
114 void _create_folder();
115
116 void _text_changed(const String &p_text);
117 void _nonempty_confirmation_ok_pressed();
118 void _renderer_selected();
119 void _remove_created_folder();
120
121 void ok_pressed() override;
122 void cancel_pressed() override;
123
124protected:
125 void _notification(int p_what);
126 static void _bind_methods();
127
128public:
129 void set_zip_path(const String &p_path);
130 void set_zip_title(const String &p_title);
131 void set_mode(Mode p_mode);
132 void set_project_path(const String &p_path);
133
134 void ask_for_path_and_show();
135 void show_dialog();
136
137 ProjectDialog();
138};
139
140class ProjectListItemControl : public HBoxContainer {
141 GDCLASS(ProjectListItemControl, HBoxContainer)
142
143 VBoxContainer *main_vbox = nullptr;
144 TextureButton *favorite_button = nullptr;
145 Button *explore_button = nullptr;
146
147 TextureRect *project_icon = nullptr;
148 Label *project_title = nullptr;
149 Label *project_path = nullptr;
150 TextureRect *project_unsupported_features = nullptr;
151 HBoxContainer *tag_container = nullptr;
152
153 bool project_is_missing = false;
154 bool icon_needs_reload = true;
155 bool is_selected = false;
156 bool is_hovering = false;
157
158 void _favorite_button_pressed();
159 void _explore_button_pressed();
160
161protected:
162 void _notification(int p_what);
163 static void _bind_methods();
164
165public:
166 void set_project_title(const String &p_title);
167 void set_project_path(const String &p_path);
168 void set_tags(const PackedStringArray &p_tags, ProjectList *p_parent_list);
169 void set_project_icon(const Ref<Texture2D> &p_icon);
170 void set_unsupported_features(PackedStringArray p_features);
171
172 bool should_load_project_icon() const;
173 void set_selected(bool p_selected);
174
175 void set_is_favorite(bool p_favorite);
176 void set_is_missing(bool p_missing);
177 void set_is_grayed(bool p_grayed);
178
179 ProjectListItemControl();
180};
181
182class ProjectList : public ScrollContainer {
183 GDCLASS(ProjectList, ScrollContainer)
184
185 friend class ProjectManager;
186
187public:
188 enum FilterOption {
189 EDIT_DATE,
190 NAME,
191 PATH,
192 TAGS,
193 };
194
195 // Can often be passed by copy
196 struct Item {
197 String project_name;
198 String description;
199 PackedStringArray tags;
200 String tag_sort_string;
201 String path;
202 String icon;
203 String main_scene;
204 PackedStringArray unsupported_features;
205 uint64_t last_edited = 0;
206 bool favorite = false;
207 bool grayed = false;
208 bool missing = false;
209 int version = 0;
210
211 ProjectListItemControl *control = nullptr;
212
213 Item() {}
214
215 Item(const String &p_name,
216 const String &p_description,
217 const PackedStringArray &p_tags,
218 const String &p_path,
219 const String &p_icon,
220 const String &p_main_scene,
221 const PackedStringArray &p_unsupported_features,
222 uint64_t p_last_edited,
223 bool p_favorite,
224 bool p_grayed,
225 bool p_missing,
226 int p_version) {
227 project_name = p_name;
228 description = p_description;
229 tags = p_tags;
230 path = p_path;
231 icon = p_icon;
232 main_scene = p_main_scene;
233 unsupported_features = p_unsupported_features;
234 last_edited = p_last_edited;
235 favorite = p_favorite;
236 grayed = p_grayed;
237 missing = p_missing;
238 version = p_version;
239 control = nullptr;
240
241 PackedStringArray sorted_tags = tags;
242 sorted_tags.sort();
243 tag_sort_string = String().join(sorted_tags);
244 }
245
246 _FORCE_INLINE_ bool operator==(const Item &l) const {
247 return path == l.path;
248 }
249 };
250
251private:
252 bool project_opening_initiated = false;
253
254 String _search_term;
255 FilterOption _order_option = FilterOption::EDIT_DATE;
256 HashSet<String> _selected_project_paths;
257 String _last_clicked; // Project key
258 VBoxContainer *_scroll_children = nullptr;
259 int _icon_load_index = 0;
260
261 Vector<Item> _projects;
262
263 ConfigFile _config;
264 String _config_path;
265
266 void _panel_input(const Ref<InputEvent> &p_ev, Node *p_hb);
267 void _favorite_pressed(Node *p_hb);
268 void _show_project(const String &p_path);
269
270 void _clear_project_selection();
271 void _toggle_project(int p_index);
272 void _select_project_nocheck(int p_index);
273 void _deselect_project_nocheck(int p_index);
274 void _select_project_range(int p_begin, int p_end);
275
276 void _create_project_item_control(int p_index);
277 void _remove_project(int p_index, bool p_update_settings);
278
279 static Item load_project_data(const String &p_property_key, bool p_favorite);
280 void _update_icons_async();
281 void _load_project_icon(int p_index);
282
283 void _global_menu_new_window(const Variant &p_tag);
284 void _global_menu_open_project(const Variant &p_tag);
285
286protected:
287 void _notification(int p_what);
288 static void _bind_methods();
289
290public:
291 static const char *SIGNAL_SELECTION_CHANGED;
292 static const char *SIGNAL_PROJECT_ASK_OPEN;
293
294 void load_projects();
295 int get_project_count() const;
296
297 void sort_projects();
298
299 void add_project(const String &dir_path, bool favorite);
300 void set_project_version(const String &p_project_path, int version);
301 int refresh_project(const String &dir_path);
302 void ensure_project_visible(int p_index);
303
304 void select_project(int p_index);
305 void select_first_visible_project();
306 void erase_selected_projects(bool p_delete_project_contents);
307 Vector<Item> get_selected_projects() const;
308 const HashSet<String> &get_selected_project_keys() const;
309 int get_single_selected_index() const;
310
311 bool is_any_project_missing() const;
312 void erase_missing_projects();
313
314 void set_search_term(String p_search_term);
315 void add_search_tag(const String &p_tag);
316 void set_order_option(int p_option);
317
318 void update_dock_menu();
319 void migrate_config();
320 void save_config();
321
322 ProjectList();
323};
324
325class ProjectManager : public Control {
326 GDCLASS(ProjectManager, Control);
327
328 HashMap<String, Ref<Texture2D>> icon_type_cache;
329 void _build_icon_type_cache(Ref<Theme> p_theme);
330
331 static ProjectManager *singleton;
332
333 Panel *background_panel = nullptr;
334 TabContainer *tabs = nullptr;
335 ProjectList *_project_list = nullptr;
336
337 LineEdit *search_box = nullptr;
338 Label *loading_label = nullptr;
339 OptionButton *filter_option = nullptr;
340 PanelContainer *search_panel = nullptr;
341
342 Button *create_btn = nullptr;
343 Button *import_btn = nullptr;
344 Button *scan_btn = nullptr;
345 Button *open_btn = nullptr;
346 Button *run_btn = nullptr;
347 Button *rename_btn = nullptr;
348 Button *manage_tags_btn = nullptr;
349 Button *erase_btn = nullptr;
350 Button *erase_missing_btn = nullptr;
351 Button *about_btn = nullptr;
352
353 VBoxContainer *local_projects_vb = nullptr;
354 EditorAssetLibrary *asset_library = nullptr;
355
356 Ref<StyleBox> tag_stylebox;
357
358 EditorFileDialog *scan_dir = nullptr;
359 ConfirmationDialog *language_restart_ask = nullptr;
360
361 ConfirmationDialog *erase_ask = nullptr;
362 Label *erase_ask_label = nullptr;
363 // Comment out for now until we have a better warning system to
364 // ensure users delete their project only.
365 //CheckBox *delete_project_contents = nullptr;
366
367 ConfirmationDialog *erase_missing_ask = nullptr;
368 ConfirmationDialog *multi_open_ask = nullptr;
369 ConfirmationDialog *multi_run_ask = nullptr;
370 ConfirmationDialog *multi_scan_ask = nullptr;
371 ConfirmationDialog *ask_full_convert_dialog = nullptr;
372 ConfirmationDialog *ask_update_settings = nullptr;
373 ConfirmationDialog *open_templates = nullptr;
374 EditorAbout *about = nullptr;
375
376 HBoxContainer *settings_hb = nullptr;
377
378 AcceptDialog *run_error_diag = nullptr;
379 AcceptDialog *dialog_error = nullptr;
380 ProjectDialog *npdialog = nullptr;
381
382 Button *full_convert_button = nullptr;
383 OptionButton *language_btn = nullptr;
384 LinkButton *version_btn = nullptr;
385
386 HashSet<String> tag_set;
387 PackedStringArray current_project_tags;
388 PackedStringArray forbidden_tag_characters{ "/", "\\", "-" };
389 ConfirmationDialog *tag_manage_dialog = nullptr;
390 HFlowContainer *project_tags = nullptr;
391 HFlowContainer *all_tags = nullptr;
392 Label *tag_edit_error = nullptr;
393 Button *create_tag_btn = nullptr;
394 ConfirmationDialog *create_tag_dialog = nullptr;
395 LineEdit *new_tag_name = nullptr;
396 Label *tag_error = nullptr;
397
398 void _open_asset_library();
399 void _scan_projects();
400 void _run_project();
401 void _run_project_confirm();
402 void _open_selected_projects();
403 void _open_selected_projects_ask();
404 void _full_convert_button_pressed();
405 void _perform_full_project_conversion();
406 void _import_project();
407 void _new_project();
408 void _rename_project();
409 void _erase_project();
410 void _erase_missing_projects();
411 void _erase_project_confirm();
412 void _erase_missing_projects_confirm();
413 void _show_about();
414 void _update_project_buttons();
415 void _language_selected(int p_id);
416 void _restart_confirm();
417 void _confirm_update_settings();
418
419 void _manage_project_tags();
420 void _add_project_tag(const String &p_tag);
421 void _delete_project_tag(const String &p_tag);
422 void _apply_project_tags();
423 void _set_new_tag_name(const String p_name);
424 void _create_new_tag();
425
426 void _load_recent_projects();
427 void _on_project_created(const String &dir);
428 void _on_projects_updated();
429 void _scan_multiple_folders(PackedStringArray p_files);
430 void _scan_begin(const String &p_base);
431 void _scan_dir(const String &path);
432
433 void _install_project(const String &p_zip_path, const String &p_title);
434
435 void _dim_window();
436 virtual void shortcut_input(const Ref<InputEvent> &p_ev) override;
437 void _files_dropped(PackedStringArray p_files);
438
439 void _version_button_pressed();
440 void _on_order_option_changed(int p_idx);
441 void _on_tab_changed(int p_tab);
442 void _on_search_term_changed(const String &p_term);
443
444 static Ref<Texture2D> _file_dialog_get_icon(const String &p_path);
445 static Ref<Texture2D> _file_dialog_get_thumbnail(const String &p_path);
446
447protected:
448 void _notification(int p_what);
449 static void _bind_methods();
450
451public:
452 static ProjectManager *get_singleton() { return singleton; }
453
454 LineEdit *get_search_box();
455 void add_new_tag(const String &p_tag);
456
457 ProjectManager();
458 ~ProjectManager();
459};
460
461class ProjectTag : public HBoxContainer {
462 GDCLASS(ProjectTag, HBoxContainer);
463
464 String tag_string;
465 Button *button = nullptr;
466 bool display_close = false;
467
468protected:
469 void _notification(int p_what);
470
471public:
472 ProjectTag(const String &p_text, bool p_display_close = false);
473
474 void connect_button_to(const Callable &p_callable);
475 const String get_tag() const;
476};
477
478#endif // PROJECT_MANAGER_H
479