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_settings.h"
|
||||||
#include "editor/editor_themes.h"
|
#include "editor/editor_themes.h"
|
||||||
#include "editor/editor_vcs_interface.h"
|
#include "editor/editor_vcs_interface.h"
|
||||||
|
#include "editor/plugins/asset_library_editor_plugin.h"
|
||||||
#include "main/main.h"
|
#include "main/main.h"
|
||||||
#include "scene/gui/center_container.h"
|
#include "scene/gui/center_container.h"
|
||||||
#include "scene/gui/check_box.h"
|
#include "scene/gui/check_box.h"
|
||||||
|
|
@ -62,58 +63,9 @@
|
||||||
|
|
||||||
constexpr int GODOT4_CONFIG_VERSION = 5;
|
constexpr int GODOT4_CONFIG_VERSION = 5;
|
||||||
|
|
||||||
class ProjectDialog : public ConfirmationDialog {
|
/// Project Dialog.
|
||||||
GDCLASS(ProjectDialog, ConfirmationDialog);
|
|
||||||
|
|
||||||
public:
|
void ProjectDialog::_set_message(const String &p_msg, MessageType p_type, InputType input_type) {
|
||||||
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) {
|
|
||||||
msg->set_text(p_msg);
|
msg->set_text(p_msg);
|
||||||
Ref<Texture2D> current_path_icon = status_rect->get_texture();
|
Ref<Texture2D> current_path_icon = status_rect->get_texture();
|
||||||
Ref<Texture2D> current_install_icon = install_status_rect->get_texture();
|
Ref<Texture2D> current_install_icon = install_status_rect->get_texture();
|
||||||
|
|
@ -150,9 +102,9 @@ private:
|
||||||
if (window_size.x < contents_min_size.x || window_size.y < contents_min_size.y) {
|
if (window_size.x < contents_min_size.x || window_size.y < contents_min_size.y) {
|
||||||
set_size(window_size.max(contents_min_size));
|
set_size(window_size.max(contents_min_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _test_path() {
|
String ProjectDialog::_test_path() {
|
||||||
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||||
String valid_path, valid_install_path;
|
String valid_path, valid_install_path;
|
||||||
if (d->change_dir(project_path->get_text()) == OK) {
|
if (d->change_dir(project_path->get_text()) == OK) {
|
||||||
|
|
@ -170,7 +122,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid_path.is_empty()) {
|
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);
|
get_ok_button()->set_disabled(true);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +135,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valid_install_path.is_empty()) {
|
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);
|
get_ok_button()->set_disabled(true);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +149,7 @@ private:
|
||||||
|
|
||||||
unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io);
|
unzFile pkg = unzOpen2(valid_path.utf8().get_data(), &io);
|
||||||
if (!pkg) {
|
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);
|
get_ok_button()->set_disabled(true);
|
||||||
unzClose(pkg);
|
unzClose(pkg);
|
||||||
return "";
|
return "";
|
||||||
|
|
@ -220,7 +172,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == UNZ_END_OF_LIST_OF_FILE) {
|
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);
|
get_ok_button()->set_disabled(true);
|
||||||
unzClose(pkg);
|
unzClose(pkg);
|
||||||
return "";
|
return "";
|
||||||
|
|
@ -246,20 +198,20 @@ private:
|
||||||
d->list_dir_end();
|
d->list_dir_end();
|
||||||
|
|
||||||
if (!is_folder_empty) {
|
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);
|
get_ok_button()->set_disabled(true);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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();
|
install_path_container->hide();
|
||||||
get_ok_button()->set_disabled(true);
|
get_ok_button()->set_disabled(true);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (valid_path.ends_with("zip")) {
|
} 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);
|
get_ok_button()->set_disabled(true);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
@ -284,24 +236,24 @@ private:
|
||||||
|
|
||||||
if (!is_folder_empty) {
|
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()) {
|
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);
|
get_ok_button()->set_disabled(true);
|
||||||
return "";
|
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);
|
get_ok_button()->set_disabled(false);
|
||||||
return valid_path;
|
return valid_path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set_message("");
|
_set_message("");
|
||||||
set_message("", MESSAGE_SUCCESS, INSTALL_PATH);
|
_set_message("", MESSAGE_SUCCESS, INSTALL_PATH);
|
||||||
get_ok_button()->set_disabled(false);
|
get_ok_button()->set_disabled(false);
|
||||||
return valid_path;
|
return valid_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _path_text_changed(const String &p_path) {
|
void ProjectDialog::_path_text_changed(const String &p_path) {
|
||||||
String sp = _test_path();
|
String sp = _test_path();
|
||||||
if (!sp.is_empty()) {
|
if (!sp.is_empty()) {
|
||||||
// If the project name is empty or default, infer the project name from the selected folder name
|
// If the project name is empty or default, infer the project name from the selected folder name
|
||||||
|
|
@ -324,9 +276,9 @@ private:
|
||||||
if (!created_folder_path.is_empty() && created_folder_path != p_path) {
|
if (!created_folder_path.is_empty() && created_folder_path != p_path) {
|
||||||
_remove_created_folder();
|
_remove_created_folder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _file_selected(const String &p_path) {
|
void ProjectDialog::_file_selected(const String &p_path) {
|
||||||
String p = p_path;
|
String p = p_path;
|
||||||
if (mode == MODE_IMPORT) {
|
if (mode == MODE_IMPORT) {
|
||||||
if (p.ends_with("project.godot")) {
|
if (p.ends_with("project.godot")) {
|
||||||
|
|
@ -338,7 +290,7 @@ private:
|
||||||
install_path_container->show();
|
install_path_container->show();
|
||||||
get_ok_button()->set_disabled(false);
|
get_ok_button()->set_disabled(false);
|
||||||
} else {
|
} 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);
|
get_ok_button()->set_disabled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -352,23 +304,23 @@ private:
|
||||||
} else {
|
} else {
|
||||||
get_ok_button()->call_deferred(SNAME("grab_focus"));
|
get_ok_button()->call_deferred(SNAME("grab_focus"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _path_selected(const String &p_path) {
|
void ProjectDialog::_path_selected(const String &p_path) {
|
||||||
String sp = p_path.simplify_path();
|
String sp = p_path.simplify_path();
|
||||||
project_path->set_text(sp);
|
project_path->set_text(sp);
|
||||||
_path_text_changed(sp);
|
_path_text_changed(sp);
|
||||||
get_ok_button()->call_deferred(SNAME("grab_focus"));
|
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();
|
String sp = p_path.simplify_path();
|
||||||
install_path->set_text(sp);
|
install_path->set_text(sp);
|
||||||
_path_text_changed(sp);
|
_path_text_changed(sp);
|
||||||
get_ok_button()->call_deferred(SNAME("grab_focus"));
|
get_ok_button()->call_deferred(SNAME("grab_focus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _browse_path() {
|
void ProjectDialog::_browse_path() {
|
||||||
fdialog->set_current_dir(project_path->get_text());
|
fdialog->set_current_dir(project_path->get_text());
|
||||||
|
|
||||||
if (mode == MODE_IMPORT) {
|
if (mode == MODE_IMPORT) {
|
||||||
|
|
@ -380,18 +332,18 @@ private:
|
||||||
fdialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
|
fdialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
|
||||||
}
|
}
|
||||||
fdialog->popup_file_dialog();
|
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_current_dir(install_path->get_text());
|
||||||
fdialog_install->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
|
fdialog_install->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
|
||||||
fdialog_install->popup_file_dialog();
|
fdialog_install->popup_file_dialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _create_folder() {
|
void ProjectDialog::_create_folder() {
|
||||||
const String project_name_no_edges = project_name->get_text().strip_edges();
|
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(".")) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,9 +366,9 @@ private:
|
||||||
dialog_error->popup_centered();
|
dialog_error->popup_centered();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _text_changed(const String &p_text) {
|
void ProjectDialog::_text_changed(const String &p_text) {
|
||||||
if (mode != MODE_NEW) {
|
if (mode != MODE_NEW) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -424,16 +376,16 @@ private:
|
||||||
_test_path();
|
_test_path();
|
||||||
|
|
||||||
if (p_text.strip_edges().is_empty()) {
|
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;
|
is_folder_empty = true;
|
||||||
ok_pressed();
|
ok_pressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _renderer_selected() {
|
void ProjectDialog::_renderer_selected() {
|
||||||
String renderer_type = renderer_button_group->get_pressed_button()->get_meta(SNAME("rendering_method"));
|
String renderer_type = renderer_button_group->get_pressed_button()->get_meta(SNAME("rendering_method"));
|
||||||
|
|
||||||
if (renderer_type == "forward_plus") {
|
if (renderer_type == "forward_plus") {
|
||||||
|
|
@ -460,15 +412,25 @@ private:
|
||||||
} else {
|
} else {
|
||||||
WARN_PRINT("Unknown renderer type. Please report this as a bug on GitHub.");
|
WARN_PRINT("Unknown renderer type. Please report this as a bug on GitHub.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
String dir = project_path->get_text();
|
||||||
|
|
||||||
if (mode == MODE_RENAME) {
|
if (mode == MODE_RENAME) {
|
||||||
String dir2 = _test_path();
|
String dir2 = _test_path();
|
||||||
if (dir2.is_empty()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,12 +439,12 @@ private:
|
||||||
String project_godot = dir2.path_join("project.godot");
|
String project_godot = dir2.path_join("project.godot");
|
||||||
Error err = cfg.load(project_godot);
|
Error err = cfg.load(project_godot);
|
||||||
if (err != OK) {
|
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 {
|
} else {
|
||||||
cfg.set_value("application", "config/name", project_name->get_text().strip_edges());
|
cfg.set_value("application", "config/name", project_name->get_text().strip_edges());
|
||||||
err = cfg.save(project_godot);
|
err = cfg.save(project_godot);
|
||||||
if (err != OK) {
|
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";
|
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) {
|
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 {
|
} else {
|
||||||
// Store default project icon in SVG format.
|
// Store default project icon in SVG format.
|
||||||
Error err;
|
Error err;
|
||||||
|
|
@ -550,7 +512,7 @@ private:
|
||||||
fa_icon->store_string(get_default_project_icon());
|
fa_icon->store_string(get_default_project_icon());
|
||||||
|
|
||||||
if (err != OK) {
|
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);
|
EditorVCSInterface::create_vcs_metadata_files(EditorVCSInterface::VCSMetadata(vcs_metadata_selection->get_selected()), dir);
|
||||||
|
|
@ -663,19 +625,9 @@ private:
|
||||||
hide();
|
hide();
|
||||||
emit_signal(SNAME("project_created"), dir);
|
emit_signal(SNAME("project_created"), dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _remove_created_folder() {
|
void ProjectDialog::cancel_pressed() {
|
||||||
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 {
|
|
||||||
_remove_created_folder();
|
_remove_created_folder();
|
||||||
|
|
||||||
project_path->clear();
|
project_path->clear();
|
||||||
|
|
@ -690,39 +642,25 @@ private:
|
||||||
if (install_status_rect->get_texture() == msg->get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons"))) {
|
if (install_status_rect->get_texture() == msg->get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons"))) {
|
||||||
msg->show();
|
msg->show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _notification(int p_what) {
|
void ProjectDialog::set_zip_path(const String &p_path) {
|
||||||
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) {
|
|
||||||
zip_path = 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;
|
zip_title = p_title;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_mode(Mode p_mode) {
|
void ProjectDialog::set_mode(Mode p_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);
|
project_path->set_text(p_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_dialog() {
|
void ProjectDialog::show_dialog() {
|
||||||
if (mode == MODE_RENAME) {
|
if (mode == MODE_RENAME) {
|
||||||
project_path->set_editable(false);
|
project_path->set_editable(false);
|
||||||
browse->hide();
|
browse->hide();
|
||||||
|
|
@ -744,7 +682,7 @@ public:
|
||||||
String project_godot = project_path->get_text().path_join("project.godot");
|
String project_godot = project_path->get_text().path_join("project.godot");
|
||||||
Error err = cfg.load(project_godot);
|
Error err = cfg.load(project_godot);
|
||||||
if (err != OK) {
|
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();
|
status_rect->show();
|
||||||
msg->show();
|
msg->show();
|
||||||
get_ok_button()->set_disabled(true);
|
get_ok_button()->set_disabled(true);
|
||||||
|
|
@ -816,9 +754,22 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
popup_centered(Size2(500, 0) * EDSCALE);
|
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);
|
VBoxContainer *vb = memnew(VBoxContainer);
|
||||||
add_child(vb);
|
add_child(vb);
|
||||||
|
|
||||||
|
|
@ -1005,35 +956,14 @@ public:
|
||||||
fdialog_install->connect("file_selected", callable_mp(this, &ProjectDialog::_install_path_selected));
|
fdialog_install->connect("file_selected", callable_mp(this, &ProjectDialog::_install_path_selected));
|
||||||
|
|
||||||
set_hide_on_ok(false);
|
set_hide_on_ok(false);
|
||||||
mode = MODE_NEW;
|
|
||||||
|
|
||||||
dialog_error = memnew(AcceptDialog);
|
dialog_error = memnew(AcceptDialog);
|
||||||
add_child(dialog_error);
|
add_child(dialog_error);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
class ProjectListItemControl : public HBoxContainer {
|
/// Project List and friends.
|
||||||
GDCLASS(ProjectListItemControl, HBoxContainer)
|
|
||||||
public:
|
|
||||||
TextureButton *favorite_button;
|
|
||||||
TextureRect *icon;
|
|
||||||
bool icon_needs_reload;
|
|
||||||
bool hover;
|
|
||||||
|
|
||||||
ProjectListItemControl() {
|
void ProjectListItemControl::_notification(int p_what) {
|
||||||
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) {
|
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case NOTIFICATION_MOUSE_ENTER: {
|
case NOTIFICATION_MOUSE_ENTER: {
|
||||||
hover = true;
|
hover = true;
|
||||||
|
|
@ -1051,129 +981,18 @@ public:
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
class ProjectList : public ScrollContainer {
|
void ProjectListItemControl::set_is_favorite(bool fav) {
|
||||||
GDCLASS(ProjectList, ScrollContainer)
|
favorite_button->set_modulate(fav ? Color(1, 1, 1, 1) : Color(1, 1, 1, 0.2));
|
||||||
public:
|
}
|
||||||
static const char *SIGNAL_SELECTION_CHANGED;
|
|
||||||
static const char *SIGNAL_PROJECT_ASK_OPEN;
|
|
||||||
|
|
||||||
enum MenuOptions {
|
ProjectListItemControl::ProjectListItemControl() {
|
||||||
GLOBAL_NEW_WINDOW,
|
set_focus_mode(FocusMode::FOCUS_ALL);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
_FORCE_INLINE_ bool operator==(const Item &l) const {
|
|
||||||
return path == l.path;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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 {
|
struct ProjectListComparator {
|
||||||
FilterOption order_option = FilterOption::EDIT_DATE;
|
ProjectList::FilterOption order_option = ProjectList::FilterOption::EDIT_DATE;
|
||||||
|
|
||||||
// operator<
|
// operator<
|
||||||
_FORCE_INLINE_ bool operator()(const ProjectList::Item &a, const ProjectList::Item &b) const {
|
_FORCE_INLINE_ bool operator()(const ProjectList::Item &a, const ProjectList::Item &b) const {
|
||||||
|
|
@ -1184,9 +1003,9 @@ struct ProjectListComparator {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
switch (order_option) {
|
switch (order_option) {
|
||||||
case PATH:
|
case ProjectList::PATH:
|
||||||
return a.path < b.path;
|
return a.path < b.path;
|
||||||
case EDIT_DATE:
|
case ProjectList::EDIT_DATE:
|
||||||
return a.last_edited > b.last_edited;
|
return a.last_edited > b.last_edited;
|
||||||
default:
|
default:
|
||||||
return a.project_name < b.project_name;
|
return a.project_name < b.project_name;
|
||||||
|
|
@ -1194,24 +1013,8 @@ struct ProjectListComparator {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ProjectList::ProjectList() {
|
const char *ProjectList::SIGNAL_SELECTION_CHANGED = "selection_changed";
|
||||||
_order_option = FilterOption::EDIT_DATE;
|
const char *ProjectList::SIGNAL_PROJECT_ASK_OPEN = "project_ask_open";
|
||||||
_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProjectList::_notification(int p_what) {
|
void ProjectList::_notification(int p_what) {
|
||||||
switch (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) {
|
void ProjectList::load_project_icon(int p_index) {
|
||||||
Item &item = _projects.write[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;
|
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) {
|
ProjectList::Item ProjectList::load_project_data(const String &p_path, bool p_favorite) {
|
||||||
String conf = p_path.path_join("project.godot");
|
String conf = p_path.path_join("project.godot");
|
||||||
bool grayed = false;
|
bool grayed = false;
|
||||||
|
|
@ -1853,7 +1662,7 @@ void ProjectList::erase_selected_projects(bool p_delete_project_contents) {
|
||||||
update_dock_menu();
|
update_dock_menu();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draws selected project highlight
|
// Draws selected project highlight.
|
||||||
void ProjectList::_panel_draw(Node *p_hb) {
|
void ProjectList::_panel_draw(Node *p_hb) {
|
||||||
Control *hb = Object::cast_to<Control>(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) {
|
void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
|
||||||
Ref<InputEventMouseButton> mb = p_ev;
|
Ref<InputEventMouseButton> mb = p_ev;
|
||||||
int clicked_index = p_hb->get_index();
|
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);
|
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() {
|
void ProjectList::_bind_methods() {
|
||||||
ADD_SIGNAL(MethodInfo(SIGNAL_SELECTION_CHANGED));
|
ADD_SIGNAL(MethodInfo(SIGNAL_SELECTION_CHANGED));
|
||||||
ADD_SIGNAL(MethodInfo(SIGNAL_PROJECT_ASK_OPEN));
|
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;
|
ProjectManager *ProjectManager::singleton = nullptr;
|
||||||
|
|
||||||
void ProjectManager::_notification(int p_what) {
|
void ProjectManager::_notification(int p_what) {
|
||||||
|
|
@ -2438,6 +2254,7 @@ void ProjectManager::_scan_dir(const String &path) {
|
||||||
}
|
}
|
||||||
da->list_dir_end();
|
da->list_dir_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectManager::_scan_begin(const String &p_base) {
|
void ProjectManager::_scan_begin(const String &p_base) {
|
||||||
print_line("Scanning projects at: " + p_base);
|
print_line("Scanning projects at: " + p_base);
|
||||||
_scan_dir(p_base);
|
_scan_dir(p_base);
|
||||||
|
|
|
||||||
|
|
@ -31,21 +31,254 @@
|
||||||
#ifndef PROJECT_MANAGER_H
|
#ifndef PROJECT_MANAGER_H
|
||||||
#define PROJECT_MANAGER_H
|
#define PROJECT_MANAGER_H
|
||||||
|
|
||||||
|
#include "core/io/config_file.h"
|
||||||
#include "editor/editor_about.h"
|
#include "editor/editor_about.h"
|
||||||
#include "editor/plugins/asset_library_editor_plugin.h"
|
|
||||||
#include "scene/gui/dialogs.h"
|
#include "scene/gui/dialogs.h"
|
||||||
#include "scene/gui/file_dialog.h"
|
#include "scene/gui/file_dialog.h"
|
||||||
#include "scene/gui/scroll_container.h"
|
#include "scene/gui/scroll_container.h"
|
||||||
#include "scene/gui/tree.h"
|
|
||||||
|
|
||||||
class CheckBox;
|
class CheckBox;
|
||||||
class ProjectDialog;
|
class EditorAssetLibrary;
|
||||||
class ProjectList;
|
class EditorFileDialog;
|
||||||
|
|
||||||
enum FilterOption {
|
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,
|
EDIT_DATE,
|
||||||
NAME,
|
NAME,
|
||||||
PATH,
|
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 {
|
class ProjectManager : public Control {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue