diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 64d9a93b9f2d..270ef162295c 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -47,10 +47,7 @@ Copyright: 2007-2022, Juan Linietsky, Ariel Manzur. 2014-2022, Godot Engine contributors. License: Expat -Files: ./icon.png - ./icon.svg - ./logo.png - ./logo.svg +Files: ./editor/icons/icon_logo_godot.svg Comment: Godot Engine logo Copyright: 2017, Andrea CalabrĂ³ License: CC-BY-4.0 diff --git a/LOGO_LICENSE.md b/LOGO_LICENSE.md deleted file mode 100644 index 52ee37fb2f3a..000000000000 --- a/LOGO_LICENSE.md +++ /dev/null @@ -1,5 +0,0 @@ -Godot Engine Logo -Copyright (c) 2017 Andrea CalabrĂ³ - -This work is licensed under a Creative Commons Attribution 4.0 International -License (CC-BY-4.0 International) . diff --git a/editor/editor_about_ramatak.cpp b/editor/editor_about_ramatak.cpp new file mode 100644 index 000000000000..d5784167dada --- /dev/null +++ b/editor/editor_about_ramatak.cpp @@ -0,0 +1,296 @@ +#include "editor_about_ramatak.h" + +#include "core/authors.gen.h" +#include "core/color.h" +#include "core/license.gen.h" +#include "core/version.h" +#include "editor_node.h" +#include "scene/gui/nine_patch_rect.h" +#include "scene/gui/scroll_container.h" +#include "scene/resources/gradient.h" +#include "scene/resources/style_box.h" +#include "scene/resources/texture.h" + +// The metadata key used to store and retrieve the version text to copy to the clipboard. +static const String META_TEXT_TO_COPY = "text_to_copy"; + +void EditorAboutRamatak::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + int dialog_margin = get_constant("margin", "Dialogs"); + _panel_style->set_expand_margin_size_individual(dialog_margin, 0, dialog_margin, 0); + + _logo_ramatak->set_texture(get_icon("FaceAbout", "EditorIcons")); + Ref font = get_font("source", "EditorFonts"); + _tpl_text->add_font_override("normal_font", font); + _version_text->add_font_override("font", get_font("about", "EditorFonts")); + _about_text->add_font_override("font", get_font("about", "EditorFonts")); + _logo_godot->set_texture(get_icon("LogoGodot", "EditorIcons")); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + const bool dark_theme = EditorSettings::get_singleton()->is_dark_theme(); + Color font_color = dark_theme ? Color(0.94, 0.94, 0.94) : Color(0.02, 0.02, 0.02); + Color font_outline_color = dark_theme ? Color(0.32, 0.32, 0.32, 0.5) : Color(0.94, 0.94, 0.94, 0.8); + + _gradient->set_color(0, dark_theme ? Color(0.07, 0.82, 0.49) : Color(0.05, 0.03, 0.22)); + _logo_ramatak->set_modulate(dark_theme ? Color(0.05, 0.03, 0.22) : Color(0.07, 0.82, 0.49)); + _title->set_texture(get_icon(dark_theme ? "TitleDark" : "TitleLight", "EditorIcons")); + _version_text->add_color_override("font_color", font_color); + _version_text->add_color_override("font_outline_modulate", font_outline_color); + _about_text->add_color_override("font_color", font_color); + _about_text->add_color_override("font_outline_modulate", font_outline_color); + } break; + } +} + +void EditorAboutRamatak::_license_tree_selected() { + TreeItem *selected = _tpl_tree->get_selected(); + _tpl_text->scroll_to_line(0); + _tpl_text->set_text(selected->get_metadata(0)); +} + +void EditorAboutRamatak::_link_clicked(const String &p_link) { + OS::get_singleton()->shell_open(p_link); +} + +void EditorAboutRamatak::_bind_methods() { + ClassDB::bind_method(D_METHOD("_license_tree_selected"), &EditorAboutRamatak::_license_tree_selected); + ClassDB::bind_method(D_METHOD("_link_clicked"), &EditorAboutRamatak::_link_clicked); +} + +ScrollContainer *EditorAboutRamatak::_populate_list(const String &p_name, const List &p_sections, const char *const *const p_src[], const int p_flag_single_column) { + ScrollContainer *sc = memnew(ScrollContainer); + sc->set_name(p_name); + sc->set_v_size_flags(Control::SIZE_EXPAND); + + VBoxContainer *vbc = memnew(VBoxContainer); + vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + sc->add_child(vbc); + + for (int i = 0; i < p_sections.size(); i++) { + bool single_column = p_flag_single_column & 1 << i; + const char *const *names_ptr = p_src[i]; + if (*names_ptr) { + Label *lbl = memnew(Label); + lbl->set_text(p_sections[i]); + vbc->add_child(lbl); + + ItemList *il = memnew(ItemList); + il->set_h_size_flags(Control::SIZE_EXPAND_FILL); + il->set_same_column_width(true); + il->set_auto_height(true); + il->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + il->add_constant_override("hseparation", 16 * EDSCALE); + while (*names_ptr) { + il->add_item(String::utf8(*names_ptr++), nullptr, false); + } + il->set_max_columns(il->get_item_count() < 4 || single_column ? 1 : 16); + vbc->add_child(il); + + HSeparator *hs = memnew(HSeparator); + hs->set_modulate(Color(0, 0, 0, 0)); + vbc->add_child(hs); + } + } + + return sc; +} + +EditorAboutRamatak::EditorAboutRamatak() { + set_title("Ramatak Mobile Studio"); + set_hide_on_ok(true); + set_custom_minimum_size(Size2(800, 0) * EDSCALE); + + _panel = memnew(PanelContainer); + _panel->set_h_size_flags(Control::SIZE_EXPAND_FILL); + _panel->set_v_size_flags(Control::SIZE_EXPAND_FILL); + add_child(_panel); + + _panel_style = memnew(StyleBoxTexture); + _panel_style->set_default_margin(MARGIN_TOP, 8 * EDSCALE); + _panel_style->set_default_margin(MARGIN_BOTTOM, 8 * EDSCALE); + _panel->add_style_override("panel", _panel_style); + + GradientTexture *grad_tex = memnew(GradientTexture); + _gradient = memnew(Gradient); + _gradient->set_color(1, Color(0, 0, 0, 0)); + _gradient->add_point(0.8, Color(0, 0, 0, 0)); + grad_tex->set_gradient(_gradient); + _panel_style->set_texture(grad_tex); + + VBoxContainer *vbc = memnew(VBoxContainer); + _panel->add_child(vbc); + + HBoxContainer *hbc = memnew(HBoxContainer); + hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hbc->set_alignment(BoxContainer::ALIGN_CENTER); + hbc->add_constant_override("separation", 30 * EDSCALE); + vbc->add_child(hbc); + + _logo_ramatak = memnew(TextureRect); + _logo_ramatak->set_expand(true); + _logo_ramatak->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); + _logo_ramatak->set_custom_minimum_size(Size2(200, 200) * EDSCALE); + hbc->add_child(_logo_ramatak); + + VBoxContainer *info_vbc = memnew(VBoxContainer); + info_vbc->set_alignment(VBoxContainer::ALIGN_CENTER); + hbc->add_child(info_vbc); + + HBoxContainer *title_hbc = memnew(HBoxContainer); + info_vbc->add_child(title_hbc); + + _title = memnew(TextureRect); + title_hbc->add_child(_title); + + _version_text = memnew(Label); + _version_text->set_text("v" + itos(VERSION_MAJOR) + "." + itos(VERSION_MINOR) + "." + itos(VERSION_PATCH)); + _version_text->set_v_size_flags(Control::SIZE_SHRINK_END); + title_hbc->add_child(_version_text); + + _about_text = memnew(Label); + _about_text->set_text(String::utf8("\xc2\xa9 Ramatak Inc.\n\n") + TTR("Powered by Godot Engine") + "\n" + + TTR(String::utf8("\xc2\xa9 Juan Linietsky, Ariel Manzur, and Godot Engine contributors.")) + "\n"); + info_vbc->add_child(_about_text); + + HBoxContainer *hbc_buttons = memnew(HBoxContainer); + hbc_buttons->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + hbc_buttons->add_constant_override("separation", 16); + vbc->add_child(hbc_buttons); + + Button *show_godot = memnew(Button); + show_godot->set_text("About Godot"); + hbc_buttons->add_child(show_godot); + + Button *show_licenses = memnew(Button); + show_licenses->set_text("Third-party Licenses"); + hbc_buttons->add_child(show_licenses); + + // About Godot + + AcceptDialog *about_godot = memnew(AcceptDialog); + about_godot->set_title("About Godot"); + about_godot->set_hide_on_ok(true); + about_godot->set_custom_minimum_size(Size2(760, 200) * EDSCALE); + add_child(about_godot); + show_godot->connect("pressed", about_godot, "popup_centered"); + + HBoxContainer *hbc_godot = memnew(HBoxContainer); + about_godot->add_child(hbc_godot); + + _logo_godot = memnew(TextureRect); + _logo_godot->set_expand(true); + _logo_godot->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); + _logo_godot->set_custom_minimum_size(Size2(300, 0) * EDSCALE); + _logo_godot->set_v_size_flags(Control::SIZE_EXPAND_FILL); + hbc_godot->add_child(_logo_godot); + + RichTextLabel *desc_text = memnew(RichTextLabel); + desc_text->set_use_bbcode(true); + desc_text->set_h_size_flags(Control::SIZE_EXPAND_FILL); + StyleBoxEmpty *rtl_style = memnew(StyleBoxEmpty); + rtl_style->set_default_margin(MARGIN_LEFT, 3 * EDSCALE); + rtl_style->set_default_margin(MARGIN_RIGHT, 3 * EDSCALE); + rtl_style->set_default_margin(MARGIN_TOP, 3 * EDSCALE); + rtl_style->set_default_margin(MARGIN_BOTTOM, 3 * EDSCALE); + desc_text->add_style_override("normal", rtl_style); + desc_text->set_bbcode(vformat(TTR("Ramatak Mobile Studio is a modified version of the [url=%s]Godot Engine[/url], a free, libre, and open source cross-platform game engine for creation of 2D and 3D games.\n\nYou can help the original project by contributing with [url=%s]code[/url], or via a [url=%s]donation[/url]."), + "https://godotengine.org", "https://github.com/godotengine/godot", "https://godotengine.org/donate/")); + desc_text->connect("meta_clicked", this, "_link_clicked"); + hbc_godot->add_child(desc_text); + + // Third-party Licenses + + AcceptDialog *thirdparty_licenses = memnew(AcceptDialog); + thirdparty_licenses->set_title("Third-party Licenses"); + thirdparty_licenses->set_resizable(true); + thirdparty_licenses->set_hide_on_ok(true); + thirdparty_licenses->set_custom_minimum_size(Size2(600, 300) * EDSCALE); + add_child(thirdparty_licenses); + show_licenses->connect("pressed", thirdparty_licenses, "popup_centered", varray(Size2(700, 600 * EDSCALE))); + + ScrollContainer *scroll = memnew(ScrollContainer); + thirdparty_licenses->add_child(scroll); + + VBoxContainer *license_thirdparty = memnew(VBoxContainer); + license_thirdparty->set_name(TTR("Third-party Licenses")); + license_thirdparty->set_h_size_flags(Control::SIZE_EXPAND_FILL); + license_thirdparty->set_v_size_flags(Control::SIZE_EXPAND_FILL); + scroll->add_child(license_thirdparty); + + Label *tpl_label = memnew(Label); + tpl_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + tpl_label->set_autowrap(true); + tpl_label->set_text(TTR("The following is an exhaustive list of all such third-party components used in this software with their respective copyright statements and license terms.")); + tpl_label->set_size(Size2(630, 1) * EDSCALE); + license_thirdparty->add_child(tpl_label); + + HSplitContainer *tpl_hbc = memnew(HSplitContainer); + tpl_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + tpl_hbc->set_v_size_flags(Control::SIZE_EXPAND_FILL); + tpl_hbc->set_split_offset(240 * EDSCALE); + license_thirdparty->add_child(tpl_hbc); + + _tpl_tree = memnew(Tree); + _tpl_tree->set_hide_root(true); + TreeItem *root = _tpl_tree->create_item(); + TreeItem *tpl_ti_all = _tpl_tree->create_item(root); + tpl_ti_all->set_text(0, TTR("All Components")); + TreeItem *tpl_ti_tp = _tpl_tree->create_item(root); + tpl_ti_tp->set_text(0, TTR("Components")); + tpl_ti_tp->set_selectable(0, false); + TreeItem *tpl_ti_lc = _tpl_tree->create_item(root); + tpl_ti_lc->set_text(0, TTR("Licenses")); + tpl_ti_lc->set_selectable(0, false); + String long_text = ""; + + for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) { + const ComponentCopyright &component = COPYRIGHT_INFO[component_index]; + TreeItem *ti = _tpl_tree->create_item(tpl_ti_tp); + String component_name = component.name; + ti->set_text(0, component_name); + String text = component_name + "\n"; + long_text += "- " + component_name + "\n"; + for (int part_index = 0; part_index < component.part_count; part_index++) { + const ComponentCopyrightPart &part = component.parts[part_index]; + text += "\n Files:"; + for (int file_num = 0; file_num < part.file_count; file_num++) { + text += "\n " + String(part.files[file_num]); + } + String copyright; + for (int copyright_index = 0; copyright_index < part.copyright_count; copyright_index++) { + copyright += String::utf8("\n \xc2\xa9 ") + String::utf8(part.copyright_statements[copyright_index]); + } + text += copyright; + long_text += copyright; + String license = "\n License: " + String(part.license) + "\n"; + text += license; + long_text += license + "\n"; + } + ti->set_metadata(0, text); + } + + for (int i = 0; i < LICENSE_COUNT; i++) { + TreeItem *ti = _tpl_tree->create_item(tpl_ti_lc); + String licensename = String(LICENSE_NAMES[i]); + ti->set_text(0, licensename); + long_text += "- " + licensename + "\n\n"; + String licensebody = String(LICENSE_BODIES[i]); + ti->set_metadata(0, licensebody); + long_text += " " + licensebody.replace("\n", "\n ") + "\n\n"; + } + + tpl_ti_all->set_metadata(0, long_text); + tpl_hbc->add_child(_tpl_tree); + + _tpl_text = memnew(RichTextLabel); + _tpl_text->add_constant_override("line_separation", 6 * EDSCALE); + _tpl_text->set_h_size_flags(Control::SIZE_EXPAND_FILL); + _tpl_text->set_v_size_flags(Control::SIZE_EXPAND_FILL); + tpl_hbc->add_child(_tpl_text); + + _tpl_tree->connect("item_selected", this, "_license_tree_selected"); + tpl_ti_all->select(0); + _tpl_text->set_text(tpl_ti_all->get_metadata(0)); +} diff --git a/editor/editor_about_ramatak.h b/editor/editor_about_ramatak.h new file mode 100644 index 000000000000..15c772969fda --- /dev/null +++ b/editor/editor_about_ramatak.h @@ -0,0 +1,52 @@ +#ifndef EDITOR_ABOUT_RAMATAK_H +#define EDITOR_ABOUT_RAMATAK_H + +#include "scene/gui/control.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/item_list.h" +#include "scene/gui/link_button.h" +#include "scene/gui/panel_container.h" +#include "scene/gui/rich_text_label.h" +#include "scene/gui/scroll_container.h" +#include "scene/gui/separator.h" +#include "scene/gui/split_container.h" +#include "scene/gui/tab_container.h" +#include "scene/gui/texture_rect.h" +#include "scene/gui/tree.h" + +#include "editor_scale.h" +#include "scene/resources/dynamic_font.h" +#include "scene/resources/style_box.h" + +/** + * NOTE: Do not assume the EditorAboutRamatak singleton to be available in this class' methods. + * EditorAboutRamatak is also used from the project manager where EditorAboutRamatak isn't initialized. + */ +class EditorAboutRamatak : public AcceptDialog { + GDCLASS(EditorAboutRamatak, AcceptDialog); + +private: + void _license_tree_selected(); + void _link_clicked(const String &p_link); + ScrollContainer *_populate_list(const String &p_name, const List &p_sections, const char *const *const p_src[], const int p_flag_single_column = 0); + + PanelContainer *_panel; + Tree *_tpl_tree; + RichTextLabel *_tpl_text; + TextureRect *_logo_ramatak; + TextureRect *_logo_godot; + TextureRect *_title; + Label *_version_text; + Label *_about_text; + StyleBoxTexture *_panel_style; + Gradient *_gradient; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + EditorAboutRamatak(); +}; + +#endif // EDITOR_ABOUT_RAMATAK_H diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index abb1098cfadc..ee3972a95c14 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -283,4 +283,8 @@ void editor_register_fonts(Ref p_theme) { MAKE_SOURCE_FONT(df_text_editor_status_code, default_font_size); p_theme->set_font("status_source", "EditorFonts", df_text_editor_status_code); + + MAKE_DEFAULT_FONT(df_about, default_font_size * 1.2); + p_theme->set_font("about", "EditorFonts", df_about); + df_about->set_outline_size(1); } diff --git a/editor/editor_key_manager.cpp b/editor/editor_key_manager.cpp new file mode 100644 index 000000000000..13a977fb405a --- /dev/null +++ b/editor/editor_key_manager.cpp @@ -0,0 +1,1132 @@ +#include "editor/editor_key_manager.h" + +#include "editor_node.h" +#include "editor_scale.h" +#include "scene/gui/grid_container.h" +#include "scene/gui/tree.h" + +void EditorKeyManager::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + // List all saved keys. + if (EditorSettings::get_singleton()->has_setting("key_manager/keys")) { + Dictionary keys = EditorSettings::get_singleton()->get("key_manager/keys"); + Array sha256_list = keys.keys(); + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < sha256_list.size(); i++) { + String sha256 = sha256_list[i]; + + TreeItem *item = key_list->create_item(key_list->get_root()); + item->set_text(0, keys[sha256].get("alias")); + item->add_button(0, get_icon("Remove", "EditorIcons"), -1, false, TTR("Remove")); + item->set_metadata(0, sha256); + + if (!da->file_exists(keys[sha256].get("path"))) { + item->set_custom_color(0, get_color("error_color", "Editor")); + item->set_meta("missing", true); + } + } + } + + add_keys->set_icon(get_icon("Load", "EditorIcons")); + create_key_existing->set_icon(get_icon("Add", "EditorIcons")); + create_key_new->set_icon(get_icon("New", "EditorIcons")); + refresh->set_icon(get_icon("Reload", "EditorIcons")); + } +} + +int EditorKeyManager::_run_keytool(const List &p_arguments, String *r_output, bool p_expects_return) { + int result = OK; + String keytool = OS::get_singleton()->get_environment("RAMATAK_KEYTOOL_HOME"); + + r_output->clear(); + result = OS::get_singleton()->execute(keytool, p_arguments, true, nullptr, r_output); + + if (result == OK && p_expects_return && r_output->empty()) { + result = FAILED; + } + + if (result != OK) { + EditorNode::get_singleton()->show_warning(TTR("Error calling keytool binary. Path in JAVA_HOME or RAMATAK_KEYTOOL_HOME environment variables either invalid or not set.")); + } + + return result; +} + +Dictionary EditorKeyManager::_extract_key_info(const String &p_output) { + int from = 0; + Dictionary keys; + while (true) { + from = p_output.find("Alias name: ", from); + if (from == -1) { + break; + } + + from += String("Alias name: ").size() - 1; + + Dictionary values; + values["alias"] = p_output.substr(from, p_output.find("\n", from) - from); + + int limit = p_output.find("***", from); + if (limit == -1) { + limit = p_output.length() - 1; + } + + from = p_output.find("Owner: ", from); + + if (from == -1 || from >= limit) { + from = limit; + continue; + } + + from += String("Owner: ").size() - 1; + + // Fill up all tags first, so they don't return null. + for (Map::Element *E=dname_tags.front(); E; E = E->next()) { + values[E->get()] = ""; + } + + String dname = p_output.substr(from, p_output.find("\n", from) - from); + Vector dname_list = dname.split(", ", false); + for (int i = 0; i < dname_list.size(); i++) { + String item = dname_list[i]; + String tag = item.substr(0, item.find("=")); + + values[dname_tags[tag]] = item.substr(tag.size()); + } + + from = p_output.find(" until:", from); + if (from == -1 || from >= limit) { + from = limit; + continue; + } + + from -= 4; + + int year_start = p_output.substr(from, 4).to_int(); + from = p_output.find("\n", from); + int year_end = p_output.substr(from, 4).to_int(); + values["validity"] = year_end - year_start; + + from = p_output.find("SHA256: ", from); + if (from == -1 || from >= limit) { + from = limit; + continue; + } + + from += String("SHA256: ").size() - 1; + + String sha256 = p_output.substr(from, p_output.find("\n", from) - from); + keys[sha256] = values; + + from = limit; + } + + return keys; +} + +void EditorKeyManager::_add_keys_pressed() { + _add_key_mode = ADD_EXISTING; + + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->popup_centered_ratio(); +} + +void EditorKeyManager::_create_key_existing_pressed() { + if (new_key) { + return; + } + + _add_key_mode = ADD_NEW; + + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->popup_centered_ratio(); +} + +void EditorKeyManager::_create_key_new_pressed() { + if (new_key) { + return; + } + + _add_key_mode = ADD_CREATE_FILE; + + file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE); + file_dialog->set_current_file("new_keystore.keystore"); + file_dialog->popup_centered_ratio(); +} + +void EditorKeyManager::_open_keystore(const String &p_path) { + switch (_add_key_mode) { + case ADD_EXISTING: { + add_pass->clear(); + add_pass->set_editable(true); + unlock_keystore->set_disabled(false); + add_keys_list->clear(); + add_keys_dialog->get_ok()->set_disabled(true); + add_keys_dialog->popup_centered(); + add_pass->grab_focus(); + } break; + + case ADD_CREATE_FILE: + case ADD_NEW: { + _new_key(p_path); + } break; + + case ADD_MOVED: { + current_key_data["path"] = p_path; + _refresh_pressed(); + } break; + } +} + +void EditorKeyManager::_new_key(const String &p_path) { + current_key_data = Dictionary(); + current_key_data["path"] = p_path; + + new_key = key_list->create_item(key_list->get_root()); + new_key->set_text(0, TTR("[New]")); + new_key->select(0); + + from_keystore->set_text(p_path.get_file()); + from_keystore->set_tooltip(String()); + from_keystore->set_underline_mode(LinkButton::UNDERLINE_MODE_NEVER); + from_keystore->set_mouse_filter(MOUSE_FILTER_IGNORE); + refresh->set_disabled(true); + default_for->set_disabled(true); + default_for->select(0); + + alias->set_editable(true); + pass_store->set_editable(true); + pass_store->add_color_override("font_color", get_color("font_color", "Label")); + pass_store_confirm->set_editable(true); + pass_store_confirm->add_color_override("font_color", get_color("font_color", "Label")); + validity->set_editable(true); + owner->set_editable(true); + org_unit->set_editable(true); + org->set_editable(true); + local->set_editable(true); + state->set_editable(true); + country_code->set_editable(true); + + alias->set_text(""); + pass_store->set_text(""); + pass_store_confirm->set_text(""); + validity->set_min(1); + validity->set_value(1); + owner->set_text(""); + org_unit->set_text(""); + org->set_text(""); + local->set_text(""); + state->set_text(""); + country_code->set_text(""); + + reset->hide(); + save->hide(); + cancel->show(); + create->show(); + create->set_disabled(true); +} + +void EditorKeyManager::_add_keys_list_edited() { + bool has_keys_checked = false; + + TreeItem *item = add_keys_list->get_root()->get_children(); + while (item) { + if (item->is_checked(0)) { + has_keys_checked = true; + break; + } + + item = item->get_next(); + } + + add_keys_dialog->get_ok()->set_disabled(!has_keys_checked); +} + +void EditorKeyManager::_add_keys_dialog_confirmed() { + Dictionary keys = EditorSettings::get_singleton()->get("key_manager/keys"); + + TreeItem *item = add_keys_list->get_root()->get_children(); + TreeItem *added_item; + while (item) { + if (!item->is_checked(0)) { + item = item->get_next(); + continue; + } + + Dictionary data = item->get_metadata(0); + keys.merge(data); + String sha256 = data.keys()[0]; + + added_item = key_list->create_item(key_list->get_root()); + added_item->set_text(0, keys[sha256].get("alias")); + added_item->add_button(0, get_icon("Remove", "EditorIcons"), -1, false, TTR("Remove")); + added_item->set_metadata(0, sha256); + + item = item->get_next(); + } + + added_item->select(0); + + EditorSettings::get_singleton()->set_manually("key_manager/keys", keys); + EditorSettings::get_singleton()->save(); + + add_keys_dialog->hide(); +} + +void EditorKeyManager::_key_list_cell_selected() { + TreeItem *item = key_list->get_selected(); + ERR_FAIL_COND(!item); + + if (new_key) { + if (item != new_key) { + key_list->get_root()->remove_child(new_key); + new_key = nullptr; + + validity->set_editable(false); + owner->set_editable(false); + org_unit->set_editable(false); + org->set_editable(false); + local->set_editable(false); + state->set_editable(false); + country_code->set_editable(false); + + reset->show(); + save->show(); + cancel->hide(); + create->hide(); + } else { + return; + } + } + + Dictionary keys = EditorSettings::get_singleton()->get("key_manager/keys"); + String sha256 = item->get_metadata(0); + current_key_data = keys[sha256]; + + bool is_missing = item->has_meta("missing"); + + from_keystore->set_text(String(current_key_data["path"]).get_file()); + from_keystore->set_underline_mode(is_missing ? LinkButton::UNDERLINE_MODE_NEVER : LinkButton::UNDERLINE_MODE_ON_HOVER); + from_keystore->set_tooltip(current_key_data["path"]); + from_keystore->set_mouse_filter(is_missing ? MOUSE_FILTER_IGNORE : MOUSE_FILTER_PASS); + from_keystore->add_color_override("font_color", is_missing ? get_color("error_color", "Editor") : get_color("font_color", "LinkButton")); + from_keystore->set_meta("folder", String(current_key_data["path"]).get_base_dir()); + refresh->set_disabled(false); + + bool is_debug = EditorSettings::get_singleton()->get("key_manager/default_debug") == sha256; + bool is_release = EditorSettings::get_singleton()->get("key_manager/default_release") == sha256; + if (is_debug && is_release) { + default_for->select(DEFAULT_DEBUG_RELEASE); + } else if (is_debug) { + default_for->select(DEFAULT_DEBUG); + } else if (is_release) { + default_for->select(DEFAULT_RELEASE); + } else { + default_for->select(DEFAULT_NOTHING); + } + default_for->set_disabled(is_missing); + + alias->set_editable(!is_missing); + pass_store->set_editable(!is_missing); + pass_store_confirm->set_editable(!is_missing); + pass_store_confirm->add_color_override("font_color", get_color("font_color", "Label")); + validity->set_min(0); + + alias->set_text(current_key_data["alias"]); + pass_store->set_text(current_key_data["pass_store"]); + pass_store_confirm->set_text(current_key_data["pass_store"]); + validity->set_value(current_key_data["validity"]); + owner->set_text(current_key_data["owner"]); + org_unit->set_text(current_key_data["org_unit"]); + org->set_text(current_key_data["org"]); + local->set_text(current_key_data["local"]); + state->set_text(current_key_data["state"]); + country_code->set_text(current_key_data["country_code"]); + + missing_file->set_visible(is_missing); + + reset->set_disabled(true); + save->set_disabled(true); +} + +void EditorKeyManager::_key_list_button_pressed(Object *p_item, int p_column, int p_idx) { + TreeItem *item = Object::cast_to(p_item); + ERR_FAIL_COND(!item); + + String sha256 = item->get_metadata(0); + Dictionary keys = EditorSettings::get_singleton()->get("key_manager/keys"); + keys.erase(sha256); + EditorSettings::get_singleton()->set_manually("key_manager/keys", keys); + + if (EditorSettings::get_singleton()->get("key_manager/default_debug") == sha256) { + EditorSettings::get_singleton()->set_manually("key_manager/default_debug", String()); + } + if (EditorSettings::get_singleton()->get("key_manager/default_release") == sha256) { + EditorSettings::get_singleton()->set_manually("key_manager/default_release", String()); + } + + EditorSettings::get_singleton()->save(); + + if (key_list->get_selected() == item) { + current_key_data.clear(); + _unselect(); + } + + key_list->get_root()->remove_child(item); +} + +void EditorKeyManager::_unselect() { + from_keystore->set_text(TTR("[None]")); + from_keystore->set_tooltip(String()); + from_keystore->set_underline_mode(LinkButton::UNDERLINE_MODE_NEVER); + from_keystore->set_mouse_filter(MOUSE_FILTER_IGNORE); + refresh->set_disabled(true); + default_for->set_disabled(true); + default_for->select(0); + + alias->set_editable(false); + pass_store->set_editable(false); + pass_store_confirm->set_editable(false); + pass_store_confirm->add_color_override("font_color", get_color("font_color", "Label")); + validity->set_editable(false); + owner->set_editable(false); + org_unit->set_editable(false); + org->set_editable(false); + local->set_editable(false); + state->set_editable(false); + country_code->set_editable(false); + + alias->set_text(""); + pass_store->set_text(""); + pass_store_confirm->set_text(""); + validity->set_value(0); + owner->set_text(""); + org_unit->set_text(""); + org->set_text(""); + local->set_text(""); + state->set_text(""); + country_code->set_text(""); +} + +void EditorKeyManager::_from_keystore_pressed() { + OS::get_singleton()->shell_open(from_keystore->get_meta("folder")); +} + +void EditorKeyManager::_default_for_selected(int p_index) { + TreeItem *item = key_list->get_selected(); + ERR_FAIL_COND(!item); + + String sha256 = item->get_metadata(0); + switch (p_index) { + case DEFAULT_NOTHING: { + if (EditorSettings::get_singleton()->get("key_manager/default_debug") == sha256) { + EditorSettings::get_singleton()->set_manually("key_manager/default_debug", String()); + } + if (EditorSettings::get_singleton()->get("key_manager/default_release") == sha256) { + EditorSettings::get_singleton()->set_manually("key_manager/default_release", String()); + } + } break; + case DEFAULT_DEBUG: { + EditorSettings::get_singleton()->set_manually("key_manager/default_debug", sha256); + } break; + case DEFAULT_RELEASE: { + EditorSettings::get_singleton()->set_manually("key_manager/default_release", sha256); + } break; + case DEFAULT_DEBUG_RELEASE: { + EditorSettings::get_singleton()->set_manually("key_manager/default_debug", sha256); + EditorSettings::get_singleton()->set_manually("key_manager/default_release", sha256); + } break; + } + + EditorSettings::get_singleton()->save(); +} + +void EditorKeyManager::_refresh_pressed() { + String path = current_key_data["path"]; + TreeItem *item = key_list->get_selected(); + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (!da->file_exists(path)) { + item->set_meta("missing", true); + item->set_custom_color(0, get_color("error_color", "Editor")); + item->deselect(0); + item->select(0); + + return; + } + + List args; + args.push_back("-list"); + args.push_back("-v"); + args.push_back("-alias"); + args.push_back(current_key_data["alias"]); + args.push_back("-keystore"); + args.push_back(path); + args.push_back("-storepass"); + args.push_back(current_key_data["pass_store"]); + + Dictionary keys = EditorSettings::get_singleton()->get("key_manager/keys"); + String sha256 = item->get_metadata(0); + + String output; + int result = _run_keytool(args, &output); + if (result != OK || output.begins_with("keytool error")) { + if (result == OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not unlock keystore. The entry may have been deleted or the password may be incorrect.")); + } + + // Save if the path now points to a file that exists at least. + if (keys[sha256].get("path") != path) { + keys[sha256].set("path", current_key_data); + EditorSettings::get_singleton()->set_manually("key_manager/keys", keys); + } + } else { + Dictionary key = _extract_key_info(output); + if (!key.has(sha256)) { + EditorNode::get_singleton()->show_warning(TTR("The entry's SHA256 differs from the one stored. May be an incorrect file.")); + + item->set_meta("missing", true); + item->deselect(0); + item->select(0); + + return; + } + + current_key_data.merge(key[sha256], true); + keys[sha256] = current_key_data; + EditorSettings::get_singleton()->set_manually("key_manager/keys", keys); + } + + if (item->has_meta("missing")) { + item->remove_meta("missing"); + item->set_custom_color(0, get_color("font_color", "Tree")); + } + + item->deselect(0); + item->select(0); +} + +void EditorKeyManager::_keystore_text_changed(const String &p_text, const String &p_name) { + bool has_changed = false; + if (!new_key) { + has_changed = current_key_data["alias"] != alias->get_text() || current_key_data["pass_store"] != pass_store->get_text(); + } + + bool password_valid = true; + if (p_name == "pass_store") { + if (pass_store->get_text().strip_edges().empty() || pass_store->get_text().strip_edges().length() < KEY_PASSWORD_MINSIZE) { + password_valid = false; + pass_store->add_color_override("font_color", get_color("error_color", "Editor")); + } else { + pass_store->add_color_override("font_color", get_color("font_color", "Label")); + } + } + if (pass_store->get_text() != pass_store_confirm->get_text()) { + password_valid = false; + pass_store_confirm->add_color_override("font_color", get_color("error_color", "Editor")); + } else { + pass_store_confirm->add_color_override("font_color", get_color("font_color", "Label")); + } + + if (!new_key) { + reset->set_disabled(!has_changed); + save->set_disabled(!has_changed || !password_valid || alias->get_text().empty()); + } else { + create->set_disabled(!password_valid || alias->get_text().empty()); + } +} + +void EditorKeyManager::_find_file_pressed() { + _add_key_mode = ADD_MOVED; + + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->popup_centered_ratio(); +} + +void EditorKeyManager::_reset_pressed() { + alias->set_text(current_key_data["alias"]); + pass_store->set_text(current_key_data["pass_store"]); + pass_store_confirm->set_text(current_key_data["pass_store"]); + pass_store_confirm->add_color_override("font_color", get_color("font_color", "Label")); + validity->set_value(current_key_data["validity"]); + owner->set_text(current_key_data["owner"]); + org_unit->set_text(current_key_data["org_unit"]); + org->set_text(current_key_data["org"]); + local->set_text(current_key_data["local"]); + state->set_text(current_key_data["state"]); + country_code->set_text(current_key_data["country_code"]); + + reset->set_disabled(true); + save->set_disabled(true); +} + +void EditorKeyManager::_save_pressed() { + String path = current_key_data["path"]; + TreeItem *item = key_list->get_selected(); + + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (!da->file_exists(path)) { + item->set_meta("missing", true); + item->deselect(0); + item->select(0); + + return; + } + + bool has_alias_changed = false; + + if (alias->get_text() != current_key_data["alias"]) { + List args; + args.push_back("-changealias"); + args.push_back("-alias"); + args.push_back(current_key_data["alias"]); + args.push_back("-destalias"); + args.push_back(alias->get_text()); + args.push_back("-keystore"); + args.push_back(path); + args.push_back("-storepass"); + args.push_back(current_key_data["pass_store"]); + + String output; + int result = _run_keytool(args, &output, false); + if (result != OK) { + return; + } + + if (output.begins_with("keytool error")) { + EditorNode::get_singleton()->show_warning(TTR("Could not change alias in keystore file. The alias may be invalid or the password may be incorrect.")); + return; + } + + has_alias_changed = true; + + current_key_data["alias"] = alias->get_text(); + key_list->get_selected()->set_text(0, current_key_data["alias"]); + } + + String password = pass_store->get_text(); + Dictionary keys = EditorSettings::get_singleton()->get("key_manager/keys"); + + if (password != current_key_data["pass_store"]) { + List args; + args.push_back("-storepasswd"); + args.push_back("-new"); + args.push_back(password); + args.push_back("-keystore"); + args.push_back(path); + args.push_back("-storepass"); + args.push_back(current_key_data["pass_store"]); + + bool password_valid = true; + + String output; + int result = _run_keytool(args, &output, false); + if (result != OK) { + return; + } + + if (output.begins_with("keytool error")) { + if (!has_alias_changed) { + // Attempt to use the new password to unlock it. In case the user is trying to update a keystore that was modified externally. + args.clear(); + args.push_back("-list"); + args.push_back("-alias"); + args.push_back(current_key_data["alias"]); + args.push_back("-keystore"); + args.push_back(path); + args.push_back("-storepass"); + args.push_back(password); + + output.clear(); + result = _run_keytool(args, &output); + if (result != OK) { + return; + } + + if (output.begins_with("keytool error")) { + EditorNode::get_singleton()->show_warning(TTR("Could not change password in keystore file. Original password might have been changed externally.")); + return; + } + } else { + password_valid = false; + EditorNode::get_singleton()->show_warning(TTR("Could not change password in keystore file.")); + } + } + + if (password_valid) { + current_key_data["pass_store"] = password; + + // Change the stored password for all keys that also share this keystore. + Array sha256_list = keys.keys(); + for (int i = 0; i < sha256_list.size(); i++) { + Dictionary key = keys[sha256_list[i]]; + if (key["path"] != path) { + continue; + } + + key["pass_store"] = password; + keys[sha256_list[i]] = key; + } + } + } + + keys[key_list->get_selected()->get_metadata(0)] = current_key_data; + EditorSettings::get_singleton()->set_manually("key_manager/keys", keys); + EditorSettings::get_singleton()->save(); + + reset->set_disabled(true); + save->set_disabled(true); +} + +void EditorKeyManager::_cancel_pressed() { + key_list->get_root()->remove_child(new_key); + new_key = nullptr; + + _unselect(); +} + +void EditorKeyManager::_create_pressed() { + current_key_data["alias"] = alias->get_text(); + current_key_data["pass_store"] = pass_store->get_text(); + current_key_data["validity"] = (int)validity->get_value(); + current_key_data["owner"] = owner->get_text(); + current_key_data["org_unit"] = org_unit->get_text(); + current_key_data["org"] = org->get_text(); + current_key_data["local"] = local->get_text(); + current_key_data["state"] = state->get_text(); + current_key_data["country_code"] = country_code->get_text(); + + List args; + args.push_back("-genkey"); + args.push_back("-keystore"); + args.push_back(current_key_data["path"]); + args.push_back("-alias"); + args.push_back(current_key_data["alias"]); + args.push_back("-dname"); + + String dname_arg; + for (Map::Element *E=dname_tags.front(); E; E = E->next()) { + dname_arg += E->key() + "=" + current_key_data[E->get()]; + if (E->next()) { + dname_arg += ","; + } + } + args.push_back(dname_arg); + + args.push_back("-keyalg"); + args.push_back("RSA"); + args.push_back("-validity"); + args.push_back(itos(validity->get_value() * 365)); + args.push_back("-storepass"); + args.push_back(pass_store->get_text()); + + String output; + int result = _run_keytool(args, &output, false); + if (result != OK ) { + return; + } + + if (output.begins_with("keytool error")) { + EditorNode::get_singleton()->show_warning(TTR("Could not create key or keystore file.")); + return; + } + + args.clear(); + args.push_back("-list"); + args.push_back("-alias"); + args.push_back(current_key_data["alias"]); + args.push_back("-keystore"); + args.push_back(current_key_data["path"]); + args.push_back("-storepass"); + args.push_back(pass_store->get_text()); + + output.clear(); + result = _run_keytool(args, &output); + if (result != OK || output.begins_with("keytool error")) { + if (result == OK ) { + EditorNode::get_singleton()->show_warning(TTR("Could not read keystore file.")); + } + + return; + } + + int from = output.find("(SHA-256): "); + String sha256 = output.substr(from + String("(SHA-256): ").length()); + Dictionary keys = EditorSettings::get_singleton()->get("key_manager/keys"); + keys[sha256] = current_key_data; + EditorSettings::get_singleton()->set_manually("key_manager/keys", keys); + EditorSettings::get_singleton()->save(); + + new_key->set_text(0, current_key_data["alias"]); + new_key->add_button(0, get_icon("Remove", "EditorIcons"), -1, false, TTR("Remove")); + new_key->set_metadata(0, sha256); + + validity->set_editable(false); + owner->set_editable(false); + org_unit->set_editable(false); + org->set_editable(false); + local->set_editable(false); + state->set_editable(false); + country_code->set_editable(false); + + TreeItem *item = new_key; + new_key = nullptr; + item->deselect(0); + item->select(0); + + reset->show(); + save->show(); + cancel->hide(); + create->hide(); +} + +void EditorKeyManager::_unlock_keystore_pressed() { + String path = ProjectSettings::get_singleton()->globalize_path(file_dialog->get_current_file()); + String password = add_pass->get_text(); + + List args; + args.push_back("-list"); + args.push_back("-v"); + args.push_back("-keystore"); + args.push_back(path); + args.push_back("-storepass"); + args.push_back(password); + + String output; + int result = _run_keytool(args, &output); + if (result != OK || output.begins_with("keytool error")) { + add_pass->grab_focus(); + + if (result == OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not unlock keystore. The password may be incorrect.")); + } + + add_pass->select_all(); + + return; + } + + add_pass->set_editable(false); + unlock_keystore->set_disabled(true); + add_keys_list->grab_focus(); + + TreeItem *root = add_keys_list->create_item(); + Dictionary keys = EditorSettings::get_singleton()->get("key_manager/keys"); + Dictionary keys_extracted = _extract_key_info(output); + Array sha256_list = keys_extracted.keys(); + for (int i = 0; i < sha256_list.size(); i++) { + String sha256 = sha256_list[i]; + + Dictionary values = keys_extracted[sha256]; + values["path"] = file_dialog->get_current_path(); + values["pass_store"] = password; + + TreeItem *item = add_keys_list->create_item(root); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_text(0, keys_extracted[sha256].get("alias")); + + // Check if the key has already been added before. + if (keys.has(sha256)) { + // Fade out added keys for easier visual grepping. + item->set_custom_color(0, get_color("font_color", "Label") * Color(1, 1, 1, 0.5)); + } else { + Dictionary key; + key[sha256] = values; + item->set_metadata(0, key); + item->set_editable(0, true); + } + } +} + +void EditorKeyManager::_pass_box_entered(const String &p_text) { + _unlock_keystore_pressed(); +} + +void EditorKeyManager::_bind_methods() { + ClassDB::bind_method(D_METHOD("_add_keys_pressed"), &EditorKeyManager::_add_keys_pressed); + ClassDB::bind_method(D_METHOD("_create_key_existing_pressed"), &EditorKeyManager::_create_key_existing_pressed); + ClassDB::bind_method(D_METHOD("_create_key_new_pressed"), &EditorKeyManager::_create_key_new_pressed); + ClassDB::bind_method(D_METHOD("_open_keystore"), &EditorKeyManager::_open_keystore); + ClassDB::bind_method(D_METHOD("_add_keys_list_edited"), &EditorKeyManager::_add_keys_list_edited); + ClassDB::bind_method(D_METHOD("_add_keys_dialog_confirmed"), &EditorKeyManager::_add_keys_dialog_confirmed); + ClassDB::bind_method(D_METHOD("_key_list_cell_selected"), &EditorKeyManager::_key_list_cell_selected); + ClassDB::bind_method(D_METHOD("_key_list_button_pressed"), &EditorKeyManager::_key_list_button_pressed); + ClassDB::bind_method(D_METHOD("_from_keystore_pressed"), &EditorKeyManager::_from_keystore_pressed); + ClassDB::bind_method(D_METHOD("_refresh_pressed"), &EditorKeyManager::_refresh_pressed); + ClassDB::bind_method(D_METHOD("_default_for_selected"), &EditorKeyManager::_default_for_selected); + ClassDB::bind_method(D_METHOD("_keystore_text_changed"), &EditorKeyManager::_keystore_text_changed); + ClassDB::bind_method(D_METHOD("_find_file_pressed"), &EditorKeyManager::_find_file_pressed); + ClassDB::bind_method(D_METHOD("_reset_pressed"), &EditorKeyManager::_reset_pressed); + ClassDB::bind_method(D_METHOD("_save_pressed"), &EditorKeyManager::_save_pressed); + ClassDB::bind_method(D_METHOD("_cancel_pressed"), &EditorKeyManager::_cancel_pressed); + ClassDB::bind_method(D_METHOD("_create_pressed"), &EditorKeyManager::_create_pressed); + ClassDB::bind_method(D_METHOD("_unlock_keystore_pressed"), &EditorKeyManager::_unlock_keystore_pressed); + ClassDB::bind_method(D_METHOD("_pass_box_entered"), &EditorKeyManager::_pass_box_entered);; +} + +EditorKeyManager::EditorKeyManager() { + EDITOR_DEF("key_manager/keys", Dictionary()); + EDITOR_DEF("key_manager/default_debug", String()); + EDITOR_DEF("key_manager/default_release", String()); + + dname_tags["CN"] = "owner"; + dname_tags["OU"] = "org_unit"; + dname_tags["O"] = "org"; + dname_tags["L"] = "local"; + dname_tags["ST"] = "state"; + dname_tags["C"] = "country_code"; + + set_name(TTR("Key Manager")); + + VBoxContainer *vbc = memnew(VBoxContainer); + add_child(vbc); + HBoxContainer *hbc = memnew(HBoxContainer); + vbc->add_child(hbc); + + Label *l = memnew(Label(TTR("Keys:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(l); + + file_dialog = memnew(EditorFileDialog); + file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM); + file_dialog->add_filter("*.keystore; " + TTR("Keystore File")); + file_dialog->add_filter("*.jks; " + TTR("Java Keystore File")); + add_child(file_dialog); + file_dialog->connect("file_selected", this, "_open_keystore"); + + add_keys = memnew(Button); + add_keys->set_flat(true); + add_keys->set_tooltip(TTR("Add one or more keys from a keystore file.")); + hbc->add_child(add_keys); + add_keys->connect("pressed", this, "_add_keys_pressed"); + create_key_existing = memnew(Button); + create_key_existing->set_flat(true); + create_key_existing->set_tooltip(TTR("Create a new key in an existing keystore file.")); + hbc->add_child(create_key_existing); + create_key_existing->connect("pressed", this, "_create_key_existing_pressed"); + create_key_new = memnew(Button); + create_key_new->set_flat(true); + create_key_new->set_tooltip(TTR("Create a new key together with a new keystore file.")); + hbc->add_child(create_key_new); + create_key_new->connect("pressed", this, "_create_key_new_pressed"); + + key_list = memnew(Tree); + key_list->set_hide_root(true); + key_list->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); + key_list->set_v_size_flags(SIZE_EXPAND_FILL); + vbc->add_child(key_list); + key_list->create_item(); + key_list->connect("cell_selected", this, "_key_list_cell_selected"); + key_list->connect("button_pressed", this, "_key_list_button_pressed"); + + add_keys_dialog = memnew(ConfirmationDialog); + add_keys_dialog->set_title(TTR("Add Keys")); + add_keys_dialog->get_ok()->set_text(TTR("Add")); + add_keys_dialog->set_hide_on_ok(false); + add_keys_dialog->set_custom_minimum_size(Size2(400, 500) * EDSCALE); + add_child(add_keys_dialog); + add_keys_dialog->connect("confirmed", this, "_add_keys_dialog_confirmed"); + vbc = memnew(VBoxContainer); + add_keys_dialog->add_child(vbc); + l = memnew(Label(TTR("Password:"))); + vbc->add_child(l); + hbc = memnew(HBoxContainer); + vbc->add_child(hbc); + add_pass = memnew(LineEdit); + add_pass->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(add_pass); + add_pass->connect("text_entered", this, "_pass_box_entered"); + unlock_keystore = memnew(Button); + unlock_keystore->set_text(TTR("Unlock")); + hbc->add_child(unlock_keystore); + unlock_keystore->connect("pressed", this, "_unlock_keystore_pressed"); + add_keys_list = memnew(Tree); + add_keys_list->set_hide_root(true); + add_keys_list->set_v_size_flags(SIZE_EXPAND_FILL); + vbc->add_child(add_keys_list); + add_keys_list->connect("item_edited", this, "_add_keys_list_edited"); + + vbc = memnew(VBoxContainer); + add_child(vbc); + GridContainer *gc = memnew(GridContainer); + gc->set_columns(2); + vbc->add_child(gc); + + l = memnew(Label(TTR("From Keystore:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + hbc = memnew(HBoxContainer); + gc->add_child(hbc); + from_keystore = memnew(LinkButton()); + from_keystore->set_text(TTR("[None]")); + from_keystore->set_underline_mode(LinkButton::UNDERLINE_MODE_NEVER); + from_keystore->set_mouse_filter(MOUSE_FILTER_IGNORE); + from_keystore->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(from_keystore); + from_keystore->connect("pressed", this, "_from_keystore_pressed"); + + refresh = memnew(Button); + refresh->set_flat(true); + refresh->set_disabled(true); + refresh->set_tooltip(TTR("Refresh data from the keystore file.")); + hbc->add_child(refresh); + refresh->connect("pressed", this, "_refresh_pressed"); + + hbc = memnew(HBoxContainer); + vbc->add_child(hbc); + l = memnew(Label(TTR("Set as Default For:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + default_for = memnew(OptionButton); + default_for->set_disabled(true); + default_for->add_item(TTR("Nothing"), DEFAULT_NOTHING); + default_for->add_item("Release", DEFAULT_RELEASE); + default_for->add_item("Debug", DEFAULT_DEBUG); + default_for->add_item("Release and Debug", DEFAULT_DEBUG_RELEASE); + default_for->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(default_for); + default_for->connect("item_selected", this, "_default_for_selected"); + + HSeparator *sep = memnew(HSeparator); + vbc->add_child(sep); + + gc = memnew(GridContainer); + gc->set_columns(2); + gc->set_custom_minimum_size(Size2(400 * EDSCALE, 0)); + vbc->add_child(gc); + + l = memnew(Label(TTR("Store Password:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + pass_store = memnew(LineEdit); + pass_store->set_editable(false); + pass_store->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(pass_store); + pass_store->connect("text_changed", this, "_keystore_text_changed", varray("pass_store")); + + l = memnew(Label(TTR("Confirm Store Password:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + pass_store_confirm = memnew(LineEdit); + pass_store_confirm->set_editable(false); + pass_store_confirm->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(pass_store_confirm); + pass_store_confirm->connect("text_changed", this, "_keystore_text_changed", varray("pass_store")); + + sep = memnew(HSeparator); + vbc->add_child(sep); + + gc = memnew(GridContainer); + gc->set_columns(2); + gc->set_custom_minimum_size(Size2(400 * EDSCALE, 0)); + vbc->add_child(gc); + + l = memnew(Label(TTR("Alias:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + alias = memnew(LineEdit); + alias->set_editable(false); + alias->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(alias); + alias->connect("text_changed", this, "_keystore_text_changed", varray("alias")); + + l = memnew(Label(TTR("Validity (Years):"))); + gc->add_child(l); + validity = memnew(SpinBox); + validity->set_editable(false); + validity->set_max(99); + gc->add_child(validity); + + l = memnew(Label(TTR("First and Last Name:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + owner = memnew(LineEdit); + owner->set_editable(false); + owner->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(owner); + + l = memnew(Label(TTR("Organizational Unit:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + org_unit = memnew(LineEdit); + org_unit->set_editable(false); + org_unit->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(org_unit); + + l = memnew(Label(TTR("Organization:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + org = memnew(LineEdit); + org->set_editable(false); + org->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(org); + + l = memnew(Label(TTR("City or Locality:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + local = memnew(LineEdit); + local->set_editable(false); + local->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(local); + + l = memnew(Label(TTR("State or Province:"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + state = memnew(LineEdit); + state->set_editable(false); + state->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(state); + + l = memnew(Label(TTR("Country Code (XX):"))); + l->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(l); + country_code = memnew(LineEdit); + country_code->set_editable(false); + country_code->set_h_size_flags(SIZE_EXPAND_FILL); + gc->add_child(country_code); + + missing_file = memnew(VBoxContainer); + missing_file->hide(); + vbc->add_child(missing_file); + + sep = memnew(HSeparator); + missing_file->add_child(sep); + + l = memnew(Label(TTR("The keystore file is missing. It could have been renamed, moved, or deleted."))); + l->set_align(Label::ALIGN_CENTER); + l->set_autowrap(true); + missing_file->add_child(l); + Button *find_file = memnew(Button); + find_file->set_text(TTR("Find File")); + missing_file->add_child(find_file); + find_file->set_h_size_flags(SIZE_SHRINK_CENTER); + find_file->connect("pressed", this, "_find_file_pressed"); + + hbc = memnew(HBoxContainer); + hbc->set_alignment(HBoxContainer::ALIGN_CENTER); + hbc->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_END); + vbc->add_child(hbc); + + reset = memnew(Button); + reset->set_text(TTR("Reset")); + reset->set_disabled(true); + hbc->add_child(reset); + reset->connect("pressed", this, "_reset_pressed"); + save = memnew(Button); + save->set_text(TTR("Save")); + save->set_disabled(true); + hbc->add_child(save); + save->connect("pressed", this, "_save_pressed"); + + cancel = memnew(Button); + cancel->set_text(TTR("Cancel")); + cancel->hide(); + hbc->add_child(cancel); + cancel->connect("pressed", this, "_cancel_pressed"); + create = memnew(Button); + create->set_text(TTR("Create")); + create->hide(); + hbc->add_child(create); + create->connect("pressed", this, "_create_pressed"); +} diff --git a/editor/editor_key_manager.h b/editor/editor_key_manager.h new file mode 100644 index 000000000000..2ff2e153ede9 --- /dev/null +++ b/editor/editor_key_manager.h @@ -0,0 +1,120 @@ +#ifndef EDITOR_KEY_MANAGER_H +#define EDITOR_KEY_MANAGER_H + +#include "core/dictionary.h" +#include "editor/editor_file_dialog.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/link_button.h" +#include "scene/gui/option_button.h" +#include "scene/gui/spin_box.h" +#include "scene/gui/split_container.h" +#include "scene/gui/tree.h" + +class EditorKeyManager : public HSplitContainer { + GDCLASS(EditorKeyManager, HSplitContainer); + +public: + enum AddKeyMode { + ADD_EXISTING, + ADD_NEW, + ADD_CREATE_FILE, + ADD_MOVED + }; + + enum DefaultType { + DEFAULT_NOTHING, + DEFAULT_RELEASE, + DEFAULT_DEBUG, + DEFAULT_DEBUG_RELEASE + }; + + const int KEY_PASSWORD_MINSIZE = 6; + +private: + Map dname_tags; + + AddKeyMode _add_key_mode = ADD_EXISTING; + + Dictionary current_key_data; + + Button *add_keys; + Button *create_key_existing; + Button *create_key_new; + + EditorFileDialog *file_dialog; + + ConfirmationDialog *add_keys_dialog; + LineEdit *add_pass; + Button *unlock_keystore; + Tree *add_keys_list; + + Tree *key_list; + TreeItem *new_key = nullptr; + + LinkButton *from_keystore; + OptionButton *default_for; + Button *refresh; + + LineEdit *alias; + LineEdit *pass_store; + LineEdit *pass_store_confirm; + SpinBox *validity; + LineEdit *owner; + LineEdit *org_unit; + LineEdit *org; + LineEdit *local; + LineEdit *state; + LineEdit *country_code; + + VBoxContainer *missing_file; + + Button *reset; + Button *save; + Button *cancel; + Button *create; + + void _notification(int p_what); + + int _run_keytool(const List &p_arguments, String *r_output, bool p_expects_return = true); + Dictionary _extract_key_info(const String &p_output); + + void _add_keys_pressed(); + void _create_key_existing_pressed(); + void _create_key_new_pressed(); + + void _open_keystore(const String &p_path); + + void _new_key(const String &p_path); + + void _add_keys_list_edited(); + void _add_keys_dialog_confirmed(); + + void _key_list_cell_selected(); + void _key_list_button_pressed(Object *p_item, int p_column, int p_idx); + + void _unselect(); + + void _from_keystore_pressed(); + void _refresh_pressed(); + void _default_for_selected(int p_index); + void _keystore_text_changed(const String &p_text, const String &p_name); + + void _find_file_pressed(); + + void _reset_pressed(); + void _save_pressed(); + void _cancel_pressed(); + void _create_pressed(); + + void _unlock_keystore_pressed(); + void _pass_box_entered(const String &p_text); + +protected: + static void _bind_methods(); + +public: + EditorKeyManager(); +}; + +#endif // EDITOR_KEY_MANAGER_H diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index da5856f527c2..127b419f5061 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -69,7 +69,7 @@ #include "editor/audio_stream_preview.h" #include "editor/dependency_editor.h" -#include "editor/editor_about.h" +#include "editor/editor_about_ramatak.h" #include "editor/editor_audio_buses.h" #include "editor/editor_export.h" #include "editor/editor_feature_profile.h" @@ -486,6 +486,9 @@ void EditorNode::_notification(int p_what) { get_tree()->connect("files_dropped", this, "_dropped_files"); get_tree()->connect("global_menu_action", this, "_global_menu_action"); + tabbar_panel->add_style_override("panel", gui_base->get_stylebox("tabbar_background", "TabContainer")); + launch_pad->add_style_override("panel", gui_base->get_stylebox("LaunchPad", "EditorStyles")); + /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ } break; @@ -577,6 +580,9 @@ void EditorNode::_notification(int p_what) { settings_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); help_menu->add_style_override("hover", gui_base->get_stylebox("MenuHover", "EditorStyles")); + tabbar_panel->add_style_override("panel", gui_base->get_stylebox("tabbar_background", "TabContainer")); + launch_pad->add_style_override("panel", gui_base->get_stylebox("LaunchPad", "EditorStyles")); + if (EDITOR_GET("interface/scene_tabs/resize_if_many_tabs")) { scene_tabs->set_min_width(int(EDITOR_GET("interface/scene_tabs/minimum_width")) * EDSCALE); } else { @@ -625,6 +631,7 @@ void EditorNode::_notification(int p_what) { PopupMenu *p = help_menu->get_popup(); p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_icon("HelpSearch", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_icon("ExternalLink", "EditorIcons")); +#if 0 p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_icon("ExternalLink", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_icon("ExternalLink", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_icon("ExternalLink", "EditorIcons")); @@ -632,6 +639,8 @@ void EditorNode::_notification(int p_what) { p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_icon("ExternalLink", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Godot", "EditorIcons")); p->set_item_icon(p->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), gui_base->get_icon("Heart", "EditorIcons")); +#endif + p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_icon("Issue", "EditorIcons")); _update_update_spinner(); } break; @@ -2926,7 +2935,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { OS::get_singleton()->shell_open("https://godotengine.org/community"); } break; case HELP_ABOUT: { - about->popup_centered_minsize(Size2(780, 500) * EDSCALE); + about->popup_centered(); } break; case HELP_SUPPORT_GODOT_DEVELOPMENT: { OS::get_singleton()->shell_open("https://godotengine.org/donate"); @@ -6261,7 +6270,10 @@ EditorNode::EditorNode() { scene_tabs->connect("reposition_active_tab_request", this, "_reposition_active_tab"); scene_tabs->connect("resized", this, "_update_scene_tabs"); + tabbar_panel = memnew(PanelContainer); + srt->add_child(tabbar_panel); tabbar_container = memnew(HBoxContainer); + tabbar_panel->add_child(tabbar_container); scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL); scene_tabs_context_menu = memnew(PopupMenu); @@ -6269,7 +6281,6 @@ EditorNode::EditorNode() { scene_tabs_context_menu->connect("id_pressed", this, "_menu_option"); scene_tabs_context_menu->set_hide_on_window_lose_focus(true); - srt->add_child(tabbar_container); tabbar_container->add_child(scene_tabs); distraction_free = memnew(ToolButton); #ifdef OSX_ENABLED @@ -6337,9 +6348,6 @@ EditorNode::EditorNode() { gui_base->add_child(save_accept); save_accept->connect("confirmed", this, "_menu_option", make_binds((int)MenuOptions::FILE_SAVE_AS_SCENE)); - project_export = memnew(ProjectExportDialog); - gui_base->add_child(project_export); - dependency_error = memnew(DependencyErrorDialog); gui_base->add_child(dependency_error); @@ -6349,6 +6357,10 @@ EditorNode::EditorNode() { settings_config_dialog = memnew(EditorSettingsDialog); gui_base->add_child(settings_config_dialog); + project_export = memnew(ProjectExportDialog); + gui_base->add_child(project_export); + project_export->connect("key_manager_requested", settings_config_dialog, "popup_edit_settings", varray(2)); + project_settings = memnew(ProjectSettingsEditor(&editor_data)); gui_base->add_child(project_settings); ProjectSettings::get_singleton()->connect("project_settings_changed", this, "_project_settings_changed"); @@ -6361,7 +6373,7 @@ EditorNode::EditorNode() { feature_profile_manager = memnew(EditorFeatureProfileManager); gui_base->add_child(feature_profile_manager); - about = memnew(EditorAbout); + about = memnew(EditorAboutRamatak); gui_base->add_child(about); feature_profile_manager->connect("current_feature_profile_changed", this, "_feature_profile_changed"); @@ -6610,9 +6622,13 @@ EditorNode::EditorNode() { p->add_icon_shortcut(gui_base->get_icon("Godot", "EditorIcons"), ED_SHORTCUT("editor/about", TTR("About Godot")), HELP_ABOUT); p->add_icon_shortcut(gui_base->get_icon("Heart", "EditorIcons"), ED_SHORTCUT("editor/support_development", TTR("Support Godot Development")), HELP_SUPPORT_GODOT_DEVELOPMENT); #endif + p->add_icon_shortcut(gui_base->get_icon("Issue", "EditorIcons"), ED_SHORTCUT("editor/about", TTR("About Ramatak Mobile Studio")), HELP_ABOUT); + + launch_pad = memnew(PanelContainer); + menu_hb->add_child(launch_pad); HBoxContainer *play_hb = memnew(HBoxContainer); - menu_hb->add_child(play_hb); + launch_pad->add_child(play_hb); play_button = memnew(ToolButton); play_hb->add_child(play_button); diff --git a/editor/editor_node.h b/editor/editor_node.h index a1f67ba12d0d..f458c6b4233c 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -52,7 +52,7 @@ class ConfirmationDialog; class Control; class DependencyEditor; class DependencyErrorDialog; -class EditorAbout; +class EditorAboutRamatak; class EditorExport; class EditorFeatureProfileManager; class EditorFileServer; @@ -283,6 +283,7 @@ class EditorNode : public Node { MenuButton *settings_menu; MenuButton *help_menu; PopupMenu *tool_menu; + PanelContainer *launch_pad; ToolButton *export_button; ToolButton *prev_scene; ToolButton *play_button; @@ -321,7 +322,7 @@ class EditorNode : public Node { Button *select_current_scene_button; AcceptDialog *accept; AcceptDialog *save_accept; - EditorAbout *about; + EditorAboutRamatak *about; AcceptDialog *warning; int overridden_default_layout; @@ -383,6 +384,7 @@ class EditorNode : public Node { Timer *dock_drag_timer; bool docks_visible; + PanelContainer *tabbar_panel; HBoxContainer *tabbar_container; ToolButton *distraction_free; ToolButton *scene_tab_add; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index b14a4e1c5342..d9f1bd0e3f90 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -344,19 +344,19 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { // Theme _initial_set("interface/theme/preset", "Default"); - hints["interface/theme/preset"] = PropertyInfo(Variant::STRING, "interface/theme/preset", PROPERTY_HINT_ENUM, "Default,Alien,Arc,Godot 2,Grey,Light,Solarized (Dark),Solarized (Light),Custom", PROPERTY_USAGE_DEFAULT); + hints["interface/theme/preset"] = PropertyInfo(Variant::STRING, "interface/theme/preset", PROPERTY_HINT_ENUM, "Default,Alien,Arc,Godot 2,Godot 3,Grey,Light,Solarized (Dark),Solarized (Light),Custom", PROPERTY_USAGE_DEFAULT); _initial_set("interface/theme/icon_and_font_color", 0); hints["interface/theme/icon_and_font_color"] = PropertyInfo(Variant::INT, "interface/theme/icon_and_font_color", PROPERTY_HINT_ENUM, "Auto,Dark,Light", PROPERTY_USAGE_DEFAULT); _initial_set("interface/theme/base_color", Color(0.2, 0.23, 0.31)); hints["interface/theme/base_color"] = PropertyInfo(Variant::COLOR, "interface/theme/base_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); _initial_set("interface/theme/accent_color", Color(0.41, 0.61, 0.91)); hints["interface/theme/accent_color"] = PropertyInfo(Variant::COLOR, "interface/theme/accent_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT); - _initial_set("interface/theme/contrast", 0.25); + _initial_set("interface/theme/contrast", 0.3); hints["interface/theme/contrast"] = PropertyInfo(Variant::REAL, "interface/theme/contrast", PROPERTY_HINT_RANGE, "-1, 1, 0.01"); _initial_set("interface/theme/relationship_line_opacity", 0.1); hints["interface/theme/relationship_line_opacity"] = PropertyInfo(Variant::REAL, "interface/theme/relationship_line_opacity", PROPERTY_HINT_RANGE, "0.00, 1, 0.01"); _initial_set("interface/theme/highlight_tabs", false); - _initial_set("interface/theme/border_size", 1); + _initial_set("interface/theme/border_size", 0); _initial_set("interface/theme/use_graph_node_headers", false); hints["interface/theme/border_size"] = PropertyInfo(Variant::INT, "interface/theme/border_size", PROPERTY_HINT_RANGE, "0,2,1", PROPERTY_USAGE_DEFAULT); _initial_set("interface/theme/additional_spacing", 0); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 14f161c1e7c8..ff6f33e4f722 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -65,13 +65,15 @@ static Ref make_empty_stylebox(float p_margin_left = -1, float p_ return style; } -static Ref make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) { +static Ref make_flat_stylebox(Color p_color, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1, int p_corner_width = 0) { Ref style(memnew(StyleBoxFlat)); style->set_bg_color(p_color); style->set_default_margin(MARGIN_LEFT, p_margin_left * EDSCALE); style->set_default_margin(MARGIN_RIGHT, p_margin_right * EDSCALE); style->set_default_margin(MARGIN_BOTTOM, p_margin_bottom * EDSCALE); style->set_default_margin(MARGIN_TOP, p_margin_top * EDSCALE); + style->set_corner_detail(Math::ceil(0.8 * p_corner_width * EDSCALE)); + style->set_corner_radius_all(p_corner_width * EDSCALE); return style; } @@ -219,8 +221,6 @@ void editor_register_and_generate_icons(Ref p_theme, bool p_dark_theme = exceptions.insert("ProceduralSky"); exceptions.insert("EditorControlAnchor"); exceptions.insert("DefaultProjectIcon"); - exceptions.insert("GuiChecked"); - exceptions.insert("GuiRadioChecked"); exceptions.insert("GuiCloseCustomizable"); exceptions.insert("GuiGraphNodePort"); exceptions.insert("GuiResizer"); @@ -233,12 +233,16 @@ void editor_register_and_generate_icons(Ref p_theme, bool p_dark_theme = exceptions.insert("StatusSuccess"); exceptions.insert("StatusWarning"); exceptions.insert("OverbrightIndicator"); + exceptions.insert("FaceAbout"); + exceptions.insert("LogoGodot"); } // These ones should be converted even if we are using a dark theme. + const Color accent_color = EDITOR_GET("interface/theme/accent_color"); const Color error_color = p_theme->get_color("error_color", "Editor"); const Color success_color = p_theme->get_color("success_color", "Editor"); const Color warning_color = p_theme->get_color("warning_color", "Editor"); + dark_icon_color_dictionary[Color::html("#699ce8")] = accent_color; dark_icon_color_dictionary[Color::html("#ff0000")] = error_color; dark_icon_color_dictionary[Color::html("#45ff8b")] = success_color; dark_icon_color_dictionary[Color::html("#dbab09")] = warning_color; @@ -309,8 +313,8 @@ Ref create_editor_theme(const Ref p_theme) { // Please, use alphabet order if you've added new theme here(After "Default" and "Custom") if (preset == "Default") { - preset_accent_color = Color(0.41, 0.61, 0.91); - preset_base_color = Color(0.2, 0.23, 0.31); + preset_accent_color = Color(0.07, 0.82, 0.49); + preset_base_color = Color(0.14, 0.13, 0.21); preset_contrast = default_contrast; } else if (preset == "Custom") { accent_color = EDITOR_GET("interface/theme/accent_color"); @@ -328,6 +332,10 @@ Ref create_editor_theme(const Ref p_theme) { preset_accent_color = Color(0.53, 0.67, 0.89); preset_base_color = Color(0.24, 0.23, 0.27); preset_contrast = 0.25; + } else if (preset == "Godot 3") { + preset_accent_color = Color(0.41, 0.61, 0.91); + preset_base_color = Color(0.2, 0.23, 0.31); + preset_contrast = default_contrast; } else if (preset == "Grey") { preset_accent_color = Color(0.44, 0.73, 0.98); preset_base_color = Color(0.24, 0.24, 0.24); @@ -345,8 +353,8 @@ Ref create_editor_theme(const Ref p_theme) { preset_base_color = Color(0.99, 0.96, 0.89); preset_contrast = 0.06; } else { // Default - preset_accent_color = Color(0.41, 0.61, 0.91); - preset_base_color = Color(0.2, 0.23, 0.31); + preset_accent_color = Color(0.07, 0.82, 0.49); + preset_base_color = Color(0.14, 0.13, 0.21); preset_contrast = default_contrast; } @@ -379,17 +387,20 @@ Ref create_editor_theme(const Ref p_theme) { const Color contrast_color_2 = base_color.linear_interpolate(mono_color, MAX(contrast * 1.5, default_contrast * 1.5)); const Color font_color = mono_color.linear_interpolate(base_color, 0.25); - const Color font_color_hl = mono_color.linear_interpolate(base_color, 0.15); - const Color font_color_focus = mono_color.linear_interpolate(base_color, 0.15); - const Color font_color_disabled = Color(mono_color.r, mono_color.g, mono_color.b, 0.3); + const Color font_color_hl = mono_color.linear_interpolate(base_color, 0.125); + const Color font_color_focus = mono_color.linear_interpolate(base_color, 0.125); + const Color font_color_disabled = Color(mono_color.r, mono_color.g, mono_color.b, 0.35); const Color font_color_readonly = Color(mono_color.r, mono_color.g, mono_color.b, 0.65); - const Color font_color_selection = accent_color * Color(1, 1, 1, 0.4); + const Color selection_color = accent_color * Color(1, 1, 1, 0.4); const Color color_disabled = mono_color.inverted().linear_interpolate(base_color, 0.7); const Color color_disabled_bg = mono_color.inverted().linear_interpolate(base_color, 0.9); const Color icon_color_normal = Color(1, 1, 1); Color icon_color_hover = icon_color_normal * (dark_theme ? 1.15 : 1.45); icon_color_hover.a = 1.0; + Color icon_color_focus = icon_color_hover; + Color icon_color_disabled = icon_color_normal; + icon_color_disabled.a = 0.4; // Make the pressed icon color overbright because icons are not completely white on a dark theme. // On a light theme, icons are dark, so we need to modulate them with an even brighter color. Color icon_color_pressed = accent_color * (dark_theme ? 1.15 : 3.5); @@ -397,7 +408,7 @@ Ref create_editor_theme(const Ref p_theme) { const Color separator_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.1); - const Color highlight_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.2); + const Color highlight_color = Color(accent_color.r, accent_color.g, accent_color.b, 0.275); theme->set_color("accent_color", "Editor", accent_color); theme->set_color("highlight_color", "Editor", highlight_color); @@ -463,26 +474,27 @@ Ref create_editor_theme(const Ref p_theme) { // Highlighted tabs and border width Color tab_color = highlight_tabs ? base_color.linear_interpolate(font_color, contrast) : base_color; // Ensure borders are visible when using an editor scale below 100%. - const int border_width = CLAMP(border_size, 0, 3) * MAX(1, EDSCALE); - + const int border_width = CLAMP(border_size, 0, 2) * MAX(1, EDSCALE); + const int corner_width = 3; const int default_margin_size = 4; - const int margin_size_extra = default_margin_size + CLAMP(border_size, 0, 3); + const int margin_size_extra = default_margin_size + CLAMP(border_size, 0, 2); - // styleboxes - // this is the most commonly used stylebox, variations should be made as duplicate of this - Ref style_default = make_flat_stylebox(base_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size); + // Styleboxes + // This is the most commonly used stylebox, variations should be made as duplicate of this + Ref style_default = make_flat_stylebox(base_color, default_margin_size, default_margin_size, default_margin_size, default_margin_size, corner_width); style_default->set_border_width_all(border_width); style_default->set_border_color(base_color); - style_default->set_draw_center(true); // Button and widgets const float extra_spacing = EDITOR_GET("interface/theme/additional_spacing"); + const Vector2 widget_default_margin = Vector2(extra_spacing + 6, extra_spacing + default_margin_size + 1) * EDSCALE; + Ref style_widget = style_default->duplicate(); - style_widget->set_default_margin(MARGIN_LEFT, (extra_spacing + 6) * EDSCALE); - style_widget->set_default_margin(MARGIN_TOP, (extra_spacing + default_margin_size) * EDSCALE); - style_widget->set_default_margin(MARGIN_RIGHT, (extra_spacing + 6) * EDSCALE); - style_widget->set_default_margin(MARGIN_BOTTOM, (extra_spacing + default_margin_size) * EDSCALE); + style_widget->set_default_margin(MARGIN_LEFT, widget_default_margin.x); + style_widget->set_default_margin(MARGIN_TOP, widget_default_margin.y); + style_widget->set_default_margin(MARGIN_RIGHT, widget_default_margin.x); + style_widget->set_default_margin(MARGIN_BOTTOM, widget_default_margin.y); style_widget->set_bg_color(dark_color_1); style_widget->set_border_color(dark_color_2); @@ -491,69 +503,88 @@ Ref create_editor_theme(const Ref p_theme) { style_widget_disabled->set_bg_color(color_disabled_bg); Ref style_widget_focus = style_widget->duplicate(); + style_widget_focus->set_draw_center(false); + style_widget_focus->set_border_width_all(Math::round(2 * MAX(1, EDSCALE))); style_widget_focus->set_border_color(accent_color); Ref style_widget_pressed = style_widget->duplicate(); style_widget_pressed->set_border_color(accent_color); Ref style_widget_hover = style_widget->duplicate(); - style_widget_hover->set_border_color(contrast_color_1); + style_widget_hover->set_bg_color(mono_color * Color(1, 1, 1, 0.11)); + style_widget_hover->set_border_color(mono_color * Color(1, 1, 1, 0.05)); // style for windows, popups, etc.. Ref style_popup = style_default->duplicate(); - const int popup_margin_size = default_margin_size * EDSCALE * 2; + const int popup_margin_size = default_margin_size * EDSCALE * 3; style_popup->set_default_margin(MARGIN_LEFT, popup_margin_size); style_popup->set_default_margin(MARGIN_TOP, popup_margin_size); style_popup->set_default_margin(MARGIN_RIGHT, popup_margin_size); style_popup->set_default_margin(MARGIN_BOTTOM, popup_margin_size); - style_popup->set_border_color(contrast_color_1); - style_popup->set_border_width_all(MAX(EDSCALE, border_width)); const Color shadow_color = Color(0, 0, 0, dark_theme ? 0.3 : 0.1); style_popup->set_shadow_color(shadow_color); style_popup->set_shadow_size(4 * EDSCALE); Ref style_popup_separator(memnew(StyleBoxLine)); style_popup_separator->set_color(separator_color); - style_popup_separator->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width)); - style_popup_separator->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width)); - style_popup_separator->set_thickness(MAX(EDSCALE, border_width)); + style_popup_separator->set_grow_begin(popup_margin_size - MAX(Math::round(EDSCALE), border_width) - 6 * EDSCALE); + style_popup_separator->set_grow_end(popup_margin_size - MAX(Math::round(EDSCALE), border_width) - 6 * EDSCALE); + style_popup_separator->set_thickness(MAX(Math::round(EDSCALE), border_width)); Ref style_popup_labeled_separator_left(memnew(StyleBoxLine)); - style_popup_labeled_separator_left->set_grow_begin(popup_margin_size - MAX(EDSCALE, border_width)); + style_popup_labeled_separator_left->set_grow_begin(popup_margin_size - MAX(Math::round(EDSCALE), border_width) - 6 * EDSCALE); style_popup_labeled_separator_left->set_color(separator_color); - style_popup_labeled_separator_left->set_thickness(MAX(EDSCALE, border_width)); + style_popup_labeled_separator_left->set_thickness(MAX(Math::round(EDSCALE), border_width)); Ref style_popup_labeled_separator_right(memnew(StyleBoxLine)); - style_popup_labeled_separator_right->set_grow_end(popup_margin_size - MAX(EDSCALE, border_width)); + style_popup_labeled_separator_right->set_grow_end(popup_margin_size - MAX(Math::round(EDSCALE), border_width) - 6 * EDSCALE); style_popup_labeled_separator_right->set_color(separator_color); - style_popup_labeled_separator_right->set_thickness(MAX(EDSCALE, border_width)); + style_popup_labeled_separator_right->set_thickness(MAX(Math::round(EDSCALE), border_width)); Ref style_empty = make_empty_stylebox(default_margin_size, default_margin_size, default_margin_size, default_margin_size); // Tabs - const int tab_default_margin_side = 10 * EDSCALE + extra_spacing * EDSCALE; - const int tab_default_margin_vertical = 5 * EDSCALE + extra_spacing * EDSCALE; + Ref style_tabbar_background = make_flat_stylebox(dark_color_1, 0, 0, 0, 0, corner_width * EDSCALE); + style_tabbar_background->set_corner_radius(CORNER_BOTTOM_LEFT, 0); + style_tabbar_background->set_corner_radius(CORNER_BOTTOM_RIGHT, 0); + + Ref style_tab_base = style_widget->duplicate(); + + style_tab_base->set_border_width_all(0); + // Don't round the top corners to avoid creating a small blank space between the tabs and the main panel. + // This also makes the top highlight look better. + style_tab_base->set_corner_radius(CORNER_BOTTOM_LEFT, 0); + style_tab_base->set_corner_radius(CORNER_BOTTOM_RIGHT, 0); - Ref style_tab_selected = style_widget->duplicate(); + // When using a border width greater than 0, visually line up the left of the selected tab with the underlying panel. + style_tab_base->set_expand_margin_size(MARGIN_LEFT, -border_width); - style_tab_selected->set_border_width_all(border_width); - style_tab_selected->set_border_width(MARGIN_BOTTOM, 0); - style_tab_selected->set_border_color(dark_color_3); - style_tab_selected->set_expand_margin_size(MARGIN_BOTTOM, border_width); - style_tab_selected->set_default_margin(MARGIN_LEFT, tab_default_margin_side); - style_tab_selected->set_default_margin(MARGIN_RIGHT, tab_default_margin_side); - style_tab_selected->set_default_margin(MARGIN_BOTTOM, tab_default_margin_vertical); - style_tab_selected->set_default_margin(MARGIN_TOP, tab_default_margin_vertical); - style_tab_selected->set_bg_color(tab_color); + style_tab_base->set_default_margin(MARGIN_LEFT, widget_default_margin.x + 5 * EDSCALE); + style_tab_base->set_default_margin(MARGIN_RIGHT, widget_default_margin.x + 5 * EDSCALE); + style_tab_base->set_default_margin(MARGIN_BOTTOM, widget_default_margin.y); + style_tab_base->set_default_margin(MARGIN_TOP, widget_default_margin.y); - Ref style_tab_unselected = style_tab_selected->duplicate(); + Ref style_tab_selected = style_tab_base->duplicate(); + + style_tab_selected->set_bg_color(highlight_tabs ? tab_color : base_color); + // Add a highlight line at the top of the selected tab. + style_tab_selected->set_border_width(MARGIN_TOP, Math::round(2 * EDSCALE)); + // Make the highlight line prominent, but not too prominent as to not be distracting. + Color tab_highlight = dark_color_2.linear_interpolate(accent_color, 0.75); + style_tab_selected->set_border_color(tab_highlight); + style_tab_selected->set_corner_radius_all(0); + + Ref style_tab_unselected = style_tab_base->duplicate(); + style_tab_unselected->set_expand_margin_size(MARGIN_BOTTOM, 0); style_tab_unselected->set_bg_color(dark_color_1); - style_tab_unselected->set_border_color(dark_color_2); + // Add some spacing between unselected tabs to make them easier to distinguish from each other + style_tab_unselected->set_border_color(Color(0, 0, 0, 0)); - Ref style_tab_disabled = style_tab_selected->duplicate(); + Ref style_tab_disabled = style_tab_base->duplicate(); + style_tab_disabled->set_expand_margin_size(MARGIN_BOTTOM, 0); style_tab_disabled->set_bg_color(color_disabled_bg); - style_tab_disabled->set_border_color(color_disabled); + style_tab_disabled->set_border_color(color_disabled_bg); // Editor background Color background_color_opaque = background_color; @@ -561,10 +592,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_stylebox("Background", "EditorStyles", make_flat_stylebox(background_color_opaque, default_margin_size, default_margin_size, default_margin_size, default_margin_size)); // Focus - Ref style_focus = style_default->duplicate(); - style_focus->set_draw_center(false); - style_focus->set_border_color(contrast_color_2); - theme->set_stylebox("Focus", "EditorStyles", style_focus); + theme->set_stylebox("Focus", "EditorStyles", style_widget_focus); // Menu Ref style_menu = style_widget->duplicate(); @@ -582,32 +610,18 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_stylebox("ScriptEditorPanel", "EditorStyles", make_empty_stylebox(default_margin_size, 0, default_margin_size, default_margin_size)); theme->set_stylebox("ScriptEditor", "EditorStyles", make_empty_stylebox(0, 0, 0, 0)); - // Play button group - theme->set_stylebox("PlayButtonPanel", "EditorStyles", style_empty); + // Launch Pad + Ref style_launch_pad = make_flat_stylebox(dark_color_1, 2 * EDSCALE, 0, 2 * EDSCALE, 0, corner_width); + style_launch_pad->set_corner_radius_all(corner_width * EDSCALE); + theme->set_stylebox("LaunchPad", "EditorStyles", style_launch_pad); //MenuButton - Ref style_menu_hover_border = style_widget->duplicate(); - style_menu_hover_border->set_draw_center(false); - style_menu_hover_border->set_border_width_all(0); - style_menu_hover_border->set_border_width(MARGIN_BOTTOM, border_width); - style_menu_hover_border->set_border_color(accent_color); - - Ref style_menu_hover_bg = style_widget->duplicate(); - style_menu_hover_bg->set_border_width_all(0); - style_menu_hover_bg->set_bg_color(dark_color_1); - theme->set_stylebox("normal", "MenuButton", style_menu); - theme->set_stylebox("hover", "MenuButton", style_menu); + theme->set_stylebox("hover", "MenuButton", style_widget_hover); theme->set_stylebox("pressed", "MenuButton", style_menu); theme->set_stylebox("focus", "MenuButton", style_menu); theme->set_stylebox("disabled", "MenuButton", style_menu); - theme->set_stylebox("normal", "PopupMenu", style_menu); - theme->set_stylebox("hover", "PopupMenu", style_menu_hover_bg); - theme->set_stylebox("pressed", "PopupMenu", style_menu); - theme->set_stylebox("focus", "PopupMenu", style_menu); - theme->set_stylebox("disabled", "PopupMenu", style_menu); - theme->set_stylebox("normal", "ToolButton", style_menu); theme->set_stylebox("hover", "ToolButton", style_menu); theme->set_stylebox("pressed", "ToolButton", style_menu); @@ -622,7 +636,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_color("font_color_focus", "ToolButton", font_color_focus); theme->set_color("font_color_pressed", "ToolButton", accent_color); - theme->set_stylebox("MenuHover", "EditorStyles", style_menu_hover_border); + theme->set_stylebox("MenuHover", "EditorStyles", style_widget_hover); // Buttons theme->set_stylebox("normal", "Button", style_widget); @@ -639,13 +653,27 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_color("icon_color_normal", "Button", icon_color_normal); theme->set_color("icon_color_hover", "Button", icon_color_hover); theme->set_color("icon_color_pressed", "Button", icon_color_pressed); + theme->set_color("icon_color_focus", "Button", icon_color_pressed); + theme->set_color("icon_color_disabled", "Button", icon_color_disabled); // OptionButton - theme->set_stylebox("normal", "OptionButton", style_widget); - theme->set_stylebox("hover", "OptionButton", style_widget_hover); - theme->set_stylebox("pressed", "OptionButton", style_widget_pressed); - theme->set_stylebox("focus", "OptionButton", style_widget_focus); - theme->set_stylebox("disabled", "OptionButton", style_widget_disabled); + Ref style_option_button_focus = style_widget_focus->duplicate(); + Ref style_option_button_normal = style_widget->duplicate(); + Ref style_option_button_hover = style_widget_hover->duplicate(); + Ref style_option_button_pressed = style_widget_pressed->duplicate(); + Ref style_option_button_disabled = style_widget_disabled->duplicate(); + + style_option_button_focus->set_default_margin(MARGIN_RIGHT, 4 * EDSCALE); + style_option_button_normal->set_default_margin(MARGIN_RIGHT, 4 * EDSCALE); + style_option_button_hover->set_default_margin(MARGIN_RIGHT, 4 * EDSCALE); + style_option_button_pressed->set_default_margin(MARGIN_RIGHT, 4 * EDSCALE); + style_option_button_disabled->set_default_margin(MARGIN_RIGHT, 4 * EDSCALE); + + theme->set_stylebox("normal", "OptionButton", style_option_button_normal); + theme->set_stylebox("hover", "OptionButton", style_option_button_hover); + theme->set_stylebox("pressed", "OptionButton", style_option_button_pressed); + theme->set_stylebox("focus", "OptionButton", style_option_button_focus); + theme->set_stylebox("disabled", "OptionButton", style_option_button_disabled); theme->set_color("font_color", "OptionButton", font_color); theme->set_color("font_color_hover", "OptionButton", font_color_hl); @@ -663,6 +691,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_stylebox("pressed", "CheckButton", style_menu); theme->set_stylebox("disabled", "CheckButton", style_menu); theme->set_stylebox("hover", "CheckButton", style_menu); + theme->set_stylebox("hover_pressed", "CheckButton", style_menu); theme->set_icon("on", "CheckButton", theme->get_icon("GuiToggleOn", "EditorIcons")); theme->set_icon("on_disabled", "CheckButton", theme->get_icon("GuiToggleOnDisabled", "EditorIcons")); @@ -676,8 +705,8 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_color("font_color_disabled", "CheckButton", font_color_disabled); theme->set_color("icon_color_hover", "CheckButton", icon_color_hover); - theme->set_constant("hseparation", "CheckButton", 4 * EDSCALE); - theme->set_constant("check_vadjust", "CheckButton", 0 * EDSCALE); + theme->set_constant("hseparation", "CheckButton", 8 * EDSCALE); + theme->set_constant("check_vadjust", "CheckButton", 0); // Checkbox Ref sb_checkbox = style_menu->duplicate(); @@ -706,14 +735,35 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_color("font_color_disabled", "CheckBox", font_color_disabled); theme->set_color("icon_color_hover", "CheckBox", icon_color_hover); + theme->set_color("icon_color_normal", "CheckBox", icon_color_normal); + theme->set_color("icon_color_hover", "CheckBox", icon_color_hover); + theme->set_color("icon_color_focus", "CheckBox", icon_color_focus); + theme->set_color("icon_color_pressed", "CheckBox", icon_color_pressed); + theme->set_color("icon_color_disabled", "CheckBox", icon_color_disabled); + theme->set_constant("hseparation", "CheckBox", 4 * EDSCALE); - theme->set_constant("check_vadjust", "CheckBox", 0 * EDSCALE); + theme->set_constant("check_vadjust", "CheckBox", 0); // PopupDialog theme->set_stylebox("panel", "PopupDialog", style_popup); // PopupMenu - theme->set_stylebox("panel", "PopupMenu", style_popup); + Ref style_popup_menu = style_popup->duplicate(); + style_popup_menu->set_default_margin(MARGIN_LEFT, 6 * EDSCALE); + style_popup_menu->set_default_margin(MARGIN_TOP, 4 * EDSCALE); + style_popup_menu->set_default_margin(MARGIN_RIGHT, 6 * EDSCALE); + style_popup_menu->set_default_margin(MARGIN_BOTTOM, 4 * EDSCALE); + // Always display a border for PopupMenus so they can be distinguished from their background. + style_popup_menu->set_border_width_all(EDSCALE); + theme->set_stylebox("panel", "PopupMenu", style_popup_menu); + + Ref style_menu_hover = style_widget_hover->duplicate(); + // Don't use rounded corners for hover highlights since the StyleBox touches the PopupMenu's edges. + style_menu_hover->set_corner_radius_all(0); + style_menu_hover->set_expand_margin_size(MARGIN_LEFT, 5 * EDSCALE); + style_menu_hover->set_expand_margin_size(MARGIN_RIGHT, 5 * EDSCALE); + theme->set_stylebox("hover", "PopupMenu", style_menu_hover); + theme->set_stylebox("separator", "PopupMenu", style_popup_separator); theme->set_stylebox("labeled_separator_left", "PopupMenu", style_popup_labeled_separator_left); theme->set_stylebox("labeled_separator_right", "PopupMenu", style_popup_labeled_separator_right); @@ -731,7 +781,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_icon("visibility_hidden", "PopupMenu", theme->get_icon("GuiVisibilityHidden", "EditorIcons")); theme->set_icon("visibility_visible", "PopupMenu", theme->get_icon("GuiVisibilityVisible", "EditorIcons")); theme->set_icon("visibility_xray", "PopupMenu", theme->get_icon("GuiVisibilityXray", "EditorIcons")); - theme->set_constant("vseparation", "PopupMenu", (extra_spacing + default_margin_size + 1) * EDSCALE); + theme->set_constant("vseparation", "PopupMenu", (extra_spacing + default_margin_size) * EDSCALE); for (int i = 0; i < 16; i++) { Color si_base_color = accent_color; @@ -742,22 +792,24 @@ Ref create_editor_theme(const Ref p_theme) { Ref sub_inspector_bg; - sub_inspector_bg = make_flat_stylebox(dark_color_1.linear_interpolate(si_base_color, 0.08), 2, 0, 2, 2); - - sub_inspector_bg->set_border_width_all(2); + sub_inspector_bg = style_default->duplicate(); + sub_inspector_bg->set_bg_color(dark_color_1.linear_interpolate(si_base_color, 0.08)); + sub_inspector_bg->set_border_width_all(2 * EDSCALE); + sub_inspector_bg->set_border_color(si_base_color * Color(0.7, 0.7, 0.7, 0.8)); sub_inspector_bg->set_default_margin(MARGIN_LEFT, 4 * EDSCALE); sub_inspector_bg->set_default_margin(MARGIN_RIGHT, 4 * EDSCALE); sub_inspector_bg->set_default_margin(MARGIN_BOTTOM, 4 * EDSCALE); sub_inspector_bg->set_default_margin(MARGIN_TOP, 4 * EDSCALE); - sub_inspector_bg->set_border_color(si_base_color * Color(0.7, 0.7, 0.7, 0.8)); - sub_inspector_bg->set_draw_center(true); + sub_inspector_bg->set_corner_radius(CORNER_TOP_LEFT, 0); + sub_inspector_bg->set_corner_radius(CORNER_TOP_RIGHT, 0); theme->set_stylebox("sub_inspector_bg" + itos(i), "Editor", sub_inspector_bg); - Ref bg_color; - bg_color.instance(); - bg_color->set_bg_color(si_base_color * Color(0.7, 0.7, 0.7, 0.8)); - bg_color->set_border_width_all(0); + // EditorProperty background while it has a sub-inspector open. + Ref bg_color = make_flat_stylebox(si_base_color * Color(0.7, 0.7, 0.7, 0.8), 0, 0, 0, 0, corner_width); + bg_color->set_anti_aliased(false); + bg_color->set_corner_radius(CORNER_BOTTOM_LEFT, 0); + bg_color->set_corner_radius(CORNER_BOTTOM_RIGHT, 0); Ref bg_color_selected; bg_color_selected.instance(); @@ -790,6 +842,15 @@ Ref create_editor_theme(const Ref p_theme) { style_tree_bg->set_border_color(dark_color_3); theme->set_stylebox("bg", "Tree", style_tree_bg); + Ref style_tree_focus = style_default->duplicate(); + style_tree_focus->set_bg_color(highlight_color); + style_tree_focus->set_border_width_all(0); + + Ref style_tree_selected = style_tree_focus->duplicate(); + style_tree_selected->set_expand_margin_size(MARGIN_LEFT, 16); + theme->set_stylebox("selected", "Tree", style_tree_selected); + theme->set_stylebox("selected_focus", "Tree", style_tree_selected); + const Color guide_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.05); Color relationship_line_color = Color(mono_color.r, mono_color.g, mono_color.b, relationship_line_opacity); // Tree @@ -799,7 +860,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_icon("arrow_collapsed", "Tree", theme->get_icon("GuiTreeArrowRight", "EditorIcons")); theme->set_icon("updown", "Tree", theme->get_icon("GuiTreeUpdown", "EditorIcons")); theme->set_icon("select_arrow", "Tree", theme->get_icon("GuiDropdown", "EditorIcons")); - theme->set_stylebox("bg_focus", "Tree", style_focus); + theme->set_stylebox("bg_focus", "Tree", style_widget_focus); theme->set_stylebox("custom_button", "Tree", make_empty_stylebox()); theme->set_stylebox("custom_button_pressed", "Tree", make_empty_stylebox()); theme->set_stylebox("custom_button_hover", "Tree", style_widget); @@ -829,14 +890,6 @@ Ref create_editor_theme(const Ref p_theme) { style_tree_hover->set_border_width_all(0); theme->set_stylebox("hover", "Tree", style_tree_hover); - Ref style_tree_focus = style_default->duplicate(); - style_tree_focus->set_bg_color(highlight_color); - style_tree_focus->set_border_width_all(0); - theme->set_stylebox("selected_focus", "Tree", style_tree_focus); - - Ref style_tree_selected = style_tree_focus->duplicate(); - theme->set_stylebox("selected", "Tree", style_tree_selected); - Ref style_tree_cursor = style_default->duplicate(); style_tree_cursor->set_draw_center(false); style_tree_cursor->set_border_width_all(MAX(1, border_width)); @@ -869,21 +922,23 @@ Ref create_editor_theme(const Ref p_theme) { style_itemlist_cursor->set_draw_center(false); style_itemlist_cursor->set_border_width_all(border_width); style_itemlist_cursor->set_border_color(highlight_color); + theme->set_stylebox("cursor", "ItemList", style_itemlist_cursor); theme->set_stylebox("cursor_unfocused", "ItemList", style_itemlist_cursor); theme->set_stylebox("selected_focus", "ItemList", style_tree_focus); - theme->set_stylebox("selected", "ItemList", style_tree_selected); - theme->set_stylebox("bg_focus", "ItemList", style_focus); + theme->set_stylebox("selected", "ItemList", style_tree_focus); + theme->set_stylebox("bg_focus", "ItemList", style_widget_focus); theme->set_stylebox("bg", "ItemList", style_itemlist_bg); theme->set_color("font_color", "ItemList", font_color); theme->set_color("font_color_selected", "ItemList", mono_color); theme->set_color("guide_color", "ItemList", guide_color); theme->set_constant("vseparation", "ItemList", 3 * EDSCALE); - theme->set_constant("hseparation", "ItemList", 3 * EDSCALE); - theme->set_constant("icon_margin", "ItemList", default_margin_size * EDSCALE); + theme->set_constant("hseparation", "ItemList", 6 * EDSCALE); + theme->set_constant("icon_margin", "ItemList", 6 * EDSCALE); theme->set_constant("line_separation", "ItemList", 3 * EDSCALE); // Tabs & TabContainer + theme->set_stylebox("tabbar_background", "TabContainer", style_tabbar_background); theme->set_stylebox("tab_fg", "TabContainer", style_tab_selected); theme->set_stylebox("tab_bg", "TabContainer", style_tab_unselected); theme->set_stylebox("tab_disabled", "TabContainer", style_tab_disabled); @@ -915,11 +970,15 @@ Ref create_editor_theme(const Ref p_theme) { Ref style_content_panel = style_default->duplicate(); style_content_panel->set_border_color(dark_color_3); style_content_panel->set_border_width_all(border_width); - // compensate the border - style_content_panel->set_default_margin(MARGIN_TOP, margin_size_extra * EDSCALE); + style_content_panel->set_border_width(MARGIN_TOP, 0); + style_content_panel->set_corner_radius(CORNER_TOP_LEFT, 0); + style_content_panel->set_corner_radius(CORNER_TOP_RIGHT, 0); + // Compensate the border + style_content_panel->set_default_margin(MARGIN_LEFT, margin_size_extra * EDSCALE); + style_content_panel->set_default_margin(MARGIN_TOP, (2 + margin_size_extra) * EDSCALE); style_content_panel->set_default_margin(MARGIN_RIGHT, margin_size_extra * EDSCALE); style_content_panel->set_default_margin(MARGIN_BOTTOM, margin_size_extra * EDSCALE); - style_content_panel->set_default_margin(MARGIN_LEFT, margin_size_extra * EDSCALE); + theme->set_stylebox("panel", "TabContainer", style_content_panel); // These styleboxes can be used on tabs against the base color background (e.g. nested tabs). Ref style_tab_selected_odd = style_tab_selected->duplicate(); @@ -930,26 +989,29 @@ Ref create_editor_theme(const Ref p_theme) { style_content_panel_odd->set_bg_color(color_disabled_bg); theme->set_stylebox("panel_odd", "TabContainer", style_content_panel_odd); + theme->set_type_variation("tab_container_odd", "TabContainer"); + theme->set_stylebox("tab_fg", "tab_container_odd", style_tab_selected_odd); + theme->set_stylebox("panel", "tab_container_odd", style_content_panel_odd); + // This stylebox is used in 3d and 2d viewports (no borders). Ref style_content_panel_vp = style_content_panel->duplicate(); style_content_panel_vp->set_default_margin(MARGIN_LEFT, border_width * 2); style_content_panel_vp->set_default_margin(MARGIN_TOP, default_margin_size * EDSCALE); style_content_panel_vp->set_default_margin(MARGIN_RIGHT, border_width * 2); style_content_panel_vp->set_default_margin(MARGIN_BOTTOM, border_width * 2); - theme->set_stylebox("panel", "TabContainer", style_content_panel); theme->set_stylebox("Content", "EditorStyles", style_content_panel_vp); // This stylebox is used by preview tabs in the Theme Editor. Ref style_theme_preview_tab = style_tab_selected_odd->duplicate(); - style_theme_preview_tab->set_expand_margin_size(MARGIN_BOTTOM, 3 * EDSCALE); + style_theme_preview_tab->set_expand_margin_size(MARGIN_BOTTOM, 5 * EDSCALE); theme->set_stylebox("ThemeEditorPreviewFG", "EditorStyles", style_theme_preview_tab); Ref style_theme_preview_bg_tab = style_tab_unselected->duplicate(); style_theme_preview_bg_tab->set_expand_margin_size(MARGIN_BOTTOM, 2 * EDSCALE); theme->set_stylebox("ThemeEditorPreviewBG", "EditorStyles", style_theme_preview_bg_tab); // Separators - theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, border_width)); - theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, border_width, 0, 0, true)); + theme->set_stylebox("separator", "HSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width))); + theme->set_stylebox("separator", "VSeparator", make_line_stylebox(separator_color, MAX(Math::round(EDSCALE), border_width), 0, 0, true)); // Debugger @@ -966,23 +1028,41 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_stylebox("BottomPanelDebuggerOverride", "EditorStyles", style_panel_invisible_top); // LineEdit - theme->set_stylebox("normal", "LineEdit", style_widget); + Ref style_line_edit = style_widget->duplicate(); + // The original style_widget style has an extra 1 pixel offset that makes LineEdits not align with Buttons, + // so this compensates for that. + style_line_edit->set_default_margin(MARGIN_TOP, style_line_edit->get_default_margin(MARGIN_TOP) - 1 * EDSCALE); + + // Don't round the bottom corner to make the line look sharper. + style_line_edit->set_corner_radius(CORNER_BOTTOM_LEFT, 0); + style_line_edit->set_corner_radius(CORNER_BOTTOM_RIGHT, 0); + + // Add a bottom line to make LineEdits more visible, especially in sectioned inspectors + // such as the Project Settings. + style_line_edit->set_border_width(MARGIN_BOTTOM, Math::round(2 * EDSCALE)); + style_line_edit->set_border_color(dark_color_2); + + Ref style_line_edit_disabled = style_line_edit->duplicate(); + style_line_edit_disabled->set_border_color(color_disabled); + style_line_edit_disabled->set_bg_color(color_disabled_bg); + + theme->set_stylebox("normal", "LineEdit", style_line_edit); theme->set_stylebox("focus", "LineEdit", style_widget_focus); - theme->set_stylebox("read_only", "LineEdit", style_widget_disabled); + theme->set_stylebox("read_only", "LineEdit", style_line_edit_disabled); theme->set_icon("clear", "LineEdit", theme->get_icon("GuiClose", "EditorIcons")); theme->set_color("read_only", "LineEdit", font_color_disabled); theme->set_color("font_color", "LineEdit", font_color); theme->set_color("font_color_selected", "LineEdit", mono_color); theme->set_color("font_color_uneditable", "LineEdit", font_color_readonly); theme->set_color("cursor_color", "LineEdit", font_color); - theme->set_color("selection_color", "LineEdit", font_color_selection); + theme->set_color("selection_color", "LineEdit", selection_color); theme->set_color("clear_button_color", "LineEdit", font_color); theme->set_color("clear_button_color_pressed", "LineEdit", accent_color); // TextEdit - theme->set_stylebox("normal", "TextEdit", style_widget); - theme->set_stylebox("focus", "TextEdit", style_widget_hover); - theme->set_stylebox("read_only", "TextEdit", style_widget_disabled); + theme->set_stylebox("normal", "TextEdit", style_line_edit); + theme->set_stylebox("focus", "TextEdit", style_widget_focus); + theme->set_stylebox("read_only", "TextEdit", style_line_edit_disabled); theme->set_constant("side_margin", "TabContainer", 0); theme->set_icon("tab", "TextEdit", theme->get_icon("GuiTab", "EditorIcons")); theme->set_icon("space", "TextEdit", theme->get_icon("GuiSpace", "EditorIcons")); @@ -991,7 +1071,8 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_color("font_color", "TextEdit", font_color); theme->set_color("font_color_readonly", "TextEdit", font_color_readonly); theme->set_color("caret_color", "TextEdit", font_color); - theme->set_color("selection_color", "TextEdit", font_color_selection); + theme->set_color("selection_color", "TextEdit", selection_color); + theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE); // H/VSplitContainer theme->set_stylebox("bg", "VSplitContainer", make_stylebox(theme->get_icon("GuiVsplitBg", "EditorIcons"), 1, 1, 1, 1)); @@ -1025,6 +1106,7 @@ Ref create_editor_theme(const Ref p_theme) { style_window->set_border_color(tab_color); style_window->set_border_width(MARGIN_TOP, 24 * EDSCALE); style_window->set_expand_margin_size(MARGIN_TOP, 24 * EDSCALE); + theme->set_stylebox("panel", "WindowDialog", style_window); theme->set_color("title_color", "WindowDialog", font_color); theme->set_icon("close", "WindowDialog", theme->get_icon("GuiClose", "EditorIcons")); @@ -1034,7 +1116,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_constant("title_height", "WindowDialog", 24 * EDSCALE); theme->set_font("title_font", "WindowDialog", theme->get_font("title", "EditorFonts")); - // complex window, for now only Editor settings and Project settings + // Complex window, for now only Editor settings and Project settings Ref style_complex_window = style_window->duplicate(); style_complex_window->set_bg_color(dark_color_2); style_complex_window->set_border_color(highlight_tabs ? tab_color : dark_color_2); @@ -1042,14 +1124,18 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_stylebox("panel", "ProjectSettingsEditor", style_complex_window); theme->set_stylebox("panel", "EditorAbout", style_complex_window); + // AcceptDialog + theme->set_stylebox("panel", "AcceptDialog", style_window); + theme->set_constant("buttons_separation", "AcceptDialog", 8 * EDSCALE); + // HScrollBar Ref empty_icon = memnew(ImageTexture); - theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabber", "EditorIcons"), 6, 6, 6, 6, 2, 2, 2, 2)); - theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberHl", "EditorIcons"), 5, 5, 5, 5, 2, 2, 2, 2)); - theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberPressed", "EditorIcons"), 6, 6, 6, 6, 2, 2, 2, 2)); + theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabber", "EditorIcons"), 6, 6, 6, 6, 1, 1, 1, 1)); + theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberHl", "EditorIcons"), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberPressed", "EditorIcons"), 6, 6, 6, 6, 1, 1, 1, 1)); theme->set_icon("increment", "HScrollBar", empty_icon); theme->set_icon("increment_highlight", "HScrollBar", empty_icon); @@ -1059,11 +1145,11 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_icon("decrement_pressed", "HScrollBar", empty_icon); // VScrollBar - theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabber", "EditorIcons"), 6, 6, 6, 6, 2, 2, 2, 2)); - theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberHl", "EditorIcons"), 5, 5, 5, 5, 2, 2, 2, 2)); - theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberPressed", "EditorIcons"), 6, 6, 6, 6, 2, 2, 2, 2)); + theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabber", "EditorIcons"), 6, 6, 6, 6, 1, 1, 1, 1)); + theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberHl", "EditorIcons"), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberPressed", "EditorIcons"), 6, 6, 6, 6, 1, 1, 1, 1)); theme->set_icon("increment", "VScrollBar", empty_icon); theme->set_icon("increment_highlight", "VScrollBar", empty_icon); @@ -1075,15 +1161,15 @@ Ref create_editor_theme(const Ref p_theme) { // HSlider theme->set_icon("grabber_highlight", "HSlider", theme->get_icon("GuiSliderGrabberHl", "EditorIcons")); theme->set_icon("grabber", "HSlider", theme->get_icon("GuiSliderGrabber", "EditorIcons")); - theme->set_stylebox("slider", "HSlider", make_flat_stylebox(dark_color_3, 0, default_margin_size / 2, 0, default_margin_size / 2)); - theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2)); + theme->set_stylebox("slider", "HSlider", make_flat_stylebox(dark_color_3, 0, default_margin_size / 2, 0, default_margin_size / 2, corner_width)); + theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2, corner_width)); theme->set_stylebox("grabber_area_highlight", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2)); // VSlider theme->set_icon("grabber", "VSlider", theme->get_icon("GuiSliderGrabber", "EditorIcons")); theme->set_icon("grabber_highlight", "VSlider", theme->get_icon("GuiSliderGrabberHl", "EditorIcons")); - theme->set_stylebox("slider", "VSlider", make_flat_stylebox(dark_color_3, default_margin_size / 2, 0, default_margin_size / 2, 0)); - theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0)); + theme->set_stylebox("slider", "VSlider", make_flat_stylebox(dark_color_3, default_margin_size / 2, 0, default_margin_size / 2, 0, corner_width)); + theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0, corner_width)); theme->set_stylebox("grabber_area_highlight", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0)); //RichTextLabel @@ -1098,7 +1184,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_color("headline_color", "EditorHelp", mono_color); // Panel - theme->set_stylebox("panel", "Panel", make_flat_stylebox(dark_color_1, 6, 4, 6, 4)); + theme->set_stylebox("panel", "Panel", make_flat_stylebox(dark_color_1, 6, 4, 6, 4, corner_width)); // Label theme->set_stylebox("normal", "Label", style_empty); @@ -1119,16 +1205,17 @@ Ref create_editor_theme(const Ref p_theme) { // TooltipPanel Ref style_tooltip = style_popup->duplicate(); - float v = MAX(border_size * EDSCALE, 1.0); + style_tooltip->set_shadow_size(0); + float v = default_margin_size * EDSCALE * 0.5; style_tooltip->set_default_margin(MARGIN_LEFT, v); style_tooltip->set_default_margin(MARGIN_TOP, v); style_tooltip->set_default_margin(MARGIN_RIGHT, v); style_tooltip->set_default_margin(MARGIN_BOTTOM, v); - style_tooltip->set_bg_color(Color(mono_color.r, mono_color.g, mono_color.b, 0.9)); - style_tooltip->set_border_width_all(border_width); + style_tooltip->set_bg_color(dark_color_3 * Color(0.8, 0.8, 0.8, 0.9)); + style_tooltip->set_border_width_all(0); style_tooltip->set_border_color(mono_color); - theme->set_color("font_color", "TooltipLabel", font_color.inverted()); - theme->set_color("font_color_shadow", "TooltipLabel", mono_color.inverted() * Color(1, 1, 1, 0.1)); + theme->set_color("font_color", "TooltipLabel", font_color); + theme->set_color("font_color_shadow", "TooltipLabel", Color(0, 0, 0, 0)); theme->set_stylebox("panel", "TooltipPanel", style_tooltip); // PopupPanel @@ -1180,7 +1267,6 @@ Ref create_editor_theme(const Ref p_theme) { style_minimap_node = make_flat_stylebox(Color(0, 0, 0), 0, 0, 0, 0); } style_minimap_camera->set_border_width_all(1); - style_minimap_node->set_corner_radius_all(1); theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera); theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node); @@ -1195,23 +1281,31 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_color("resizer_color", "GraphEditMinimap", minimap_resizer_color); // GraphNode - const float mv = dark_theme ? 0.0 : 1.0; - const float mv2 = 1.0 - mv; - const int gn_margin_side = 28; - Ref graphsb = make_flat_stylebox(Color(mv, mv, mv, 0.7), gn_margin_side, 24, gn_margin_side, 5); + const int gn_margin_side = 2; + const int gn_margin_bottom = 2; + + // StateMachine + const int sm_margin_side = 10; + + Color graphnode_bg = dark_color_3; + if (!dark_theme) { + graphnode_bg = prop_section_color; + } + + Ref graphsb = make_flat_stylebox(graphnode_bg.linear_interpolate(style_tree_bg->get_bg_color(), 0.3), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width); graphsb->set_border_width_all(border_width); - graphsb->set_border_color(Color(mv2, mv2, mv2, 0.9)); - Ref graphsbselected = make_flat_stylebox(Color(mv, mv, mv, 0.9), gn_margin_side, 24, gn_margin_side, 5); + graphsb->set_border_color(graphnode_bg); + Ref graphsbselected = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 1), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width); graphsbselected->set_border_width_all(border_width); - graphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.9)); + graphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.6)); graphsbselected->set_shadow_size(8 * EDSCALE); graphsbselected->set_shadow_color(shadow_color); - Ref graphsbcomment = make_flat_stylebox(Color(mv, mv, mv, 0.3), gn_margin_side, 24, gn_margin_side, 5); + Ref graphsbcomment = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.3), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width); graphsbcomment->set_border_width_all(border_width); - graphsbcomment->set_border_color(Color(mv2, mv2, mv2, 0.9)); - Ref graphsbcommentselected = make_flat_stylebox(Color(mv, mv, mv, 0.4), gn_margin_side, 24, gn_margin_side, 5); + graphsbcomment->set_border_color(graphnode_bg); + Ref graphsbcommentselected = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.4), gn_margin_side, 24, gn_margin_side, gn_margin_bottom, corner_width); graphsbcommentselected->set_border_width_all(border_width); - graphsbcommentselected->set_border_color(Color(mv2, mv2, mv2, 0.9)); + graphsbcommentselected->set_border_color(graphnode_bg); Ref graphsbbreakpoint = graphsbselected->duplicate(); graphsbbreakpoint->set_draw_center(false); graphsbbreakpoint->set_border_color(warning_color); @@ -1220,10 +1314,10 @@ Ref create_editor_theme(const Ref p_theme) { graphsbposition->set_draw_center(false); graphsbposition->set_border_color(error_color); graphsbposition->set_shadow_color(error_color * Color(1.0, 1.0, 1.0, 0.2)); - Ref smgraphsb = make_flat_stylebox(Color(mv, mv, mv, 0.7), gn_margin_side, 24, gn_margin_side, 5); + Ref smgraphsb = make_flat_stylebox(dark_color_3 * Color(1, 1, 1, 0.7), sm_margin_side, 24, sm_margin_side, gn_margin_bottom, corner_width); smgraphsb->set_border_width_all(border_width); - smgraphsb->set_border_color(Color(mv2, mv2, mv2, 0.9)); - Ref smgraphsbselected = make_flat_stylebox(Color(mv, mv, mv, 0.9), gn_margin_side, 24, gn_margin_side, 5); + smgraphsb->set_border_color(graphnode_bg); + Ref smgraphsbselected = make_flat_stylebox(graphnode_bg * Color(1, 1, 1, 0.9), sm_margin_side, 24, sm_margin_side, gn_margin_bottom, corner_width); smgraphsbselected->set_border_width_all(border_width); smgraphsbselected->set_border_color(Color(accent_color.r, accent_color.g, accent_color.b, 0.9)); smgraphsbselected->set_shadow_size(8 * EDSCALE); @@ -1236,6 +1330,11 @@ Ref create_editor_theme(const Ref p_theme) { graphsbcommentselected->set_border_width(MARGIN_TOP, 24 * EDSCALE); } + graphsb->set_corner_detail(corner_width * EDSCALE); + graphsbselected->set_corner_detail(corner_width * EDSCALE); + graphsbcomment->set_corner_detail(corner_width * EDSCALE); + graphsbcommentselected->set_corner_detail(corner_width * EDSCALE); + theme->set_stylebox("frame", "GraphNode", graphsb); theme->set_stylebox("selectedframe", "GraphNode", graphsbselected); theme->set_stylebox("comment", "GraphNode", graphsbcomment); @@ -1245,15 +1344,15 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_stylebox("state_machine_frame", "GraphNode", smgraphsb); theme->set_stylebox("state_machine_selectedframe", "GraphNode", smgraphsbselected); - Color default_node_color = Color(mv2, mv2, mv2); - theme->set_color("title_color", "GraphNode", default_node_color); - default_node_color.a = 0.7; - theme->set_color("close_color", "GraphNode", default_node_color); - theme->set_color("resizer_color", "GraphNode", default_node_color); + Color node_decoration_color = dark_color_1.inverted(); + theme->set_color("title_color", "GraphNode", node_decoration_color); + node_decoration_color.a = 0.7; + theme->set_color("close_color", "GraphNode", node_decoration_color); + theme->set_color("resizer_color", "GraphNode", node_decoration_color); theme->set_constant("port_offset", "GraphNode", 14 * EDSCALE); theme->set_constant("title_h_offset", "GraphNode", -16 * EDSCALE); - theme->set_constant("title_offset", "GraphNode", 20 * EDSCALE); + theme->set_constant("title_offset", "GraphNode", 21 * EDSCALE); theme->set_constant("close_h_offset", "GraphNode", 20 * EDSCALE); theme->set_constant("close_offset", "GraphNode", 20 * EDSCALE); theme->set_constant("separation", "GraphNode", 1 * EDSCALE); @@ -1263,7 +1362,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_icon("port", "GraphNode", theme->get_icon("GuiGraphNodePort", "EditorIcons")); // GridContainer - theme->set_constant("vseparation", "GridContainer", (extra_spacing + default_margin_size) * EDSCALE); + theme->set_constant("vseparation", "GridContainer", (extra_spacing + default_margin_size - 2) * EDSCALE); // FileDialog theme->set_icon("folder", "FileDialog", theme->get_icon("Folder", "EditorIcons")); @@ -1289,7 +1388,7 @@ Ref create_editor_theme(const Ref p_theme) { theme->set_icon("bg", "ColorPickerButton", theme->get_icon("GuiMiniCheckerboard", "EditorIcons")); // ColorPresetButton - Ref preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2); + Ref preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2, 2); preset_sb->set_anti_aliased(false); theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb); @@ -1322,21 +1421,18 @@ Ref create_editor_theme(const Ref p_theme) { const float mono_value = mono_color.r; const Color alpha1 = Color(mono_value, mono_value, mono_value, 0.07); const Color alpha2 = Color(mono_value, mono_value, mono_value, 0.14); - const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.7); - - // editor main color - const Color main_color = dark_theme ? Color(0.34, 0.7, 1.0) : Color(0.02, 0.5, 1.0); + const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.27); - const Color symbol_color = Color(0.34, 0.57, 1.0).linear_interpolate(mono_color, dark_theme ? 0.5 : 0.3); - const Color keyword_color = Color(1.0, 0.44, 0.52); - const Color control_flow_keyword_color = dark_theme ? Color(1.0, 0.55, 0.8) : Color(0.8, 0.4, 0.6); - const Color basetype_color = dark_theme ? Color(0.26, 1.0, 0.76) : Color(0.0, 0.76, 0.38); - const Color type_color = basetype_color.linear_interpolate(mono_color, dark_theme ? 0.4 : 0.3); + const Color symbol_color = dark_theme ? Color(0.67, 0.79, 1) : Color(0, 0, 0.61); + const Color keyword_color = dark_theme ? Color(1.0, 0.44, 0.52) : Color(0.9, 0.135, 0.51); + const Color control_flow_keyword_color = dark_theme ? Color(1.0, 0.55, 0.8) : Color(0.743, 0.12, 0.8); + const Color basetype_color = dark_theme ? Color(0.26, 1.0, 0.76) : Color(0, 0.6, 0.2); + const Color type_color = dark_theme ? Color(0.56, 1, 0.86) : Color(0.11, 0.55, 0.4); const Color usertype_color = basetype_color.linear_interpolate(mono_color, dark_theme ? 0.7 : 0.5); - const Color comment_color = dim_color; - const Color string_color = (dark_theme ? Color(1.0, 0.85, 0.26) : Color(1.0, 0.82, 0.09)).linear_interpolate(mono_color, dark_theme ? 0.5 : 0.3); + const Color comment_color = dark_theme ? dim_color : Color(0.08, 0.08, 0.08, 0.5); + const Color string_color = dark_theme ? Color(1, 0.93, 0.63) : Color(0.6, 0.42, 0); - const Color te_background_color = dark_theme ? background_color : base_color; + const Color te_background_color = dark_theme ? background_color : dark_color_3; const Color completion_background_color = dark_theme ? base_color : background_color; const Color completion_selected_color = alpha1; const Color completion_existing_color = alpha2; @@ -1344,25 +1440,24 @@ Ref create_editor_theme(const Ref p_theme) { const Color completion_font_color = font_color; const Color text_color = font_color; const Color line_number_color = dim_color; - const Color safe_line_number_color = dim_color * Color(1, 1.2, 1, 1.5); + const Color safe_line_number_color = dark_theme ? (dim_color * Color(1, 1.2, 1, 1.5)) : Color(0, 0.4, 0, 0.75); const Color caret_color = mono_color; const Color caret_background_color = mono_color.inverted(); - const Color text_selected_color = dark_color_3; - const Color selection_color = accent_color * Color(1, 1, 1, 0.35); - const Color brace_mismatch_color = error_color; + const Color text_selected_color = Color(0, 0, 0, 0); + const Color brace_mismatch_color = dark_theme ? error_color : Color(1, 0.08, 0, 1); const Color current_line_color = alpha1; const Color line_length_guideline_color = dark_theme ? base_color : background_color; const Color word_highlighted_color = alpha1; - const Color number_color = basetype_color.linear_interpolate(mono_color, dark_theme ? 0.5 : 0.3); - const Color function_color = main_color; - const Color member_variable_color = main_color.linear_interpolate(mono_color, 0.6); + const Color number_color = dark_theme ? Color(0.63, 1, 0.88) : Color(0, 0.55, 0.28, 1); + const Color function_color = dark_theme ? Color(0.34, 0.7, 1.0) : Color(0, 0.225, 0.9, 1); + const Color member_variable_color = dark_theme ? Color(0.34, 0.7, 1.0).linear_interpolate(mono_color, 0.6) : Color(0, 0.4, 0.68, 1); const Color mark_color = Color(error_color.r, error_color.g, error_color.b, 0.3); const Color bookmark_color = Color(0.08, 0.49, 0.98); - const Color breakpoint_color = error_color; + const Color breakpoint_color = dark_theme ? error_color : Color(1, 0.27, 0.2, 1); const Color executing_line_color = Color(0.2, 0.8, 0.2, 0.4); const Color code_folding_color = alpha3; const Color search_result_color = alpha1; - const Color search_result_border_color = Color(0.41, 0.61, 0.91, 0.38); + const Color search_result_border_color = dark_theme ? Color(0.41, 0.61, 0.91, 0.38) : Color(0, 0.4, 1, 0.38); EditorSettings *setting = EditorSettings::get_singleton(); String text_editor_color_theme = setting->get("text_editor/theme/color_theme"); diff --git a/editor/icons/icon_GUI_checked.svg b/editor/icons/icon_GUI_checked.svg index 31b2995939a2..70500c219822 100644 --- a/editor/icons/icon_GUI_checked.svg +++ b/editor/icons/icon_GUI_checked.svg @@ -1 +1 @@ - + diff --git a/editor/icons/icon_GUI_radio_checked.svg b/editor/icons/icon_GUI_radio_checked.svg index 65ef086c9ad8..a768d67f6a88 100644 --- a/editor/icons/icon_GUI_radio_checked.svg +++ b/editor/icons/icon_GUI_radio_checked.svg @@ -1 +1 @@ - + diff --git a/editor/icons/icon_face_about.svg b/editor/icons/icon_face_about.svg new file mode 100644 index 000000000000..e64b229a51b9 --- /dev/null +++ b/editor/icons/icon_face_about.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/icon_godot_docs.svg b/editor/icons/icon_godot_docs.svg deleted file mode 100644 index 97010d5fde03..000000000000 --- a/editor/icons/icon_godot_docs.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/editor/icons/icon_logo_godot.svg b/editor/icons/icon_logo_godot.svg new file mode 100644 index 000000000000..f8712de43d9d --- /dev/null +++ b/editor/icons/icon_logo_godot.svg @@ -0,0 +1,198 @@ + +image/svg+xml diff --git a/editor/icons/icon_title_dark.svg b/editor/icons/icon_title_dark.svg new file mode 100644 index 000000000000..7fe1c16cde74 --- /dev/null +++ b/editor/icons/icon_title_dark.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/icon_title_light.svg b/editor/icons/icon_title_light.svg new file mode 100644 index 000000000000..f72b17bd6793 --- /dev/null +++ b/editor/icons/icon_title_light.svg @@ -0,0 +1 @@ + diff --git a/editor/project_export.cpp b/editor/project_export.cpp index d4d56047e58f..2a401435d874 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -234,6 +234,7 @@ void ProjectExportDialog::_edit_preset(int p_index) { export_path->update_property(); runnable->set_disabled(false); runnable->set_pressed(current->is_runnable()); + open_key_manager->set_visible(current->get_platform()->get_os_name() == "Android"); parameters->edit(current.ptr()); export_filter->select(current->get_export_filter()); @@ -968,6 +969,8 @@ void ProjectExportDialog::_bind_methods() { ClassDB::bind_method("_force_update_current_preset_parameters", &ProjectExportDialog::_force_update_current_preset_parameters); ADD_PROPERTY(PropertyInfo(Variant::STRING, "export_path"), "set_export_path", "get_export_path"); + + ADD_SIGNAL(MethodInfo("key_manager_requested")); } ProjectExportDialog::ProjectExportDialog() { @@ -1033,6 +1036,12 @@ ProjectExportDialog::ProjectExportDialog() { export_path->set_save_mode(); export_path->connect("property_changed", this, "_export_path_changed"); + open_key_manager = memnew(Button); + settings_vb->add_child(open_key_manager); + open_key_manager->set_text(TTR("Open Key Manager")); + open_key_manager->hide(); + open_key_manager->connect("pressed", this, "emit_signal", varray("key_manager_requested")); + // Subsections. sections = memnew(TabContainer); diff --git a/editor/project_export.h b/editor/project_export.h index c394ff97ee99..7c84a6279428 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -68,6 +68,7 @@ class ProjectExportDialog : public ConfirmationDialog { LineEdit *name; EditorPropertyPath *export_path; + Button *open_key_manager; EditorInspector *parameters; CheckButton *runnable; diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index aac729dc0e90..7b24277cbdb1 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -2242,7 +2242,7 @@ void ProjectManager::_erase_missing_projects() { } void ProjectManager::_show_about() { - about->popup_centered(Size2(780, 500) * EDSCALE); + about->popup_centered(); } void ProjectManager::_language_selected(int p_id) { @@ -2784,7 +2784,7 @@ ProjectManager::ProjectManager() { add_child(open_templates); } - about = memnew(EditorAbout); + about = memnew(EditorAboutRamatak); add_child(about); } diff --git a/editor/project_manager.h b/editor/project_manager.h index 17187934c937..8b9db995b578 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -31,7 +31,7 @@ #ifndef PROJECT_MANAGER_H #define PROJECT_MANAGER_H -#include "editor/editor_about.h" +#include "editor/editor_about_ramatak.h" #include "editor/plugins/asset_library_editor_plugin.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" @@ -72,7 +72,7 @@ class ProjectManager : public Control { ConfirmationDialog *multi_scan_ask; ConfirmationDialog *ask_update_settings; ConfirmationDialog *open_templates = nullptr; - EditorAbout *about; + EditorAboutRamatak *about; AcceptDialog *run_error_diag; AcceptDialog *dialog_error; ProjectDialog *npdialog; diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 7e52b9bbd81c..2e33a1d6ac47 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -2036,6 +2036,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { //translations TabContainer *translations = memnew(TabContainer); + translations->set_theme_type_variation("tab_container_odd"); translations->set_tab_align(TabContainer::ALIGN_LEFT); translations->set_name(TTR("Localization")); tab_container->add_child(translations); diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index 5b5c7f25378d..a7d4c939c6dc 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -38,6 +38,7 @@ #include "editor_property_name_processor.h" #include "editor_scale.h" #include "editor_settings.h" +#include "editor/editor_key_manager.h" #include "scene/gui/margin_container.h" #include "script_editor_debugger.h" @@ -77,19 +78,21 @@ void EditorSettingsDialog::cancel_pressed() { EditorSettings::get_singleton()->notify_changes(); } -void EditorSettingsDialog::popup_edit_settings() { +void EditorSettingsDialog::popup_edit_settings(int p_idx) { if (!EditorSettings::get_singleton()) { return; } + if (p_idx > -1) { + ERR_FAIL_INDEX(p_idx, tabs->get_tab_count()); + tabs->set_current_tab(p_idx); + } + EditorSettings::get_singleton()->list_text_editor_themes(); // make sure we have an up to date list of themes inspector->edit(EditorSettings::get_singleton()); inspector->get_inspector()->update_tree(); - search_box->select_all(); - search_box->grab_focus(); - _update_shortcuts(); set_process_unhandled_input(true); @@ -386,6 +389,8 @@ void EditorSettingsDialog::_editor_restart_close() { } void EditorSettingsDialog::_bind_methods() { + ClassDB::bind_method(D_METHOD("popup_edit_settings"), &EditorSettingsDialog::popup_edit_settings); + ClassDB::bind_method(D_METHOD("_unhandled_input"), &EditorSettingsDialog::_unhandled_input); ClassDB::bind_method(D_METHOD("_settings_save"), &EditorSettingsDialog::_settings_save); ClassDB::bind_method(D_METHOD("_settings_changed"), &EditorSettingsDialog::_settings_changed); @@ -507,6 +512,10 @@ EditorSettingsDialog::EditorSettingsDialog() { EditorSettings::get_singleton()->connect("settings_changed", this, "_settings_changed"); get_ok()->set_text(TTR("Close")); + // Key Manager Tab + + tabs->add_child(memnew(EditorKeyManager)); + updating = false; } diff --git a/editor/settings_config_dialog.h b/editor/settings_config_dialog.h index cbb7232084ca..2b25b500956e 100644 --- a/editor/settings_config_dialog.h +++ b/editor/settings_config_dialog.h @@ -101,7 +101,7 @@ class EditorSettingsDialog : public AcceptDialog { static void _bind_methods(); public: - void popup_edit_settings(); + void popup_edit_settings(int p_idx = -1); EditorSettingsDialog(); ~EditorSettingsDialog(); diff --git a/main/main.cpp b/main/main.cpp index a78de7bf3fcc..d0ba9b120f9c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1532,18 +1532,64 @@ Error Main::setup2(Thread::ID p_main_tid_override) { #ifdef TOOLS_ENABLED ClassDB::set_current_api(ClassDB::API_EDITOR); EditorNode::register_editor_types(); - ClassDB::set_current_api(ClassDB::API_CORE); - String JAVA_HOME = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("jdk/"); - OS::get_singleton()->set_environment("JAVA_HOME", JAVA_HOME); - - String RAMATAK_ANDROID_SDK_HOME = OS::get_singleton()->get_environment("RAMATAK_ANDROID_SDK_HOME"); - if (RAMATAK_ANDROID_SDK_HOME.empty()) { - String sdk_dir = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("android/sdk"); - print_line("MAIN: RAMATAK_ANDROID_SDK_HOME not set, defaulting to " + sdk_dir); - OS::get_singleton()->set_environment("RAMATAK_ANDROID_SDK_HOME", sdk_dir); + + bool respect_all_env = !OS::get_singleton()->get_environment("RAMATAK_RESPECT_ALL_ENV").empty(); + bool respect_java_home = !OS::get_singleton()->get_environment("RAMATAK_RESPECT_JAVA_HOME").empty(); + bool respect_keytool_home = !OS::get_singleton()->get_environment("RAMATAK_RESPECT_KEYTOOL_HOME").empty(); + bool respect_sdkamanager_home = !OS::get_singleton()->get_environment("RAMATAK_RESPECT_SDKMANAGER_HOME").empty(); + bool respect_android_home = !OS::get_singleton()->get_environment("RAMATAK_RESPECT_ANDROID_HOME").empty(); + + String java_default = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("jdk/"); + String java_home = OS::get_singleton()->get_environment("JAVA_HOME"); + if (respect_java_home || respect_all_env) { + if (java_home.empty()) { + print_line("MAIN: JAVA_HOME not set, defaulting to " + java_default); + OS::get_singleton()->set_environment("JAVA_HOME", java_default); + } else { + print_line("MAIN: JAVA_HOME set, to " + java_home); + } + } else { + print_line("MAIN: JAVA_HOME set, to " + java_default); + } + + String keytool_default = OS::get_singleton()->get_environment("JAVA_HOME").plus_file("bin/keytool"); + String keytool_home = OS::get_singleton()->get_environment("RAMATAK_KEYTOOL_HOME"); + if (respect_keytool_home || respect_all_env) { + if (keytool_home.empty()) { + print_line("MAIN: RAMATAK_KEYTOOL_HOME not set, defaulting to " + keytool_default); + OS::get_singleton()->set_environment("RAMATAK_KEYTOOL_HOME", keytool_default); + } else { + print_line("MAIN: RAMATAK_KEYTOOL_HOME set, to " + keytool_home); + } + } else { + print_line("MAIN: RAMATAK_KEYTOOL_HOME set, to " + keytool_default); + } + + String sdkmanager_default = OS::get_singleton()->get_environment("JAVA_HOME").plus_file("bin/sdkmanager"); + String sdkmanager_home = OS::get_singleton()->get_environment("RAMATAK_SDKMANAGER_HOME"); + if (respect_sdkamanager_home || respect_all_env) { + if (keytool_home.empty()) { + print_line("MAIN: RAMATAK_SDKMANAGER_HOME not set, defaulting to " + sdkmanager_default); + OS::get_singleton()->set_environment("RAMATAK_SDKMANAGER_HOME", sdkmanager_default); + } else { + print_line("MAIN: RAMATAK_SDKMANAGER_HOME set, to " + sdkmanager_home); + } + } else { + print_line("MAIN: RAMATAK_SDKMANAGER_HOME set, to " + sdkmanager_default); + } + + String ramatak_android_sdk_default = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("android/sdk"); + String ramatak_android_sdk_home = OS::get_singleton()->get_environment("RAMATAK_ANDROID_SDK_HOME"); + if (respect_android_home || respect_all_env) { + if (ramatak_android_sdk_home.empty()) { + print_line("MAIN: RAMATAK_ANDROID_SDK_HOME not set, defaulting to " + ramatak_android_sdk_default); + OS::get_singleton()->set_environment("RAMATAK_ANDROID_SDK_HOME", ramatak_android_sdk_default); + } else { + print_line("MAIN: RAMATAK_ANDROID_SDK_HOME set, to " + ramatak_android_sdk_home); + } } else { - print_line("MAIN: RAMATAK_ANDROID_SDK_HOME set, to " + RAMATAK_ANDROID_SDK_HOME); + print_line("MAIN: RAMATAK_ANDROID_SDK_HOME set, to " + ramatak_android_sdk_default); } #endif diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 4c8e3c8541b1..009784ea9fbe 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -228,6 +228,9 @@ static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/instal static const int DEFAULT_MIN_SDK_VERSION = 19; // Should match the value in 'platform/android/java/app/config.gradle#minSdk' static const int DEFAULT_TARGET_SDK_VERSION = 32; // Should match the value in 'platform/android/java/app/config.gradle#targetSdk' +static const int KEY_FROM_MANAGER = 0; +static const int KEY_FROM_MANUAL = 1; + #ifndef ANDROID_ENABLED void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud; @@ -1714,9 +1717,11 @@ void EditorExportPlatformAndroid::get_export_options(List *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("architectures"), abi)), is_default)); } + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "keystore/debug_key", PROPERTY_HINT_ENUM, "From Key Manager,Manual"), KEY_FROM_MANAGER)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "keystore/release_key", PROPERTY_HINT_ENUM, "From Key Manager,Manual"), KEY_FROM_MANAGER)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); @@ -2107,9 +2112,22 @@ bool EditorExportPlatformAndroid::can_export(const Ref &p_pr // Validate the rest of the configuration. - String dk = p_preset->get("keystore/debug"); - String dk_user = p_preset->get("keystore/debug_user"); - String dk_password = p_preset->get("keystore/debug_password"); + String dk; + String dk_user; + String dk_password; + + String default_debug = EditorSettings::get_singleton()->get("key_manager/default_debug"); + int debug_key = p_preset->get("keystore/debug_key"); + if (debug_key == KEY_FROM_MANAGER && !default_debug.empty()) { + Dictionary data = EditorSettings::get_singleton()->get("key_manager/keys").get(default_debug); + dk = data["path"]; + dk_user = data["alias"]; + dk_password = data["password"]; + } else { + dk = p_preset->get("keystore/debug"); + dk_user = p_preset->get("keystore/debug_user"); + dk_password = p_preset->get("keystore/debug_password"); + } if ((dk.empty() || dk_user.empty() || dk_password.empty()) && (!dk.empty() || !dk_user.empty() || !dk_password.empty())) { valid = false; @@ -2149,9 +2167,22 @@ bool EditorExportPlatformAndroid::can_export(const Ref &p_pr } } - String rk = p_preset->get("keystore/release"); - String rk_user = p_preset->get("keystore/release_user"); - String rk_password = p_preset->get("keystore/release_password"); + String rk; + String rk_user; + String rk_password; + + String default_release = EditorSettings::get_singleton()->get("key_manager/default_release"); + int release_key = p_preset->get("keystore/release_key"); + if (release_key == KEY_FROM_MANAGER && !default_release.empty()) { + Dictionary data = EditorSettings::get_singleton()->get("key_manager/keys").get(default_release); + rk = data["path"]; + rk_user = data["alias"]; + rk_password = data["password"]; + } else { + rk = p_preset->get("keystore/release"); + rk_user = p_preset->get("keystore/release_user"); + rk_password = p_preset->get("keystore/release_password"); + } if ((rk.empty() || rk_user.empty() || rk_password.empty()) && (!rk.empty() || !rk_user.empty() || !rk_password.empty())) { valid = false; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 4a42d2431513..5a559032d40d 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -267,6 +267,7 @@ void TabContainer::_notification(int p_what) { } Vector tabs = _get_tabs(); + Ref tabbar_style = get_stylebox("tabbar_background"); Ref tab_bg = get_stylebox("tab_bg"); Ref tab_fg = get_stylebox("tab_fg"); Ref tab_disabled = get_stylebox("tab_disabled"); @@ -347,6 +348,9 @@ void TabContainer::_notification(int p_what) { break; } + // Draw background for the tabbar. + tabbar_style->draw(canvas, Rect2(0, 0, size.width, header_height)); + if (all_tabs_in_front) { // Draw the tab area. panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));