From 7496f99060d7655cbf974dba3851838faba629d4 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Tue, 30 Jan 2024 17:35:46 +0100 Subject: [PATCH 1/6] Create a MainScreenButton theme variation This turns custom font theme properties into a proper variation, which makes applying it simpler. --- editor/editor_node.cpp | 15 +++++---------- editor/themes/editor_fonts.cpp | 5 +++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index e676d24c67e..579b526b5eb 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -519,11 +519,6 @@ void EditorNode::_update_theme(bool p_skip_creation) { help_menu->set_item_icon(help_menu->get_item_index(HELP_ABOUT), theme->get_icon(SNAME("Godot"), EditorStringName(EditorIcons))); help_menu->set_item_icon(help_menu->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), theme->get_icon(SNAME("Heart"), EditorStringName(EditorIcons))); - for (int i = 0; i < main_editor_buttons.size(); i++) { - main_editor_buttons.write[i]->add_theme_font_override("font", theme->get_font(SNAME("main_button_font"), EditorStringName(EditorFonts))); - main_editor_buttons.write[i]->add_theme_font_size_override("font_size", theme->get_font_size(SNAME("main_button_font_size"), EditorStringName(EditorFonts))); - } - if (EditorDebuggerNode::get_singleton()->is_visible()) { bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))); } @@ -3279,21 +3274,21 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed Button *tb = memnew(Button); tb->set_flat(true); tb->set_toggle_mode(true); - tb->connect("pressed", callable_mp(singleton, &EditorNode::editor_select).bind(singleton->main_editor_buttons.size())); + tb->set_theme_type_variation("MainScreenButton"); tb->set_name(p_editor->get_name()); tb->set_text(p_editor->get_name()); Ref icon = p_editor->get_icon(); + if (icon.is_null() && singleton->theme->has_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { + icon = singleton->theme->get_icon(p_editor->get_name(), EditorStringName(EditorIcons)); + } if (icon.is_valid()) { tb->set_icon(icon); // Make sure the control is updated if the icon is reimported. icon->connect_changed(callable_mp((Control *)tb, &Control::update_minimum_size)); - } else if (singleton->theme->has_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { - tb->set_icon(singleton->theme->get_icon(p_editor->get_name(), EditorStringName(EditorIcons))); } - tb->add_theme_font_override("font", singleton->theme->get_font(SNAME("main_button_font"), EditorStringName(EditorFonts))); - tb->add_theme_font_size_override("font_size", singleton->theme->get_font_size(SNAME("main_button_font_size"), EditorStringName(EditorFonts))); + tb->connect("pressed", callable_mp(singleton, &EditorNode::editor_select).bind(singleton->main_editor_buttons.size())); singleton->main_editor_buttons.push_back(tb); singleton->main_editor_button_hb->add_child(tb); diff --git a/editor/themes/editor_fonts.cpp b/editor/themes/editor_fonts.cpp index ee613877027..a4f72b26e3e 100644 --- a/editor/themes/editor_fonts.cpp +++ b/editor/themes/editor_fonts.cpp @@ -390,8 +390,9 @@ void editor_register_fonts(const Ref &p_theme) { p_theme->set_font("title", EditorStringName(EditorFonts), bold_fc); p_theme->set_font_size("title_size", EditorStringName(EditorFonts), default_font_size + 1 * EDSCALE); - p_theme->set_font("main_button_font", EditorStringName(EditorFonts), bold_fc); - p_theme->set_font_size("main_button_font_size", EditorStringName(EditorFonts), default_font_size + 1 * EDSCALE); + p_theme->set_type_variation("MainScreenButton", "Button"); + p_theme->set_font("font", "MainScreenButton", bold_fc); + p_theme->set_font_size("font_size", "MainScreenButton", default_font_size + 2 * EDSCALE); p_theme->set_font("font", "Label", default_fc); From bac037b1e0adc20aa37f2920f586ed9f8ec0e3f0 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Tue, 30 Jan 2024 17:35:46 +0100 Subject: [PATCH 2/6] Improve the project manager UI --- editor/icons/ProjectList.svg | 1 + editor/icons/TitleBarLogo.svg | 1 + editor/project_manager.cpp | 298 +++++++++++++++++++++------------- editor/project_manager.h | 46 ++++-- 4 files changed, 220 insertions(+), 126 deletions(-) create mode 100644 editor/icons/ProjectList.svg create mode 100644 editor/icons/TitleBarLogo.svg diff --git a/editor/icons/ProjectList.svg b/editor/icons/ProjectList.svg new file mode 100644 index 00000000000..ee72c39c7de --- /dev/null +++ b/editor/icons/ProjectList.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/TitleBarLogo.svg b/editor/icons/TitleBarLogo.svg new file mode 100644 index 00000000000..65bb367c617 --- /dev/null +++ b/editor/icons/TitleBarLogo.svg @@ -0,0 +1 @@ + diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index d18e5a237f3..1f2e0839d34 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -52,7 +52,6 @@ #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" #include "main/main.h" -#include "scene/gui/center_container.h" #include "scene/gui/check_box.h" #include "scene/gui/color_rect.h" #include "scene/gui/flow_container.h" @@ -75,49 +74,60 @@ ProjectManager *ProjectManager::singleton = nullptr; void ProjectManager::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_TRANSLATION_CHANGED: - case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - settings_hb->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT); - queue_redraw(); - } break; - case NOTIFICATION_ENTER_TREE: { Engine::get_singleton()->set_editor_hint(false); } break; case NOTIFICATION_THEME_CHANGED: { + const int top_bar_separation = get_theme_constant(SNAME("top_bar_separation"), EditorStringName(Editor)); + root_container->add_theme_constant_override("margin_left", top_bar_separation); + root_container->add_theme_constant_override("margin_top", top_bar_separation); + root_container->add_theme_constant_override("margin_bottom", top_bar_separation); + root_container->add_theme_constant_override("margin_right", top_bar_separation); + main_vbox->add_theme_constant_override("separation", top_bar_separation); + background_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("Background"), EditorStringName(EditorStyles))); - loading_label->add_theme_font_override("font", get_theme_font(SNAME("bold"), EditorStringName(EditorFonts))); - search_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("search_panel"), SNAME("ProjectManager"))); + main_view_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer"))); - // Top bar. - search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - language_btn->set_icon(get_editor_theme_icon(SNAME("Environment"))); + title_bar_logo->set_icon(get_editor_theme_icon(SNAME("TitleBarLogo"))); - // Sidebar. - create_btn->set_icon(get_editor_theme_icon(SNAME("Add"))); - import_btn->set_icon(get_editor_theme_icon(SNAME("Load"))); - scan_btn->set_icon(get_editor_theme_icon(SNAME("Search"))); - open_btn->set_icon(get_editor_theme_icon(SNAME("Edit"))); - run_btn->set_icon(get_editor_theme_icon(SNAME("Play"))); - rename_btn->set_icon(get_editor_theme_icon(SNAME("Rename"))); - manage_tags_btn->set_icon(get_editor_theme_icon("Script")); - erase_btn->set_icon(get_editor_theme_icon(SNAME("Remove"))); - erase_missing_btn->set_icon(get_editor_theme_icon(SNAME("Clear"))); - create_tag_btn->set_icon(get_editor_theme_icon("Add")); + _set_main_view_icon(MAIN_VIEW_PROJECTS, get_editor_theme_icon(SNAME("ProjectList"))); + _set_main_view_icon(MAIN_VIEW_ASSETLIB, get_editor_theme_icon(SNAME("AssetLib"))); - tag_error->add_theme_color_override("font_color", get_theme_color("error_color", EditorStringName(Editor))); - tag_edit_error->add_theme_color_override("font_color", get_theme_color("error_color", EditorStringName(Editor))); + // Project list. + { + loading_label->add_theme_font_override("font", get_theme_font(SNAME("bold"), EditorStringName(EditorFonts))); + search_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("search_panel"), SNAME("ProjectManager"))); - create_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - import_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - scan_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - open_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - run_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - rename_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - manage_tags_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - erase_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - erase_missing_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + // Top bar. + search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); + language_btn->set_icon(get_editor_theme_icon(SNAME("Environment"))); + + // Sidebar. + create_btn->set_icon(get_editor_theme_icon(SNAME("Add"))); + import_btn->set_icon(get_editor_theme_icon(SNAME("Load"))); + scan_btn->set_icon(get_editor_theme_icon(SNAME("Search"))); + open_btn->set_icon(get_editor_theme_icon(SNAME("Edit"))); + run_btn->set_icon(get_editor_theme_icon(SNAME("Play"))); + rename_btn->set_icon(get_editor_theme_icon(SNAME("Rename"))); + manage_tags_btn->set_icon(get_editor_theme_icon("Script")); + erase_btn->set_icon(get_editor_theme_icon(SNAME("Remove"))); + erase_missing_btn->set_icon(get_editor_theme_icon(SNAME("Clear"))); + create_tag_btn->set_icon(get_editor_theme_icon("Add")); + + tag_error->add_theme_color_override("font_color", get_theme_color("error_color", EditorStringName(Editor))); + tag_edit_error->add_theme_color_override("font_color", get_theme_color("error_color", EditorStringName(Editor))); + + create_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + import_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + scan_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + open_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + run_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + rename_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + manage_tags_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + erase_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + erase_missing_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + } // Asset library popup. if (asset_library) { @@ -126,23 +136,6 @@ void ProjectManager::_notification(int p_what) { } } break; - case NOTIFICATION_RESIZED: { - if (open_templates && open_templates->is_visible()) { - open_templates->popup_centered(); - } - if (asset_library) { - real_t size = get_size().x / EDSCALE; - // Adjust names of tabs to fit the new size. - if (size < 650) { - local_projects_vb->set_name(TTR("Local")); - asset_library->set_name(TTR("Asset Library")); - } else { - local_projects_vb->set_name(TTR("Local Projects")); - asset_library->set_name(TTR("Asset Library Projects")); - } - } - } break; - case NOTIFICATION_READY: { int default_sorting = (int)EDITOR_GET("project_manager/sorting_order"); filter_option->select(default_sorting); @@ -237,17 +230,68 @@ void ProjectManager::_update_size_limits() { } } -void ProjectManager::_show_about() { - about->popup_centered(Size2(780, 500) * EDSCALE); +Button *ProjectManager::_add_main_view(MainViewTab p_id, const String &p_name, const Ref &p_icon, Control *p_view_control) { + ERR_FAIL_INDEX_V(p_id, MAIN_VIEW_MAX, nullptr); + ERR_FAIL_COND_V(main_view_map.has(p_id), nullptr); + ERR_FAIL_COND_V(main_view_toggle_map.has(p_id), nullptr); + + Button *toggle_button = memnew(Button); + toggle_button->set_flat(true); + toggle_button->set_theme_type_variation("MainScreenButton"); + toggle_button->set_toggle_mode(true); + toggle_button->set_button_group(main_view_toggles_group); + toggle_button->set_text(p_name); + toggle_button->connect("pressed", callable_mp(this, &ProjectManager::_select_main_view).bind((int)p_id)); + + main_view_toggles->add_child(toggle_button); + main_view_toggle_map[p_id] = toggle_button; + + _set_main_view_icon(p_id, p_icon); + + p_view_control->set_visible(false); + main_view_container->add_child(p_view_control); + main_view_map[p_id] = p_view_control; + + return toggle_button; } -void ProjectManager::_version_button_pressed() { - DisplayServer::get_singleton()->clipboard_set(version_btn->get_text()); +void ProjectManager::_set_main_view_icon(MainViewTab p_id, const Ref &p_icon) { + ERR_FAIL_INDEX(p_id, MAIN_VIEW_MAX); + ERR_FAIL_COND(!main_view_toggle_map.has(p_id)); + + Button *toggle_button = main_view_toggle_map[p_id]; + + Ref old_icon = toggle_button->get_icon(); + if (old_icon.is_valid()) { + old_icon->disconnect_changed(callable_mp((Control *)toggle_button, &Control::update_minimum_size)); + } + + if (p_icon.is_valid()) { + toggle_button->set_icon(p_icon); + // Make sure the control is updated if the icon is reimported. + p_icon->connect_changed(callable_mp((Control *)toggle_button, &Control::update_minimum_size)); + } else { + toggle_button->set_icon(Ref()); + } } -void ProjectManager::_on_tab_changed(int p_tab) { +void ProjectManager::_select_main_view(int p_id) { + MainViewTab view_id = (MainViewTab)p_id; + + ERR_FAIL_INDEX(view_id, MAIN_VIEW_MAX); + ERR_FAIL_COND(!main_view_map.has(view_id)); + ERR_FAIL_COND(!main_view_toggle_map.has(view_id)); + + if (current_main_view != view_id) { + main_view_toggle_map[current_main_view]->set_pressed_no_signal(false); + main_view_map[current_main_view]->set_visible(false); + current_main_view = view_id; + } + main_view_toggle_map[current_main_view]->set_pressed_no_signal(true); + main_view_map[current_main_view]->set_visible(true); + #ifndef ANDROID_ENABLED - if (p_tab == 0) { // Projects + if (current_main_view == MAIN_VIEW_PROJECTS && search_box->is_inside_tree()) { // Automatically grab focus when the user moves from the Templates tab // back to the Projects tab. search_box->grab_focus(); @@ -258,9 +302,13 @@ void ProjectManager::_on_tab_changed(int p_tab) { #endif } +void ProjectManager::_show_about() { + about->popup_centered(Size2(780, 500) * EDSCALE); +} + void ProjectManager::_open_asset_library() { asset_library->disable_community_support(); - tabs->set_current_tab(1); + _select_main_view(MAIN_VIEW_ASSETLIB); } // Quick settings. @@ -292,6 +340,12 @@ void ProjectManager::_dim_window() { set_modulate(dim_color); } +// Footer. + +void ProjectManager::_version_button_pressed() { + DisplayServer::get_singleton()->clipboard_set(version_btn->get_text()); +} + // Project list. void ProjectManager::_scan_projects() { @@ -607,7 +661,7 @@ void ProjectManager::_on_search_term_changed(const String &p_term) { } void ProjectManager::_on_search_term_submitted(const String &p_text) { - if (tabs->get_current_tab() != 0) { + if (current_main_view != MAIN_VIEW_PROJECTS) { return; } @@ -790,7 +844,7 @@ void ProjectManager::shortcut_input(const Ref &p_ev) { } #endif - if (tabs->get_current_tab() != 0) { + if (current_main_view != MAIN_VIEW_PROJECTS) { return; } @@ -970,51 +1024,44 @@ ProjectManager::ProjectManager() { add_child(background_panel); background_panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - VBoxContainer *vb = memnew(VBoxContainer); - background_panel->add_child(vb); - vb->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 8 * EDSCALE); + root_container = memnew(MarginContainer); + add_child(root_container); + root_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - Control *center_box = memnew(Control); - center_box->set_v_size_flags(Control::SIZE_EXPAND_FILL); - vb->add_child(center_box); + main_vbox = memnew(VBoxContainer); + root_container->add_child(main_vbox); - tabs = memnew(TabContainer); - tabs->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - center_box->add_child(tabs); - tabs->connect("tab_changed", callable_mp(this, &ProjectManager::_on_tab_changed)); + // Title bar. + { + title_bar = memnew(HBoxContainer); + main_vbox->add_child(title_bar); + + title_bar_logo = memnew(Button); + title_bar_logo->set_flat(true); + title_bar->add_child(title_bar_logo); + title_bar_logo->connect("pressed", callable_mp(this, &ProjectManager::_show_about)); + + HBoxContainer *left_spacer = memnew(HBoxContainer); + left_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS); + left_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); + title_bar->add_child(left_spacer); + + main_view_toggles = memnew(HBoxContainer); + title_bar->add_child(main_view_toggles); + + main_view_toggles_group.instantiate(); + + Control *right_spacer = memnew(Control); + right_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS); + right_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); + title_bar->add_child(right_spacer); + + quick_settings_hbox = memnew(HBoxContainer); + title_bar->add_child(quick_settings_hbox); + } // Quick settings. { - settings_hb = memnew(HBoxContainer); - settings_hb->set_alignment(BoxContainer::ALIGNMENT_END); - settings_hb->set_h_grow_direction(Control::GROW_DIRECTION_BEGIN); - settings_hb->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT); - - // A VBoxContainer that contains a dummy Control node to adjust the LinkButton's vertical position. - VBoxContainer *spacer_vb = memnew(VBoxContainer); - settings_hb->add_child(spacer_vb); - - Control *v_spacer = memnew(Control); - spacer_vb->add_child(v_spacer); - - version_btn = memnew(LinkButton); - String hash = String(VERSION_HASH); - if (hash.length() != 0) { - hash = " " + vformat("[%s]", hash.left(9)); - } - version_btn->set_text("v" VERSION_FULL_BUILD + hash); - // Fade the version label to be less prominent, but still readable. - version_btn->set_self_modulate(Color(1, 1, 1, 0.6)); - version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); - version_btn->set_tooltip_text(TTR("Click to copy.")); - version_btn->connect("pressed", callable_mp(this, &ProjectManager::_version_button_pressed)); - spacer_vb->add_child(version_btn); - - // Add a small horizontal spacer between the version and language buttons - // to distinguish them. - Control *h_spacer = memnew(Control); - settings_hb->add_child(h_spacer); - language_btn = memnew(OptionButton); language_btn->set_focus_mode(Control::FOCUS_NONE); language_btn->set_fit_to_longest_item(false); @@ -1023,7 +1070,6 @@ ProjectManager::ProjectManager() { #ifdef ANDROID_ENABLED // The language selection dropdown doesn't work on Android (as the setting isn't saved), see GH-60353. // Also, the dropdown it spawns is very tall and can't be scrolled without a hardware mouse. - // Hiding the language selection dropdown also leaves more space for the version label to display. language_btn->hide(); #endif @@ -1050,15 +1096,18 @@ ProjectManager::ProjectManager() { } } - settings_hb->add_child(language_btn); - center_box->add_child(settings_hb); + quick_settings_hbox->add_child(language_btn); } + main_view_container = memnew(PanelContainer); + main_view_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + main_vbox->add_child(main_view_container); + // Project list view. { local_projects_vb = memnew(VBoxContainer); - local_projects_vb->set_name(TTR("Local Projects")); - tabs->add_child(local_projects_vb); + local_projects_vb->set_name("LocalProjectsTab"); + _add_main_view(MAIN_VIEW_PROJECTS, TTR("Projects"), Ref(), local_projects_vb); // Project list's top bar. { @@ -1178,23 +1227,41 @@ ProjectManager::ProjectManager() { erase_missing_btn->set_text(TTR("Remove Missing")); erase_missing_btn->connect("pressed", callable_mp(this, &ProjectManager::_erase_missing_projects)); tree_vb->add_child(erase_missing_btn); - - tree_vb->add_spacer(); - - about_btn = memnew(Button); - about_btn->set_text(TTR("About")); - about_btn->connect("pressed", callable_mp(this, &ProjectManager::_show_about)); - tree_vb->add_child(about_btn); } } + // Asset library view. if (AssetLibraryEditorPlugin::is_available()) { asset_library = memnew(EditorAssetLibrary(true)); - asset_library->set_name(TTR("Asset Library Projects")); - tabs->add_child(asset_library); + asset_library->set_name("AssetLibraryTab"); + _add_main_view(MAIN_VIEW_ASSETLIB, TTR("Asset Library"), Ref(), asset_library); asset_library->connect("install_asset", callable_mp(this, &ProjectManager::_install_project)); } else { - print_verbose("Asset Library not available (due to using Web editor, or SSL support disabled)."); + VBoxContainer *asset_library_filler = memnew(VBoxContainer); + asset_library_filler->set_name("AssetLibraryTab"); + Button *asset_library_toggle = _add_main_view(MAIN_VIEW_ASSETLIB, TTR("Asset Library"), Ref(), asset_library_filler); + asset_library_toggle->set_disabled(true); + asset_library_toggle->set_tooltip_text(TTR("Asset Library not available (due to using Web editor, or because SSL support disabled).")); + } + + // Footer bar. + { + HBoxContainer *footer_bar = memnew(HBoxContainer); + footer_bar->set_alignment(BoxContainer::ALIGNMENT_END); + main_vbox->add_child(footer_bar); + + version_btn = memnew(LinkButton); + String hash = String(VERSION_HASH); + if (hash.length() != 0) { + hash = " " + vformat("[%s]", hash.left(9)); + } + version_btn->set_text("v" VERSION_FULL_BUILD + hash); + // Fade the version label to be less prominent, but still readable. + version_btn->set_self_modulate(Color(1, 1, 1, 0.6)); + version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); + version_btn->set_tooltip_text(TTR("Click to copy the version information.")); + version_btn->connect("pressed", callable_mp(this, &ProjectManager::_version_button_pressed)); + footer_bar->add_child(version_btn); } // Dialogs. @@ -1386,6 +1453,7 @@ ProjectManager::ProjectManager() { } _update_size_limits(); + _select_main_view(MAIN_VIEW_PROJECTS); } ProjectManager::~ProjectManager() { diff --git a/editor/project_manager.h b/editor/project_manager.h index 7ed8df8a9da..ffccac18587 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -41,11 +41,13 @@ class EditorFileDialog; class HFlowContainer; class LineEdit; class LinkButton; +class MarginContainer; class OptionButton; class PanelContainer; class ProjectDialog; class ProjectList; class TabContainer; +class VBoxContainer; class ProjectManager : public Control { GDCLASS(ProjectManager, Control); @@ -65,21 +67,39 @@ class ProjectManager : public Control { void _update_size_limits(); + MarginContainer *root_container = nullptr; Panel *background_panel = nullptr; - Button *about_btn = nullptr; - LinkButton *version_btn = nullptr; + VBoxContainer *main_vbox = nullptr; + + HBoxContainer *title_bar = nullptr; + Button *title_bar_logo = nullptr; + HBoxContainer *main_view_toggles = nullptr; + HBoxContainer *quick_settings_hbox = nullptr; + + enum MainViewTab { + MAIN_VIEW_PROJECTS, + MAIN_VIEW_ASSETLIB, + MAIN_VIEW_MAX + }; + + MainViewTab current_main_view = MAIN_VIEW_PROJECTS; + HashMap main_view_map; + HashMap main_view_toggle_map; + + PanelContainer *main_view_container = nullptr; + Ref main_view_toggles_group; + + Button *_add_main_view(MainViewTab p_id, const String &p_name, const Ref &p_icon, Control *p_view_control); + void _set_main_view_icon(MainViewTab p_id, const Ref &p_icon); + void _select_main_view(int p_id); + + VBoxContainer *local_projects_vb = nullptr; + EditorAssetLibrary *asset_library = nullptr; ConfirmationDialog *open_templates = nullptr; EditorAbout *about = nullptr; void _show_about(); - void _version_button_pressed(); - - TabContainer *tabs = nullptr; - VBoxContainer *local_projects_vb = nullptr; - EditorAssetLibrary *asset_library = nullptr; - - void _on_tab_changed(int p_tab); void _open_asset_library(); // Quick settings. @@ -91,6 +111,12 @@ class ProjectManager : public Control { void _restart_confirm(); void _dim_window(); + // Footer. + + LinkButton *version_btn = nullptr; + + void _version_button_pressed(); + // Project list. ProjectList *_project_list = nullptr; @@ -121,8 +147,6 @@ class ProjectManager : public Control { ConfirmationDialog *multi_open_ask = nullptr; ConfirmationDialog *multi_run_ask = nullptr; - HBoxContainer *settings_hb = nullptr; - AcceptDialog *run_error_diag = nullptr; AcceptDialog *dialog_error = nullptr; ProjectDialog *npdialog = nullptr; From 4d97c33503bff8c83a82ce3be5f55c0fb577db39 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Tue, 30 Jan 2024 17:35:46 +0100 Subject: [PATCH 3/6] Add a network mode setting to bring the editor offline This PR also adds default font styles for RichTextLabels in the editor, and improves the introduction dialog when you don't have any local projects available. The offline mode is implemented in the asset library plugin, alongside some code improvements. --- doc/classes/EditorSettings.xml | 5 +- editor/editor_settings.cpp | 19 ++-- editor/editor_settings.h | 5 ++ .../plugins/asset_library_editor_plugin.cpp | 89 +++++++++++++------ editor/plugins/asset_library_editor_plugin.h | 19 ++-- editor/project_manager.cpp | 76 +++++++++++----- editor/project_manager.h | 9 +- editor/themes/editor_fonts.cpp | 10 +++ 8 files changed, 165 insertions(+), 67 deletions(-) diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 2f24acd684f..ae6740eed10 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -666,7 +666,7 @@ The extra spacing to add to various GUI elements in the editor (in pixels). Increasing this value is useful to improve usability on touch screens, at the cost of reducing the amount of usable screen real estate. - See also [member interface/theme/spacing_preset]. + See also [member interface/theme/spacing_preset]. The base color to use for user interface elements in the editor. Secondary colors (such as darker/lighter variants) are derived from this color. @@ -724,6 +724,9 @@ Specify the multiplier to apply to the scale for the editor gizmo handles to improve usability on touchscreen devices. [b]Note:[/b] Defaults to [code]1[/code] on non-touchscreen devices. + + Determines whether online features are enabled in the editor, such as the Asset Library. Setting this property to "Offline" is recommended to limit editor's internet activity, especially if privacy is a concern. + The address to listen to when starting the remote debugger. This can be set to [code]0.0.0.0[/code] to allow external clients to connect to the remote debugger (instead of restricting the remote debugger to connections from [code]localhost[/code]). diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 3b1a69459d4..fa606748291 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -766,15 +766,22 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { /* Network */ - // Debug - _initial_set("network/debug/remote_host", "127.0.0.1"); // Hints provided in setup_network + // General + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "network/connection/network_mode", 0, "Offline,Online"); - EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "network/debug/remote_port", 6007, "1,65535,1") + // HTTP Proxy + _initial_set("network/http_proxy/host", ""); + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "network/http_proxy/port", 8080, "1,65535,1") // SSL EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "network/tls/editor_tls_certificates", _SYSTEM_CERTS_PATH, "*.crt,*.pem", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); - // Debugger/profiler + // Debug + _initial_set("network/debug/remote_host", "127.0.0.1"); // Hints provided in setup_network + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "network/debug/remote_port", 6007, "1,65535,1") + + /* Debugger/profiler */ + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "debugger/auto_switch_to_remote_scene_tree", false, "") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "debugger/profiler_frame_history_size", 3600, "60,10000,1") EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "debugger/profiler_frame_max_functions", 64, "16,512,1") @@ -782,10 +789,6 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "debugger/remote_inspect_refresh_interval", 0.2, "0.02,10,0.01,or_greater") EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "debugger/profile_native_calls", false, "") - // HTTP Proxy - _initial_set("network/http_proxy/host", ""); - EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "network/http_proxy/port", 8080, "1,65535,1") - /* Extra config */ // TRANSLATORS: Project Manager here refers to the tool used to create/manage Godot projects. diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 2e280ac9d6e..87ce0cfd579 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -57,6 +57,11 @@ public: Vector install_files; }; + enum NetworkMode { + NETWORK_OFFLINE, + NETWORK_ONLINE, + }; + private: struct VariantContainer { int order = 0; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 20129556860..539bd0331a9 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -643,6 +643,15 @@ void EditorAssetLibrary::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { _update_repository_options(); setup_http_request(request); + + const bool loading_blocked_new = ((int)EDITOR_GET("network/connection/network_mode") == EditorSettings::NETWORK_OFFLINE); + if (loading_blocked_new != loading_blocked) { + loading_blocked = loading_blocked_new; + + if (!loading_blocked && is_visible()) { + _request_current_config(); // Reload config now that the network is available. + } + } } break; } } @@ -929,9 +938,7 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, String p_image_url, Imag } void EditorAssetLibrary::_repository_changed(int p_repository_id) { - library_error->hide(); - library_info->set_text(TTR("Loading...")); - library_info->show(); + _set_library_message(TTR("Loading...")); asset_top_page->hide(); asset_bottom_page->hide(); @@ -1104,10 +1111,14 @@ void EditorAssetLibrary::_api_request(const String &p_request, RequestType p_req if (requesting != REQUESTING_NONE) { request->cancel_request(); } + error_hb->hide(); + + if (loading_blocked) { + _set_library_message_with_action(TTR("The Asset Library requires an online connection and involves sending data over the internet."), TTR("Go Online"), callable_mp(this, &EditorAssetLibrary::_force_online_mode)); + return; + } requesting = p_request_type; - - error_hb->hide(); request->request(host + "/" + p_request + p_arguments); } @@ -1156,8 +1167,7 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const if (error_abort) { if (requesting == REQUESTING_CONFIG) { - library_info->hide(); - library_error->show(); + _set_library_message_with_action(TTR("Failed to get repository configuration."), TTR("Retry"), callable_mp(this, &EditorAssetLibrary::_request_current_config)); } error_hb->show(); return; @@ -1265,18 +1275,17 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const } if (!filter->get_text().is_empty()) { - library_info->set_text( + _set_library_message( vformat(TTR("No results for \"%s\" for support level(s): %s."), filter->get_text(), support_list)); } else { // No results, even though the user didn't search for anything specific. // This is typically because the version number changed recently // and no assets compatible with the new version have been published yet. - library_info->set_text( + _set_library_message( vformat(TTR("No results compatible with %s %s for support level(s): %s.\nCheck the enabled support levels using the 'Support' button in the top-right corner."), String(VERSION_SHORT_NAME).capitalize(), String(VERSION_BRANCH), support_list)); } - library_info->show(); } else { - library_info->hide(); + library_message_box->hide(); } for (int i = 0; i < result.size(); i++) { @@ -1432,6 +1441,39 @@ void EditorAssetLibrary::_update_asset_items_columns() { asset_items_column_width = (get_size().x / new_columns) - (100 * EDSCALE); } +void EditorAssetLibrary::_set_library_message(const String &p_message) { + library_message->set_text(p_message); + + if (library_message_action.is_valid()) { + library_message_button->disconnect("pressed", library_message_action); + library_message_action = Callable(); + } + library_message_button->hide(); + + library_message_box->show(); +} + +void EditorAssetLibrary::_set_library_message_with_action(const String &p_message, const String &p_action_text, const Callable &p_action) { + library_message->set_text(p_message); + + library_message_button->set_text(p_action_text); + if (library_message_action.is_valid()) { + library_message_button->disconnect("pressed", library_message_action); + library_message_action = Callable(); + } + library_message_action = p_action; + library_message_button->connect("pressed", library_message_action); + library_message_button->show(); + + library_message_box->show(); +} + +void EditorAssetLibrary::_force_online_mode() { + EditorSettings::get_singleton()->set_setting("network/connection/network_mode", EditorSettings::NETWORK_ONLINE); + EditorSettings::get_singleton()->notify_changes(); + EditorSettings::get_singleton()->save(); +} + void EditorAssetLibrary::disable_community_support() { support->get_popup()->set_item_checked(SUPPORT_COMMUNITY, false); } @@ -1443,7 +1485,7 @@ void EditorAssetLibrary::_bind_methods() { EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { requesting = REQUESTING_NONE; templates_only = p_templates_only; - initial_loading = true; + loading_blocked = ((int)EDITOR_GET("network/connection/network_mode") == EditorSettings::NETWORK_OFFLINE); VBoxContainer *library_main = memnew(VBoxContainer); add_child(library_main); @@ -1567,22 +1609,17 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { library_vb_border->add_child(library_vb); - library_info = memnew(Label); - library_info->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - library_vb->add_child(library_info); + library_message_box = memnew(VBoxContainer); + library_message_box->hide(); + library_vb->add_child(library_message_box); - library_error = memnew(VBoxContainer); - library_error->hide(); - library_vb->add_child(library_error); + library_message = memnew(Label); + library_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + library_message_box->add_child(library_message); - library_error_label = memnew(Label(TTR("Failed to get repository configuration."))); - library_error_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - library_error->add_child(library_error_label); - - library_error_retry = memnew(Button(TTR("Retry"))); - library_error_retry->set_h_size_flags(SIZE_SHRINK_CENTER); - library_error_retry->connect("pressed", callable_mp(this, &EditorAssetLibrary::_request_current_config)); - library_error->add_child(library_error_retry); + library_message_button = memnew(Button); + library_message_button->set_h_size_flags(SIZE_SHRINK_CENTER); + library_message_box->add_child(library_message_button); asset_top_page = memnew(HBoxContainer); library_vb->add_child(asset_top_page); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 2dff1869ef2..bbce14a9dd1 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -191,10 +191,14 @@ class EditorAssetLibrary : public PanelContainer { PanelContainer *library_scroll_bg = nullptr; ScrollContainer *library_scroll = nullptr; VBoxContainer *library_vb = nullptr; - Label *library_info = nullptr; - VBoxContainer *library_error = nullptr; - Label *library_error_label = nullptr; - Button *library_error_retry = nullptr; + VBoxContainer *library_message_box = nullptr; + Label *library_message = nullptr; + Button *library_message_button = nullptr; + Callable library_message_action; + + void _set_library_message(const String &p_message); + void _set_library_message_with_action(const String &p_message, const String &p_action_text, const Callable &p_action); + LineEdit *filter = nullptr; Timer *filter_debounce_timer = nullptr; OptionButton *categories = nullptr; @@ -213,8 +217,11 @@ class EditorAssetLibrary : public PanelContainer { HTTPRequest *request = nullptr; - bool templates_only; - bool initial_loading; + bool templates_only = false; + bool initial_loading = true; + bool loading_blocked = false; + + void _force_online_mode(); enum Support { SUPPORT_OFFICIAL, diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 1f2e0839d34..4c01df3997a 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -59,6 +59,7 @@ #include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" +#include "scene/gui/rich_text_label.h" #include "scene/gui/separator.h" #include "scene/gui/texture_rect.h" #include "scene/main/window.h" @@ -137,21 +138,15 @@ void ProjectManager::_notification(int p_what) { } break; case NOTIFICATION_READY: { - int default_sorting = (int)EDITOR_GET("project_manager/sorting_order"); + const int default_sorting = (int)EDITOR_GET("project_manager/sorting_order"); filter_option->select(default_sorting); _project_list->set_order_option(default_sorting); -#ifndef ANDROID_ENABLED - if (_project_list->get_project_count() >= 1) { - // Focus on the search box immediately to allow the user - // to search without having to reach for their mouse - search_box->grab_focus(); - } -#endif + _select_main_view(MAIN_VIEW_PROJECTS); // Suggest browsing asset library to get templates/demos. - if (asset_library && open_templates && _project_list->get_project_count() == 0) { - open_templates->popup_centered(); + if (asset_library && _project_list->get_project_count() == 0) { + _suggest_asset_library(); } } break; @@ -303,10 +298,54 @@ void ProjectManager::_select_main_view(int p_id) { } void ProjectManager::_show_about() { - about->popup_centered(Size2(780, 500) * EDSCALE); + about_dialog->popup_centered(Size2(780, 500) * EDSCALE); } -void ProjectManager::_open_asset_library() { +void ProjectManager::_suggest_asset_library() { + if (!suggest_asset_library_dialog) { + suggest_asset_library_dialog = memnew(ConfirmationDialog); + suggest_asset_library_dialog->set_title(TTR("Getting Started with Godot")); + add_child(suggest_asset_library_dialog); + suggest_asset_library_dialog->connect("confirmed", callable_mp(this, &ProjectManager::_open_asset_library_confirmed)); + + VBoxContainer *suggest_vbox = memnew(VBoxContainer); + suggest_asset_library_dialog->add_child(suggest_vbox); + + suggest_asset_library_label = memnew(RichTextLabel); + suggest_asset_library_label->set_use_bbcode(true); + suggest_asset_library_label->set_fit_content(true); + suggest_asset_library_label->set_h_size_flags(SIZE_EXPAND_FILL); + suggest_asset_library_label->add_theme_style_override("normal", memnew(StyleBoxEmpty)); + suggest_vbox->add_child(suggest_asset_library_label); + } + + const int network_mode = EDITOR_GET("network/connection/network_mode"); + if (network_mode == EditorSettings::NETWORK_OFFLINE) { + const String line1 = TTR("You don't have any projects yet."); + const String line2 = TTR("Get started with one of the official project templates from the Asset Library!"); + const String line3 = TTR("Note: The Asset Library requires an online connection and involves sending data over the internet."); + + suggest_asset_library_label->set_text(vformat("%s\n\n[b]%s[/b]\n\n[i]%s[/i]", line1, line2, line3)); + suggest_asset_library_dialog->set_ok_button_text(TTR("Go Online and Open Asset Library")); + } else { + const String line1 = TTR("You don't have any projects yet."); + const String line2 = TTR("Get started with one of the official project templates from the Asset Library!"); + + suggest_asset_library_label->set_text(vformat("%s\n\n[b]%s[/b]", line1, line2)); + suggest_asset_library_dialog->set_ok_button_text(TTR("Open Asset Library")); + } + + suggest_asset_library_dialog->popup_centered(Size2(540, 100) * EDSCALE); +} + +void ProjectManager::_open_asset_library_confirmed() { + const int network_mode = EDITOR_GET("network/connection/network_mode"); + if (network_mode == EditorSettings::NETWORK_OFFLINE) { + EditorSettings::get_singleton()->set_setting("network/connection/network_mode", EditorSettings::NETWORK_ONLINE); + EditorSettings::get_singleton()->notify_changes(); + EditorSettings::get_singleton()->save(); + } + asset_library->disable_community_support(); _select_main_view(MAIN_VIEW_ASSETLIB); } @@ -1339,16 +1378,8 @@ ProjectManager::ProjectManager() { dialog_error = memnew(AcceptDialog); add_child(dialog_error); - if (asset_library) { - open_templates = memnew(ConfirmationDialog); - open_templates->set_text(TTR("You currently don't have any projects.\nWould you like to explore official example projects in the Asset Library?")); - open_templates->set_ok_button_text(TTR("Open Asset Library")); - open_templates->connect("confirmed", callable_mp(this, &ProjectManager::_open_asset_library)); - add_child(open_templates); - } - - about = memnew(EditorAbout); - add_child(about); + about_dialog = memnew(EditorAbout); + add_child(about_dialog); } // Tag management. @@ -1453,7 +1484,6 @@ ProjectManager::ProjectManager() { } _update_size_limits(); - _select_main_view(MAIN_VIEW_PROJECTS); } ProjectManager::~ProjectManager() { diff --git a/editor/project_manager.h b/editor/project_manager.h index ffccac18587..f3f443968c3 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -46,6 +46,7 @@ class OptionButton; class PanelContainer; class ProjectDialog; class ProjectList; +class RichTextLabel; class TabContainer; class VBoxContainer; @@ -96,11 +97,13 @@ class ProjectManager : public Control { VBoxContainer *local_projects_vb = nullptr; EditorAssetLibrary *asset_library = nullptr; - ConfirmationDialog *open_templates = nullptr; - EditorAbout *about = nullptr; + ConfirmationDialog *suggest_asset_library_dialog = nullptr; + RichTextLabel *suggest_asset_library_label = nullptr; + EditorAbout *about_dialog = nullptr; void _show_about(); - void _open_asset_library(); + void _suggest_asset_library(); + void _open_asset_library_confirmed(); // Quick settings. diff --git a/editor/themes/editor_fonts.cpp b/editor/themes/editor_fonts.cpp index a4f72b26e3e..3479c67b467 100644 --- a/editor/themes/editor_fonts.cpp +++ b/editor/themes/editor_fonts.cpp @@ -370,6 +370,9 @@ void editor_register_fonts(const Ref &p_theme) { Ref italic_fc = default_fc->duplicate(); italic_fc->set_variation_transform(Transform2D(1.0, 0.2, 0.0, 1.0, 0.0, 0.0)); + Ref bold_italic_fc = bold_fc->duplicate(); + bold_italic_fc->set_variation_transform(Transform2D(1.0, 0.2, 0.0, 1.0, 0.0, 0.0)); + // Setup theme. p_theme->set_default_font(default_fc); // Default theme font config. @@ -394,6 +397,8 @@ void editor_register_fonts(const Ref &p_theme) { p_theme->set_font("font", "MainScreenButton", bold_fc); p_theme->set_font_size("font_size", "MainScreenButton", default_font_size + 2 * EDSCALE); + // Labels. + p_theme->set_font("font", "Label", default_fc); p_theme->set_type_variation("HeaderSmall", "Label"); @@ -408,6 +413,11 @@ void editor_register_fonts(const Ref &p_theme) { p_theme->set_font("font", "HeaderLarge", bold_fc); p_theme->set_font_size("font_size", "HeaderLarge", default_font_size + 3 * EDSCALE); + p_theme->set_font("normal_font", "RichTextLabel", default_fc); + p_theme->set_font("bold_font", "RichTextLabel", bold_fc); + p_theme->set_font("italics_font", "RichTextLabel", italic_fc); + p_theme->set_font("bold_italics_font", "RichTextLabel", bold_italic_fc); + // Documentation fonts p_theme->set_font_size("doc_size", EditorStringName(EditorFonts), int(EDITOR_GET("text_editor/help/help_font_size")) * EDSCALE); p_theme->set_font("doc", EditorStringName(EditorFonts), default_fc); From 068c0d2699066f833694cf912d299acbf6b8755a Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Tue, 30 Jan 2024 17:35:46 +0100 Subject: [PATCH 4/6] Implement a quick settings dialog for the project manager Currently it allows to adjust language, theme preset, UI scaling, and network mode. Project manager has been updated to support dynamic theme updates. --- editor/editor_node.cpp | 61 ++-- editor/project_manager.cpp | 273 ++++++++-------- editor/project_manager.h | 16 +- .../project_manager/quick_settings_dialog.cpp | 306 ++++++++++++++++++ .../project_manager/quick_settings_dialog.h | 87 +++++ 5 files changed, 572 insertions(+), 171 deletions(-) create mode 100644 editor/project_manager/quick_settings_dialog.cpp create mode 100644 editor/project_manager/quick_settings_dialog.h diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 579b526b5eb..3691172c180 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -498,40 +498,43 @@ void EditorNode::_update_theme(bool p_skip_creation) { update_preview_themes(CanvasItemEditor::THEME_PREVIEW_EDITOR); } - gui_base->add_theme_style_override("panel", theme->get_stylebox(SNAME("Background"), EditorStringName(EditorStyles))); - main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, theme->get_constant(SNAME("window_border_margin"), EditorStringName(Editor))); - main_vbox->add_theme_constant_override("separation", theme->get_constant(SNAME("top_bar_separation"), EditorStringName(Editor))); + // Update styles. + { + gui_base->add_theme_style_override("panel", theme->get_stylebox(SNAME("Background"), EditorStringName(EditorStyles))); + main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, theme->get_constant(SNAME("window_border_margin"), EditorStringName(Editor))); + main_vbox->add_theme_constant_override("separation", theme->get_constant(SNAME("top_bar_separation"), EditorStringName(Editor))); - scene_root_parent->add_theme_style_override("panel", theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles))); - bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); - main_menu->add_theme_style_override("hover", theme->get_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles))); - distraction_free->set_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons))); - bottom_panel_raise->set_icon(theme->get_icon(SNAME("ExpandBottomDock"), EditorStringName(EditorIcons))); + scene_root_parent->add_theme_style_override("panel", theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles))); + bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); + main_menu->add_theme_style_override("hover", theme->get_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles))); + distraction_free->set_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons))); + bottom_panel_raise->set_icon(theme->get_icon(SNAME("ExpandBottomDock"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_DOCS), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_QA), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_REPORT_A_BUG), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_COPY_SYSTEM_INFO), theme->get_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_SUGGEST_A_FEATURE), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_SEND_DOCS_FEEDBACK), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_COMMUNITY), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_ABOUT), theme->get_icon(SNAME("Godot"), EditorStringName(EditorIcons))); - help_menu->set_item_icon(help_menu->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), theme->get_icon(SNAME("Heart"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_DOCS), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_QA), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_REPORT_A_BUG), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_COPY_SYSTEM_INFO), theme->get_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_SUGGEST_A_FEATURE), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_SEND_DOCS_FEEDBACK), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_COMMUNITY), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_ABOUT), theme->get_icon(SNAME("Godot"), EditorStringName(EditorIcons))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), theme->get_icon(SNAME("Heart"), EditorStringName(EditorIcons))); - if (EditorDebuggerNode::get_singleton()->is_visible()) { - bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))); - } + if (EditorDebuggerNode::get_singleton()->is_visible()) { + bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))); + } - for (int i = 0; i < main_editor_buttons.size(); i++) { - Button *tb = main_editor_buttons[i]; - EditorPlugin *p_editor = editor_table[i]; - Ref icon = p_editor->get_icon(); + for (int i = 0; i < main_editor_buttons.size(); i++) { + Button *tb = main_editor_buttons[i]; + EditorPlugin *p_editor = editor_table[i]; + Ref icon = p_editor->get_icon(); - if (icon.is_valid()) { - tb->set_icon(icon); - } else if (theme->has_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { - tb->set_icon(theme->get_icon(p_editor->get_name(), EditorStringName(EditorIcons))); + if (icon.is_valid()) { + tb->set_icon(icon); + } else if (theme->has_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { + tb->set_icon(theme->get_icon(p_editor->get_name(), EditorStringName(EditorIcons))); + } } } } diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 4c01df3997a..c9234cc071b 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -38,7 +38,6 @@ #include "core/io/stream_peer_tls.h" #include "core/os/keyboard.h" #include "core/os/os.h" -#include "core/string/translation.h" #include "core/version.h" #include "editor/editor_about.h" #include "editor/editor_settings.h" @@ -48,6 +47,7 @@ #include "editor/project_manager/project_dialog.h" #include "editor/project_manager/project_list.h" #include "editor/project_manager/project_tag.h" +#include "editor/project_manager/quick_settings_dialog.h" #include "editor/themes/editor_icons.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" @@ -63,6 +63,7 @@ #include "scene/gui/separator.h" #include "scene/gui/texture_rect.h" #include "scene/main/window.h" +#include "scene/theme/theme_db.h" #include "servers/display_server.h" #include "servers/navigation_server_3d.h" #include "servers/physics_server_2d.h" @@ -77,64 +78,9 @@ void ProjectManager::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { Engine::get_singleton()->set_editor_hint(false); - } break; - case NOTIFICATION_THEME_CHANGED: { - const int top_bar_separation = get_theme_constant(SNAME("top_bar_separation"), EditorStringName(Editor)); - root_container->add_theme_constant_override("margin_left", top_bar_separation); - root_container->add_theme_constant_override("margin_top", top_bar_separation); - root_container->add_theme_constant_override("margin_bottom", top_bar_separation); - root_container->add_theme_constant_override("margin_right", top_bar_separation); - main_vbox->add_theme_constant_override("separation", top_bar_separation); - - background_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("Background"), EditorStringName(EditorStyles))); - main_view_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer"))); - - title_bar_logo->set_icon(get_editor_theme_icon(SNAME("TitleBarLogo"))); - - _set_main_view_icon(MAIN_VIEW_PROJECTS, get_editor_theme_icon(SNAME("ProjectList"))); - _set_main_view_icon(MAIN_VIEW_ASSETLIB, get_editor_theme_icon(SNAME("AssetLib"))); - - // Project list. - { - loading_label->add_theme_font_override("font", get_theme_font(SNAME("bold"), EditorStringName(EditorFonts))); - search_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("search_panel"), SNAME("ProjectManager"))); - - // Top bar. - search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - language_btn->set_icon(get_editor_theme_icon(SNAME("Environment"))); - - // Sidebar. - create_btn->set_icon(get_editor_theme_icon(SNAME("Add"))); - import_btn->set_icon(get_editor_theme_icon(SNAME("Load"))); - scan_btn->set_icon(get_editor_theme_icon(SNAME("Search"))); - open_btn->set_icon(get_editor_theme_icon(SNAME("Edit"))); - run_btn->set_icon(get_editor_theme_icon(SNAME("Play"))); - rename_btn->set_icon(get_editor_theme_icon(SNAME("Rename"))); - manage_tags_btn->set_icon(get_editor_theme_icon("Script")); - erase_btn->set_icon(get_editor_theme_icon(SNAME("Remove"))); - erase_missing_btn->set_icon(get_editor_theme_icon(SNAME("Clear"))); - create_tag_btn->set_icon(get_editor_theme_icon("Add")); - - tag_error->add_theme_color_override("font_color", get_theme_color("error_color", EditorStringName(Editor))); - tag_edit_error->add_theme_color_override("font_color", get_theme_color("error_color", EditorStringName(Editor))); - - create_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - import_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - scan_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - open_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - run_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - rename_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - manage_tags_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - erase_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - erase_missing_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); - } - - // Asset library popup. - if (asset_library) { - // Removes extra border margins. - asset_library->add_theme_style_override("panel", memnew(StyleBoxEmpty)); - } + // Theme has already been created in the constructor, so we can skip that step. + _update_theme(true); } break; case NOTIFICATION_READY: { @@ -161,6 +107,12 @@ void ProjectManager::_notification(int p_what) { case NOTIFICATION_WM_ABOUT: { _show_about(); } break; + + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + if (EditorThemeManager::is_generated_theme_outdated()) { + _update_theme(); + } + } break; } } @@ -221,7 +173,94 @@ void ProjectManager::_update_size_limits() { // We try to set it to half the screen resolution, but no smaller than the minimum window size. Size2 half_screen_rect = (screen_rect.size * EDSCALE) / 2; Size2 maximum_popup_size = MAX(half_screen_rect, minimum_size); - language_btn->get_popup()->set_max_size(maximum_popup_size); + quick_settings_dialog->update_size_limits(maximum_popup_size); + } +} + +void ProjectManager::_update_theme(bool p_skip_creation) { + if (!p_skip_creation) { + theme = EditorThemeManager::generate_theme(theme); + DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), EditorStringName(Editor))); + } + + List> editor_themes; + editor_themes.push_back(theme); + editor_themes.push_back(ThemeDB::get_singleton()->get_default_theme()); + + ThemeContext *node_tc = ThemeDB::get_singleton()->get_theme_context(this); + if (node_tc) { + node_tc->set_themes(editor_themes); + } else { + ThemeDB::get_singleton()->create_theme_context(this, editor_themes); + } + + Window *owner_window = get_window(); + if (owner_window) { + ThemeContext *window_tc = ThemeDB::get_singleton()->get_theme_context(owner_window); + if (window_tc) { + window_tc->set_themes(editor_themes); + } else { + ThemeDB::get_singleton()->create_theme_context(owner_window, editor_themes); + } + } + + // Update styles. + { + const int top_bar_separation = get_theme_constant(SNAME("top_bar_separation"), EditorStringName(Editor)); + root_container->add_theme_constant_override("margin_left", top_bar_separation); + root_container->add_theme_constant_override("margin_top", top_bar_separation); + root_container->add_theme_constant_override("margin_bottom", top_bar_separation); + root_container->add_theme_constant_override("margin_right", top_bar_separation); + main_vbox->add_theme_constant_override("separation", top_bar_separation); + + background_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("Background"), EditorStringName(EditorStyles))); + main_view_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer"))); + + title_bar_logo->set_icon(get_editor_theme_icon(SNAME("TitleBarLogo"))); + + _set_main_view_icon(MAIN_VIEW_PROJECTS, get_editor_theme_icon(SNAME("ProjectList"))); + _set_main_view_icon(MAIN_VIEW_ASSETLIB, get_editor_theme_icon(SNAME("AssetLib"))); + + // Project list. + { + loading_label->add_theme_font_override("font", get_theme_font(SNAME("bold"), EditorStringName(EditorFonts))); + search_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("search_panel"), SNAME("ProjectManager"))); + + // Top bar. + search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); + quick_settings_button->set_icon(get_editor_theme_icon(SNAME("Tools"))); + + // Sidebar. + create_btn->set_icon(get_editor_theme_icon(SNAME("Add"))); + import_btn->set_icon(get_editor_theme_icon(SNAME("Load"))); + scan_btn->set_icon(get_editor_theme_icon(SNAME("Search"))); + open_btn->set_icon(get_editor_theme_icon(SNAME("Edit"))); + run_btn->set_icon(get_editor_theme_icon(SNAME("Play"))); + rename_btn->set_icon(get_editor_theme_icon(SNAME("Rename"))); + manage_tags_btn->set_icon(get_editor_theme_icon("Script")); + erase_btn->set_icon(get_editor_theme_icon(SNAME("Remove"))); + erase_missing_btn->set_icon(get_editor_theme_icon(SNAME("Clear"))); + create_tag_btn->set_icon(get_editor_theme_icon("Add")); + + tag_error->add_theme_color_override("font_color", get_theme_color("error_color", EditorStringName(Editor))); + tag_edit_error->add_theme_color_override("font_color", get_theme_color("error_color", EditorStringName(Editor))); + + create_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + import_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + scan_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + open_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + run_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + rename_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + manage_tags_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + erase_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + erase_missing_btn->add_theme_constant_override("h_separation", get_theme_constant(SNAME("sidebar_button_icon_separation"), SNAME("ProjectManager"))); + } + + // Asset library popup. + if (asset_library) { + // Removes extra border margins. + asset_library->add_theme_style_override("panel", memnew(StyleBoxEmpty)); + } } } @@ -350,24 +389,6 @@ void ProjectManager::_open_asset_library_confirmed() { _select_main_view(MAIN_VIEW_ASSETLIB); } -// Quick settings. - -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); - - restart_required_dialog->popup_centered(); -} - -void ProjectManager::_restart_confirm() { - List args = OS::get_singleton()->get_cmdline_args(); - Error err = OS::get_singleton()->create_instance(args); - ERR_FAIL_COND(err); - - _dim_window(); - get_tree()->quit(); -} - void ProjectManager::_dim_window() { // This method must be called before calling `get_tree()->quit()`. // Otherwise, its effect won't be visible @@ -379,6 +400,21 @@ void ProjectManager::_dim_window() { set_modulate(dim_color); } +// Quick settings. + +void ProjectManager::_show_quick_settings() { + quick_settings_dialog->popup_centered(Size2(600, 200) * EDSCALE); +} + +void ProjectManager::_restart_confirmed() { + List args = OS::get_singleton()->get_cmdline_args(); + Error err = OS::get_singleton()->create_instance(args); + ERR_FAIL_COND(err); + + _dim_window(); + get_tree()->quit(); +} + // Footer. void ProjectManager::_version_button_pressed() { @@ -1048,10 +1084,9 @@ ProjectManager::ProjectManager() { Window::set_root_layout_direction(pm_root_dir); EditorThemeManager::initialize(); - Ref theme = EditorThemeManager::generate_theme(); + theme = EditorThemeManager::generate_theme(); DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), EditorStringName(Editor))); - set_theme(theme); set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); _build_icon_type_cache(theme); @@ -1075,67 +1110,36 @@ ProjectManager::ProjectManager() { title_bar = memnew(HBoxContainer); main_vbox->add_child(title_bar); + HBoxContainer *left_hbox = memnew(HBoxContainer); + left_hbox->set_alignment(BoxContainer::ALIGNMENT_BEGIN); + left_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL); + left_hbox->set_stretch_ratio(1.0); + title_bar->add_child(left_hbox); + title_bar_logo = memnew(Button); title_bar_logo->set_flat(true); - title_bar->add_child(title_bar_logo); + left_hbox->add_child(title_bar_logo); title_bar_logo->connect("pressed", callable_mp(this, &ProjectManager::_show_about)); - HBoxContainer *left_spacer = memnew(HBoxContainer); - left_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS); - left_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); - title_bar->add_child(left_spacer); - main_view_toggles = memnew(HBoxContainer); + main_view_toggles->set_alignment(BoxContainer::ALIGNMENT_CENTER); + main_view_toggles->set_h_size_flags(Control::SIZE_EXPAND_FILL); + main_view_toggles->set_stretch_ratio(2.0); title_bar->add_child(main_view_toggles); main_view_toggles_group.instantiate(); - Control *right_spacer = memnew(Control); - right_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS); - right_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); - title_bar->add_child(right_spacer); + HBoxContainer *right_hbox = memnew(HBoxContainer); + right_hbox->set_alignment(BoxContainer::ALIGNMENT_END); + right_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL); + right_hbox->set_stretch_ratio(1.0); + title_bar->add_child(right_hbox); - quick_settings_hbox = memnew(HBoxContainer); - title_bar->add_child(quick_settings_hbox); - } - - // Quick settings. - { - language_btn = memnew(OptionButton); - language_btn->set_focus_mode(Control::FOCUS_NONE); - language_btn->set_fit_to_longest_item(false); - language_btn->set_flat(true); - language_btn->connect("item_selected", callable_mp(this, &ProjectManager::_language_selected)); -#ifdef ANDROID_ENABLED - // The language selection dropdown doesn't work on Android (as the setting isn't saved), see GH-60353. - // Also, the dropdown it spawns is very tall and can't be scrolled without a hardware mouse. - language_btn->hide(); -#endif - - Vector editor_languages; - List editor_settings_properties; - EditorSettings::get_singleton()->get_property_list(&editor_settings_properties); - for (const PropertyInfo &pi : editor_settings_properties) { - if (pi.name == "interface/editor/editor_language") { - editor_languages = pi.hint_string.split(","); - break; - } - } - - String current_lang = EDITOR_GET("interface/editor/editor_language"); - language_btn->set_text(current_lang); - - for (int i = 0; i < editor_languages.size(); i++) { - const String &lang = editor_languages[i]; - String lang_name = TranslationServer::get_singleton()->get_locale_name(lang); - language_btn->add_item(vformat("[%s] %s", lang, lang_name), i); - language_btn->set_item_metadata(i, lang); - if (current_lang == lang) { - language_btn->select(i); - } - } - - quick_settings_hbox->add_child(language_btn); + quick_settings_button = memnew(Button); + quick_settings_button->set_flat(true); + quick_settings_button->set_text(TTR("Settings")); + right_hbox->add_child(quick_settings_button); + quick_settings_button->connect("pressed", callable_mp(this, &ProjectManager::_show_quick_settings)); } main_view_container = memnew(PanelContainer); @@ -1305,12 +1309,9 @@ ProjectManager::ProjectManager() { // Dialogs. { - restart_required_dialog = memnew(ConfirmationDialog); - restart_required_dialog->set_ok_button_text(TTR("Restart Now")); - restart_required_dialog->get_ok_button()->connect("pressed", callable_mp(this, &ProjectManager::_restart_confirm)); - restart_required_dialog->set_cancel_button_text(TTR("Continue")); - restart_required_dialog->set_text(TTR("Settings changed!\nThe project manager must be restarted for changes to take effect.")); - add_child(restart_required_dialog); + quick_settings_dialog = memnew(QuickSettingsDialog); + add_child(quick_settings_dialog); + quick_settings_dialog->connect("restart_required", callable_mp(this, &ProjectManager::_restart_confirmed)); scan_dir = memnew(EditorFileDialog); scan_dir->set_previews_enabled(false); diff --git a/editor/project_manager.h b/editor/project_manager.h index f3f443968c3..16c7bd9dac2 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -46,6 +46,7 @@ class OptionButton; class PanelContainer; class ProjectDialog; class ProjectList; +class QuickSettingsDialog; class RichTextLabel; class TabContainer; class VBoxContainer; @@ -66,7 +67,10 @@ class ProjectManager : public Control { // Main layout. + Ref theme; + void _update_size_limits(); + void _update_theme(bool p_skip_creation = false); MarginContainer *root_container = nullptr; Panel *background_panel = nullptr; @@ -75,7 +79,7 @@ class ProjectManager : public Control { HBoxContainer *title_bar = nullptr; Button *title_bar_logo = nullptr; HBoxContainer *main_view_toggles = nullptr; - HBoxContainer *quick_settings_hbox = nullptr; + Button *quick_settings_button = nullptr; enum MainViewTab { MAIN_VIEW_PROJECTS, @@ -105,14 +109,14 @@ class ProjectManager : public Control { void _suggest_asset_library(); void _open_asset_library_confirmed(); + void _dim_window(); + // Quick settings. - OptionButton *language_btn = nullptr; - ConfirmationDialog *restart_required_dialog = nullptr; + QuickSettingsDialog *quick_settings_dialog = nullptr; - void _language_selected(int p_id); - void _restart_confirm(); - void _dim_window(); + void _show_quick_settings(); + void _restart_confirmed(); // Footer. diff --git a/editor/project_manager/quick_settings_dialog.cpp b/editor/project_manager/quick_settings_dialog.cpp new file mode 100644 index 00000000000..255b15f7f67 --- /dev/null +++ b/editor/project_manager/quick_settings_dialog.cpp @@ -0,0 +1,306 @@ +/**************************************************************************/ +/* quick_settings_dialog.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "quick_settings_dialog.h" + +#include "core/config/project_settings.h" +#include "core/string/translation.h" +#include "editor/editor_settings.h" +#include "editor/editor_string_names.h" +#include "editor/themes/editor_scale.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/label.h" +#include "scene/gui/margin_container.h" +#include "scene/gui/option_button.h" +#include "scene/gui/panel_container.h" + +void QuickSettingsDialog::_fetch_setting_values() { + editor_languages.clear(); + editor_themes.clear(); + editor_scales.clear(); + editor_network_modes.clear(); + + { + List editor_settings_properties; + EditorSettings::get_singleton()->get_property_list(&editor_settings_properties); + + for (const PropertyInfo &pi : editor_settings_properties) { + if (pi.name == "interface/editor/editor_language") { + editor_languages = pi.hint_string.split(","); + } else if (pi.name == "interface/theme/preset") { + editor_themes = pi.hint_string.split(","); + } else if (pi.name == "interface/editor/display_scale") { + editor_scales = pi.hint_string.split(","); + } else if (pi.name == "network/connection/network_mode") { + editor_network_modes = pi.hint_string.split(","); + } + } + } +} + +void QuickSettingsDialog::_update_current_values() { + // Language options. + { + const String current_lang = EDITOR_GET("interface/editor/editor_language"); + + for (int i = 0; i < editor_languages.size(); i++) { + const String &lang_value = editor_languages[i]; + if (current_lang == lang_value) { + language_option_button->set_text(current_lang); + language_option_button->select(i); + } + } + } + + // Theme options. + { + const String current_theme = EDITOR_GET("interface/theme/preset"); + + for (int i = 0; i < editor_themes.size(); i++) { + const String &theme_value = editor_themes[i]; + if (current_theme == theme_value) { + theme_option_button->set_text(current_theme); + theme_option_button->select(i); + + custom_theme_label->set_visible(current_theme == "Custom"); + } + } + } + + // Scale options. + { + const int current_scale = EDITOR_GET("interface/editor/display_scale"); + + for (int i = 0; i < editor_scales.size(); i++) { + const String &scale_value = editor_scales[i]; + if (current_scale == i) { + scale_option_button->set_text(scale_value); + scale_option_button->select(i); + } + } + } + + // Network mode options. + { + const int current_network_mode = EDITOR_GET("network/connection/network_mode"); + + for (int i = 0; i < editor_network_modes.size(); i++) { + const String &network_mode_value = editor_network_modes[i]; + if (current_network_mode == i) { + network_mode_option_button->set_text(network_mode_value); + network_mode_option_button->select(i); + } + } + } +} + +void QuickSettingsDialog::_add_setting_control(const String &p_text, Control *p_control) { + HBoxContainer *container = memnew(HBoxContainer); + settings_list->add_child(container); + + Label *label = memnew(Label(p_text)); + label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + container->add_child(label); + + p_control->set_h_size_flags(Control::SIZE_EXPAND_FILL); + p_control->set_stretch_ratio(2.0); + container->add_child(p_control); +} + +void QuickSettingsDialog::_language_selected(int p_id) { + const String selected_language = language_option_button->get_item_metadata(p_id); + _set_setting_value("interface/editor/editor_language", selected_language, true); +} + +void QuickSettingsDialog::_theme_selected(int p_id) { + const String selected_theme = theme_option_button->get_item_text(p_id); + _set_setting_value("interface/theme/preset", selected_theme); + + custom_theme_label->set_visible(selected_theme == "Custom"); +} + +void QuickSettingsDialog::_scale_selected(int p_id) { + _set_setting_value("interface/editor/display_scale", p_id, true); +} + +void QuickSettingsDialog::_network_mode_selected(int p_id) { + _set_setting_value("network/connection/network_mode", p_id); +} + +void QuickSettingsDialog::_set_setting_value(const String &p_setting, const Variant &p_value, bool p_restart_required) { + EditorSettings::get_singleton()->set(p_setting, p_value); + EditorSettings::get_singleton()->notify_changes(); + EditorSettings::get_singleton()->save(); + + if (p_restart_required) { + restart_required_label->show(); + + if (!restart_required_button) { + restart_required_button = add_button(TTR("Restart Now"), !GLOBAL_GET("gui/common/swap_cancel_ok")); + restart_required_button->connect("pressed", callable_mp(this, &QuickSettingsDialog::_request_restart)); + } + } +} + +void QuickSettingsDialog::_request_restart() { + emit_signal("restart_required"); +} + +void QuickSettingsDialog::update_size_limits(const Size2 &p_max_popup_size) { + language_option_button->get_popup()->set_max_size(p_max_popup_size); +} + +void QuickSettingsDialog::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + settings_list_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("Background"), EditorStringName(EditorStyles))); + + restart_required_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); + custom_theme_label->add_theme_color_override("font_color", get_theme_color(SNAME("font_placeholder_color"), EditorStringName(Editor))); + } break; + + case NOTIFICATION_VISIBILITY_CHANGED: { + if (is_visible()) { + _update_current_values(); + } + } break; + } +} + +void QuickSettingsDialog::_bind_methods() { + ADD_SIGNAL(MethodInfo("restart_required")); +} + +QuickSettingsDialog::QuickSettingsDialog() { + set_title(TTR("Quick Settings")); + set_ok_button_text(TTR("Close")); + + VBoxContainer *main_vbox = memnew(VBoxContainer); + add_child(main_vbox); + main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + + // Settings grid. + { + _fetch_setting_values(); + + settings_list_panel = memnew(PanelContainer); + main_vbox->add_child(settings_list_panel); + + settings_list = memnew(VBoxContainer); + settings_list_panel->add_child(settings_list); + + // Language options. + { + language_option_button = memnew(OptionButton); + language_option_button->set_fit_to_longest_item(false); + language_option_button->connect("item_selected", callable_mp(this, &QuickSettingsDialog::_language_selected)); + + for (int i = 0; i < editor_languages.size(); i++) { + const String &lang_value = editor_languages[i]; + String lang_name = TranslationServer::get_singleton()->get_locale_name(lang_value); + language_option_button->add_item(vformat("[%s] %s", lang_value, lang_name), i); + language_option_button->set_item_metadata(i, lang_value); + } + + _add_setting_control(TTR("Language"), language_option_button); + } + + // Theme options. + { + theme_option_button = memnew(OptionButton); + theme_option_button->set_fit_to_longest_item(false); + theme_option_button->connect("item_selected", callable_mp(this, &QuickSettingsDialog::_theme_selected)); + + for (int i = 0; i < editor_themes.size(); i++) { + const String &theme_value = editor_themes[i]; + theme_option_button->add_item(theme_value, i); + } + + _add_setting_control(TTR("Interface Theme"), theme_option_button); + + custom_theme_label = memnew(Label(TTR("Custom preset can be further configured in the editor."))); + custom_theme_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + custom_theme_label->set_custom_minimum_size(Size2(220, 0) * EDSCALE); + custom_theme_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD); + custom_theme_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + custom_theme_label->set_stretch_ratio(2.0); + custom_theme_label->hide(); + settings_list->add_child(custom_theme_label); + } + + // Scale options. + { + scale_option_button = memnew(OptionButton); + scale_option_button->set_fit_to_longest_item(false); + scale_option_button->connect("item_selected", callable_mp(this, &QuickSettingsDialog::_scale_selected)); + + for (int i = 0; i < editor_scales.size(); i++) { + const String &scale_value = editor_scales[i]; + scale_option_button->add_item(scale_value, i); + } + + _add_setting_control(TTR("Display Scale"), scale_option_button); + } + + // Network mode options. + { + network_mode_option_button = memnew(OptionButton); + network_mode_option_button->set_fit_to_longest_item(false); + network_mode_option_button->connect("item_selected", callable_mp(this, &QuickSettingsDialog::_network_mode_selected)); + + for (int i = 0; i < editor_network_modes.size(); i++) { + const String &network_mode_value = editor_network_modes[i]; + network_mode_option_button->add_item(network_mode_value, i); + } + + _add_setting_control(TTR("Network Mode"), network_mode_option_button); + } + + _update_current_values(); + +#ifdef ANDROID_ENABLED + // The language selection dropdown doesn't work on Android (as the setting isn't saved), see GH-60353. + // Also, the dropdown it spawns is very tall and can't be scrolled without a hardware mouse. + language_option_button->hide(); + scale_option_button->hide(); +#endif + } + + // Restart required panel. + { + restart_required_label = memnew(Label(TTR("Settings changed! The project manager must be restarted for changes to take effect."))); + restart_required_label->set_custom_minimum_size(Size2(560, 0) * EDSCALE); + restart_required_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD); + restart_required_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + restart_required_label->hide(); + main_vbox->add_child(restart_required_label); + } +} diff --git a/editor/project_manager/quick_settings_dialog.h b/editor/project_manager/quick_settings_dialog.h new file mode 100644 index 00000000000..c4c4e601c93 --- /dev/null +++ b/editor/project_manager/quick_settings_dialog.h @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* quick_settings_dialog.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef QUICK_SETTINGS_DIALOG_H +#define QUICK_SETTINGS_DIALOG_H + +#include "scene/gui/dialogs.h" + +class Button; +class Label; +class MarginContainer; +class OptionButton; +class PanelContainer; +class VBoxContainer; + +class QuickSettingsDialog : public AcceptDialog { + GDCLASS(QuickSettingsDialog, AcceptDialog); + + Vector editor_languages; + Vector editor_themes; + Vector editor_scales; + Vector editor_network_modes; + + void _fetch_setting_values(); + void _update_current_values(); + + PanelContainer *settings_list_panel = nullptr; + VBoxContainer *settings_list = nullptr; + + void _add_setting_control(const String &p_text, Control *p_control); + + OptionButton *language_option_button = nullptr; + OptionButton *theme_option_button = nullptr; + OptionButton *scale_option_button = nullptr; + OptionButton *network_mode_option_button = nullptr; + + Label *custom_theme_label = nullptr; + + void _language_selected(int p_id); + void _theme_selected(int p_id); + void _scale_selected(int p_id); + void _network_mode_selected(int p_id); + void _set_setting_value(const String &p_setting, const Variant &p_value, bool p_restart_required = false); + + Label *restart_required_label = nullptr; + Button *restart_required_button = nullptr; + + void _request_restart(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void update_size_limits(const Size2 &p_max_popup_size); + + QuickSettingsDialog(); +}; + +#endif // QUICK_SETTINGS_DIALOG_H From 28e8a4c0ee782fc54b822401885b38172b9b0e41 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Tue, 30 Jan 2024 17:35:46 +0100 Subject: [PATCH 5/6] Replace empty list dialog with an integrated panel --- .../plugins/asset_library_editor_plugin.cpp | 20 +- editor/project_manager.cpp | 327 ++++++++++-------- editor/project_manager.h | 22 +- editor/themes/editor_fonts.cpp | 3 + editor/themes/editor_theme_manager.cpp | 24 +- 5 files changed, 234 insertions(+), 162 deletions(-) diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 539bd0331a9..4477884b7af 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -1043,6 +1043,7 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int Button *first = memnew(Button); first->set_text(TTR("First", "Pagination")); + first->set_theme_type_variation("PanelBackgroundButton"); if (p_page != 0) { first->connect("pressed", callable_mp(this, &EditorAssetLibrary::_search).bind(0)); } else { @@ -1053,6 +1054,7 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int Button *prev = memnew(Button); prev->set_text(TTR("Previous", "Pagination")); + prev->set_theme_type_variation("PanelBackgroundButton"); if (p_page > 0) { prev->connect("pressed", callable_mp(this, &EditorAssetLibrary::_search).bind(p_page - 1)); } else { @@ -1063,26 +1065,22 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int hbc->add_child(memnew(VSeparator)); for (int i = from; i < to; i++) { + Button *current = memnew(Button); + // Add padding to make page number buttons easier to click. + current->set_text(vformat(" %d ", i + 1)); + current->set_theme_type_variation("PanelBackgroundButton"); if (i == p_page) { - Button *current = memnew(Button); - // Keep the extended padding for the currently active page (see below). - current->set_text(vformat(" %d ", i + 1)); current->set_disabled(true); current->set_focus_mode(Control::FOCUS_NONE); - - hbc->add_child(current); } else { - Button *current = memnew(Button); - // Add padding to make page number buttons easier to click. - current->set_text(vformat(" %d ", i + 1)); current->connect("pressed", callable_mp(this, &EditorAssetLibrary::_search).bind(i)); - - hbc->add_child(current); } + hbc->add_child(current); } Button *next = memnew(Button); next->set_text(TTR("Next", "Pagination")); + next->set_theme_type_variation("PanelBackgroundButton"); if (p_page < p_page_count - 1) { next->connect("pressed", callable_mp(this, &EditorAssetLibrary::_search).bind(p_page + 1)); } else { @@ -1094,6 +1092,7 @@ HBoxContainer *EditorAssetLibrary::_make_pages(int p_page, int p_page_count, int Button *last = memnew(Button); last->set_text(TTR("Last", "Pagination")); + last->set_theme_type_variation("PanelBackgroundButton"); if (p_page != p_page_count - 1) { last->connect("pressed", callable_mp(this, &EditorAssetLibrary::_search).bind(p_page_count - 1)); } else { @@ -1619,6 +1618,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { library_message_button = memnew(Button); library_message_button->set_h_size_flags(SIZE_SHRINK_CENTER); + library_message_button->set_theme_type_variation("PanelBackgroundButton"); library_message_box->add_child(library_message_button); asset_top_page = memnew(HBoxContainer); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index c9234cc071b..f0583c36277 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -86,14 +86,10 @@ void ProjectManager::_notification(int p_what) { case NOTIFICATION_READY: { const int default_sorting = (int)EDITOR_GET("project_manager/sorting_order"); filter_option->select(default_sorting); - _project_list->set_order_option(default_sorting); + project_list->set_order_option(default_sorting); _select_main_view(MAIN_VIEW_PROJECTS); - - // Suggest browsing asset library to get templates/demos. - if (asset_library && _project_list->get_project_count() == 0) { - _suggest_asset_library(); - } + _update_list_placeholder(); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -112,6 +108,7 @@ void ProjectManager::_notification(int p_what) { if (EditorThemeManager::is_generated_theme_outdated()) { _update_theme(); } + _update_list_placeholder(); } break; } } @@ -224,7 +221,14 @@ void ProjectManager::_update_theme(bool p_skip_creation) { // Project list. { loading_label->add_theme_font_override("font", get_theme_font(SNAME("bold"), EditorStringName(EditorFonts))); - search_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("search_panel"), SNAME("ProjectManager"))); + project_list_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("project_list"), SNAME("ProjectManager"))); + + empty_list_create_project->set_icon(get_editor_theme_icon(SNAME("Add"))); + empty_list_import_project->set_icon(get_editor_theme_icon(SNAME("Load"))); + empty_list_open_assetlib->set_icon(get_editor_theme_icon(SNAME("AssetLib"))); + + empty_list_online_warning->add_theme_font_override("font", get_theme_font(SNAME("italic"), EditorStringName(EditorFonts))); + empty_list_online_warning->add_theme_color_override("font_color", get_theme_color(SNAME("font_placeholder_color"), EditorStringName(Editor))); // Top bar. search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); @@ -340,43 +344,6 @@ void ProjectManager::_show_about() { about_dialog->popup_centered(Size2(780, 500) * EDSCALE); } -void ProjectManager::_suggest_asset_library() { - if (!suggest_asset_library_dialog) { - suggest_asset_library_dialog = memnew(ConfirmationDialog); - suggest_asset_library_dialog->set_title(TTR("Getting Started with Godot")); - add_child(suggest_asset_library_dialog); - suggest_asset_library_dialog->connect("confirmed", callable_mp(this, &ProjectManager::_open_asset_library_confirmed)); - - VBoxContainer *suggest_vbox = memnew(VBoxContainer); - suggest_asset_library_dialog->add_child(suggest_vbox); - - suggest_asset_library_label = memnew(RichTextLabel); - suggest_asset_library_label->set_use_bbcode(true); - suggest_asset_library_label->set_fit_content(true); - suggest_asset_library_label->set_h_size_flags(SIZE_EXPAND_FILL); - suggest_asset_library_label->add_theme_style_override("normal", memnew(StyleBoxEmpty)); - suggest_vbox->add_child(suggest_asset_library_label); - } - - const int network_mode = EDITOR_GET("network/connection/network_mode"); - if (network_mode == EditorSettings::NETWORK_OFFLINE) { - const String line1 = TTR("You don't have any projects yet."); - const String line2 = TTR("Get started with one of the official project templates from the Asset Library!"); - const String line3 = TTR("Note: The Asset Library requires an online connection and involves sending data over the internet."); - - suggest_asset_library_label->set_text(vformat("%s\n\n[b]%s[/b]\n\n[i]%s[/i]", line1, line2, line3)); - suggest_asset_library_dialog->set_ok_button_text(TTR("Go Online and Open Asset Library")); - } else { - const String line1 = TTR("You don't have any projects yet."); - const String line2 = TTR("Get started with one of the official project templates from the Asset Library!"); - - suggest_asset_library_label->set_text(vformat("%s\n\n[b]%s[/b]", line1, line2)); - suggest_asset_library_dialog->set_ok_button_text(TTR("Open Asset Library")); - } - - suggest_asset_library_dialog->popup_centered(Size2(540, 100) * EDSCALE); -} - void ProjectManager::_open_asset_library_confirmed() { const int network_mode = EDITOR_GET("network/connection/network_mode"); if (network_mode == EditorSettings::NETWORK_OFFLINE) { @@ -389,6 +356,11 @@ void ProjectManager::_open_asset_library_confirmed() { _select_main_view(MAIN_VIEW_ASSETLIB); } +void ProjectManager::_show_error(const String &p_message, const Size2 &p_min_size) { + error_dialog->set_text(p_message); + error_dialog->popup_centered(p_min_size); +} + void ProjectManager::_dim_window() { // This method must be called before calling `get_tree()->quit()`. // Otherwise, its effect won't be visible @@ -423,12 +395,32 @@ void ProjectManager::_version_button_pressed() { // Project list. +void ProjectManager::_update_list_placeholder() { + if (project_list->get_project_count() > 0) { + empty_list_placeholder->hide(); + return; + } + + empty_list_open_assetlib->set_visible(asset_library); + + const int network_mode = EDITOR_GET("network/connection/network_mode"); + if (network_mode == EditorSettings::NETWORK_OFFLINE) { + empty_list_open_assetlib->set_text(TTR("Go Online and Open Asset Library")); + empty_list_online_warning->set_visible(true); + } else { + empty_list_open_assetlib->set_text(TTR("Open Asset Library")); + empty_list_online_warning->set_visible(false); + } + + empty_list_placeholder->show(); +} + void ProjectManager::_scan_projects() { scan_dir->popup_file_dialog(); } void ProjectManager::_run_project() { - const HashSet &selected_list = _project_list->get_selected_project_keys(); + const HashSet &selected_list = project_list->get_selected_project_keys(); if (selected_list.size() < 1) { return; @@ -443,13 +435,12 @@ void ProjectManager::_run_project() { } void ProjectManager::_run_project_confirm() { - Vector selected_list = _project_list->get_selected_projects(); + Vector selected_list = project_list->get_selected_projects(); for (int i = 0; i < selected_list.size(); ++i) { const String &selected_main = selected_list[i].main_scene; if (selected_main.is_empty()) { - run_error_diag->set_text(TTR("Can't run project: no main scene defined.\nPlease edit the project and set the main scene in the Project Settings under the \"Application\" category.")); - run_error_diag->popup_centered(); + _show_error(TTR("Can't run project: Project has no main scene defined.\nPlease edit the project and set the main scene in the Project Settings under the \"Application\" category.")); continue; } @@ -457,8 +448,7 @@ void ProjectManager::_run_project_confirm() { // `.substr(6)` on `ProjectSettings::get_singleton()->get_imported_files_path()` strips away the leading "res://". if (!DirAccess::exists(path.path_join(ProjectSettings::get_singleton()->get_imported_files_path().substr(6)))) { - run_error_diag->set_text(TTR("Can't run project: Assets need to be imported.\nPlease edit the project to trigger the initial import.")); - run_error_diag->popup_centered(); + _show_error(TTR("Can't run project: Assets need to be imported first.\nPlease edit the project to trigger the initial import.")); continue; } @@ -483,14 +473,13 @@ void ProjectManager::_open_selected_projects() { // This is especially important for the Web project manager. loading_label->show(); - const HashSet &selected_list = _project_list->get_selected_project_keys(); + const HashSet &selected_list = project_list->get_selected_project_keys(); for (const String &path : selected_list) { String conf = path.path_join("project.godot"); if (!FileAccess::exists(conf)) { - dialog_error->set_text(vformat(TTR("Can't open project at '%s'."), path)); - dialog_error->popup_centered(); + _show_error(vformat(TTR("Can't open project at '%s'."), path)); return; } @@ -511,14 +500,14 @@ void ProjectManager::_open_selected_projects() { ERR_FAIL_COND(err); } - _project_list->project_opening_initiated = true; + project_list->project_opening_initiated = true; _dim_window(); get_tree()->quit(); } void ProjectManager::_open_selected_projects_ask() { - const HashSet &selected_list = _project_list->get_selected_project_keys(); + const HashSet &selected_list = project_list->get_selected_project_keys(); if (selected_list.size() < 1) { return; @@ -532,7 +521,7 @@ void ProjectManager::_open_selected_projects_ask() { return; } - ProjectList::Item project = _project_list->get_selected_projects()[0]; + ProjectList::Item project = project_list->get_selected_projects()[0]; if (project.missing) { return; } @@ -569,8 +558,7 @@ void ProjectManager::_open_selected_projects_ask() { } // Check if the file was generated by a newer, incompatible engine version. if (config_version > ProjectSettings::CONFIG_VERSION) { - dialog_error->set_text(vformat(TTR("Can't open project \"%s\" at the following path:\n\n%s\n\nThe project settings were created by a newer engine version, whose settings are not compatible with this version."), project.project_name, project.path)); - dialog_error->popup_centered(popup_min_size); + _show_error(vformat(TTR("Can't open project \"%s\" at the following path:\n\n%s\n\nThe project settings were created by a newer engine version, whose settings are not compatible with this version."), project.project_name, project.path), popup_min_size); return; } // Check if the project is using features not supported by this build of Godot. @@ -608,38 +596,38 @@ void ProjectManager::_open_selected_projects_ask() { } void ProjectManager::_install_project(const String &p_zip_path, const String &p_title) { - npdialog->set_mode(ProjectDialog::MODE_INSTALL); - npdialog->set_zip_path(p_zip_path); - npdialog->set_zip_title(p_title); - npdialog->show_dialog(); + project_dialog->set_mode(ProjectDialog::MODE_INSTALL); + project_dialog->set_zip_path(p_zip_path); + project_dialog->set_zip_title(p_title); + project_dialog->show_dialog(); } void ProjectManager::_import_project() { - npdialog->set_mode(ProjectDialog::MODE_IMPORT); - npdialog->ask_for_path_and_show(); + project_dialog->set_mode(ProjectDialog::MODE_IMPORT); + project_dialog->ask_for_path_and_show(); } void ProjectManager::_new_project() { - npdialog->set_mode(ProjectDialog::MODE_NEW); - npdialog->show_dialog(); + project_dialog->set_mode(ProjectDialog::MODE_NEW); + project_dialog->show_dialog(); } void ProjectManager::_rename_project() { - const HashSet &selected_list = _project_list->get_selected_project_keys(); + const HashSet &selected_list = project_list->get_selected_project_keys(); if (selected_list.size() == 0) { return; } for (const String &E : selected_list) { - npdialog->set_project_path(E); - npdialog->set_mode(ProjectDialog::MODE_RENAME); - npdialog->show_dialog(); + project_dialog->set_project_path(E); + project_dialog->set_mode(ProjectDialog::MODE_RENAME); + project_dialog->show_dialog(); } } void ProjectManager::_erase_project() { - const HashSet &selected_list = _project_list->get_selected_project_keys(); + const HashSet &selected_list = project_list->get_selected_project_keys(); if (selected_list.size() == 0) { return; @@ -663,17 +651,19 @@ void ProjectManager::_erase_missing_projects() { } void ProjectManager::_erase_project_confirm() { - _project_list->erase_selected_projects(false); + project_list->erase_selected_projects(false); _update_project_buttons(); + _update_list_placeholder(); } void ProjectManager::_erase_missing_projects_confirm() { - _project_list->erase_missing_projects(); + project_list->erase_missing_projects(); _update_project_buttons(); + _update_list_placeholder(); } void ProjectManager::_update_project_buttons() { - Vector selected_projects = _project_list->get_selected_projects(); + Vector selected_projects = project_list->get_selected_projects(); bool empty_selection = selected_projects.is_empty(); bool is_missing_project_selected = false; @@ -690,48 +680,48 @@ void ProjectManager::_update_project_buttons() { manage_tags_btn->set_disabled(empty_selection || is_missing_project_selected || selected_projects.size() > 1); run_btn->set_disabled(empty_selection || is_missing_project_selected); - erase_missing_btn->set_disabled(!_project_list->is_any_project_missing()); + erase_missing_btn->set_disabled(!project_list->is_any_project_missing()); } void ProjectManager::_on_projects_updated() { - Vector selected_projects = _project_list->get_selected_projects(); + Vector selected_projects = project_list->get_selected_projects(); int index = 0; for (int i = 0; i < selected_projects.size(); ++i) { - index = _project_list->refresh_project(selected_projects[i].path); + index = project_list->refresh_project(selected_projects[i].path); } if (index != -1) { - _project_list->ensure_project_visible(index); + project_list->ensure_project_visible(index); } - _project_list->update_dock_menu(); + project_list->update_dock_menu(); } void ProjectManager::_on_project_created(const String &dir) { - _project_list->add_project(dir, false); - _project_list->save_config(); + project_list->add_project(dir, false); + project_list->save_config(); search_box->clear(); - int i = _project_list->refresh_project(dir); - _project_list->select_project(i); - _project_list->ensure_project_visible(i); + int i = project_list->refresh_project(dir); + project_list->select_project(i); + project_list->ensure_project_visible(i); _open_selected_projects_ask(); - _project_list->update_dock_menu(); + project_list->update_dock_menu(); } void ProjectManager::_on_order_option_changed(int p_idx) { if (is_inside_tree()) { - _project_list->set_order_option(p_idx); + project_list->set_order_option(p_idx); } } void ProjectManager::_on_search_term_changed(const String &p_term) { - _project_list->set_search_term(p_term); - _project_list->sort_projects(); + project_list->set_search_term(p_term); + project_list->sort_projects(); // Select the first visible project in the list. // This makes it possible to open a project without ever touching the mouse, // as the search field is automatically focused on startup. - _project_list->select_first_visible_project(); + project_list->select_first_visible_project(); _update_project_buttons(); } @@ -754,7 +744,7 @@ void ProjectManager::_manage_project_tags() { project_tags->get_child(i)->queue_free(); } - const ProjectList::Item item = _project_list->get_selected_projects()[0]; + const ProjectList::Item item = project_list->get_selected_projects()[0]; current_project_tags = item.tags; for (const String &tag : current_project_tags) { ProjectTag *tag_control = memnew(ProjectTag(tag, true)); @@ -798,7 +788,7 @@ void ProjectManager::_apply_project_tags() { } ConfigFile cfg; - const String project_godot = _project_list->get_selected_projects()[0].path.path_join("project.godot"); + const String project_godot = project_list->get_selected_projects()[0].path.path_join("project.godot"); Error err = cfg.load(project_godot); if (err != OK) { tag_edit_error->set_text(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err)); @@ -876,7 +866,7 @@ void ProjectManager::_full_convert_button_pressed() { } void ProjectManager::_perform_full_project_conversion() { - Vector selected_list = _project_list->get_selected_projects(); + Vector selected_list = project_list->get_selected_projects(); if (selected_list.is_empty()) { return; } @@ -894,7 +884,7 @@ void ProjectManager::_perform_full_project_conversion() { Error err = OS::get_singleton()->create_instance(args); ERR_FAIL_COND(err); - _project_list->set_project_version(path, GODOT4_CONFIG_VERSION); + project_list->set_project_version(path, GODOT4_CONFIG_VERSION); } // Input and I/O. @@ -930,15 +920,15 @@ void ProjectManager::shortcut_input(const Ref &p_ev) { _open_selected_projects_ask(); } break; case Key::HOME: { - if (_project_list->get_project_count() > 0) { - _project_list->select_project(0); + if (project_list->get_project_count() > 0) { + project_list->select_project(0); _update_project_buttons(); } } break; case Key::END: { - if (_project_list->get_project_count() > 0) { - _project_list->select_project(_project_list->get_project_count() - 1); + if (project_list->get_project_count() > 0) { + project_list->select_project(project_list->get_project_count() - 1); _update_project_buttons(); } @@ -948,10 +938,10 @@ void ProjectManager::shortcut_input(const Ref &p_ev) { break; } - int index = _project_list->get_single_selected_index(); + int index = project_list->get_single_selected_index(); if (index > 0) { - _project_list->select_project(index - 1); - _project_list->ensure_project_visible(index - 1); + project_list->select_project(index - 1); + project_list->ensure_project_visible(index - 1); _update_project_buttons(); } @@ -962,10 +952,10 @@ void ProjectManager::shortcut_input(const Ref &p_ev) { break; } - int index = _project_list->get_single_selected_index(); - if (index + 1 < _project_list->get_project_count()) { - _project_list->select_project(index + 1); - _project_list->ensure_project_visible(index + 1); + int index = project_list->get_single_selected_index(); + if (index + 1 < project_list->get_project_count()) { + project_list->select_project(index + 1); + project_list->ensure_project_visible(index + 1); _update_project_buttons(); } @@ -1008,7 +998,7 @@ void ProjectManager::_files_dropped(PackedStringArray p_files) { for (const String &E : folders_set) { folders.push_back(E); } - _project_list->find_projects_multiple(folders); + project_list->find_projects_multiple(folders); } // Object methods. @@ -1159,7 +1149,7 @@ ProjectManager::ProjectManager() { local_projects_vb->add_child(hb); create_btn = memnew(Button); - create_btn->set_text(TTR("New")); + create_btn->set_text(TTR("Create")); create_btn->set_shortcut(ED_SHORTCUT("project_manager/new_project", TTR("New Project"), KeyModifierMask::CMD_OR_CTRL | Key::N)); create_btn->connect("pressed", callable_mp(this, &ProjectManager::_new_project)); hb->add_child(create_btn); @@ -1178,9 +1168,8 @@ ProjectManager::ProjectManager() { loading_label = memnew(Label(TTR("Loading, please wait..."))); loading_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); - hb->add_child(loading_label); - // The loading label is shown later. loading_label->hide(); + hb->add_child(loading_label); search_box = memnew(LineEdit); search_box->set_placeholder(TTR("Filter Projects")); @@ -1215,61 +1204,116 @@ ProjectManager::ProjectManager() { // Project list and its sidebar. { - HBoxContainer *search_tree_hb = memnew(HBoxContainer); - local_projects_vb->add_child(search_tree_hb); - search_tree_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL); + HBoxContainer *project_list_hbox = memnew(HBoxContainer); + local_projects_vb->add_child(project_list_hbox); + project_list_hbox->set_v_size_flags(Control::SIZE_EXPAND_FILL); - search_panel = memnew(PanelContainer); - search_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL); - search_tree_hb->add_child(search_panel); + project_list_panel = memnew(PanelContainer); + project_list_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL); + project_list_hbox->add_child(project_list_panel); - _project_list = memnew(ProjectList); - _project_list->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); - search_panel->add_child(_project_list); - _project_list->connect(ProjectList::SIGNAL_LIST_CHANGED, callable_mp(this, &ProjectManager::_update_project_buttons)); - _project_list->connect(ProjectList::SIGNAL_SELECTION_CHANGED, callable_mp(this, &ProjectManager::_update_project_buttons)); - _project_list->connect(ProjectList::SIGNAL_PROJECT_ASK_OPEN, callable_mp(this, &ProjectManager::_open_selected_projects_ask)); + project_list = memnew(ProjectList); + project_list->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED); + project_list_panel->add_child(project_list); + project_list->connect(ProjectList::SIGNAL_LIST_CHANGED, callable_mp(this, &ProjectManager::_update_project_buttons)); + project_list->connect(ProjectList::SIGNAL_LIST_CHANGED, callable_mp(this, &ProjectManager::_update_list_placeholder)); + project_list->connect(ProjectList::SIGNAL_SELECTION_CHANGED, callable_mp(this, &ProjectManager::_update_project_buttons)); + project_list->connect(ProjectList::SIGNAL_PROJECT_ASK_OPEN, callable_mp(this, &ProjectManager::_open_selected_projects_ask)); + + // Empty project list placeholder. + { + empty_list_placeholder = memnew(VBoxContainer); + empty_list_placeholder->set_v_size_flags(Control::SIZE_SHRINK_CENTER); + empty_list_placeholder->add_theme_constant_override("separation", 16 * EDSCALE); + empty_list_placeholder->hide(); + project_list_panel->add_child(empty_list_placeholder); + + RichTextLabel *empty_list_message = memnew(RichTextLabel); + empty_list_message->set_use_bbcode(true); + empty_list_message->set_fit_content(true); + empty_list_message->set_h_size_flags(SIZE_EXPAND_FILL); + empty_list_message->add_theme_style_override("normal", memnew(StyleBoxEmpty)); + + const String line1 = TTR("You don't have any projects yet."); + const String line2 = TTR("Get started by creating a new one,\nimporting one that exists, or by downloading a project template from the Asset Library!"); + empty_list_message->set_text(vformat("[center][b]%s[/b] %s[/center]", line1, line2)); + empty_list_placeholder->add_child(empty_list_message); + + HBoxContainer *empty_list_actions = memnew(HBoxContainer); + empty_list_actions->set_alignment(BoxContainer::ALIGNMENT_CENTER); + empty_list_placeholder->add_child(empty_list_actions); + + empty_list_create_project = memnew(Button); + empty_list_create_project->set_text(TTR("Create New Project")); + empty_list_create_project->set_theme_type_variation("PanelBackgroundButton"); + empty_list_actions->add_child(empty_list_create_project); + empty_list_create_project->connect("pressed", callable_mp(this, &ProjectManager::_new_project)); + + empty_list_import_project = memnew(Button); + empty_list_import_project->set_text(TTR("Import Existing Project")); + empty_list_import_project->set_theme_type_variation("PanelBackgroundButton"); + empty_list_actions->add_child(empty_list_import_project); + empty_list_import_project->connect("pressed", callable_mp(this, &ProjectManager::_import_project)); + + empty_list_open_assetlib = memnew(Button); + empty_list_open_assetlib->set_text(TTR("Open Asset Library")); + empty_list_open_assetlib->set_theme_type_variation("PanelBackgroundButton"); + empty_list_actions->add_child(empty_list_open_assetlib); + empty_list_open_assetlib->connect("pressed", callable_mp(this, &ProjectManager::_open_asset_library_confirmed)); + + empty_list_online_warning = memnew(Label); + empty_list_online_warning->set_horizontal_alignment(HorizontalAlignment::HORIZONTAL_ALIGNMENT_CENTER); + empty_list_online_warning->set_custom_minimum_size(Size2(220, 0) * EDSCALE); + empty_list_online_warning->set_autowrap_mode(TextServer::AUTOWRAP_WORD); + empty_list_online_warning->set_h_size_flags(Control::SIZE_EXPAND_FILL); + empty_list_online_warning->set_text(TTR("Note: The Asset Library requires an online connection and involves sending data over the internet.")); + empty_list_placeholder->add_child(empty_list_online_warning); + } // The side bar with the edit, run, rename, etc. buttons. - VBoxContainer *tree_vb = memnew(VBoxContainer); - tree_vb->set_custom_minimum_size(Size2(120, 120)); - search_tree_hb->add_child(tree_vb); + VBoxContainer *project_list_sidebar = memnew(VBoxContainer); + project_list_sidebar->set_custom_minimum_size(Size2(120, 120)); + project_list_hbox->add_child(project_list_sidebar); - tree_vb->add_child(memnew(HSeparator)); + project_list_sidebar->add_child(memnew(HSeparator)); open_btn = memnew(Button); open_btn->set_text(TTR("Edit")); open_btn->set_shortcut(ED_SHORTCUT("project_manager/edit_project", TTR("Edit Project"), KeyModifierMask::CMD_OR_CTRL | Key::E)); open_btn->connect("pressed", callable_mp(this, &ProjectManager::_open_selected_projects_ask)); - tree_vb->add_child(open_btn); + project_list_sidebar->add_child(open_btn); run_btn = memnew(Button); run_btn->set_text(TTR("Run")); run_btn->set_shortcut(ED_SHORTCUT("project_manager/run_project", TTR("Run Project"), KeyModifierMask::CMD_OR_CTRL | Key::R)); run_btn->connect("pressed", callable_mp(this, &ProjectManager::_run_project)); - tree_vb->add_child(run_btn); + project_list_sidebar->add_child(run_btn); rename_btn = memnew(Button); rename_btn->set_text(TTR("Rename")); // The F2 shortcut isn't overridden with Enter on macOS as Enter is already used to edit a project. rename_btn->set_shortcut(ED_SHORTCUT("project_manager/rename_project", TTR("Rename Project"), Key::F2)); rename_btn->connect("pressed", callable_mp(this, &ProjectManager::_rename_project)); - tree_vb->add_child(rename_btn); + project_list_sidebar->add_child(rename_btn); manage_tags_btn = memnew(Button); manage_tags_btn->set_text(TTR("Manage Tags")); - tree_vb->add_child(manage_tags_btn); + project_list_sidebar->add_child(manage_tags_btn); erase_btn = memnew(Button); erase_btn->set_text(TTR("Remove")); erase_btn->set_shortcut(ED_SHORTCUT("project_manager/remove_project", TTR("Remove Project"), Key::KEY_DELETE)); erase_btn->connect("pressed", callable_mp(this, &ProjectManager::_erase_project)); - tree_vb->add_child(erase_btn); + project_list_sidebar->add_child(erase_btn); + + Control *filler = memnew(Control); + filler->set_v_size_flags(Control::SIZE_EXPAND_FILL); + project_list_sidebar->add_child(filler); erase_missing_btn = memnew(Button); erase_missing_btn->set_text(TTR("Remove Missing")); erase_missing_btn->connect("pressed", callable_mp(this, &ProjectManager::_erase_missing_projects)); - tree_vb->add_child(erase_missing_btn); + project_list_sidebar->add_child(erase_missing_btn); } } @@ -1320,7 +1364,7 @@ ProjectManager::ProjectManager() { scan_dir->set_title(TTR("Select a Folder to Scan")); // must be after mode or it's overridden scan_dir->set_current_dir(EDITOR_GET("filesystem/directories/default_project_path")); add_child(scan_dir); - scan_dir->connect("dir_selected", callable_mp(_project_list, &ProjectList::find_projects)); + scan_dir->connect("dir_selected", callable_mp(project_list, &ProjectList::find_projects)); erase_missing_ask = memnew(ConfirmationDialog); erase_missing_ask->set_ok_button_text(TTR("Remove All")); @@ -1367,17 +1411,14 @@ ProjectManager::ProjectManager() { ask_full_convert_dialog->connect("confirmed", callable_mp(this, &ProjectManager::_perform_full_project_conversion)); add_child(ask_full_convert_dialog); - npdialog = memnew(ProjectDialog); - npdialog->connect("projects_updated", callable_mp(this, &ProjectManager::_on_projects_updated)); - npdialog->connect("project_created", callable_mp(this, &ProjectManager::_on_project_created)); - add_child(npdialog); + project_dialog = memnew(ProjectDialog); + project_dialog->connect("projects_updated", callable_mp(this, &ProjectManager::_on_projects_updated)); + project_dialog->connect("project_created", callable_mp(this, &ProjectManager::_on_project_created)); + add_child(project_dialog); - run_error_diag = memnew(AcceptDialog); - run_error_diag->set_title(TTR("Can't run project")); - add_child(run_error_diag); - - dialog_error = memnew(AcceptDialog); - add_child(dialog_error); + error_dialog = memnew(AcceptDialog); + error_dialog->set_title(TTR("Error")); + add_child(error_dialog); about_dialog = memnew(EditorAbout); add_child(about_dialog); @@ -1469,7 +1510,7 @@ ProjectManager::ProjectManager() { String autoscan_path = EDITOR_GET("filesystem/directories/autoscan_project_path"); if (!autoscan_path.is_empty()) { if (dir_access->dir_exists(autoscan_path)) { - _project_list->find_projects(autoscan_path); + project_list->find_projects(autoscan_path); scanned_for_projects = true; } else { Error error = dir_access->make_dir_recursive(autoscan_path); @@ -1480,7 +1521,7 @@ ProjectManager::ProjectManager() { } if (!scanned_for_projects) { - _project_list->update_project_list(); + project_list->update_project_list(); } } diff --git a/editor/project_manager.h b/editor/project_manager.h index 16c7bd9dac2..1c829e971d2 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -101,14 +101,14 @@ class ProjectManager : public Control { VBoxContainer *local_projects_vb = nullptr; EditorAssetLibrary *asset_library = nullptr; - ConfirmationDialog *suggest_asset_library_dialog = nullptr; - RichTextLabel *suggest_asset_library_label = nullptr; EditorAbout *about_dialog = nullptr; void _show_about(); - void _suggest_asset_library(); void _open_asset_library_confirmed(); + AcceptDialog *error_dialog = nullptr; + + void _show_error(const String &p_message, const Size2 &p_min_size = Size2()); void _dim_window(); // Quick settings. @@ -126,12 +126,20 @@ class ProjectManager : public Control { // Project list. - ProjectList *_project_list = nullptr; + VBoxContainer *empty_list_placeholder = nullptr; + Button *empty_list_create_project = nullptr; + Button *empty_list_import_project = nullptr; + Button *empty_list_open_assetlib = nullptr; + Label *empty_list_online_warning = nullptr; + + void _update_list_placeholder(); + + ProjectList *project_list = nullptr; LineEdit *search_box = nullptr; Label *loading_label = nullptr; OptionButton *filter_option = nullptr; - PanelContainer *search_panel = nullptr; + PanelContainer *project_list_panel = nullptr; Button *create_btn = nullptr; Button *import_btn = nullptr; @@ -154,9 +162,7 @@ class ProjectManager : public Control { ConfirmationDialog *multi_open_ask = nullptr; ConfirmationDialog *multi_run_ask = nullptr; - AcceptDialog *run_error_diag = nullptr; - AcceptDialog *dialog_error = nullptr; - ProjectDialog *npdialog = nullptr; + ProjectDialog *project_dialog = nullptr; void _scan_projects(); void _run_project(); diff --git a/editor/themes/editor_fonts.cpp b/editor/themes/editor_fonts.cpp index 3479c67b467..c13ee6e6b0b 100644 --- a/editor/themes/editor_fonts.cpp +++ b/editor/themes/editor_fonts.cpp @@ -388,6 +388,9 @@ void editor_register_fonts(const Ref &p_theme) { p_theme->set_font("main_bold_msdf", EditorStringName(EditorFonts), bold_fc_msdf); p_theme->set_font_size("bold_size", EditorStringName(EditorFonts), default_font_size); + p_theme->set_font("italic", EditorStringName(EditorFonts), italic_fc); + p_theme->set_font_size("italic_size", EditorStringName(EditorFonts), default_font_size); + // Title font. p_theme->set_font("title", EditorStringName(EditorFonts), bold_fc); diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index bccfe6d7866..d89bbb72232 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -1590,7 +1590,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref &p_the void EditorThemeManager::_populate_editor_styles(const Ref &p_theme, ThemeConfiguration &p_config) { // Project manager. { - p_theme->set_stylebox("search_panel", "ProjectManager", p_config.tree_panel_style); + p_theme->set_stylebox("project_list", "ProjectManager", p_config.tree_panel_style); p_theme->set_constant("sidebar_button_icon_separation", "ProjectManager", int(6 * EDSCALE)); // ProjectTag. @@ -1769,6 +1769,28 @@ void EditorThemeManager::_populate_editor_styles(const Ref &p_theme p_theme->set_stylebox("pressed", "EditorLogFilterButton", editor_log_button_pressed); } + // Buttons styles that stand out against the panel background (e.g. AssetLib). + { + p_theme->set_type_variation("PanelBackgroundButton", "Button"); + + Ref panel_button_style = p_config.button_style->duplicate(); + panel_button_style->set_bg_color(p_config.base_color.lerp(p_config.mono_color, 0.08)); + + Ref panel_button_style_hover = p_config.button_style_hover->duplicate(); + panel_button_style_hover->set_bg_color(p_config.base_color.lerp(p_config.mono_color, 0.16)); + + Ref panel_button_style_pressed = p_config.button_style_pressed->duplicate(); + panel_button_style_pressed->set_bg_color(p_config.base_color.lerp(p_config.mono_color, 0.20)); + + Ref panel_button_style_disabled = p_config.button_style_disabled->duplicate(); + panel_button_style_disabled->set_bg_color(p_config.disabled_bg_color); + + p_theme->set_stylebox("normal", "PanelBackgroundButton", panel_button_style); + p_theme->set_stylebox("hover", "PanelBackgroundButton", panel_button_style_hover); + p_theme->set_stylebox("pressed", "PanelBackgroundButton", panel_button_style_pressed); + p_theme->set_stylebox("disabled", "PanelBackgroundButton", panel_button_style_disabled); + } + // Top bar selectors. { p_theme->set_type_variation("TopBarOptionButton", "OptionButton"); From f923b58f88e43c09c45ffd155a71d3eb8349bf22 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Tue, 30 Jan 2024 17:35:46 +0100 Subject: [PATCH 6/6] Correctly handle failures to open a project --- editor/project_manager.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index f0583c36277..ffb482d103a 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -474,12 +474,12 @@ void ProjectManager::_open_selected_projects() { loading_label->show(); const HashSet &selected_list = project_list->get_selected_project_keys(); - for (const String &path : selected_list) { String conf = path.path_join("project.godot"); if (!FileAccess::exists(conf)) { - _show_error(vformat(TTR("Can't open project at '%s'."), path)); + loading_label->hide(); + _show_error(vformat(TTR("Can't open project at '%s'.\nProject file doesn't exist or is inaccessible."), path)); return; } @@ -497,7 +497,12 @@ void ProjectManager::_open_selected_projects() { args.push_back("--editor"); Error err = OS::get_singleton()->create_instance(args); - ERR_FAIL_COND(err); + if (err != OK) { + loading_label->hide(); + _show_error(vformat(TTR("Can't open project at '%s'.\nFailed to start the editor."), path)); + ERR_PRINT(vformat("Failed to start an editor instance for the project at '%s', error code %d.", path, err)); + return; + } } project_list->project_opening_initiated = true; @@ -508,7 +513,6 @@ void ProjectManager::_open_selected_projects() { void ProjectManager::_open_selected_projects_ask() { const HashSet &selected_list = project_list->get_selected_project_keys(); - if (selected_list.size() < 1) { return; }