From 8d93b6a54c4b9bec2a81368015aa905f4b5efdd9 Mon Sep 17 00:00:00 2001 From: Haoyu Qiu Date: Fri, 13 Sep 2024 00:50:09 +0800 Subject: [PATCH] Add translation preview in editor --- core/string/translation_domain.cpp | 33 +++++++- core/string/translation_domain.h | 9 +++ core/string/translation_server.cpp | 11 --- core/string/translation_server.h | 6 +- doc/classes/TranslationDomain.xml | 18 +++++ editor/editor_node.cpp | 51 +++++++++++- editor/editor_node.h | 4 + .../gui/editor_translation_preview_button.cpp | 76 +++++++++++++++++ .../gui/editor_translation_preview_button.h | 47 +++++++++++ .../gui/editor_translation_preview_menu.cpp | 81 +++++++++++++++++++ editor/gui/editor_translation_preview_menu.h | 46 +++++++++++ editor/plugins/canvas_item_editor_plugin.cpp | 11 +++ editor/plugins/node_3d_editor_plugin.cpp | 62 +++++++------- editor/plugins/node_3d_editor_plugin.h | 1 + scene/main/node.cpp | 7 -- 15 files changed, 407 insertions(+), 56 deletions(-) create mode 100644 editor/gui/editor_translation_preview_button.cpp create mode 100644 editor/gui/editor_translation_preview_button.h create mode 100644 editor/gui/editor_translation_preview_menu.cpp create mode 100644 editor/gui/editor_translation_preview_menu.h diff --git a/core/string/translation_domain.cpp b/core/string/translation_domain.cpp index cd27fdba548..507f1b995ff 100644 --- a/core/string/translation_domain.cpp +++ b/core/string/translation_domain.cpp @@ -287,7 +287,11 @@ void TranslationDomain::clear() { } StringName TranslationDomain::translate(const StringName &p_message, const StringName &p_context) const { - const String &locale = TranslationServer::get_singleton()->get_locale(); + if (!enabled) { + return p_message; + } + + const String &locale = locale_override.is_empty() ? TranslationServer::get_singleton()->get_locale() : locale_override; StringName res = get_message_from_translations(locale, p_message, p_context); const String &fallback = TranslationServer::get_singleton()->get_fallback_locale(); @@ -302,7 +306,11 @@ StringName TranslationDomain::translate(const StringName &p_message, const Strin } StringName TranslationDomain::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { - const String &locale = TranslationServer::get_singleton()->get_locale(); + if (!enabled) { + return p_n == 1 ? p_message : p_message_plural; + } + + const String &locale = locale_override.is_empty() ? TranslationServer::get_singleton()->get_locale() : locale_override; StringName res = get_message_from_translations(locale, p_message, p_message_plural, p_n, p_context); const String &fallback = TranslationServer::get_singleton()->get_fallback_locale(); @@ -319,6 +327,22 @@ StringName TranslationDomain::translate_plural(const StringName &p_message, cons return res; } +String TranslationDomain::get_locale_override() const { + return locale_override; +} + +void TranslationDomain::set_locale_override(const String &p_locale) { + locale_override = p_locale.is_empty() ? p_locale : TranslationServer::get_singleton()->standardize_locale(p_locale); +} + +bool TranslationDomain::is_enabled() const { + return enabled; +} + +void TranslationDomain::set_enabled(bool p_enabled) { + enabled = p_enabled; +} + bool TranslationDomain::is_pseudolocalization_enabled() const { return pseudolocalization.enabled; } @@ -424,6 +448,10 @@ void TranslationDomain::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &TranslationDomain::clear); ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationDomain::translate, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("translate_plural", "message", "message_plural", "n", "context"), &TranslationDomain::translate_plural, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("get_locale_override"), &TranslationDomain::get_locale_override); + ClassDB::bind_method(D_METHOD("set_locale_override", "locale"), &TranslationDomain::set_locale_override); + ClassDB::bind_method(D_METHOD("is_enabled"), &TranslationDomain::is_enabled); + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &TranslationDomain::set_enabled); ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationDomain::is_pseudolocalization_enabled); ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_enabled); @@ -445,6 +473,7 @@ void TranslationDomain::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pseudolocalization_suffix", "suffix"), &TranslationDomain::set_pseudolocalization_suffix); ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationDomain::pseudolocalize); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled"); ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_accents_enabled"), "set_pseudolocalization_accents_enabled", "is_pseudolocalization_accents_enabled"); ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_double_vowels_enabled"), "set_pseudolocalization_double_vowels_enabled", "is_pseudolocalization_double_vowels_enabled"); diff --git a/core/string/translation_domain.h b/core/string/translation_domain.h index 0b62352dede..69477128add 100644 --- a/core/string/translation_domain.h +++ b/core/string/translation_domain.h @@ -49,6 +49,9 @@ class TranslationDomain : public RefCounted { String suffix = "]"; }; + bool enabled = true; + + String locale_override; HashSet> translations; PseudolocalizationConfig pseudolocalization; @@ -79,6 +82,12 @@ public: StringName translate(const StringName &p_message, const StringName &p_context) const; StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const; + String get_locale_override() const; + void set_locale_override(const String &p_locale); + + bool is_enabled() const; + void set_enabled(bool p_enabled); + bool is_pseudolocalization_enabled() const; void set_pseudolocalization_enabled(bool p_enabled); bool is_pseudolocalization_accents_enabled() const; diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp index 41c51db4475..cc0351e7af0 100644 --- a/core/string/translation_server.cpp +++ b/core/string/translation_server.cpp @@ -406,21 +406,10 @@ void TranslationServer::clear() { } StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const { - if (!enabled) { - return p_message; - } - return main_domain->translate(p_message, p_context); } StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { - if (!enabled) { - if (p_n == 1) { - return p_message; - } - return p_message_plural; - } - return main_domain->translate_plural(p_message, p_message_plural, p_n, p_context); } diff --git a/core/string/translation_server.h b/core/string/translation_server.h index ea7cadd6f26..b7e7107e2ba 100644 --- a/core/string/translation_server.h +++ b/core/string/translation_server.h @@ -47,8 +47,6 @@ class TranslationServer : public Object { mutable HashMap locale_compare_cache; - bool enabled = true; - static inline TranslationServer *singleton = nullptr; static void _bind_methods(); @@ -96,11 +94,9 @@ class TranslationServer : public Object { public: _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; } + Ref get_main_domain() const { return main_domain; } Ref get_editor_domain() const { return editor_domain; } - void set_enabled(bool p_enabled) { enabled = p_enabled; } - _FORCE_INLINE_ bool is_enabled() const { return enabled; } - void set_locale(const String &p_locale); String get_locale() const; String get_fallback_locale() const; diff --git a/doc/classes/TranslationDomain.xml b/doc/classes/TranslationDomain.xml index 5045f86260c..3f5873b2a1d 100644 --- a/doc/classes/TranslationDomain.xml +++ b/doc/classes/TranslationDomain.xml @@ -23,6 +23,12 @@ Removes all translations. + + + + Returns the locale override of the domain. Returns an empty string if locale override is disabled. + + @@ -44,6 +50,15 @@ Removes the given translation. + + + + + Sets the locale override of the domain. + If [param locale] is an empty string, locale override is disabled. Otherwise, [param locale] will be standardized to match known locales (e.g. [code]en-US[/code] would be matched to [code]en_US[/code]). + [b]Note:[/b] Calling this method does not automatically update texts in the scene tree. Please propagate the [constant MainLoop.NOTIFICATION_TRANSLATION_CHANGED] signal manually. + + @@ -65,6 +80,9 @@ + + If [code]true[/code], translation is enabled. Otherwise, [method translate] and [method translate_plural] will return the input message unchanged regardless of the current locale. + Replace all characters with their accented variants during pseudolocalization. [b]Note:[/b] Updating this property does not automatically update texts in the scene tree. Please propagate the [constant MainLoop.NOTIFICATION_TRANSLATION_CHANGED] notification manually after you have finished modifying pseudolocalization related options. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 33c36734a2e..18a3645bab0 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -509,6 +509,8 @@ void EditorNode::_update_from_settings() { ResourceImporterTexture::get_singleton()->update_imports(); + _update_translations(); + #ifdef DEBUG_ENABLED NavigationServer2D::get_singleton()->set_debug_navigation_edge_connection_color(GLOBAL_GET("debug/shapes/navigation/2d/edge_connection_color")); NavigationServer2D::get_singleton()->set_debug_navigation_geometry_edge_color(GLOBAL_GET("debug/shapes/navigation/2d/geometry_edge_color")); @@ -544,6 +546,23 @@ void EditorNode::_gdextensions_reloaded() { EditorHelp::generate_doc(true, false); } +void EditorNode::_update_translations() { + Ref main = TranslationServer::get_singleton()->get_main_domain(); + + main->clear(); + TranslationServer::get_singleton()->load_translations(); + + if (main->is_enabled() && !main->get_loaded_locales().has(main->get_locale_override())) { + // Translations for the current preview locale is removed. + main->set_enabled(false); + main->set_locale_override(String()); + scene_root->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + emit_signal(SNAME("preview_locale_changed")); + } else { + scene_root->propagate_notification(NOTIFICATION_TRANSLATION_CHANGED); + } +} + void EditorNode::_update_theme(bool p_skip_creation) { if (!p_skip_creation) { theme = EditorThemeManager::generate_theme(theme); @@ -4025,6 +4044,33 @@ void EditorNode::set_edited_scene_root(Node *p_scene, bool p_auto_add) { } } +String EditorNode::get_preview_locale() const { + const Ref &main_domain = TranslationServer::get_singleton()->get_main_domain(); + return main_domain->is_enabled() ? main_domain->get_locale_override() : String(); +} + +void EditorNode::set_preview_locale(const String &p_locale) { + const String &prev_locale = get_preview_locale(); + if (prev_locale == p_locale) { + return; + } + + // Texts set in the editor could be identifiers that should never be translated. + // So we need to disable translation entirely. + Ref main_domain = TranslationServer::get_singleton()->get_main_domain(); + main_domain->set_enabled(!p_locale.is_empty()); + main_domain->set_locale_override(p_locale); + + if (prev_locale.is_empty() == p_locale.is_empty()) { + // Switching between different locales. + scene_root->propagate_notification(NOTIFICATION_TRANSLATION_CHANGED); + } else { + // Switching between on/off. + scene_root->set_auto_translate_mode(p_locale.is_empty() ? AUTO_TRANSLATE_MODE_DISABLED : AUTO_TRANSLATE_MODE_ALWAYS); + } + emit_signal(SNAME("preview_locale_changed")); +} + Dictionary EditorNode::_get_main_scene_state() { Dictionary state; state["scene_tree_offset"] = SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value(); @@ -7060,6 +7106,7 @@ void EditorNode::_bind_methods() { ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "path"))); ADD_SIGNAL(MethodInfo("scene_changed")); ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "path"))); + ADD_SIGNAL(MethodInfo("preview_locale_changed")); } static Node *_resource_get_edited_scene() { @@ -7354,7 +7401,7 @@ EditorNode::EditorNode() { ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorNode::_update_from_settings)); GDExtensionManager::get_singleton()->connect("extensions_reloaded", callable_mp(this, &EditorNode::_gdextensions_reloaded)); - TranslationServer::get_singleton()->set_enabled(false); + TranslationServer::get_singleton()->get_main_domain()->set_enabled(false); // Load settings. if (!EditorSettings::get_singleton()) { EditorSettings::create(); @@ -7767,6 +7814,8 @@ EditorNode::EditorNode() { editor_main_screen->set_v_size_flags(Control::SIZE_EXPAND_FILL); scene_root = memnew(SubViewport); + scene_root->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + scene_root->set_translation_domain(StringName()); scene_root->set_embedding_subwindows(true); scene_root->set_disable_3d(true); scene_root->set_disable_input(true); diff --git a/editor/editor_node.h b/editor/editor_node.h index 7d7826385d6..904f3c1a3ae 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -615,6 +615,7 @@ private: void _update_vsync_mode(); void _update_from_settings(); void _gdextensions_reloaded(); + void _update_translations(); void _renderer_selected(int); void _update_renderer_color(); @@ -819,6 +820,9 @@ public: void set_edited_scene_root(Node *p_scene, bool p_auto_add); Node *get_edited_scene() { return editor_data.get_edited_scene_root(); } + String get_preview_locale() const; + void set_preview_locale(const String &p_locale); + void fix_dependencies(const String &p_for_file); int new_scene(); Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_force_open_imported = false, bool p_silent_change_tab = false); diff --git a/editor/gui/editor_translation_preview_button.cpp b/editor/gui/editor_translation_preview_button.cpp new file mode 100644 index 00000000000..4ab62174511 --- /dev/null +++ b/editor/gui/editor_translation_preview_button.cpp @@ -0,0 +1,76 @@ +/**************************************************************************/ +/* editor_translation_preview_button.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "editor_translation_preview_button.h" + +#include "core/string/translation_server.h" +#include "editor/editor_node.h" + +void EditorTranslationPreviewButton::_update() { + const String &locale = EditorNode::get_singleton()->get_preview_locale(); + + if (locale.is_empty()) { + hide(); + return; + } + + const String name = TranslationServer::get_singleton()->get_locale_name(locale); + set_text(vformat(TTR("Previewing: %s"), name == locale ? locale : name + " [" + locale + "]")); + show(); +} + +void EditorTranslationPreviewButton::pressed() { + EditorNode::get_singleton()->set_preview_locale(String()); +} + +void EditorTranslationPreviewButton::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + set_button_icon(get_editor_theme_icon(SNAME("Translation"))); + } break; + + case NOTIFICATION_TRANSLATION_CHANGED: { + _update(); + } break; + + case NOTIFICATION_READY: { + EditorNode::get_singleton()->connect("preview_locale_changed", callable_mp(this, &EditorTranslationPreviewButton::_update)); + } break; + } +} + +EditorTranslationPreviewButton::EditorTranslationPreviewButton() { + set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + set_tooltip_auto_translate_mode(AUTO_TRANSLATE_MODE_ALWAYS); + set_accessibility_name(TTRC("Disable Translation Preview")); + set_tooltip_text(TTRC("Previewing translation. Click to disable.")); + set_focus_mode(FOCUS_NONE); + set_visible(false); +} diff --git a/editor/gui/editor_translation_preview_button.h b/editor/gui/editor_translation_preview_button.h new file mode 100644 index 00000000000..33439115ee5 --- /dev/null +++ b/editor/gui/editor_translation_preview_button.h @@ -0,0 +1,47 @@ +/**************************************************************************/ +/* editor_translation_preview_button.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include "scene/gui/button.h" + +class EditorTranslationPreviewButton : public Button { + GDCLASS(EditorTranslationPreviewButton, Button); + + void _update(); + +protected: + virtual void pressed() override; + + void _notification(int p_what); + +public: + EditorTranslationPreviewButton(); +}; diff --git a/editor/gui/editor_translation_preview_menu.cpp b/editor/gui/editor_translation_preview_menu.cpp new file mode 100644 index 00000000000..3fa67d005cb --- /dev/null +++ b/editor/gui/editor_translation_preview_menu.cpp @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* editor_translation_preview_menu.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "editor/gui/editor_translation_preview_menu.h" + +#include "core/string/translation_server.h" +#include "editor/editor_node.h" + +void EditorTranslationPreviewMenu::_prepare() { + const String current_preview_locale = EditorNode::get_singleton()->get_preview_locale(); + + clear(); + reset_size(); + + add_radio_check_item(TTRC("None")); + set_item_metadata(-1, ""); + if (current_preview_locale.is_empty()) { + set_item_checked(-1, true); + } + + const Vector locales = TranslationServer::get_singleton()->get_loaded_locales(); + if (!locales.is_empty()) { + add_separator(); + } + for (const String &locale : locales) { + const String name = TranslationServer::get_singleton()->get_locale_name(locale); + add_radio_check_item(name == locale ? name : name + " [" + locale + "]"); + set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_DISABLED); + set_item_metadata(-1, locale); + if (locale == current_preview_locale) { + set_item_checked(-1, true); + } + } +} + +void EditorTranslationPreviewMenu::_pressed(int p_index) { + for (int i = 0; i < get_item_count(); i++) { + set_item_checked(i, i == p_index); + } + EditorNode::get_singleton()->set_preview_locale(get_item_metadata(p_index)); +} + +void EditorTranslationPreviewMenu::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + connect("about_to_popup", callable_mp(this, &EditorTranslationPreviewMenu::_prepare)); + connect("index_pressed", callable_mp(this, &EditorTranslationPreviewMenu::_pressed)); + } break; + } +} + +EditorTranslationPreviewMenu::EditorTranslationPreviewMenu() { + set_hide_on_checkable_item_selection(false); +} diff --git a/editor/gui/editor_translation_preview_menu.h b/editor/gui/editor_translation_preview_menu.h new file mode 100644 index 00000000000..838e78aad10 --- /dev/null +++ b/editor/gui/editor_translation_preview_menu.h @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* editor_translation_preview_menu.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include "scene/gui/popup_menu.h" + +class EditorTranslationPreviewMenu : public PopupMenu { + GDCLASS(EditorTranslationPreviewMenu, PopupMenu); + + void _prepare(); + void _pressed(int p_index); + +protected: + void _notification(int p_what); + +public: + EditorTranslationPreviewMenu(); +}; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index df016167837..33aa76fa945 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -41,6 +41,8 @@ #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_run_bar.h" #include "editor/gui/editor_toaster.h" +#include "editor/gui/editor_translation_preview_button.h" +#include "editor/gui/editor_translation_preview_menu.h" #include "editor/gui/editor_zoom_widget.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/editor_context_menu_plugin.h" @@ -5395,6 +5397,13 @@ CanvasItemEditor::CanvasItemEditor() { controls_hb->add_child(zoom_widget); zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom)); + EditorTranslationPreviewButton *translation_preview_button = memnew(EditorTranslationPreviewButton); + translation_preview_button->set_flat(true); + translation_preview_button->add_theme_constant_override("outline_size", Math::ceil(2 * EDSCALE)); + translation_preview_button->add_theme_color_override("font_outline_color", Color(0, 0, 0)); + translation_preview_button->add_theme_color_override(SceneStringName(font_color), Color(1, 1, 1)); + controls_hb->add_child(translation_preview_button); + panner.instantiate(); panner->set_callbacks(callable_mp(this, &CanvasItemEditor::_pan_callback), callable_mp(this, &CanvasItemEditor::_zoom_callback)); @@ -5675,6 +5684,8 @@ CanvasItemEditor::CanvasItemEditor() { theme_menu->set_item_checked(i, i == theme_preview); } + p->add_submenu_node_item(TTRC("Preview Translation"), memnew(EditorTranslationPreviewMenu)); + main_menu_hbox->add_child(memnew(VSeparator)); // Contextual toolbars. diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 1132ecbe632..12afa4de4c2 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -44,6 +44,8 @@ #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_run_bar.h" #include "editor/gui/editor_spin_slider.h" +#include "editor/gui/editor_translation_preview_button.h" +#include "editor/gui/editor_translation_preview_menu.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/gizmos/audio_listener_3d_gizmo_plugin.h" #include "editor/plugins/gizmos/audio_stream_player_3d_gizmo_plugin.h" @@ -2908,6 +2910,23 @@ void Node3DEditorViewport::_project_settings_changed() { viewport->set_anisotropic_filtering_level(anisotropic_filtering_level); } +static void override_button_stylebox(Button *p_button, const Ref p_stylebox) { + p_button->begin_bulk_theme_override(); + p_button->add_theme_style_override(CoreStringName(normal), p_stylebox); + p_button->add_theme_style_override("normal_mirrored", p_stylebox); + p_button->add_theme_style_override(SceneStringName(hover), p_stylebox); + p_button->add_theme_style_override("hover_mirrored", p_stylebox); + p_button->add_theme_style_override("hover_pressed", p_stylebox); + p_button->add_theme_style_override("hover_pressed_mirrored", p_stylebox); + p_button->add_theme_style_override(SceneStringName(pressed), p_stylebox); + p_button->add_theme_style_override("pressed_mirrored", p_stylebox); + p_button->add_theme_style_override("focus", p_stylebox); + p_button->add_theme_style_override("focus_mirrored", p_stylebox); + p_button->add_theme_style_override("disabled", p_stylebox); + p_button->add_theme_style_override("disabled_mirrored", p_stylebox); + p_button->end_bulk_theme_override(); +} + void Node3DEditorViewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { @@ -3250,35 +3269,9 @@ void Node3DEditorViewport::_notification(int p_what) { const Ref &information_3d_stylebox = gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)); - view_display_menu->begin_bulk_theme_override(); - view_display_menu->add_theme_style_override(CoreStringName(normal), information_3d_stylebox); - view_display_menu->add_theme_style_override("normal_mirrored", information_3d_stylebox); - view_display_menu->add_theme_style_override(SceneStringName(hover), information_3d_stylebox); - view_display_menu->add_theme_style_override("hover_mirrored", information_3d_stylebox); - view_display_menu->add_theme_style_override("hover_pressed", information_3d_stylebox); - view_display_menu->add_theme_style_override("hover_pressed_mirrored", information_3d_stylebox); - view_display_menu->add_theme_style_override(SceneStringName(pressed), information_3d_stylebox); - view_display_menu->add_theme_style_override("pressed_mirrored", information_3d_stylebox); - view_display_menu->add_theme_style_override("focus", information_3d_stylebox); - view_display_menu->add_theme_style_override("focus_mirrored", information_3d_stylebox); - view_display_menu->add_theme_style_override("disabled", information_3d_stylebox); - view_display_menu->add_theme_style_override("disabled_mirrored", information_3d_stylebox); - view_display_menu->end_bulk_theme_override(); - - preview_camera->begin_bulk_theme_override(); - preview_camera->add_theme_style_override(CoreStringName(normal), information_3d_stylebox); - preview_camera->add_theme_style_override("normal_mirrored", information_3d_stylebox); - preview_camera->add_theme_style_override(SceneStringName(hover), information_3d_stylebox); - preview_camera->add_theme_style_override("hover_mirrored", information_3d_stylebox); - preview_camera->add_theme_style_override("hover_pressed", information_3d_stylebox); - preview_camera->add_theme_style_override("hover_pressed_mirrored", information_3d_stylebox); - preview_camera->add_theme_style_override(SceneStringName(pressed), information_3d_stylebox); - preview_camera->add_theme_style_override("pressed_mirrored", information_3d_stylebox); - preview_camera->add_theme_style_override("focus", information_3d_stylebox); - preview_camera->add_theme_style_override("focus_mirrored", information_3d_stylebox); - preview_camera->add_theme_style_override("disabled", information_3d_stylebox); - preview_camera->add_theme_style_override("disabled_mirrored", information_3d_stylebox); - preview_camera->end_bulk_theme_override(); + override_button_stylebox(view_display_menu, information_3d_stylebox); + override_button_stylebox(translation_preview_button, information_3d_stylebox); + override_button_stylebox(preview_camera, information_3d_stylebox); frame_time_gradient->set_color(0, get_theme_color(SNAME("success_color"), EditorStringName(Editor))); frame_time_gradient->set_color(1, get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); @@ -5597,12 +5590,15 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p vbox->set_offset(SIDE_LEFT, 10 * EDSCALE); vbox->set_offset(SIDE_TOP, 10 * EDSCALE); + HBoxContainer *hbox = memnew(HBoxContainer); + vbox->add_child(hbox); + view_display_menu = memnew(MenuButton); view_display_menu->set_flat(false); view_display_menu->set_h_size_flags(0); view_display_menu->set_shortcut_context(this); view_display_menu->set_accessibility_name(TTRC("View")); - vbox->add_child(view_display_menu); + hbox->add_child(view_display_menu); view_display_menu->get_popup()->set_hide_on_checkable_item_selection(false); @@ -5745,6 +5741,9 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p ED_SHORTCUT("spatial_editor/instant_scale", TTRC("Begin Scale Transformation")); ED_SHORTCUT("spatial_editor/collision_reposition", TTRC("Reposition Using Collisions"), KeyModifierMask::SHIFT | Key::G); + translation_preview_button = memnew(EditorTranslationPreviewButton); + hbox->add_child(translation_preview_button); + preview_camera = memnew(CheckBox); preview_camera->set_text(TTR("Preview")); // Using Control even on macOS to avoid conflict with Quick Open shortcut. @@ -9357,6 +9356,9 @@ Node3DEditor::Node3DEditor() { p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTRC("View Origin")), MENU_VIEW_ORIGIN); p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTRC("View Grid"), Key::NUMBERSIGN), MENU_VIEW_GRID); + p->add_separator(); + p->add_submenu_node_item(TTRC("Preview Translation"), memnew(EditorTranslationPreviewMenu)); + p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTRC("Settings...")), MENU_VIEW_CAMERA_SETTINGS); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index dc193eda8b7..f3205d03563 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -243,6 +243,7 @@ private: EditorSelection *editor_selection = nullptr; + Button *translation_preview_button = nullptr; CheckBox *preview_camera = nullptr; SubViewportContainer *subviewport_container = nullptr; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index c6d91b26099..8ea38492270 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -197,13 +197,6 @@ void Node::_notification(int p_notification) { data.is_auto_translate_dirty = true; data.is_translation_domain_dirty = true; -#ifdef TOOLS_ENABLED - // Don't translate UI elements when they're being edited. - if (is_part_of_edited_scene()) { - set_message_translation(false); - } -#endif - if (data.input) { add_to_group("_vp_input" + itos(get_viewport()->get_instance_id())); }