mirror of https://github.com/godotengine/godot
Reorganize ProjectManager code to be more idiomatic
This commit is contained in:
parent
ac2e82463c
commit
10420f91ba
|
|
@ -47,6 +47,7 @@
|
|||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_themes.h"
|
||||
#include "editor/editor_vcs_interface.h"
|
||||
#include "editor/plugins/asset_library_editor_plugin.h"
|
||||
#include "main/main.h"
|
||||
#include "scene/gui/center_container.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
|
|
@ -62,58 +63,9 @@
|
|||
|
||||
constexpr int GODOT4_CONFIG_VERSION = 5;
|
||||
|
||||
class ProjectDialog : public ConfirmationDialog {
|
||||
GDCLASS(ProjectDialog, ConfirmationDialog);
|
||||
/// Project Dialog.
|
||||
|
||||
public:
|
||||
bool is_folder_empty = true;
|
||||
enum Mode {
|
||||
MODE_NEW,
|
||||
MODE_IMPORT,
|
||||
MODE_INSTALL,
|
||||
MODE_RENAME,
|
||||
};
|
||||
|
||||
private:
|
||||
enum MessageType {
|
||||
MESSAGE_ERROR,
|
||||
MESSAGE_WARNING,
|
||||
MESSAGE_SUCCESS,
|
||||
};
|
||||
|
||||
enum InputType {
|
||||
PROJECT_PATH,
|
||||
INSTALL_PATH,
|
||||
};
|
||||
|
||||
Mode mode;
|
||||
Button *browse;
|
||||
Button *install_browse;
|
||||
Button *create_dir;
|
||||
Container *name_container;
|
||||
Container *path_container;
|
||||
Container *install_path_container;
|
||||
Container *renderer_container;
|
||||
Label *renderer_info;
|
||||
HBoxContainer *default_files_container;
|
||||
Ref<ButtonGroup> renderer_button_group;
|
||||
Label *msg;
|
||||
LineEdit *project_path;
|
||||
LineEdit *project_name;
|
||||
LineEdit *install_path;
|
||||
TextureRect *status_rect;
|
||||
TextureRect *install_status_rect;
|
||||
EditorFileDialog *fdialog;
|
||||
EditorFileDialog *fdialog_install;
|
||||
OptionButton *vcs_metadata_selection;
|
||||
String zip_path;
|
||||
String zip_title;
|
||||
AcceptDialog *dialog_error;
|
||||
String fav_dir;
|
||||
|
||||
String created_folder_path;
|
||||
|
||||
void set_message(const String &p_msg, MessageType p_type = MESSAGE_SUCCESS, InputType input_type = PROJECT_PATH) {
|
||||
void ProjectDialog::_set_message(const String &p_msg, MessageType p_type, InputType input_type) {
|
||||
msg->set_text(p_msg);
|
||||
Ref<Texture2D> current_path_icon = status_rect->get_texture();
|
||||
Ref<Texture2D> current_install_icon = install_status_rect->get_texture();
|
||||
|
|
@ -152,7 +104,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
String _test_path() {
|
||||
String ProjectDialog::_test_path() {
|
||||
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
String valid_path, valid_install_path;
|
||||
if (d->change_dir(project_path->get_text()) == OK) {
|
||||
|
|
@ -170,7 +122,7 @@ private:
|
|||
}
|
||||
|
||||
if (valid_path.is_empty()) {
|
||||
set_message(TTR("The path specified doesn't exist."), MESSAGE_ERROR);
|
||||
_set_message(TTR("The path specified doesn't exist."), MESSAGE_ERROR);
|
||||
get_ok_button()->set_disabled(true);
|
||||
return "";
|
||||
}
|
||||
|
|
@ -183,7 +135,7 @@ private:
|
|||
}
|
||||
|
||||
if (valid_install_path.is_empty()) {
|
||||
set_message(TTR("The path specified doesn't exist."), MESSAGE_ERROR, INSTALL_PATH);
|
||||
_set_message(TTR("The path specified doesn't exist."), MESSAGE_ERROR, INSTALL_PATH);
|
||||
get_ok_button()->set_disabled(true);
|
||||
return "";
|
||||
}
|
||||
|
|
@ -197,7 +149,7 @@ private:
|
|||
|
||||
unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io);
|
||||
if (!pkg) {
|
||||
set_message(TTR("Error opening package file (it's not in ZIP format)."), MESSAGE_ERROR);
|
||||
_set_message(TTR("Error opening package file (it's not in ZIP format)."), MESSAGE_ERROR);
|
||||
get_ok_button()->set_disabled(true);
|
||||
unzClose(pkg);
|
||||
return "";
|
||||
|
|
@ -220,7 +172,7 @@ private:
|
|||
}
|
||||
|
||||
if (ret == UNZ_END_OF_LIST_OF_FILE) {
|
||||
set_message(TTR("Invalid \".zip\" project file; it doesn't contain a \"project.godot\" file."), MESSAGE_ERROR);
|
||||
_set_message(TTR("Invalid \".zip\" project file; it doesn't contain a \"project.godot\" file."), MESSAGE_ERROR);
|
||||
get_ok_button()->set_disabled(true);
|
||||
unzClose(pkg);
|
||||
return "";
|
||||
|
|
@ -246,20 +198,20 @@ private:
|
|||
d->list_dir_end();
|
||||
|
||||
if (!is_folder_empty) {
|
||||
set_message(TTR("Please choose an empty folder."), MESSAGE_WARNING, INSTALL_PATH);
|
||||
_set_message(TTR("Please choose an empty folder."), MESSAGE_WARNING, INSTALL_PATH);
|
||||
get_ok_button()->set_disabled(true);
|
||||
return "";
|
||||
}
|
||||
|
||||
} else {
|
||||
set_message(TTR("Please choose a \"project.godot\" or \".zip\" file."), MESSAGE_ERROR);
|
||||
_set_message(TTR("Please choose a \"project.godot\" or \".zip\" file."), MESSAGE_ERROR);
|
||||
install_path_container->hide();
|
||||
get_ok_button()->set_disabled(true);
|
||||
return "";
|
||||
}
|
||||
|
||||
} else if (valid_path.ends_with("zip")) {
|
||||
set_message(TTR("This directory already contains a Godot project."), MESSAGE_ERROR, INSTALL_PATH);
|
||||
_set_message(TTR("This directory already contains a Godot project."), MESSAGE_ERROR, INSTALL_PATH);
|
||||
get_ok_button()->set_disabled(true);
|
||||
return "";
|
||||
}
|
||||
|
|
@ -284,24 +236,24 @@ private:
|
|||
|
||||
if (!is_folder_empty) {
|
||||
if (valid_path == OS::get_singleton()->get_environment("HOME") || valid_path == OS::get_singleton()->get_system_dir(OS::SYSTEM_DIR_DOCUMENTS) || valid_path == OS::get_singleton()->get_executable_path().get_base_dir()) {
|
||||
set_message(TTR("You cannot save a project in the selected path. Please make a new folder or choose a new path."), MESSAGE_ERROR);
|
||||
_set_message(TTR("You cannot save a project in the selected path. Please make a new folder or choose a new path."), MESSAGE_ERROR);
|
||||
get_ok_button()->set_disabled(true);
|
||||
return "";
|
||||
}
|
||||
|
||||
set_message(TTR("The selected path is not empty. Choosing an empty folder is highly recommended."), MESSAGE_WARNING);
|
||||
_set_message(TTR("The selected path is not empty. Choosing an empty folder is highly recommended."), MESSAGE_WARNING);
|
||||
get_ok_button()->set_disabled(false);
|
||||
return valid_path;
|
||||
}
|
||||
}
|
||||
|
||||
set_message("");
|
||||
set_message("", MESSAGE_SUCCESS, INSTALL_PATH);
|
||||
_set_message("");
|
||||
_set_message("", MESSAGE_SUCCESS, INSTALL_PATH);
|
||||
get_ok_button()->set_disabled(false);
|
||||
return valid_path;
|
||||
}
|
||||
|
||||
void _path_text_changed(const String &p_path) {
|
||||
void ProjectDialog::_path_text_changed(const String &p_path) {
|
||||
String sp = _test_path();
|
||||
if (!sp.is_empty()) {
|
||||
// If the project name is empty or default, infer the project name from the selected folder name
|
||||
|
|
@ -326,7 +278,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void _file_selected(const String &p_path) {
|
||||
void ProjectDialog::_file_selected(const String &p_path) {
|
||||
String p = p_path;
|
||||
if (mode == MODE_IMPORT) {
|
||||
if (p.ends_with("project.godot")) {
|
||||
|
|
@ -338,7 +290,7 @@ private:
|
|||
install_path_container->show();
|
||||
get_ok_button()->set_disabled(false);
|
||||
} else {
|
||||
set_message(TTR("Please choose a \"project.godot\" or \".zip\" file."), MESSAGE_ERROR);
|
||||
_set_message(TTR("Please choose a \"project.godot\" or \".zip\" file."), MESSAGE_ERROR);
|
||||
get_ok_button()->set_disabled(true);
|
||||
return;
|
||||
}
|
||||
|
|
@ -354,21 +306,21 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void _path_selected(const String &p_path) {
|
||||
void ProjectDialog::_path_selected(const String &p_path) {
|
||||
String sp = p_path.simplify_path();
|
||||
project_path->set_text(sp);
|
||||
_path_text_changed(sp);
|
||||
get_ok_button()->call_deferred(SNAME("grab_focus"));
|
||||
}
|
||||
|
||||
void _install_path_selected(const String &p_path) {
|
||||
void ProjectDialog::_install_path_selected(const String &p_path) {
|
||||
String sp = p_path.simplify_path();
|
||||
install_path->set_text(sp);
|
||||
_path_text_changed(sp);
|
||||
get_ok_button()->call_deferred(SNAME("grab_focus"));
|
||||
}
|
||||
|
||||
void _browse_path() {
|
||||
void ProjectDialog::_browse_path() {
|
||||
fdialog->set_current_dir(project_path->get_text());
|
||||
|
||||
if (mode == MODE_IMPORT) {
|
||||
|
|
@ -382,16 +334,16 @@ private:
|
|||
fdialog->popup_file_dialog();
|
||||
}
|
||||
|
||||
void _browse_install_path() {
|
||||
void ProjectDialog::_browse_install_path() {
|
||||
fdialog_install->set_current_dir(install_path->get_text());
|
||||
fdialog_install->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
|
||||
fdialog_install->popup_file_dialog();
|
||||
}
|
||||
|
||||
void _create_folder() {
|
||||
void ProjectDialog::_create_folder() {
|
||||
const String project_name_no_edges = project_name->get_text().strip_edges();
|
||||
if (project_name_no_edges.is_empty() || !created_folder_path.is_empty() || project_name_no_edges.ends_with(".")) {
|
||||
set_message(TTR("Invalid project name."), MESSAGE_WARNING);
|
||||
_set_message(TTR("Invalid project name."), MESSAGE_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -416,7 +368,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void _text_changed(const String &p_text) {
|
||||
void ProjectDialog::_text_changed(const String &p_text) {
|
||||
if (mode != MODE_NEW) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -424,16 +376,16 @@ private:
|
|||
_test_path();
|
||||
|
||||
if (p_text.strip_edges().is_empty()) {
|
||||
set_message(TTR("It would be a good idea to name your project."), MESSAGE_ERROR);
|
||||
_set_message(TTR("It would be a good idea to name your project."), MESSAGE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void _nonempty_confirmation_ok_pressed() {
|
||||
void ProjectDialog::_nonempty_confirmation_ok_pressed() {
|
||||
is_folder_empty = true;
|
||||
ok_pressed();
|
||||
}
|
||||
|
||||
void _renderer_selected() {
|
||||
void ProjectDialog::_renderer_selected() {
|
||||
String renderer_type = renderer_button_group->get_pressed_button()->get_meta(SNAME("rendering_method"));
|
||||
|
||||
if (renderer_type == "forward_plus") {
|
||||
|
|
@ -462,13 +414,23 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void ok_pressed() override {
|
||||
void ProjectDialog::_remove_created_folder() {
|
||||
if (!created_folder_path.is_empty()) {
|
||||
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
d->remove(created_folder_path);
|
||||
|
||||
create_dir->set_disabled(false);
|
||||
created_folder_path = "";
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectDialog::ok_pressed() {
|
||||
String dir = project_path->get_text();
|
||||
|
||||
if (mode == MODE_RENAME) {
|
||||
String dir2 = _test_path();
|
||||
if (dir2.is_empty()) {
|
||||
set_message(TTR("Invalid project path (changed anything?)."), MESSAGE_ERROR);
|
||||
_set_message(TTR("Invalid project path (changed anything?)."), MESSAGE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -477,12 +439,12 @@ private:
|
|||
String project_godot = dir2.path_join("project.godot");
|
||||
Error err = cfg.load(project_godot);
|
||||
if (err != OK) {
|
||||
set_message(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err), MESSAGE_ERROR);
|
||||
_set_message(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err), MESSAGE_ERROR);
|
||||
} else {
|
||||
cfg.set_value("application", "config/name", project_name->get_text().strip_edges());
|
||||
err = cfg.save(project_godot);
|
||||
if (err != OK) {
|
||||
set_message(vformat(TTR("Couldn't save project at '%s' (error %d)."), project_godot, err), MESSAGE_ERROR);
|
||||
_set_message(vformat(TTR("Couldn't save project at '%s' (error %d)."), project_godot, err), MESSAGE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -542,7 +504,7 @@ private:
|
|||
initial_settings["application/config/icon"] = "res://icon.svg";
|
||||
|
||||
if (ProjectSettings::get_singleton()->save_custom(dir.path_join("project.godot"), initial_settings, Vector<String>(), false) != OK) {
|
||||
set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR);
|
||||
_set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR);
|
||||
} else {
|
||||
// Store default project icon in SVG format.
|
||||
Error err;
|
||||
|
|
@ -550,7 +512,7 @@ private:
|
|||
fa_icon->store_string(get_default_project_icon());
|
||||
|
||||
if (err != OK) {
|
||||
set_message(TTR("Couldn't create icon.svg in project path."), MESSAGE_ERROR);
|
||||
_set_message(TTR("Couldn't create icon.svg in project path."), MESSAGE_ERROR);
|
||||
}
|
||||
|
||||
EditorVCSInterface::create_vcs_metadata_files(EditorVCSInterface::VCSMetadata(vcs_metadata_selection->get_selected()), dir);
|
||||
|
|
@ -665,17 +627,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void _remove_created_folder() {
|
||||
if (!created_folder_path.is_empty()) {
|
||||
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
d->remove(created_folder_path);
|
||||
|
||||
create_dir->set_disabled(false);
|
||||
created_folder_path = "";
|
||||
}
|
||||
}
|
||||
|
||||
void cancel_pressed() override {
|
||||
void ProjectDialog::cancel_pressed() {
|
||||
_remove_created_folder();
|
||||
|
||||
project_path->clear();
|
||||
|
|
@ -692,37 +644,23 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void _notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_WM_CLOSE_REQUEST: {
|
||||
_remove_created_folder();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
static void _bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("project_created"));
|
||||
ADD_SIGNAL(MethodInfo("projects_updated"));
|
||||
}
|
||||
|
||||
public:
|
||||
void set_zip_path(const String &p_path) {
|
||||
void ProjectDialog::set_zip_path(const String &p_path) {
|
||||
zip_path = p_path;
|
||||
}
|
||||
void set_zip_title(const String &p_title) {
|
||||
|
||||
void ProjectDialog::set_zip_title(const String &p_title) {
|
||||
zip_title = p_title;
|
||||
}
|
||||
|
||||
void set_mode(Mode p_mode) {
|
||||
void ProjectDialog::set_mode(Mode p_mode) {
|
||||
mode = p_mode;
|
||||
}
|
||||
|
||||
void set_project_path(const String &p_path) {
|
||||
void ProjectDialog::set_project_path(const String &p_path) {
|
||||
project_path->set_text(p_path);
|
||||
}
|
||||
|
||||
void show_dialog() {
|
||||
void ProjectDialog::show_dialog() {
|
||||
if (mode == MODE_RENAME) {
|
||||
project_path->set_editable(false);
|
||||
browse->hide();
|
||||
|
|
@ -744,7 +682,7 @@ public:
|
|||
String project_godot = project_path->get_text().path_join("project.godot");
|
||||
Error err = cfg.load(project_godot);
|
||||
if (err != OK) {
|
||||
set_message(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err), MESSAGE_ERROR);
|
||||
_set_message(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err), MESSAGE_ERROR);
|
||||
status_rect->show();
|
||||
msg->show();
|
||||
get_ok_button()->set_disabled(true);
|
||||
|
|
@ -818,7 +756,20 @@ public:
|
|||
popup_centered(Size2(500, 0) * EDSCALE);
|
||||
}
|
||||
|
||||
ProjectDialog() {
|
||||
void ProjectDialog::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_WM_CLOSE_REQUEST: {
|
||||
_remove_created_folder();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectDialog::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("project_created"));
|
||||
ADD_SIGNAL(MethodInfo("projects_updated"));
|
||||
}
|
||||
|
||||
ProjectDialog::ProjectDialog() {
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
add_child(vb);
|
||||
|
||||
|
|
@ -1005,35 +956,14 @@ public:
|
|||
fdialog_install->connect("file_selected", callable_mp(this, &ProjectDialog::_install_path_selected));
|
||||
|
||||
set_hide_on_ok(false);
|
||||
mode = MODE_NEW;
|
||||
|
||||
dialog_error = memnew(AcceptDialog);
|
||||
add_child(dialog_error);
|
||||
}
|
||||
};
|
||||
|
||||
class ProjectListItemControl : public HBoxContainer {
|
||||
GDCLASS(ProjectListItemControl, HBoxContainer)
|
||||
public:
|
||||
TextureButton *favorite_button;
|
||||
TextureRect *icon;
|
||||
bool icon_needs_reload;
|
||||
bool hover;
|
||||
/// Project List and friends.
|
||||
|
||||
ProjectListItemControl() {
|
||||
favorite_button = nullptr;
|
||||
icon = nullptr;
|
||||
icon_needs_reload = true;
|
||||
hover = false;
|
||||
|
||||
set_focus_mode(FocusMode::FOCUS_ALL);
|
||||
}
|
||||
|
||||
void set_is_favorite(bool fav) {
|
||||
favorite_button->set_modulate(fav ? Color(1, 1, 1, 1) : Color(1, 1, 1, 0.2));
|
||||
}
|
||||
|
||||
void _notification(int p_what) {
|
||||
void ProjectListItemControl::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_MOUSE_ENTER: {
|
||||
hover = true;
|
||||
|
|
@ -1052,128 +982,17 @@ public:
|
|||
} break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ProjectList : public ScrollContainer {
|
||||
GDCLASS(ProjectList, ScrollContainer)
|
||||
public:
|
||||
static const char *SIGNAL_SELECTION_CHANGED;
|
||||
static const char *SIGNAL_PROJECT_ASK_OPEN;
|
||||
|
||||
enum MenuOptions {
|
||||
GLOBAL_NEW_WINDOW,
|
||||
GLOBAL_OPEN_PROJECT
|
||||
};
|
||||
|
||||
// Can often be passed by copy
|
||||
struct Item {
|
||||
String project_name;
|
||||
String description;
|
||||
String path;
|
||||
String icon;
|
||||
String main_scene;
|
||||
PackedStringArray unsupported_features;
|
||||
uint64_t last_edited = 0;
|
||||
bool favorite = false;
|
||||
bool grayed = false;
|
||||
bool missing = false;
|
||||
int version = 0;
|
||||
|
||||
ProjectListItemControl *control = nullptr;
|
||||
|
||||
Item() {}
|
||||
|
||||
Item(const String &p_name,
|
||||
const String &p_description,
|
||||
const String &p_path,
|
||||
const String &p_icon,
|
||||
const String &p_main_scene,
|
||||
const PackedStringArray &p_unsupported_features,
|
||||
uint64_t p_last_edited,
|
||||
bool p_favorite,
|
||||
bool p_grayed,
|
||||
bool p_missing,
|
||||
int p_version) {
|
||||
project_name = p_name;
|
||||
description = p_description;
|
||||
path = p_path;
|
||||
icon = p_icon;
|
||||
main_scene = p_main_scene;
|
||||
unsupported_features = p_unsupported_features;
|
||||
last_edited = p_last_edited;
|
||||
favorite = p_favorite;
|
||||
grayed = p_grayed;
|
||||
missing = p_missing;
|
||||
version = p_version;
|
||||
control = nullptr;
|
||||
void ProjectListItemControl::set_is_favorite(bool fav) {
|
||||
favorite_button->set_modulate(fav ? Color(1, 1, 1, 1) : Color(1, 1, 1, 0.2));
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const Item &l) const {
|
||||
return path == l.path;
|
||||
ProjectListItemControl::ProjectListItemControl() {
|
||||
set_focus_mode(FocusMode::FOCUS_ALL);
|
||||
}
|
||||
};
|
||||
|
||||
bool project_opening_initiated;
|
||||
|
||||
ProjectList();
|
||||
~ProjectList();
|
||||
|
||||
void _global_menu_new_window(const Variant &p_tag);
|
||||
void _global_menu_open_project(const Variant &p_tag);
|
||||
|
||||
void update_dock_menu();
|
||||
void migrate_config();
|
||||
void load_projects();
|
||||
void set_search_term(String p_search_term);
|
||||
void set_order_option(int p_option);
|
||||
void sort_projects();
|
||||
int get_project_count() const;
|
||||
void select_project(int p_index);
|
||||
void select_first_visible_project();
|
||||
void erase_selected_projects(bool p_delete_project_contents);
|
||||
Vector<Item> get_selected_projects() const;
|
||||
const HashSet<String> &get_selected_project_keys() const;
|
||||
void ensure_project_visible(int p_index);
|
||||
int get_single_selected_index() const;
|
||||
bool is_any_project_missing() const;
|
||||
void erase_missing_projects();
|
||||
int refresh_project(const String &dir_path);
|
||||
void add_project(const String &dir_path, bool favorite);
|
||||
void save_config();
|
||||
void set_project_version(const String &p_project_path, int version);
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
void _panel_draw(Node *p_hb);
|
||||
void _panel_input(const Ref<InputEvent> &p_ev, Node *p_hb);
|
||||
void _favorite_pressed(Node *p_hb);
|
||||
void _show_project(const String &p_path);
|
||||
|
||||
void select_range(int p_begin, int p_end);
|
||||
void toggle_select(int p_index);
|
||||
void create_project_item_control(int p_index);
|
||||
void remove_project(int p_index, bool p_update_settings);
|
||||
void update_icons_async();
|
||||
void load_project_icon(int p_index);
|
||||
static Item load_project_data(const String &p_property_key, bool p_favorite);
|
||||
|
||||
String _search_term;
|
||||
FilterOption _order_option;
|
||||
HashSet<String> _selected_project_paths;
|
||||
String _last_clicked; // Project key
|
||||
VBoxContainer *_scroll_children;
|
||||
int _icon_load_index;
|
||||
|
||||
Vector<Item> _projects;
|
||||
|
||||
ConfigFile _config;
|
||||
String _config_path;
|
||||
};
|
||||
|
||||
struct ProjectListComparator {
|
||||
FilterOption order_option = FilterOption::EDIT_DATE;
|
||||
ProjectList::FilterOption order_option = ProjectList::FilterOption::EDIT_DATE;
|
||||
|
||||
// operator<
|
||||
_FORCE_INLINE_ bool operator()(const ProjectList::Item &a, const ProjectList::Item &b) const {
|
||||
|
|
@ -1184,9 +1003,9 @@ struct ProjectListComparator {
|
|||
return false;
|
||||
}
|
||||
switch (order_option) {
|
||||
case PATH:
|
||||
case ProjectList::PATH:
|
||||
return a.path < b.path;
|
||||
case EDIT_DATE:
|
||||
case ProjectList::EDIT_DATE:
|
||||
return a.last_edited > b.last_edited;
|
||||
default:
|
||||
return a.project_name < b.project_name;
|
||||
|
|
@ -1194,24 +1013,8 @@ struct ProjectListComparator {
|
|||
}
|
||||
};
|
||||
|
||||
ProjectList::ProjectList() {
|
||||
_order_option = FilterOption::EDIT_DATE;
|
||||
_scroll_children = memnew(VBoxContainer);
|
||||
_scroll_children->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
add_child(_scroll_children);
|
||||
|
||||
_icon_load_index = 0;
|
||||
project_opening_initiated = false;
|
||||
_config_path = EditorPaths::get_singleton()->get_data_dir().path_join("projects.cfg");
|
||||
}
|
||||
|
||||
ProjectList::~ProjectList() {
|
||||
}
|
||||
|
||||
void ProjectList::update_icons_async() {
|
||||
_icon_load_index = 0;
|
||||
set_process(true);
|
||||
}
|
||||
const char *ProjectList::SIGNAL_SELECTION_CHANGED = "selection_changed";
|
||||
const char *ProjectList::SIGNAL_PROJECT_ASK_OPEN = "project_ask_open";
|
||||
|
||||
void ProjectList::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
|
|
@ -1231,6 +1034,11 @@ void ProjectList::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void ProjectList::update_icons_async() {
|
||||
_icon_load_index = 0;
|
||||
set_process(true);
|
||||
}
|
||||
|
||||
void ProjectList::load_project_icon(int p_index) {
|
||||
Item &item = _projects.write[p_index];
|
||||
|
||||
|
|
@ -1259,7 +1067,8 @@ void ProjectList::load_project_icon(int p_index) {
|
|||
item.control->icon_needs_reload = false;
|
||||
}
|
||||
|
||||
// Load project data from p_property_key and return it in a ProjectList::Item. p_favorite is passed directly into the Item.
|
||||
// Load project data from p_property_key and return it in a ProjectList::Item.
|
||||
// p_favorite is passed directly into the Item.
|
||||
ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_favorite) {
|
||||
String conf = p_path.path_join("project.godot");
|
||||
bool grayed = false;
|
||||
|
|
@ -1853,7 +1662,7 @@ void ProjectList::erase_selected_projects(bool p_delete_project_contents) {
|
|||
update_dock_menu();
|
||||
}
|
||||
|
||||
// Draws selected project highlight
|
||||
// Draws selected project highlight.
|
||||
void ProjectList::_panel_draw(Node *p_hb) {
|
||||
Control *hb = Object::cast_to<Control>(p_hb);
|
||||
|
||||
|
|
@ -1870,7 +1679,7 @@ void ProjectList::_panel_draw(Node *p_hb) {
|
|||
}
|
||||
}
|
||||
|
||||
// Input for each item in the list
|
||||
// Input for each item in the list.
|
||||
void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
|
||||
Ref<InputEventMouseButton> mb = p_ev;
|
||||
int clicked_index = p_hb->get_index();
|
||||
|
|
@ -1940,14 +1749,21 @@ void ProjectList::_show_project(const String &p_path) {
|
|||
OS::get_singleton()->shell_open(String("file://") + p_path);
|
||||
}
|
||||
|
||||
const char *ProjectList::SIGNAL_SELECTION_CHANGED = "selection_changed";
|
||||
const char *ProjectList::SIGNAL_PROJECT_ASK_OPEN = "project_ask_open";
|
||||
|
||||
void ProjectList::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo(SIGNAL_SELECTION_CHANGED));
|
||||
ADD_SIGNAL(MethodInfo(SIGNAL_PROJECT_ASK_OPEN));
|
||||
}
|
||||
|
||||
ProjectList::ProjectList() {
|
||||
_scroll_children = memnew(VBoxContainer);
|
||||
_scroll_children->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
add_child(_scroll_children);
|
||||
|
||||
_config_path = EditorPaths::get_singleton()->get_data_dir().path_join("projects.cfg");
|
||||
}
|
||||
|
||||
/// Project Manager.
|
||||
|
||||
ProjectManager *ProjectManager::singleton = nullptr;
|
||||
|
||||
void ProjectManager::_notification(int p_what) {
|
||||
|
|
@ -2438,6 +2254,7 @@ void ProjectManager::_scan_dir(const String &path) {
|
|||
}
|
||||
da->list_dir_end();
|
||||
}
|
||||
|
||||
void ProjectManager::_scan_begin(const String &p_base) {
|
||||
print_line("Scanning projects at: " + p_base);
|
||||
_scan_dir(p_base);
|
||||
|
|
|
|||
|
|
@ -31,23 +31,256 @@
|
|||
#ifndef PROJECT_MANAGER_H
|
||||
#define PROJECT_MANAGER_H
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "editor/editor_about.h"
|
||||
#include "editor/plugins/asset_library_editor_plugin.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/file_dialog.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class CheckBox;
|
||||
class ProjectDialog;
|
||||
class ProjectList;
|
||||
class EditorAssetLibrary;
|
||||
class EditorFileDialog;
|
||||
|
||||
class ProjectDialog : public ConfirmationDialog {
|
||||
GDCLASS(ProjectDialog, ConfirmationDialog);
|
||||
|
||||
public:
|
||||
enum Mode {
|
||||
MODE_NEW,
|
||||
MODE_IMPORT,
|
||||
MODE_INSTALL,
|
||||
MODE_RENAME,
|
||||
};
|
||||
|
||||
private:
|
||||
enum MessageType {
|
||||
MESSAGE_ERROR,
|
||||
MESSAGE_WARNING,
|
||||
MESSAGE_SUCCESS,
|
||||
};
|
||||
|
||||
enum InputType {
|
||||
PROJECT_PATH,
|
||||
INSTALL_PATH,
|
||||
};
|
||||
|
||||
Mode mode = MODE_NEW;
|
||||
bool is_folder_empty = true;
|
||||
|
||||
Button *browse = nullptr;
|
||||
Button *install_browse = nullptr;
|
||||
Button *create_dir = nullptr;
|
||||
Container *name_container = nullptr;
|
||||
Container *path_container = nullptr;
|
||||
Container *install_path_container = nullptr;
|
||||
|
||||
Container *renderer_container = nullptr;
|
||||
Label *renderer_info = nullptr;
|
||||
HBoxContainer *default_files_container = nullptr;
|
||||
Ref<ButtonGroup> renderer_button_group;
|
||||
|
||||
Label *msg = nullptr;
|
||||
LineEdit *project_path = nullptr;
|
||||
LineEdit *project_name = nullptr;
|
||||
LineEdit *install_path = nullptr;
|
||||
TextureRect *status_rect = nullptr;
|
||||
TextureRect *install_status_rect = nullptr;
|
||||
|
||||
OptionButton *vcs_metadata_selection = nullptr;
|
||||
|
||||
EditorFileDialog *fdialog = nullptr;
|
||||
EditorFileDialog *fdialog_install = nullptr;
|
||||
AcceptDialog *dialog_error = nullptr;
|
||||
|
||||
String zip_path;
|
||||
String zip_title;
|
||||
String fav_dir;
|
||||
|
||||
String created_folder_path;
|
||||
|
||||
void _set_message(const String &p_msg, MessageType p_type = MESSAGE_SUCCESS, InputType input_type = PROJECT_PATH);
|
||||
|
||||
String _test_path();
|
||||
void _path_text_changed(const String &p_path);
|
||||
void _path_selected(const String &p_path);
|
||||
void _file_selected(const String &p_path);
|
||||
void _install_path_selected(const String &p_path);
|
||||
|
||||
void _browse_path();
|
||||
void _browse_install_path();
|
||||
void _create_folder();
|
||||
|
||||
void _text_changed(const String &p_text);
|
||||
void _nonempty_confirmation_ok_pressed();
|
||||
void _renderer_selected();
|
||||
void _remove_created_folder();
|
||||
|
||||
void ok_pressed() override;
|
||||
void cancel_pressed() override;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_zip_path(const String &p_path);
|
||||
void set_zip_title(const String &p_title);
|
||||
void set_mode(Mode p_mode);
|
||||
void set_project_path(const String &p_path);
|
||||
|
||||
void show_dialog();
|
||||
|
||||
ProjectDialog();
|
||||
};
|
||||
|
||||
class ProjectListItemControl : public HBoxContainer {
|
||||
GDCLASS(ProjectListItemControl, HBoxContainer)
|
||||
|
||||
friend class ProjectList;
|
||||
|
||||
TextureButton *favorite_button = nullptr;
|
||||
TextureRect *icon = nullptr;
|
||||
bool icon_needs_reload = true;
|
||||
bool hover = false;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void set_is_favorite(bool fav);
|
||||
|
||||
ProjectListItemControl();
|
||||
};
|
||||
|
||||
class ProjectList : public ScrollContainer {
|
||||
GDCLASS(ProjectList, ScrollContainer)
|
||||
|
||||
friend class ProjectManager;
|
||||
|
||||
public:
|
||||
enum FilterOption {
|
||||
EDIT_DATE,
|
||||
NAME,
|
||||
PATH,
|
||||
};
|
||||
|
||||
// Can often be passed by copy
|
||||
struct Item {
|
||||
String project_name;
|
||||
String description;
|
||||
String path;
|
||||
String icon;
|
||||
String main_scene;
|
||||
PackedStringArray unsupported_features;
|
||||
uint64_t last_edited = 0;
|
||||
bool favorite = false;
|
||||
bool grayed = false;
|
||||
bool missing = false;
|
||||
int version = 0;
|
||||
|
||||
ProjectListItemControl *control = nullptr;
|
||||
|
||||
Item() {}
|
||||
|
||||
Item(const String &p_name,
|
||||
const String &p_description,
|
||||
const String &p_path,
|
||||
const String &p_icon,
|
||||
const String &p_main_scene,
|
||||
const PackedStringArray &p_unsupported_features,
|
||||
uint64_t p_last_edited,
|
||||
bool p_favorite,
|
||||
bool p_grayed,
|
||||
bool p_missing,
|
||||
int p_version) {
|
||||
project_name = p_name;
|
||||
description = p_description;
|
||||
path = p_path;
|
||||
icon = p_icon;
|
||||
main_scene = p_main_scene;
|
||||
unsupported_features = p_unsupported_features;
|
||||
last_edited = p_last_edited;
|
||||
favorite = p_favorite;
|
||||
grayed = p_grayed;
|
||||
missing = p_missing;
|
||||
version = p_version;
|
||||
control = nullptr;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const Item &l) const {
|
||||
return path == l.path;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
bool project_opening_initiated = false;
|
||||
|
||||
String _search_term;
|
||||
FilterOption _order_option = FilterOption::EDIT_DATE;
|
||||
HashSet<String> _selected_project_paths;
|
||||
String _last_clicked; // Project key
|
||||
VBoxContainer *_scroll_children = nullptr;
|
||||
int _icon_load_index = 0;
|
||||
|
||||
Vector<Item> _projects;
|
||||
|
||||
ConfigFile _config;
|
||||
String _config_path;
|
||||
|
||||
void _panel_draw(Node *p_hb);
|
||||
void _panel_input(const Ref<InputEvent> &p_ev, Node *p_hb);
|
||||
void _favorite_pressed(Node *p_hb);
|
||||
void _show_project(const String &p_path);
|
||||
|
||||
void select_range(int p_begin, int p_end);
|
||||
void toggle_select(int p_index);
|
||||
void create_project_item_control(int p_index);
|
||||
void remove_project(int p_index, bool p_update_settings);
|
||||
void update_icons_async();
|
||||
void load_project_icon(int p_index);
|
||||
static Item load_project_data(const String &p_property_key, bool p_favorite);
|
||||
|
||||
void _global_menu_new_window(const Variant &p_tag);
|
||||
void _global_menu_open_project(const Variant &p_tag);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static const char *SIGNAL_SELECTION_CHANGED;
|
||||
static const char *SIGNAL_PROJECT_ASK_OPEN;
|
||||
|
||||
void load_projects();
|
||||
int get_project_count() const;
|
||||
|
||||
void sort_projects();
|
||||
|
||||
void add_project(const String &dir_path, bool favorite);
|
||||
void set_project_version(const String &p_project_path, int version);
|
||||
int refresh_project(const String &dir_path);
|
||||
void ensure_project_visible(int p_index);
|
||||
|
||||
void select_project(int p_index);
|
||||
void select_first_visible_project();
|
||||
void erase_selected_projects(bool p_delete_project_contents);
|
||||
Vector<Item> get_selected_projects() const;
|
||||
const HashSet<String> &get_selected_project_keys() const;
|
||||
int get_single_selected_index() const;
|
||||
|
||||
bool is_any_project_missing() const;
|
||||
void erase_missing_projects();
|
||||
|
||||
void set_search_term(String p_search_term);
|
||||
void set_order_option(int p_option);
|
||||
|
||||
void update_dock_menu();
|
||||
void migrate_config();
|
||||
void save_config();
|
||||
|
||||
ProjectList();
|
||||
};
|
||||
|
||||
class ProjectManager : public Control {
|
||||
GDCLASS(ProjectManager, Control);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue