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 @@
+
+
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));