diff options
Diffstat (limited to 'editor/project_manager.cpp')
-rw-r--r-- | editor/project_manager.cpp | 756 |
1 files changed, 535 insertions, 221 deletions
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index e042e9cc96..18416868ec 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -48,34 +48,73 @@ #include "scene/gui/separator.h" #include "scene/gui/texture_rect.h" #include "scene/gui/tool_button.h" +#include "translation.h" #include "version.h" #include "version_hash.gen.h" -class NewProjectDialog : public ConfirmationDialog { +class ProjectDialog : public ConfirmationDialog { - GDCLASS(NewProjectDialog, ConfirmationDialog); + GDCLASS(ProjectDialog, ConfirmationDialog); public: enum Mode { MODE_NEW, MODE_IMPORT, - MODE_INSTALL + MODE_INSTALL, + MODE_RENAME }; private: + enum MessageType { + MESSAGE_ERROR, + MESSAGE_WARNING, + MESSAGE_SUCCESS + }; + Mode mode; - Label *pp, *pn; - Label *error; + Button *browse; + Button *create_dir; + Container *name_container; + Container *path_container; + Label *msg; LineEdit *project_path; LineEdit *project_name; + ToolButton *status_btn; FileDialog *fdialog; 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) { + msg->set_text(p_msg); + if (p_msg == "") { + status_btn->set_icon(get_icon("StatusSuccess", "EditorIcons")); + return; + } + msg->hide(); + switch (p_type) { + case MESSAGE_ERROR: + msg->add_color_override("font_color", get_color("error_color", "Editor")); + status_btn->set_icon(get_icon("StatusError", "EditorIcons")); + msg->show(); + break; + case MESSAGE_WARNING: + msg->add_color_override("font_color", get_color("warning_color", "Editor")); + status_btn->set_icon(get_icon("StatusWarning", "EditorIcons")); + break; + case MESSAGE_SUCCESS: + msg->add_color_override("font_color", get_color("success_color", "Editor")); + status_btn->set_icon(get_icon("StatusSuccess", "EditorIcons")); + break; + } + } String _test_path() { - error->set_text(""); + set_message(" "); get_ok()->set_disabled(true); DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String valid_path; @@ -86,25 +125,44 @@ private: } if (valid_path == "") { - error->set_text(TTR("Invalid project path, the path must exist!")); + set_message(TTR("The path does not exist."), MESSAGE_ERROR); memdelete(d); return ""; } - if (mode != MODE_IMPORT) { + if (mode == MODE_IMPORT || mode == MODE_RENAME) { - if (d->file_exists("project.godot")) { + if (valid_path != "" && !d->file_exists("project.godot")) { - error->set_text(TTR("Invalid project path, project.godot must not exist.")); + set_message(TTR("Please choose a 'project.godot' file."), MESSAGE_ERROR); memdelete(d); return ""; } + } else if (mode == MODE_NEW) { + + // check if the specified folder is empty, even though this is not an error, it is good to check here + d->list_dir_begin(); + bool is_empty = true; + String n = d->get_next(); + while (n != String()) { + if (!n.begins_with(".")) { // i dont know if this is enough to guarantee an empty dir + is_empty = false; + break; + } + n = d->get_next(); + } + d->list_dir_end(); + + if (!is_empty) { + set_message(TTR("Your project will be created in a non empty folder (you might want to create a new folder)."), MESSAGE_WARNING); + } + } else { - if (valid_path != "" && !d->file_exists("project.godot")) { + if (d->file_exists("project.godot")) { - error->set_text(TTR("Invalid project path, project.godot must exist.")); + set_message(TTR("Please choose a folder that does not contain a 'project.godot' file."), MESSAGE_ERROR); memdelete(d); return ""; } @@ -120,16 +178,23 @@ private: String sp = _test_path(); if (sp != "") { - sp = sp.replace("\\", "/"); - int lidx = sp.find_last("/"); + // set the project name to the select folder name + if (project_name->get_text() == "") { + sp = sp.replace("\\", "/"); + int lidx = sp.find_last("/"); + + if (lidx != -1) { + sp = sp.substr(lidx + 1, sp.length()); + } + if (sp == "" && mode == MODE_IMPORT) + sp = TTR("Imported Project"); - if (lidx != -1) { - sp = sp.substr(lidx + 1, sp.length()); + project_name->set_text(sp); } - if (sp == "" && mode == MODE_IMPORT) - sp = TTR("Imported Project"); + } - project_name->set_text(sp); + if (created_folder_path != "" && created_folder_path != p_path) { + _remove_created_folder(); } } @@ -138,13 +203,17 @@ private: String p = p_path; if (mode == MODE_IMPORT) { if (p.ends_with("project.godot")) { - p = p.get_base_dir(); + get_ok()->set_disabled(false); + } else { + set_message(TTR("Please choose a 'project.godot' file."), MESSAGE_ERROR); + get_ok()->set_disabled(true); + return; } } String sp = p.simplify_path(); project_path->set_text(sp); - _path_text_changed(sp); + set_message(TTR(" ")); // just so it does not disappear get_ok()->call_deferred("grab_focus"); } @@ -153,12 +222,13 @@ private: String p = p_path; String sp = p.simplify_path(); project_path->set_text(sp); - _path_text_changed(sp); get_ok()->call_deferred("grab_focus"); } void _browse_path() { + fdialog->set_current_dir(project_path->get_text()); + if (mode == MODE_IMPORT) { fdialog->set_mode(FileDialog::MODE_OPEN_FILE); @@ -170,161 +240,238 @@ private: fdialog->popup_centered_ratio(); } + void _create_folder() { + + if (project_name->get_text() == "" || created_folder_path != "") { + return; + } + + DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (d->change_dir(project_path->get_text()) == OK) { + if (!d->dir_exists(project_name->get_text())) { + if (d->make_dir(project_name->get_text()) == OK) { + d->change_dir(project_name->get_text()); + project_path->set_text(d->get_current_dir()); + created_folder_path = d->get_current_dir(); + create_dir->set_disabled(true); + } + } + } + memdelete(d); + } + void _text_changed(const String &p_text) { + + if (mode != MODE_NEW) + return; + _test_path(); + + if (p_text == "") + set_message(TTR("It would be a good idea to name your project."), MESSAGE_WARNING); } void ok_pressed() { - String dir = _test_path(); - if (dir == "") { - error->set_text(TTR("Invalid project path (changed anything?).")); - return; - } + String dir = project_path->get_text(); - if (mode == MODE_IMPORT) { - // nothing to do + if (mode == MODE_RENAME) { + + String dir = _test_path(); + if (dir == "") { + set_message(TTR("Invalid project path (changed anything?)."), MESSAGE_ERROR); + return; + } + + ProjectSettings *current = memnew(ProjectSettings); + current->add_singleton(ProjectSettings::Singleton("Current")); + + if (current->setup(dir, "")) { + set_message(TTR("Couldn't get project.godot in project path."), MESSAGE_ERROR); + } else { + ProjectSettings::CustomMap edited_settings; + edited_settings["application/config/name"] = project_name->get_text(); + + if (current->save_custom(dir.plus_file("/project.godot"), edited_settings, Vector<String>(), true)) { + set_message(TTR("Couldn't edit project.godot in project path."), MESSAGE_ERROR); + } + } + + hide(); + emit_signal("project_renamed"); } else { - if (mode == MODE_NEW) { - ProjectSettings::CustomMap initial_settings; - initial_settings["application/config/name"] = project_name->get_text(); - initial_settings["application/config/icon"] = "res://icon.png"; - initial_settings["rendering/environment/default_environment"] = "res://default_env.tres"; + if (mode == MODE_IMPORT) { + // nothing to do + } else { + if (mode == MODE_NEW) { - if (ProjectSettings::get_singleton()->save_custom(dir.plus_file("/project.godot"), initial_settings, Vector<String>(), false)) { - error->set_text(TTR("Couldn't create project.godot in project path.")); - } else { - ResourceSaver::save(dir.plus_file("/icon.png"), get_icon("DefaultProjectIcon", "EditorIcons")); + ProjectSettings::CustomMap initial_settings; + initial_settings["application/config/name"] = project_name->get_text(); + initial_settings["application/config/icon"] = "res://icon.png"; + initial_settings["rendering/environment/default_environment"] = "res://default_env.tres"; - FileAccess *f = FileAccess::open(dir.plus_file("/default_env.tres"), FileAccess::WRITE); - if (!f) { - error->set_text(TTR("Couldn't create project.godot in project path.")); + if (ProjectSettings::get_singleton()->save_custom(dir.plus_file("/project.godot"), initial_settings, Vector<String>(), false)) { + set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR); } else { - f->store_line("[gd_resource type=\"Environment\" load_steps=2 format=2]"); - f->store_line("[sub_resource type=\"ProceduralSky\" id=1]"); - f->store_line("[resource]"); - f->store_line("background_mode = 2"); - f->store_line("background_sky = SubResource( 1 )"); - memdelete(f); + ResourceSaver::save(dir.plus_file("/icon.png"), get_icon("DefaultProjectIcon", "EditorIcons")); + + FileAccess *f = FileAccess::open(dir.plus_file("/default_env.tres"), FileAccess::WRITE); + if (!f) { + set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR); + } else { + f->store_line("[gd_resource type=\"Environment\" load_steps=2 format=2]"); + f->store_line("[sub_resource type=\"ProceduralSky\" id=1]"); + f->store_line("[resource]"); + f->store_line("background_mode = 2"); + f->store_line("background_sky = SubResource( 1 )"); + memdelete(f); + } } - } - } else if (mode == MODE_INSTALL) { + } else if (mode == MODE_INSTALL) { - FileAccess *src_f = NULL; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + FileAccess *src_f = NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - unzFile pkg = unzOpen2(zip_path.utf8().get_data(), &io); - if (!pkg) { + unzFile pkg = unzOpen2(zip_path.utf8().get_data(), &io); + if (!pkg) { - dialog_error->set_text(TTR("Error opening package file, not in zip format.")); - return; - } + dialog_error->set_text(TTR("Error opening package file, not in zip format.")); + return; + } - int ret = unzGoToFirstFile(pkg); + int ret = unzGoToFirstFile(pkg); - Vector<String> failed_files; + Vector<String> failed_files; - int idx = 0; - while (ret == UNZ_OK) { + int idx = 0; + while (ret == UNZ_OK) { - //get filename - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0); + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0); - String path = fname; + String path = fname; - int depth = 1; //stuff from github comes with tag - bool skip = false; - while (depth > 0) { - int pp = path.find("/"); - if (pp == -1) { - skip = true; - break; + int depth = 1; //stuff from github comes with tag + bool skip = false; + while (depth > 0) { + int pp = path.find("/"); + if (pp == -1) { + skip = true; + break; + } + path = path.substr(pp + 1, path.length()); + depth--; } - path = path.substr(pp + 1, path.length()); - depth--; - } - if (skip || path == String()) { - // - } else if (path.ends_with("/")) { // a dir + if (skip || path == String()) { + // + } else if (path.ends_with("/")) { // a dir - path = path.substr(0, path.length() - 1); + path = path.substr(0, path.length() - 1); - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - da->make_dir(dir.plus_file(path)); - memdelete(da); + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + da->make_dir(dir.plus_file(path)); + memdelete(da); - } else { + } else { - Vector<uint8_t> data; - data.resize(info.uncompressed_size); + Vector<uint8_t> data; + data.resize(info.uncompressed_size); - //read - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptr(), data.size()); - unzCloseCurrentFile(pkg); + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptr(), data.size()); + unzCloseCurrentFile(pkg); - FileAccess *f = FileAccess::open(dir.plus_file(path), FileAccess::WRITE); + FileAccess *f = FileAccess::open(dir.plus_file(path), FileAccess::WRITE); - if (f) { - f->store_buffer(data.ptr(), data.size()); - memdelete(f); - } else { - failed_files.push_back(path); + if (f) { + f->store_buffer(data.ptr(), data.size()); + memdelete(f); + } else { + failed_files.push_back(path); + } } - } - idx++; - ret = unzGoToNextFile(pkg); - } + idx++; + ret = unzGoToNextFile(pkg); + } - unzClose(pkg); + unzClose(pkg); - if (failed_files.size()) { - String msg = TTR("The following files failed extraction from package:") + "\n\n"; - for (int i = 0; i < failed_files.size(); i++) { + if (failed_files.size()) { + String msg = TTR("The following files failed extraction from package:") + "\n\n"; + for (int i = 0; i < failed_files.size(); i++) { - if (i > 15) { - msg += "\nAnd " + itos(failed_files.size() - i) + " more files."; - break; + if (i > 15) { + msg += "\nAnd " + itos(failed_files.size() - i) + " more files."; + break; + } + msg += failed_files[i] + "\n"; } - msg += failed_files[i] + "\n"; - } - dialog_error->set_text(msg); - dialog_error->popup_centered_minsize(); + dialog_error->set_text(msg); + dialog_error->popup_centered_minsize(); - } else { - dialog_error->set_text(TTR("Package Installed Successfully!")); - dialog_error->popup_centered_minsize(); + } else { + dialog_error->set_text(TTR("Package Installed Successfully!")); + dialog_error->popup_centered_minsize(); + } } } + + dir = dir.replace("\\", "/"); + if (dir.ends_with("/")) + dir = dir.substr(0, dir.length() - 1); + String proj = dir.replace("/", "::"); + EditorSettings::get_singleton()->set("projects/" + proj, dir); + EditorSettings::get_singleton()->save(); + + hide(); + emit_signal("project_created", dir); } + } + + void _remove_created_folder() { - dir = dir.replace("\\", "/"); - if (dir.ends_with("/")) - dir = dir.substr(0, dir.length() - 1); - String proj = dir.replace("/", "::"); - EditorSettings::get_singleton()->set("projects/" + proj, dir); - EditorSettings::get_singleton()->save(); + if (created_folder_path != "") { + DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + d->remove(created_folder_path); + memdelete(d); - hide(); - emit_signal("project_created", dir); + create_dir->set_disabled(false); + created_folder_path = ""; + } + } + + void _toggle_message() { + msg->set_visible(!msg->is_visible()); + } + + void cancel_pressed() { + + _remove_created_folder(); + + project_path->clear(); + project_name->clear(); } protected: static void _bind_methods() { - ClassDB::bind_method("_browse_path", &NewProjectDialog::_browse_path); - ClassDB::bind_method("_text_changed", &NewProjectDialog::_text_changed); - ClassDB::bind_method("_path_text_changed", &NewProjectDialog::_path_text_changed); - ClassDB::bind_method("_path_selected", &NewProjectDialog::_path_selected); - ClassDB::bind_method("_file_selected", &NewProjectDialog::_file_selected); + ClassDB::bind_method("_browse_path", &ProjectDialog::_browse_path); + ClassDB::bind_method("_create_folder", &ProjectDialog::_create_folder); + ClassDB::bind_method("_text_changed", &ProjectDialog::_text_changed); + ClassDB::bind_method("_path_text_changed", &ProjectDialog::_path_text_changed); + ClassDB::bind_method("_path_selected", &ProjectDialog::_path_selected); + ClassDB::bind_method("_file_selected", &ProjectDialog::_file_selected); + ClassDB::bind_method("_toggle_message", &ProjectDialog::_toggle_message); ADD_SIGNAL(MethodInfo("project_created")); + ADD_SIGNAL(MethodInfo("project_renamed")); } public: @@ -340,97 +487,138 @@ public: mode = p_mode; } + void set_project_path(const String &p_path) { + project_path->set_text(p_path); + } + void show_dialog() { - project_path->clear(); - project_name->clear(); + if (mode == MODE_RENAME) { - if (mode == MODE_IMPORT) { - set_title(TTR("Import Existing Project")); - get_ok()->set_text(TTR("Import")); - pp->set_text(TTR("Project Path (Must Exist):")); - pn->set_text(TTR("Project Name:")); - pn->hide(); - project_name->hide(); + project_path->set_editable(false); + browse->hide(); - popup_centered(Size2(500, 125) * EDSCALE); + set_title(TTR("Rename Project")); + get_ok()->set_text(TTR("Rename")); + name_container->show(); - } else if (mode == MODE_NEW) { + ProjectSettings *current = memnew(ProjectSettings); + current->add_singleton(ProjectSettings::Singleton("Current")); + + if (current->setup(project_path->get_text(), "")) { + set_message(TTR("Couldn't get project.godot in the project path."), MESSAGE_ERROR); + } else if (current->has_setting("application/config/name")) { + project_name->set_text(current->get("application/config/name")); + } + project_name->grab_focus(); + + create_dir->hide(); + status_btn->hide(); + + } else { + + fav_dir = EditorSettings::get_singleton()->get("filesystem/directories/default_project_path"); + if (fav_dir != "") { + project_path->set_text(fav_dir); + fdialog->set_current_dir(fav_dir); + } else { + DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + project_path->set_text(d->get_current_dir()); + fdialog->set_current_dir(d->get_current_dir()); + memdelete(d); + } + project_name->set_text(TTR("New Game Project")); + + project_path->set_editable(true); + browse->set_disabled(false); + browse->show(); + create_dir->show(); + status_btn->show(); - set_title(TTR("Create New Project")); - get_ok()->set_text(TTR("Create")); - pp->set_text(TTR("Project Path:")); - pn->set_text(TTR("Project Name:")); - pn->show(); - project_name->show(); + if (mode == MODE_IMPORT) { + set_title(TTR("Import Existing Project")); + get_ok()->set_text(TTR("Import")); + name_container->hide(); + project_path->grab_focus(); - popup_centered(Size2(500, 145) * EDSCALE); - } else if (mode == MODE_INSTALL) { + } else if (mode == MODE_NEW) { - set_title(TTR("Install Project:") + " " + zip_title); - get_ok()->set_text(TTR("Install")); - pp->set_text(TTR("Project Path:")); - pn->hide(); - project_name->hide(); + set_title(TTR("Create New Project")); + get_ok()->set_text(TTR("Create")); + name_container->show(); + project_name->grab_focus(); - popup_centered(Size2(500, 125) * EDSCALE); + } else if (mode == MODE_INSTALL) { + + set_title(TTR("Install Project:") + " " + zip_title); + get_ok()->set_text(TTR("Install")); + name_container->hide(); + project_path->grab_focus(); + } + + _test_path(); } - project_path->grab_focus(); - _test_path(); + popup_centered(Size2(500, 125) * EDSCALE); } - NewProjectDialog() { + ProjectDialog() { VBoxContainer *vb = memnew(VBoxContainer); add_child(vb); - //set_child_rect(vb); + + name_container = memnew(VBoxContainer); + vb->add_child(name_container); Label *l = memnew(Label); + l->set_text(TTR("Project Name:")); + name_container->add_child(l); + + HBoxContainer *pnhb = memnew(HBoxContainer); + name_container->add_child(pnhb); + + project_name = memnew(LineEdit); + project_name->set_h_size_flags(SIZE_EXPAND_FILL); + pnhb->add_child(project_name); + + create_dir = memnew(Button); + pnhb->add_child(create_dir); + create_dir->set_text(TTR("Create folder")); + create_dir->connect("pressed", this, "_create_folder"); + + path_container = memnew(VBoxContainer); + vb->add_child(path_container); + + l = memnew(Label); l->set_text(TTR("Project Path:")); - vb->add_child(l); - pp = l; + path_container->add_child(l); - project_path = memnew(LineEdit); - MarginContainer *mc = memnew(MarginContainer); - vb->add_child(mc); HBoxContainer *pphb = memnew(HBoxContainer); - mc->add_child(pphb); - pphb->add_child(project_path); + path_container->add_child(pphb); + + project_path = memnew(LineEdit); project_path->set_h_size_flags(SIZE_EXPAND_FILL); + pphb->add_child(project_path); - Button *browse = memnew(Button); - pphb->add_child(browse); + // status button + status_btn = memnew(ToolButton); + status_btn->connect("pressed", this, "_toggle_message"); + pphb->add_child(status_btn); + + browse = memnew(Button); browse->set_text(TTR("Browse")); browse->connect("pressed", this, "_browse_path"); + pphb->add_child(browse); - l = memnew(Label); - l->set_text(TTR("Project Name:")); - l->set_position(Point2(5, 50)); - vb->add_child(l); - pn = l; - - project_name = memnew(LineEdit); - mc = memnew(MarginContainer); - vb->add_child(mc); - mc->add_child(project_name); - project_name->set_text(TTR("New Game Project")); - - l = memnew(Label); - l->set_text(TTR("That's a BINGO!")); - vb->add_child(l); - error = l; - l->add_color_override("font_color", Color(1, 0.4, 0.3, 0.8)); - l->set_align(Label::ALIGN_CENTER); - - DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - project_path->set_text(d->get_current_dir()); - memdelete(d); + msg = memnew(Label); + msg->set_text(TTR("That's a BINGO!")); + msg->set_align(Label::ALIGN_CENTER); + msg->hide(); + vb->add_child(msg); fdialog = memnew(FileDialog); - add_child(fdialog); fdialog->set_access(FileDialog::ACCESS_FILESYSTEM); - fdialog->set_current_dir(EditorSettings::get_singleton()->get("filesystem/directories/default_project_path")); + add_child(fdialog); project_name->connect("text_changed", this, "_text_changed"); project_path->connect("text_changed", this, "_path_text_changed"); fdialog->connect("dir_selected", this, "_path_selected"); @@ -449,13 +637,15 @@ struct ProjectItem { String conf; uint64_t last_modified; bool favorite; + bool grayed; ProjectItem() {} - ProjectItem(const String &p_project, const String &p_path, const String &p_conf, uint64_t p_last_modified, bool p_favorite = false) { + ProjectItem(const String &p_project, const String &p_path, const String &p_conf, uint64_t p_last_modified, bool p_favorite = false, bool p_grayed = false) { project = p_project; path = p_path; conf = p_conf; last_modified = p_last_modified; favorite = p_favorite; + grayed = p_grayed; } _FORCE_INLINE_ bool operator<(const ProjectItem &l) const { return last_modified > l.last_modified; } _FORCE_INLINE_ bool operator==(const ProjectItem &l) const { return project == l.project; } @@ -465,7 +655,7 @@ void ProjectManager::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { - Engine::get_singleton()->set_editor_hint(true); + Engine::get_singleton()->set_editor_hint(false); } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { @@ -480,7 +670,7 @@ void ProjectManager::_panel_draw(Node *p_hb) { hb->draw_line(Point2(0, hb->get_size().y + 1), Point2(hb->get_size().x - 10, hb->get_size().y + 1), get_color("guide_color", "Tree")); if (selected_list.has(hb->get_meta("name"))) { - hb->draw_style_box(gui_base->get_stylebox("selected", "Tree"), Rect2(Point2(), hb->get_size() - Size2(10, 0))); + hb->draw_style_box(gui_base->get_stylebox("selected", "Tree"), Rect2(Point2(), hb->get_size() - Size2(10, 0) * EDSCALE)); } } @@ -493,6 +683,7 @@ void ProjectManager::_update_project_buttons() { erase_btn->set_disabled(selected_list.size() < 1); open_btn->set_disabled(selected_list.size() < 1); + rename_btn->set_disabled(selected_list.size() < 1); } void ProjectManager::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) { @@ -560,6 +751,9 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) { if (!k->is_pressed()) return; + if (tabs->get_current_tab() != 0) + return; + bool scancode_handled = true; switch (k->get_scancode()) { @@ -737,6 +931,7 @@ void ProjectManager::_load_recent_projects() { String project = _name.get_slice("/", 1); String conf = path.plus_file("project.godot"); bool favorite = (_name.begins_with("favorite_projects/")) ? true : false; + bool grayed = false; uint64_t last_modified = 0; if (FileAccess::exists(conf)) { @@ -748,16 +943,15 @@ void ProjectManager::_load_recent_projects() { if (cache_modified > last_modified) last_modified = cache_modified; } - - ProjectItem item(project, path, conf, last_modified, favorite); - if (favorite) - favorite_projects.push_back(item); - else - projects.push_back(item); } else { - //project doesn't exist on disk but it's in the XML settings file - EditorSettings::get_singleton()->erase(_name); //remove it + grayed = true; } + + ProjectItem item(project, path, conf, last_modified, favorite, grayed); + if (favorite) + favorite_projects.push_back(item); + else + projects.push_back(item); } projects.sort(); @@ -782,14 +976,14 @@ void ProjectManager::_load_recent_projects() { String path = item.path; String conf = item.conf; bool is_favorite = item.favorite; + bool is_grayed = item.grayed; Ref<ConfigFile> cf = memnew(ConfigFile); - Error err = cf->load(conf); - ERR_CONTINUE(err != OK); + Error cf_err = cf->load(conf); String project_name = TTR("Unnamed Project"); - if (cf->has_section_key("application", "config/name")) { + if (cf_err == OK && cf->has_section_key("application", "config/name")) { project_name = static_cast<String>(cf->get_value("application", "config/name")).xml_unescape(); } @@ -797,7 +991,7 @@ void ProjectManager::_load_recent_projects() { continue; Ref<Texture> icon; - if (cf->has_section_key("application", "config/icon")) { + if (cf_err == OK && cf->has_section_key("application", "config/icon")) { String appicon = cf->get_value("application", "config/icon"); if (appicon != "") { Ref<Image> img; @@ -819,8 +1013,10 @@ void ProjectManager::_load_recent_projects() { } String main_scene; - if (cf->has_section_key("application", "run/main_scene")) { + if (cf_err == OK && cf->has_section_key("application", "run/main_scene")) { main_scene = cf->get_value("application", "run/main_scene"); + } else { + main_scene = ""; } selected_list_copy.erase(project); @@ -848,6 +1044,8 @@ void ProjectManager::_load_recent_projects() { hb->add_child(tf); VBoxContainer *vb = memnew(VBoxContainer); + if (is_grayed) + vb->set_modulate(Color(0.5, 0.5, 0.5)); vb->set_name("project"); vb->set_h_size_flags(SIZE_EXPAND_FILL); hb->add_child(vb); @@ -883,6 +1081,10 @@ void ProjectManager::_load_recent_projects() { tabs->set_current_tab(0); } +void ProjectManager::_on_project_renamed() { + _load_recent_projects(); +} + void ProjectManager::_on_project_created(const String &dir) { bool has_already = false; for (int i = 0; i < scroll_childs->get_child_count(); i++) { @@ -894,15 +1096,15 @@ void ProjectManager::_on_project_created(const String &dir) { } } if (has_already) { - _update_scroll_pos(dir); + _update_scroll_position(dir); } else { _load_recent_projects(); - _update_scroll_pos(dir); + _update_scroll_position(dir); } _open_project(); } -void ProjectManager::_update_scroll_pos(const String &dir) { +void ProjectManager::_update_scroll_position(const String &dir) { for (int i = 0; i < scroll_childs->get_child_count(); i++) { HBoxContainer *hb = Object::cast_to<HBoxContainer>(scroll_childs->get_child(i)); Label *fpath = Object::cast_to<Label>(hb->get_node(NodePath("project/path"))); @@ -926,6 +1128,13 @@ void ProjectManager::_open_project_confirm() { for (Map<String, String>::Element *E = selected_list.front(); E; E = E->next()) { const String &selected = E->key(); String path = EditorSettings::get_singleton()->get("projects/" + selected); + String conf = path + "/project.godot"; + if (!FileAccess::exists(conf)) { + dialog_error->set_text(TTR("Can't open project")); + dialog_error->popup_centered_minsize(); + return; + } + print_line("OPENING: " + path + " (" + selected + ")"); List<String> args; @@ -935,6 +1144,10 @@ void ProjectManager::_open_project_confirm() { args.push_back("--editor"); + if (OS::get_singleton()->is_disable_crash_handler()) { + args.push_back("--disable-crash-handler"); + } + String exec = OS::get_singleton()->get_executable_path(); OS::ProcessID pid = 0; @@ -986,6 +1199,10 @@ void ProjectManager::_run_project_confirm() { args.push_back("--path"); args.push_back(path); + if (OS::get_singleton()->is_disable_crash_handler()) { + args.push_back("--disable-crash-handler"); + } + String exec = OS::get_singleton()->get_executable_path(); OS::ProcessID pid = 0; @@ -1060,16 +1277,31 @@ void ProjectManager::_scan_projects() { void ProjectManager::_new_project() { - npdialog->set_mode(NewProjectDialog::MODE_NEW); + npdialog->set_mode(ProjectDialog::MODE_NEW); npdialog->show_dialog(); } void ProjectManager::_import_project() { - npdialog->set_mode(NewProjectDialog::MODE_IMPORT); + npdialog->set_mode(ProjectDialog::MODE_IMPORT); npdialog->show_dialog(); } +void ProjectManager::_rename_project() { + + if (selected_list.size() == 0) { + return; + } + + for (Map<String, String>::Element *E = selected_list.front(); E; E = E->next()) { + const String &selected = E->key(); + String path = EditorSettings::get_singleton()->get("projects/" + selected); + npdialog->set_project_path(path); + npdialog->set_mode(ProjectDialog::MODE_RENAME); + npdialog->show_dialog(); + } +} + void ProjectManager::_erase_project_confirm() { if (selected_list.size() == 0) { @@ -1094,6 +1326,28 @@ void ProjectManager::_erase_project() { erase_ask->popup_centered_minsize(); } +void ProjectManager::_language_selected(int p_id) { + + String lang = language_btn->get_item_metadata(p_id); + EditorSettings::get_singleton()->set("interface/editor/editor_language", lang); + language_btn->set_text(lang); + language_btn->set_icon(get_icon("Environment", "EditorIcons")); + + language_restart_ask->set_text(TTR("Language changed.\nThe UI will update next time the editor or project manager starts.")); + language_restart_ask->popup_centered(); +} + +void ProjectManager::_restart_confirm() { + + List<String> args = OS::get_singleton()->get_cmdline_args(); + String exec = OS::get_singleton()->get_executable_path(); + OS::ProcessID pid = 0; + Error err = OS::get_singleton()->execute(exec, args, false, &pid); + ERR_FAIL_COND(err); + + get_tree()->quit(); +} + void ProjectManager::_exit_dialog() { get_tree()->quit(); @@ -1101,7 +1355,7 @@ void ProjectManager::_exit_dialog() { void ProjectManager::_install_project(const String &p_zip_path, const String &p_title) { - npdialog->set_mode(NewProjectDialog::MODE_INSTALL); + npdialog->set_mode(ProjectDialog::MODE_INSTALL); npdialog->set_zip_path(p_zip_path); npdialog->set_zip_title(p_title); npdialog->show_dialog(); @@ -1164,12 +1418,16 @@ void ProjectManager::_bind_methods() { ClassDB::bind_method("_scan_begin", &ProjectManager::_scan_begin); ClassDB::bind_method("_import_project", &ProjectManager::_import_project); ClassDB::bind_method("_new_project", &ProjectManager::_new_project); + ClassDB::bind_method("_rename_project", &ProjectManager::_rename_project); ClassDB::bind_method("_erase_project", &ProjectManager::_erase_project); ClassDB::bind_method("_erase_project_confirm", &ProjectManager::_erase_project_confirm); + ClassDB::bind_method("_language_selected", &ProjectManager::_language_selected); + ClassDB::bind_method("_restart_confirm", &ProjectManager::_restart_confirm); ClassDB::bind_method("_exit_dialog", &ProjectManager::_exit_dialog); ClassDB::bind_method("_load_recent_projects", &ProjectManager::_load_recent_projects); + ClassDB::bind_method("_on_project_renamed", &ProjectManager::_on_project_renamed); ClassDB::bind_method("_on_project_created", &ProjectManager::_on_project_created); - ClassDB::bind_method("_update_scroll_pos", &ProjectManager::_update_scroll_pos); + ClassDB::bind_method("_update_scroll_position", &ProjectManager::_update_scroll_position); ClassDB::bind_method("_panel_draw", &ProjectManager::_panel_draw); ClassDB::bind_method("_panel_input", &ProjectManager::_panel_input); ClassDB::bind_method("_unhandled_input", &ProjectManager::_unhandled_input); @@ -1188,9 +1446,10 @@ ProjectManager::ProjectManager() { EditorSettings::get_singleton()->set_optimize_save(false); //just write settings as they came { - int dpi_mode = EditorSettings::get_singleton()->get("interface/hidpi_mode"); + int dpi_mode = EditorSettings::get_singleton()->get("interface/editor/hidpi_mode"); if (dpi_mode == 0) { - editor_set_scale(OS::get_singleton()->get_screen_dpi(0) >= 192 && OS::get_singleton()->get_screen_size(OS::get_singleton()->get_current_screen()).x > 2000 ? 2.0 : 1.0); + const int screen = OS::get_singleton()->get_current_screen(); + editor_set_scale(OS::get_singleton()->get_screen_dpi(screen) >= 192 && OS::get_singleton()->get_screen_size(screen).x > 2000 ? 2.0 : 1.0); } else if (dpi_mode == 1) { editor_set_scale(0.75); } else if (dpi_mode == 2) { @@ -1204,21 +1463,21 @@ ProjectManager::ProjectManager() { FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files")); - set_area_as_parent_rect(); + set_anchors_and_margins_preset(Control::PRESET_WIDE); set_theme(create_editor_theme()); gui_base = memnew(Control); add_child(gui_base); - gui_base->set_area_as_parent_rect(); + gui_base->set_anchors_and_margins_preset(Control::PRESET_WIDE); gui_base->set_theme(create_custom_theme()); Panel *panel = memnew(Panel); gui_base->add_child(panel); - panel->set_area_as_parent_rect(); + panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); VBoxContainer *vb = memnew(VBoxContainer); panel->add_child(vb); - vb->set_area_as_parent_rect(20 * EDSCALE); + vb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 20 * EDSCALE); vb->set_margin(MARGIN_TOP, 4 * EDSCALE); vb->set_margin(MARGIN_BOTTOM, -4 * EDSCALE); vb->add_constant_override("separation", 15 * EDSCALE); @@ -1226,7 +1485,7 @@ ProjectManager::ProjectManager() { String cp; cp.push_back(0xA9); cp.push_back(0); - OS::get_singleton()->set_window_title(_MKSTR(VERSION_NAME) + String(" - ") + TTR("Project Manager") + " - " + cp + " 2008-2017 Juan Linietsky, Ariel Manzur."); + OS::get_singleton()->set_window_title(_MKSTR(VERSION_NAME) + String(" - ") + TTR("Project Manager") + " - " + cp + " 2008-2017 Juan Linietsky, Ariel Manzur & Godot Contributors"); HBoxContainer *top_hb = memnew(HBoxContainer); vb->add_child(top_hb); @@ -1248,9 +1507,13 @@ ProjectManager::ProjectManager() { //vb->add_child(memnew(HSeparator)); //vb->add_margin_child("\n",memnew(Control)); + Control *center_box = memnew(Control); + center_box->set_v_size_flags(SIZE_EXPAND_FILL); + vb->add_child(center_box); + tabs = memnew(TabContainer); - vb->add_child(tabs); - tabs->set_v_size_flags(SIZE_EXPAND_FILL); + center_box->add_child(tabs); + tabs->set_anchors_and_margins_preset(Control::PRESET_WIDE); HBoxContainer *tree_hb = memnew(HBoxContainer); projects_hb = tree_hb; @@ -1328,6 +1591,12 @@ ProjectManager::ProjectManager() { tree_vb->add_child(import); import->connect("pressed", this, "_import_project"); + Button *rename = memnew(Button); + rename->set_text(TTR("Rename")); + tree_vb->add_child(rename); + rename->connect("pressed", this, "_rename_project"); + rename_btn = rename; + Button *erase = memnew(Button); erase->set_text(TTR("Remove")); tree_vb->add_child(erase); @@ -1345,6 +1614,40 @@ ProjectManager::ProjectManager() { WARN_PRINT("Asset Library not available, as it requires SSL to work."); } + HBoxContainer *settings_hb = memnew(HBoxContainer); + settings_hb->set_alignment(BoxContainer::ALIGN_END); + settings_hb->set_h_grow_direction(Control::GROW_DIRECTION_BEGIN); + + language_btn = memnew(OptionButton); + + Vector<String> editor_languages; + List<PropertyInfo> editor_settings_properties; + EditorSettings::get_singleton()->get_property_list(&editor_settings_properties); + for (List<PropertyInfo>::Element *E = editor_settings_properties.front(); E; E = E->next()) { + PropertyInfo &pi = E->get(); + if (pi.name == "interface/editor/editor_language") { + editor_languages = pi.hint_string.split(","); + } + } + String current_lang = EditorSettings::get_singleton()->get("interface/editor/editor_language"); + for (int i = 0; i < editor_languages.size(); i++) { + String lang = editor_languages[i]; + String lang_name = TranslationServer::get_singleton()->get_locale_name(lang); + language_btn->add_item(lang_name + " [" + lang + "]", i); + language_btn->set_item_metadata(i, lang); + if (current_lang == lang) { + language_btn->select(i); + language_btn->set_text(lang); + } + } + language_btn->set_icon(get_icon("Environment", "EditorIcons")); + + settings_hb->add_child(language_btn); + language_btn->connect("item_selected", this, "_language_selected"); + + center_box->add_child(settings_hb); + settings_hb->set_anchors_and_margins_preset(Control::PRESET_TOP_RIGHT); + CenterContainer *cc = memnew(CenterContainer); Button *cancel = memnew(Button); cancel->set_text(TTR("Exit")); @@ -1355,6 +1658,13 @@ ProjectManager::ProjectManager() { // + language_restart_ask = memnew(ConfirmationDialog); + language_restart_ask->get_ok()->set_text(TTR("Restart Now")); + language_restart_ask->get_ok()->connect("pressed", this, "_restart_confirm"); + language_restart_ask->get_cancel()->set_text(TTR("Continue")); + + gui_base->add_child(language_restart_ask); + erase_ask = memnew(ConfirmationDialog); erase_ask->get_ok()->set_text(TTR("Remove")); erase_ask->get_ok()->connect("pressed", this, "_erase_project_confirm"); @@ -1380,9 +1690,10 @@ ProjectManager::ProjectManager() { OS::get_singleton()->set_low_processor_usage_mode(true); - npdialog = memnew(NewProjectDialog); + npdialog = memnew(ProjectDialog); gui_base->add_child(npdialog); + npdialog->connect("project_renamed", this, "_on_project_renamed"); npdialog->connect("project_created", this, "_on_project_created"); _load_recent_projects(); @@ -1397,6 +1708,9 @@ ProjectManager::ProjectManager() { run_error_diag = memnew(AcceptDialog); gui_base->add_child(run_error_diag); run_error_diag->set_title(TTR("Can't run project")); + + dialog_error = memnew(AcceptDialog); + gui_base->add_child(dialog_error); } ProjectManager::~ProjectManager() { |