diff options
Diffstat (limited to 'scene')
41 files changed, 792 insertions, 517 deletions
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 85de1fedee..a79c81e8bd 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -612,6 +612,10 @@ void CollisionObject2D::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject2D::shape_find_owner); GDVIRTUAL_BIND(_input_event, "viewport", "event", "shape_idx"); + GDVIRTUAL_BIND(_mouse_enter); + GDVIRTUAL_BIND(_mouse_exit); + GDVIRTUAL_BIND(_mouse_shape_enter, "shape_idx"); + GDVIRTUAL_BIND(_mouse_shape_exit, "shape_idx"); ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "viewport", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index af216edc98..48ea59e040 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -103,6 +103,10 @@ protected: void set_body_mode(PhysicsServer2D::BodyMode p_mode); GDVIRTUAL3(_input_event, Viewport *, Ref<InputEvent>, int) + GDVIRTUAL0(_mouse_enter) + GDVIRTUAL0(_mouse_exit) + GDVIRTUAL1(_mouse_shape_enter, int) + GDVIRTUAL1(_mouse_shape_exit, int) public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 48eb2a66b1..f5e3e8b015 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -469,6 +469,8 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject3D::shape_find_owner); GDVIRTUAL_BIND(_input_event, "camera", "event", "position", "normal", "shape_idx"); + GDVIRTUAL_BIND(_mouse_enter); + GDVIRTUAL_BIND(_mouse_exit); ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "position"), PropertyInfo(Variant::VECTOR3, "normal"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("mouse_entered")); diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 51c31da79f..c638be9d90 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -113,6 +113,8 @@ protected: bool is_only_update_transform_changes_enabled() const; GDVIRTUAL5(_input_event, Camera3D *, Ref<InputEvent>, Vector3, Vector3, int) + GDVIRTUAL0(_mouse_enter) + GDVIRTUAL0(_mouse_exit) public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 40b8af7d63..e4a7cf6ee5 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -34,6 +34,7 @@ #include "scene/main/viewport.h" #include "scene/resources/theme.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" void Label3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label3D::set_horizontal_alignment); @@ -734,13 +735,13 @@ Ref<Font> Label3D::_get_font_or_default() const { } // Check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { List<StringName> theme_types; - Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); @@ -753,11 +754,11 @@ Ref<Font> Label3D::_get_font_or_default() const { // Lastly, fall back on the items defined in the default Theme, if they exist. { List<StringName> theme_types; - Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); @@ -768,7 +769,7 @@ Ref<Font> Label3D::_get_font_or_default() const { } // If they don't exist, use any type to return the default/empty value. - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, Callable(const_cast<Label3D *>(this), "_font_changed")); diff --git a/scene/SCsub b/scene/SCsub index 92288211bb..b4b2d6dd0a 100644 --- a/scene/SCsub +++ b/scene/SCsub @@ -17,6 +17,7 @@ SConscript("animation/SCsub") SConscript("audio/SCsub") SConscript("resources/SCsub") SConscript("debugger/SCsub") +SConscript("theme/SCsub") # Build it all as a library lib = env.add_library("scene", env.scene_sources) diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 0e2598cbc7..073f61bfdd 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1501,7 +1501,7 @@ bool AnimationPlayer::has_animation(const StringName &p_name) const { } Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const { - ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref<Animation>(), vformat("Animation not found: %s.", p_name)); + ERR_FAIL_COND_V_MSG(!animation_set.has(p_name), Ref<Animation>(), vformat("Animation not found: \"%s\".", p_name)); const AnimationData &data = animation_set[p_name]; diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 87a7355bb2..cee7c049a9 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -65,8 +65,9 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) { bool button_masked = mouse_button.is_valid() && (mouse_button_to_mask(mouse_button->get_button_index()) & button_mask) != MouseButton::NONE; if (button_masked || ui_accept) { was_mouse_pressed = button_masked; - on_action_event(p_event); + was_mouse_pressed = false; + return; } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index b9760499ef..e54ba7ce13 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -36,7 +36,8 @@ void CodeEdit::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: { + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_ENTER_TREE: { style_normal = get_theme_stylebox(SNAME("normal")); font = get_theme_font(SNAME("font")); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 93aafba886..a417e7b9e2 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -63,8 +63,8 @@ void ColorPicker::_notification(int p_what) { } } #endif - } break; - + [[fallthrough]]; + } case NOTIFICATION_THEME_CHANGED: { btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker"))); btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset"))); @@ -536,7 +536,7 @@ void ColorPicker::_add_preset_button(int p_size, const Color &p_color) { btn_preset->set_preset_color(p_color); btn_preset->set_custom_minimum_size(Size2(p_size, p_size)); btn_preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input).bind(p_color)); - btn_preset->set_tooltip(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1))); + btn_preset->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1))); preset_container->add_child(btn_preset); } @@ -1236,7 +1236,7 @@ ColorPicker::ColorPicker() : btn_pick->set_flat(true); hb_smpl->add_child(btn_pick); btn_pick->set_toggle_mode(true); - btn_pick->set_tooltip(RTR("Pick a color from the editor window.")); + btn_pick->set_tooltip_text(RTR("Pick a color from the editor window.")); btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed)); VBoxContainer *vbl = memnew(VBoxContainer); @@ -1276,7 +1276,7 @@ ColorPicker::ColorPicker() : text_type = memnew(Button); hhb->add_child(text_type); text_type->set_text("#"); - text_type->set_tooltip(RTR("Switch between hexadecimal and code values.")); + text_type->set_tooltip_text(RTR("Switch between hexadecimal and code values.")); if (Engine::get_singleton()->is_editor_hint()) { text_type->connect("pressed", callable_mp(this, &ColorPicker::_text_type_toggled)); } else { @@ -1337,7 +1337,7 @@ ColorPicker::ColorPicker() : btn_add_preset = memnew(Button); btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER); - btn_add_preset->set_tooltip(RTR("Add current color as a preset.")); + btn_add_preset->set_tooltip_text(RTR("Add current color as a preset.")); btn_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed)); preset_container->add_child(btn_add_preset); } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 9b6a19c50a..4de16a0a29 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -43,6 +43,7 @@ #include "scene/main/canvas_layer.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" #include "servers/rendering_server.h" #include "servers/text_server.h" @@ -193,15 +194,15 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List List<StringName> sn; String pf = p_function; if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") { - Theme::get_default()->get_color_list(get_class(), &sn); + ThemeDB::get_singleton()->get_default_theme()->get_color_list(get_class(), &sn); } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") { - Theme::get_default()->get_stylebox_list(get_class(), &sn); + ThemeDB::get_singleton()->get_default_theme()->get_stylebox_list(get_class(), &sn); } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") { - Theme::get_default()->get_font_list(get_class(), &sn); + ThemeDB::get_singleton()->get_default_theme()->get_font_list(get_class(), &sn); } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") { - Theme::get_default()->get_font_size_list(get_class(), &sn); + ThemeDB::get_singleton()->get_default_theme()->get_font_size_list(get_class(), &sn); } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") { - Theme::get_default()->get_constant_list(get_class(), &sn); + ThemeDB::get_singleton()->get_default_theme()->get_constant_list(get_class(), &sn); } sn.sort_custom<StringName::AlphCompare>(); @@ -344,7 +345,7 @@ bool Control::_get(const StringName &p_name, Variant &r_ret) const { } void Control::_get_property_list(List<PropertyInfo> *p_list) const { - Ref<Theme> theme = Theme::get_default(); + Ref<Theme> theme = ThemeDB::get_singleton()->get_default_theme(); p_list->push_back(PropertyInfo(Variant::NIL, TTRC("Theme Overrides"), PROPERTY_HINT_NONE, "theme_override_", PROPERTY_USAGE_GROUP)); @@ -429,9 +430,9 @@ void Control::_validate_property(PropertyInfo &p_property) const { // Only the default theme and the project theme are used for the list of options. // This is an imposed limitation to simplify the logic needed to leverage those options. - Theme::get_default()->get_type_variation_list(get_class_name(), &names); - if (Theme::get_project_default().is_valid()) { - Theme::get_project_default()->get_type_variation_list(get_class_name(), &names); + ThemeDB::get_singleton()->get_default_theme()->get_type_variation_list(get_class_name(), &names); + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + ThemeDB::get_singleton()->get_project_theme()->get_type_variation_list(get_class_name(), &names); } names.sort_custom<StringName::AlphCompare>(); @@ -2419,22 +2420,22 @@ T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner } // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { for (const StringName &E : p_theme_types) { - if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) { - return Theme::get_project_default()->get_theme_item(p_data_type, p_name, E); + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(p_data_type, p_name, E)) { + return ThemeDB::get_singleton()->get_project_theme()->get_theme_item(p_data_type, p_name, E); } } } // Lastly, fall back on the items defined in the default Theme, if they exist. for (const StringName &E : p_theme_types) { - if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) { - return Theme::get_default()->get_theme_item(p_data_type, p_name, E); + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(p_data_type, p_name, E)) { + return ThemeDB::get_singleton()->get_default_theme()->get_theme_item(p_data_type, p_name, E); } } // If they don't exist, use any type to return the default/empty value. - return Theme::get_default()->get_theme_item(p_data_type, p_name, p_theme_types[0]); + return ThemeDB::get_singleton()->get_default_theme()->get_theme_item(p_data_type, p_name, p_theme_types[0]); } bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner_window, Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { @@ -2475,9 +2476,9 @@ bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_ow } // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { for (const StringName &E : p_theme_types) { - if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E)) { + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(p_data_type, p_name, E)) { return true; } } @@ -2485,7 +2486,7 @@ bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_ow // Lastly, fall back on the items defined in the default Theme, if they exist. for (const StringName &E : p_theme_types) { - if (Theme::get_default()->has_theme_item(p_data_type, p_name, E)) { + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(p_data_type, p_name, E)) { return true; } } @@ -2494,13 +2495,13 @@ bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_ow void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const { if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { - if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(data.theme_type_variation) != StringName()) { - Theme::get_project_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list); + if (ThemeDB::get_singleton()->get_project_theme().is_valid() && ThemeDB::get_singleton()->get_project_theme()->get_type_variation_base(data.theme_type_variation) != StringName()) { + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list); } else { - Theme::get_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list); } } else { - Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(p_theme_type, StringName(), p_list); } } @@ -2851,17 +2852,17 @@ float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_ } // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_default_base_scale()) { - return Theme::get_project_default()->get_default_base_scale(); + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme()->has_default_base_scale()) { + return ThemeDB::get_singleton()->get_project_theme()->get_default_base_scale(); } } // Lastly, fall back on the default Theme. - if (Theme::get_default()->has_default_base_scale()) { - return Theme::get_default()->get_default_base_scale(); + if (ThemeDB::get_singleton()->get_default_theme()->has_default_base_scale()) { + return ThemeDB::get_singleton()->get_default_theme()->get_default_base_scale(); } - return Theme::get_fallback_base_scale(); + return ThemeDB::get_singleton()->get_fallback_base_scale(); } float Control::get_theme_default_base_scale() const { @@ -2902,17 +2903,17 @@ Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_th } // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_default_font()) { - return Theme::get_project_default()->get_default_font(); + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme()->has_default_font()) { + return ThemeDB::get_singleton()->get_project_theme()->get_default_font(); } } // Lastly, fall back on the default Theme. - if (Theme::get_default()->has_default_font()) { - return Theme::get_default()->get_default_font(); + if (ThemeDB::get_singleton()->get_default_theme()->has_default_font()) { + return ThemeDB::get_singleton()->get_default_theme()->get_default_font(); } - return Theme::get_fallback_font(); + return ThemeDB::get_singleton()->get_fallback_font(); } Ref<Font> Control::get_theme_default_font() const { @@ -2953,17 +2954,17 @@ int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_the } // Secondly, check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_default_font_size()) { - return Theme::get_project_default()->get_default_font_size(); + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme()->has_default_font_size()) { + return ThemeDB::get_singleton()->get_project_theme()->get_default_font_size(); } } // Lastly, fall back on the default Theme. - if (Theme::get_default()->has_default_font_size()) { - return Theme::get_default()->get_default_font_size(); + if (ThemeDB::get_singleton()->get_default_theme()->has_default_font_size()) { + return ThemeDB::get_singleton()->get_default_theme()->get_default_font_size(); } - return Theme::get_fallback_font_size(); + return ThemeDB::get_singleton()->get_fallback_font_size(); } int Control::get_theme_default_font_size() const { @@ -3062,12 +3063,12 @@ bool Control::is_auto_translating() const { // Extra properties. -void Control::set_tooltip(const String &p_tooltip) { - data.tooltip = p_tooltip; +void Control::set_tooltip_text(const String &p_hint) { + data.tooltip = p_hint; update_configuration_warnings(); } -String Control::_get_tooltip() const { +String Control::get_tooltip_text() const { return data.tooltip; } @@ -3383,9 +3384,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_grow_direction", "direction"), &Control::set_v_grow_direction); ClassDB::bind_method(D_METHOD("get_v_grow_direction"), &Control::get_v_grow_direction); - ClassDB::bind_method(D_METHOD("set_tooltip", "tooltip"), &Control::set_tooltip); + ClassDB::bind_method(D_METHOD("set_tooltip_text", "hint"), &Control::set_tooltip_text); + ClassDB::bind_method(D_METHOD("get_tooltip_text"), &Control::get_tooltip_text); ClassDB::bind_method(D_METHOD("get_tooltip", "at_position"), &Control::get_tooltip, DEFVAL(Point2())); - ClassDB::bind_method(D_METHOD("_get_tooltip"), &Control::_get_tooltip); ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Control::set_default_cursor_shape); ClassDB::bind_method(D_METHOD("get_default_cursor_shape"), &Control::get_default_cursor_shape); @@ -3475,8 +3476,8 @@ void Control::_bind_methods() { ADD_GROUP("Auto Translate", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); - ADD_GROUP("Hint", "hint_"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip"); + ADD_GROUP("Tooltip", "tooltip_"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text"); ADD_GROUP("Focus", "focus_"); ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_LEFT); diff --git a/scene/gui/control.h b/scene/gui/control.h index aa076370e5..6215594ae0 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -312,7 +312,7 @@ private: // Extra properties. - String _get_tooltip() const; + String get_tooltip_text() const; protected: // Dynamic properties. @@ -357,7 +357,6 @@ public: NOTIFICATION_MOUSE_EXIT = 42, NOTIFICATION_FOCUS_ENTER = 43, NOTIFICATION_FOCUS_EXIT = 44, - // This doesn't need to be paired with `NOTIFICATION_ENTER_TREE`. NOTIFICATION_THEME_CHANGED = 45, NOTIFICATION_SCROLL_BEGIN = 47, NOTIFICATION_SCROLL_END = 48, @@ -605,7 +604,7 @@ public: // Extra properties. - void set_tooltip(const String &p_tooltip); + void set_tooltip_text(const String &text); virtual String get_tooltip(const Point2 &p_pos) const; virtual Control *make_custom_tooltip(const String &p_text) const; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index df295a8049..57655afb4c 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -59,6 +59,38 @@ VBoxContainer *FileDialog::get_vbox() { return vbox; } +void FileDialog::_theme_changed() { + Color font_color = vbox->get_theme_color(SNAME("font_color"), SNAME("Button")); + Color font_hover_color = vbox->get_theme_color(SNAME("font_hover_color"), SNAME("Button")); + Color font_focus_color = vbox->get_theme_color(SNAME("font_focus_color"), SNAME("Button")); + Color font_pressed_color = vbox->get_theme_color(SNAME("font_pressed_color"), SNAME("Button")); + + dir_up->add_theme_color_override("icon_normal_color", font_color); + dir_up->add_theme_color_override("icon_hover_color", font_hover_color); + dir_up->add_theme_color_override("icon_focus_color", font_focus_color); + dir_up->add_theme_color_override("icon_pressed_color", font_pressed_color); + + dir_prev->add_theme_color_override("icon_color_normal", font_color); + dir_prev->add_theme_color_override("icon_color_hover", font_hover_color); + dir_prev->add_theme_color_override("icon_focus_color", font_focus_color); + dir_prev->add_theme_color_override("icon_color_pressed", font_pressed_color); + + dir_next->add_theme_color_override("icon_color_normal", font_color); + dir_next->add_theme_color_override("icon_color_hover", font_hover_color); + dir_next->add_theme_color_override("icon_focus_color", font_focus_color); + dir_next->add_theme_color_override("icon_color_pressed", font_pressed_color); + + refresh->add_theme_color_override("icon_normal_color", font_color); + refresh->add_theme_color_override("icon_hover_color", font_hover_color); + refresh->add_theme_color_override("icon_focus_color", font_focus_color); + refresh->add_theme_color_override("icon_pressed_color", font_pressed_color); + + show_hidden->add_theme_color_override("icon_normal_color", font_color); + show_hidden->add_theme_color_override("icon_hover_color", font_hover_color); + show_hidden->add_theme_color_override("icon_focus_color", font_focus_color); + show_hidden->add_theme_color_override("icon_pressed_color", font_pressed_color); +} + void FileDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -67,51 +99,22 @@ void FileDialog::_notification(int p_what) { } } break; - case NOTIFICATION_TRANSLATION_CHANGED: { - update_filters(); - } break; - - case NOTIFICATION_THEME_CHANGED: { - dir_up->set_icon(get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog"))); + case NOTIFICATION_ENTER_TREE: { + dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog"))); if (vbox->is_layout_rtl()) { - dir_prev->set_icon(get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); - dir_next->set_icon(get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); + dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); + dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); } else { - dir_prev->set_icon(get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); - dir_next->set_icon(get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); + dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); + dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); } - refresh->set_icon(get_theme_icon(SNAME("reload"), SNAME("FileDialog"))); - show_hidden->set_icon(get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog"))); - - Color font_color = get_theme_color(SNAME("font_color"), SNAME("Button")); - Color font_hover_color = get_theme_color(SNAME("font_hover_color"), SNAME("Button")); - Color font_focus_color = get_theme_color(SNAME("font_focus_color"), SNAME("Button")); - Color font_pressed_color = get_theme_color(SNAME("font_pressed_color"), SNAME("Button")); - - dir_up->add_theme_color_override("icon_normal_color", font_color); - dir_up->add_theme_color_override("icon_hover_color", font_hover_color); - dir_up->add_theme_color_override("icon_focus_color", font_focus_color); - dir_up->add_theme_color_override("icon_pressed_color", font_pressed_color); - - dir_prev->add_theme_color_override("icon_color_normal", font_color); - dir_prev->add_theme_color_override("icon_color_hover", font_hover_color); - dir_prev->add_theme_color_override("icon_focus_color", font_focus_color); - dir_prev->add_theme_color_override("icon_color_pressed", font_pressed_color); - - dir_next->add_theme_color_override("icon_color_normal", font_color); - dir_next->add_theme_color_override("icon_color_hover", font_hover_color); - dir_next->add_theme_color_override("icon_focus_color", font_focus_color); - dir_next->add_theme_color_override("icon_color_pressed", font_pressed_color); - - refresh->add_theme_color_override("icon_normal_color", font_color); - refresh->add_theme_color_override("icon_hover_color", font_hover_color); - refresh->add_theme_color_override("icon_focus_color", font_focus_color); - refresh->add_theme_color_override("icon_pressed_color", font_pressed_color); - - show_hidden->add_theme_color_override("icon_normal_color", font_color); - show_hidden->add_theme_color_override("icon_hover_color", font_hover_color); - show_hidden->add_theme_color_override("icon_focus_color", font_focus_color); - show_hidden->add_theme_color_override("icon_pressed_color", font_pressed_color); + refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog"))); + show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog"))); + _theme_changed(); + } break; + + case NOTIFICATION_TRANSLATION_CHANGED: { + update_filters(); } break; } } @@ -503,10 +506,10 @@ void FileDialog::update_file_list() { } TreeItem *root = tree->create_item(); - Ref<Texture2D> folder = get_theme_icon(SNAME("folder"), SNAME("FileDialog")); - Ref<Texture2D> file_icon = get_theme_icon(SNAME("file"), SNAME("FileDialog")); - const Color folder_color = get_theme_color(SNAME("folder_icon_modulate"), SNAME("FileDialog")); - const Color file_color = get_theme_color(SNAME("file_icon_modulate"), SNAME("FileDialog")); + Ref<Texture2D> folder = vbox->get_theme_icon(SNAME("folder"), SNAME("FileDialog")); + Ref<Texture2D> file_icon = vbox->get_theme_icon(SNAME("file"), SNAME("FileDialog")); + const Color folder_color = vbox->get_theme_color(SNAME("folder_icon_modulate"), SNAME("FileDialog")); + const Color file_color = vbox->get_theme_color(SNAME("file_icon_modulate"), SNAME("FileDialog")); List<String> files; List<String> dirs; @@ -603,7 +606,7 @@ void FileDialog::update_file_list() { ti->set_icon_modulate(0, file_color); if (mode == FILE_MODE_OPEN_DIR) { - ti->set_custom_color(0, get_theme_color(SNAME("files_disabled"), SNAME("FileDialog"))); + ti->set_custom_color(0, vbox->get_theme_color(SNAME("files_disabled"), SNAME("FileDialog"))); ti->set_selectable(0, false); } Dictionary d; @@ -1003,6 +1006,7 @@ FileDialog::FileDialog() { vbox = memnew(VBoxContainer); add_child(vbox, false, INTERNAL_MODE_FRONT); + vbox->connect("theme_changed", callable_mp(this, &FileDialog::_theme_changed)); mode = FILE_MODE_SAVE_FILE; set_title(TTRC("Save a File")); @@ -1011,13 +1015,13 @@ FileDialog::FileDialog() { dir_prev = memnew(Button); dir_prev->set_flat(true); - dir_prev->set_tooltip(RTR("Go to previous folder.")); + dir_prev->set_tooltip_text(RTR("Go to previous folder.")); dir_next = memnew(Button); dir_next->set_flat(true); - dir_next->set_tooltip(RTR("Go to next folder.")); + dir_next->set_tooltip_text(RTR("Go to next folder.")); dir_up = memnew(Button); dir_up->set_flat(true); - dir_up->set_tooltip(RTR("Go to parent folder.")); + dir_up->set_tooltip_text(RTR("Go to parent folder.")); hbc->add_child(dir_prev); hbc->add_child(dir_next); hbc->add_child(dir_up); @@ -1041,7 +1045,7 @@ FileDialog::FileDialog() { refresh = memnew(Button); refresh->set_flat(true); - refresh->set_tooltip(RTR("Refresh files.")); + refresh->set_tooltip_text(RTR("Refresh files.")); refresh->connect("pressed", callable_mp(this, &FileDialog::update_file_list)); hbc->add_child(refresh); @@ -1049,7 +1053,7 @@ FileDialog::FileDialog() { show_hidden->set_flat(true); show_hidden->set_toggle_mode(true); show_hidden->set_pressed(is_showing_hidden_files()); - show_hidden->set_tooltip(RTR("Toggle the visibility of hidden files.")); + show_hidden->set_tooltip_text(RTR("Toggle the visibility of hidden files.")); show_hidden->connect("toggled", callable_mp(this, &FileDialog::set_show_hidden_files)); hbc->add_child(show_hidden); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 4ea39691c1..4945094086 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -143,6 +143,8 @@ private: virtual void _post_popup() override; protected: + void _theme_changed(); + void _notification(int p_what); static void _bind_methods(); //bind helpers diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index be097f7543..cc27a6b7c2 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -292,8 +292,8 @@ void GradientEdit::_notification(int p_what) { if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) { picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed)); } - } break; - + [[fallthrough]]; + } case NOTIFICATION_THEME_CHANGED: { draw_spacing = BASE_SPACING * get_theme_default_base_scale(); draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale(); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 256d4b3d11..00f2e48601 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -424,6 +424,7 @@ void GraphEdit::remove_child_notify(Node *p_child) { void GraphEdit::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { port_hotzone_inner_extent = get_theme_constant("port_hotzone_inner_extent"); port_hotzone_outer_extent = get_theme_constant("port_hotzone_outer_extent"); @@ -2495,28 +2496,28 @@ GraphEdit::GraphEdit() { zoom_minus = memnew(Button); zoom_minus->set_flat(true); zoom_hb->add_child(zoom_minus); - zoom_minus->set_tooltip(RTR("Zoom Out")); + zoom_minus->set_tooltip_text(RTR("Zoom Out")); zoom_minus->connect("pressed", callable_mp(this, &GraphEdit::_zoom_minus)); zoom_minus->set_focus_mode(FOCUS_NONE); zoom_reset = memnew(Button); zoom_reset->set_flat(true); zoom_hb->add_child(zoom_reset); - zoom_reset->set_tooltip(RTR("Zoom Reset")); + zoom_reset->set_tooltip_text(RTR("Zoom Reset")); zoom_reset->connect("pressed", callable_mp(this, &GraphEdit::_zoom_reset)); zoom_reset->set_focus_mode(FOCUS_NONE); zoom_plus = memnew(Button); zoom_plus->set_flat(true); zoom_hb->add_child(zoom_plus); - zoom_plus->set_tooltip(RTR("Zoom In")); + zoom_plus->set_tooltip_text(RTR("Zoom In")); zoom_plus->connect("pressed", callable_mp(this, &GraphEdit::_zoom_plus)); zoom_plus->set_focus_mode(FOCUS_NONE); snap_button = memnew(Button); snap_button->set_flat(true); snap_button->set_toggle_mode(true); - snap_button->set_tooltip(RTR("Enable snap and show grid.")); + snap_button->set_tooltip_text(RTR("Enable snap and show grid.")); snap_button->connect("pressed", callable_mp(this, &GraphEdit::_snap_toggled)); snap_button->set_pressed(true); snap_button->set_focus_mode(FOCUS_NONE); @@ -2533,7 +2534,7 @@ GraphEdit::GraphEdit() { minimap_button = memnew(Button); minimap_button->set_flat(true); minimap_button->set_toggle_mode(true); - minimap_button->set_tooltip(RTR("Enable grid minimap.")); + minimap_button->set_tooltip_text(RTR("Enable grid minimap.")); minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled)); minimap_button->set_pressed(true); minimap_button->set_focus_mode(FOCUS_NONE); @@ -2542,7 +2543,7 @@ GraphEdit::GraphEdit() { layout_button = memnew(Button); layout_button->set_flat(true); zoom_hb->add_child(layout_button); - layout_button->set_tooltip(RTR("Arrange nodes.")); + layout_button->set_tooltip_text(RTR("Arrange nodes.")); layout_button->connect("pressed", callable_mp(this, &GraphEdit::arrange_nodes)); layout_button->set_focus_mode(FOCUS_NONE); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index b15578f222..fa7d4195cb 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -908,9 +908,10 @@ void Label::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + + ADD_GROUP("Displayed Text", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible"); - // Note: "visible_characters" and "visible_ratio" should be set after "text" to be correctly applied. ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 9eba2feaf7..9b7b67d83e 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -60,7 +60,7 @@ void MenuBar::gui_input(const Ref<InputEvent> &p_event) { if (active_menu >= 0) { get_menu_popup(active_menu)->hide(); } - _open_popup(selected_menu); + _open_popup(selected_menu, true); } return; } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { @@ -82,7 +82,7 @@ void MenuBar::gui_input(const Ref<InputEvent> &p_event) { if (active_menu >= 0) { get_menu_popup(active_menu)->hide(); } - _open_popup(selected_menu); + _open_popup(selected_menu, true); } return; } @@ -110,7 +110,7 @@ void MenuBar::gui_input(const Ref<InputEvent> &p_event) { } } -void MenuBar::_open_popup(int p_index) { +void MenuBar::_open_popup(int p_index, bool p_focus_item) { ERR_FAIL_INDEX(p_index, menu_cache.size()); PopupMenu *pm = get_menu_popup(p_index); @@ -134,6 +134,15 @@ void MenuBar::_open_popup(int p_index) { pm->set_parent_rect(Rect2(Point2(screen_pos - pm->get_position()), Size2(screen_size.x, screen_pos.y))); pm->popup(); + if (p_focus_item) { + for (int i = 0; i < pm->get_item_count(); i++) { + if (!pm->is_item_disabled(i)) { + pm->set_current_index(i); + break; + } + } + } + update(); } diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index 3c4a25fd06..b7d9933ab2 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -84,7 +84,7 @@ class MenuBar : public Control { Vector<PopupMenu *> _get_popups() const; int get_menu_idx_from_control(PopupMenu *p_child) const; - void _open_popup(int p_index); + void _open_popup(int p_index, bool p_focus_item = false); void _popup_visibility_changed(bool p_visible); void _update_submenu(const String &p_menu_name, PopupMenu *p_child); void _clear_menu(); diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index e6e17cc881..f779f87ae7 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -103,9 +103,14 @@ void MenuButton::pressed() { popup->set_position(gp); popup->set_parent_rect(Rect2(Point2(gp - popup->get_position()), size)); - // If not triggered by the mouse, start the popup with its first item selected. - if (popup->get_item_count() > 0 && !_was_pressed_by_mouse()) { - popup->set_current_index(0); + // If not triggered by the mouse, start the popup with its first enabled item focused. + if (!_was_pressed_by_mouse()) { + for (int i = 0; i < popup->get_item_count(); i++) { + if (!popup->is_item_disabled(i)) { + popup->set_current_index(i); + break; + } + } } popup->popup(); @@ -161,7 +166,10 @@ void MenuButton::_notification(int p_what) { if (menu_btn_other && menu_btn_other != this && menu_btn_other->is_switch_on_hover() && !menu_btn_other->is_disabled() && (get_parent()->is_ancestor_of(menu_btn_other) || menu_btn_other->get_parent()->is_ancestor_of(popup))) { popup->hide(); + menu_btn_other->pressed(); + // As the popup wasn't triggered by a mouse click, the item focus needs to be removed manually. + menu_btn_other->get_popup()->set_current_index(-1); } } break; } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 881acdbf3a..a87c46e8ac 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -207,12 +207,24 @@ void OptionButton::pressed() { popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); popup->set_size(Size2(size.width, 0)); - // If not triggered by the mouse, start the popup with the checked item selected. - if (popup->get_item_count() > 0) { + // If not triggered by the mouse, start the popup with the checked item (or the first enabled one) focused. + if (current != NONE_SELECTED && !popup->is_item_disabled(current)) { if (!_was_pressed_by_mouse()) { - popup->set_current_index(current > -1 ? current : 0); + popup->set_current_index(current); } else { - popup->scroll_to_item(current > -1 ? current : 0); + popup->scroll_to_item(current); + } + } else { + for (int i = 0; i < popup->get_item_count(); i++) { + if (!popup->is_item_disabled(i)) { + if (!_was_pressed_by_mouse()) { + popup->set_current_index(i); + } else { + popup->scroll_to_item(i); + } + + break; + } } } diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 7461f48a6c..c4396f636a 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -240,7 +240,8 @@ void PopupPanel::_notification(int p_what) { panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); } break; - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_READY: { panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); _update_child_rects(); } break; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 4e2aec0278..c8bb9fd530 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -216,9 +216,14 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) { submenu_pum->activated_by_keyboard = p_by_keyboard; - // If not triggered by the mouse, start the popup with its first item selected. - if (submenu_pum->get_item_count() > 0 && p_by_keyboard) { - submenu_pum->set_current_index(0); + // If not triggered by the mouse, start the popup with its first enabled item focused. + if (p_by_keyboard) { + for (int i = 0; i < submenu_pum->get_item_count(); i++) { + if (!submenu_pum->is_item_disabled(i)) { + submenu_pum->set_current_index(i); + break; + } + } } submenu_pum->popup(); @@ -278,102 +283,104 @@ void PopupMenu::_submenu_timeout() { void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - if (p_event->is_action("ui_down") && p_event->is_pressed()) { - int search_from = mouse_over + 1; - if (search_from >= items.size()) { - search_from = 0; - } - - bool match_found = false; - for (int i = search_from; i < items.size(); i++) { - if (!items[i].separator && !items[i].disabled) { - mouse_over = i; - emit_signal(SNAME("id_focused"), i); - scroll_to_item(i); - control->update(); - set_input_as_handled(); - match_found = true; - break; + if (!items.is_empty()) { + if (p_event->is_action("ui_down") && p_event->is_pressed()) { + int search_from = mouse_over + 1; + if (search_from >= items.size()) { + search_from = 0; } - } - if (!match_found) { - // If the last item is not selectable, try re-searching from the start. - for (int i = 0; i < search_from; i++) { + bool match_found = false; + for (int i = search_from; i < items.size(); i++) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; emit_signal(SNAME("id_focused"), i); scroll_to_item(i); control->update(); set_input_as_handled(); + match_found = true; break; } } - } - } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { - int search_from = mouse_over - 1; - if (search_from < 0) { - search_from = items.size() - 1; - } - bool match_found = false; - for (int i = search_from; i >= 0; i--) { - if (!items[i].separator && !items[i].disabled) { - mouse_over = i; - emit_signal(SNAME("id_focused"), i); - scroll_to_item(i); - control->update(); - set_input_as_handled(); - match_found = true; - break; + if (!match_found) { + // If the last item is not selectable, try re-searching from the start. + for (int i = 0; i < search_from; i++) { + if (!items[i].separator && !items[i].disabled) { + mouse_over = i; + emit_signal(SNAME("id_focused"), i); + scroll_to_item(i); + control->update(); + set_input_as_handled(); + break; + } + } + } + } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { + int search_from = mouse_over - 1; + if (search_from < 0) { + search_from = items.size() - 1; } - } - if (!match_found) { - // If the first item is not selectable, try re-searching from the end. - for (int i = items.size() - 1; i >= search_from; i--) { + bool match_found = false; + for (int i = search_from; i >= 0; i--) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; emit_signal(SNAME("id_focused"), i); scroll_to_item(i); control->update(); set_input_as_handled(); + match_found = true; break; } } - } - } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { - Node *n = get_parent(); - if (n) { - if (Object::cast_to<PopupMenu>(n)) { - hide(); - set_input_as_handled(); - } else if (Object::cast_to<MenuBar>(n)) { - Object::cast_to<MenuBar>(n)->gui_input(p_event); - set_input_as_handled(); - return; + + if (!match_found) { + // If the first item is not selectable, try re-searching from the end. + for (int i = items.size() - 1; i >= search_from; i--) { + if (!items[i].separator && !items[i].disabled) { + mouse_over = i; + emit_signal(SNAME("id_focused"), i); + scroll_to_item(i); + control->update(); + set_input_as_handled(); + break; + } + } } - } - } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { - if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { - _activate_submenu(mouse_over, true); - set_input_as_handled(); - } else { + } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { Node *n = get_parent(); - if (n && Object::cast_to<MenuBar>(n)) { - Object::cast_to<MenuBar>(n)->gui_input(p_event); - set_input_as_handled(); - return; + if (n) { + if (Object::cast_to<PopupMenu>(n)) { + hide(); + set_input_as_handled(); + } else if (Object::cast_to<MenuBar>(n)) { + Object::cast_to<MenuBar>(n)->gui_input(p_event); + set_input_as_handled(); + return; + } } - } - } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { - if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { - if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { + } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && !items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { _activate_submenu(mouse_over, true); + set_input_as_handled(); } else { - activate_item(mouse_over); + Node *n = get_parent(); + if (n && Object::cast_to<MenuBar>(n)) { + Object::cast_to<MenuBar>(n)->gui_input(p_event); + set_input_as_handled(); + return; + } + } + } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { + if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) { + _activate_submenu(mouse_over, true); + } else { + activate_item(mouse_over); + } + set_input_as_handled(); } - set_input_as_handled(); } } @@ -1532,14 +1539,19 @@ bool PopupMenu::is_item_shortcut_disabled(int p_idx) const { } void PopupMenu::set_current_index(int p_idx) { - ERR_FAIL_INDEX(p_idx, items.size()); + if (p_idx != -1) { + ERR_FAIL_INDEX(p_idx, items.size()); + } if (mouse_over == p_idx) { return; } mouse_over = p_idx; - scroll_to_item(mouse_over); + if (mouse_over != -1) { + scroll_to_item(mouse_over); + } + control->update(); } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 3f1f16cd51..8ce8663091 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -5172,31 +5172,36 @@ void RichTextLabel::_bind_methods() { // Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "threaded"), "set_threaded", "is_threaded"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "progress_bar_delay", PROPERTY_HINT_NONE, "suffix:ms"), "set_progress_bar_delay", "get_progress_bar_delay"); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); + + ADD_GROUP("Markup", ""); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hint_underlined"), "set_hint_underline", "is_hint_underlined"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + ADD_GROUP("Threading", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "threaded"), "set_threaded", "is_threaded"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "progress_bar_delay", PROPERTY_HINT_NONE, "suffix:ms"), "set_progress_bar_delay", "get_progress_bar_delay"); + + ADD_GROUP("Text Selection", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); + + ADD_GROUP("Displayed Text", ""); // Note: "visible_characters" and "visible_ratio" should be set after "text" to be correctly applied. ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visible_ratio", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_visible_ratio", "get_visible_ratio"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); - ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index f58cb0fe70..8fd547813d 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -317,9 +317,10 @@ void ScrollContainer::_reposition_children() { void ScrollContainer::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: - case NOTIFICATION_TRANSLATION_CHANGED: - case NOTIFICATION_THEME_CHANGED: { + case NOTIFICATION_TRANSLATION_CHANGED: { _updating_scrollbars = true; call_deferred(SNAME("_update_scrollbar_position")); } break; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 1b7aa787e7..5328ae7b8c 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -35,6 +35,7 @@ #include "core/string/translation.h" #include "scene/gui/control.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" void Window::set_title(const String &p_title) { title = p_title; @@ -1330,13 +1331,13 @@ StringName Window::get_theme_type_variation() const { void Window::_get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const { if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == theme_type_variation) { - if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(theme_type_variation) != StringName()) { - Theme::get_project_default()->get_type_dependencies(get_class_name(), theme_type_variation, p_list); + if (ThemeDB::get_singleton()->get_project_theme().is_valid() && ThemeDB::get_singleton()->get_project_theme()->get_type_variation_base(theme_type_variation) != StringName()) { + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), theme_type_variation, p_list); } else { - Theme::get_default()->get_type_dependencies(get_class_name(), theme_type_variation, p_list); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), theme_type_variation, p_list); } } else { - Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(p_theme_type, StringName(), p_list); } } @@ -1522,9 +1523,9 @@ void Window::_validate_property(PropertyInfo &p_property) const { // Only the default theme and the project theme are used for the list of options. // This is an imposed limitation to simplify the logic needed to leverage those options. - Theme::get_default()->get_type_variation_list(get_class_name(), &names); - if (Theme::get_project_default().is_valid()) { - Theme::get_project_default()->get_type_variation_list(get_class_name(), &names); + ThemeDB::get_singleton()->get_default_theme()->get_type_variation_list(get_class_name(), &names); + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { + ThemeDB::get_singleton()->get_project_theme()->get_type_variation_list(get_class_name(), &names); } names.sort_custom<StringName::AlphCompare>(); diff --git a/scene/main/window.h b/scene/main/window.h index ebaf8fea75..a5b6b26104 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -168,7 +168,6 @@ public: enum { NOTIFICATION_VISIBILITY_CHANGED = 30, NOTIFICATION_POST_POPUP = 31, - // This doesn't need to be paired with `NOTIFICATION_ENTER_TREE`. NOTIFICATION_THEME_CHANGED = 32 }; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 5243d4bdca..6c7bc552bf 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -193,12 +193,14 @@ #include "scene/resources/sky.h" #include "scene/resources/sky_material.h" #include "scene/resources/sphere_shape_3d.h" +#include "scene/resources/style_box.h" #include "scene/resources/surface_tool.h" #include "scene/resources/syntax_highlighter.h" #include "scene/resources/text_file.h" #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" #include "scene/resources/texture.h" +#include "scene/resources/theme.h" #include "scene/resources/tile_set.h" #include "scene/resources/video_stream.h" #include "scene/resources/visual_shader.h" @@ -210,6 +212,7 @@ #include "scene/resources/world_boundary_shape_2d.h" #include "scene/resources/world_boundary_shape_3d.h" #include "scene/scene_string_names.h" +#include "scene/theme/theme_db.h" #include "scene/main/shader_globals_override.h" @@ -1130,62 +1133,8 @@ void register_scene_types() { SceneDebugger::initialize(); } -void initialize_theme() { - // Allow creating the default theme at a different scale to suit higher/lower base resolutions. - float default_theme_scale = GLOBAL_DEF("gui/theme/default_theme_scale", 1.0); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_theme_scale", PropertyInfo(Variant::FLOAT, "gui/theme/default_theme_scale", PROPERTY_HINT_RANGE, "0.5,8,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - String theme_path = GLOBAL_DEF_RST("gui/theme/custom", ""); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", ""); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - TextServer::FontAntialiasing font_antialiasing = (TextServer::FontAntialiasing)(int)GLOBAL_DEF_RST("gui/theme/default_font_antialiasing", 1); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiasing", PropertyInfo(Variant::INT, "gui/theme/default_font_antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - TextServer::Hinting font_hinting = (TextServer::Hinting)(int)GLOBAL_DEF_RST("gui/theme/default_font_hinting", TextServer::HINTING_LIGHT); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_hinting", PropertyInfo(Variant::INT, "gui/theme/default_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)GLOBAL_DEF_RST("gui/theme/default_font_subpixel_positioning", TextServer::SUBPIXEL_POSITIONING_AUTO); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); - - const bool font_msdf = GLOBAL_DEF_RST("gui/theme/default_font_multichannel_signed_distance_field", false); - const bool font_generate_mipmaps = GLOBAL_DEF_RST("gui/theme/default_font_generate_mipmaps", false); - - GLOBAL_DEF_RST("gui/theme/lcd_subpixel_layout", 1); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/lcd_subpixel_layout", PropertyInfo(Variant::INT, "gui/theme/lcd_subpixel_layout", PROPERTY_HINT_ENUM, "Disabled,Horizontal RGB,Horizontal BGR,Vertical RGB,Vertical BGR")); - ProjectSettings::get_singleton()->set_restart_if_changed("gui/theme/lcd_subpixel_layout", false); - - Ref<Font> font; - if (!font_path.is_empty()) { - font = ResourceLoader::load(font_path); - if (!font.is_valid()) { - ERR_PRINT("Error loading custom font '" + font_path + "'"); - } - } - - // Always make the default theme to avoid invalid default font/icon/style in the given theme. - if (RenderingServer::get_singleton()) { - make_default_theme(default_theme_scale, font, font_subpixel_positioning, font_hinting, font_antialiasing, font_msdf, font_generate_mipmaps); - } - - if (!theme_path.is_empty()) { - Ref<Theme> theme = ResourceLoader::load(theme_path); - if (theme.is_valid()) { - Theme::set_project_default(theme); - if (font.is_valid()) { - Theme::set_fallback_font(font); - } - } else { - ERR_PRINT("Error loading custom theme '" + theme_path + "'"); - } - } -} - void unregister_scene_types() { SceneDebugger::deinitialize(); - clear_default_theme(); ResourceLoader::remove_resource_format_loader(resource_loader_texture_layered); resource_loader_texture_layered.unref(); @@ -1227,3 +1176,9 @@ void unregister_scene_types() { ColorPicker::finish_shaders(); SceneStringNames::free(); } + +void register_scene_singletons() { + GDREGISTER_CLASS(ThemeDB); + + Engine::get_singleton()->add_singleton(Engine::Singleton("ThemeDB", ThemeDB::get_singleton())); +} diff --git a/scene/register_scene_types.h b/scene/register_scene_types.h index dce8713976..cb3249c5d7 100644 --- a/scene/register_scene_types.h +++ b/scene/register_scene_types.h @@ -33,7 +33,6 @@ void register_scene_types(); void unregister_scene_types(); - -void initialize_theme(); +void register_scene_singletons(); #endif // REGISTER_SCENE_TYPES_H diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 823617328e..9d5bc18c96 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -4201,25 +4201,25 @@ void Animation::_position_track_optimize(int p_idx, real_t p_allowed_velocity_er void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_ROTATION_3D); - RotationTrack *tt = static_cast<RotationTrack *>(tracks[p_idx]); + RotationTrack *rt = static_cast<RotationTrack *>(tracks[p_idx]); int i = 0; - while (i < tt->rotations.size() - 2) { - TKey<Quaternion> t0 = tt->rotations[i]; - TKey<Quaternion> t1 = tt->rotations[i + 1]; - TKey<Quaternion> t2 = tt->rotations[i + 2]; + while (i < rt->rotations.size() - 2) { + TKey<Quaternion> t0 = rt->rotations[i]; + TKey<Quaternion> t1 = rt->rotations[i + 1]; + TKey<Quaternion> t2 = rt->rotations[i + 2]; bool erase = _quaternion_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); if (erase) { - tt->rotations.remove_at(i + 1); + rt->rotations.remove_at(i + 1); } else { i++; } } - if (tt->rotations.size() == 2) { - if ((tt->rotations[0].value - tt->rotations[1].value).length() < p_allowed_precision_error) { - tt->rotations.remove_at(1); + if (rt->rotations.size() == 2) { + if ((rt->rotations[0].value - rt->rotations[1].value).length() < p_allowed_precision_error) { + rt->rotations.remove_at(1); } } } @@ -4227,25 +4227,25 @@ void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_velocity_er void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_SCALE_3D); - ScaleTrack *tt = static_cast<ScaleTrack *>(tracks[p_idx]); + ScaleTrack *st = static_cast<ScaleTrack *>(tracks[p_idx]); int i = 0; - while (i < tt->scales.size() - 2) { - TKey<Vector3> t0 = tt->scales[i]; - TKey<Vector3> t1 = tt->scales[i + 1]; - TKey<Vector3> t2 = tt->scales[i + 2]; + while (i < st->scales.size() - 2) { + TKey<Vector3> t0 = st->scales[i]; + TKey<Vector3> t1 = st->scales[i + 1]; + TKey<Vector3> t2 = st->scales[i + 2]; bool erase = _vector3_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); if (erase) { - tt->scales.remove_at(i + 1); + st->scales.remove_at(i + 1); } else { i++; } } - if (tt->scales.size() == 2) { - if ((tt->scales[0].value - tt->scales[1].value).length() < p_allowed_precision_error) { - tt->scales.remove_at(1); + if (st->scales.size() == 2) { + if ((st->scales[0].value - st->scales[1].value).length() < p_allowed_precision_error) { + st->scales.remove_at(1); } } } @@ -4253,25 +4253,25 @@ void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_velocity_err, void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_precision_error) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_BLEND_SHAPE); - BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(tracks[p_idx]); + BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(tracks[p_idx]); int i = 0; - while (i < tt->blend_shapes.size() - 2) { - TKey<float> t0 = tt->blend_shapes[i]; - TKey<float> t1 = tt->blend_shapes[i + 1]; - TKey<float> t2 = tt->blend_shapes[i + 2]; + while (i < bst->blend_shapes.size() - 2) { + TKey<float> t0 = bst->blend_shapes[i]; + TKey<float> t1 = bst->blend_shapes[i + 1]; + TKey<float> t2 = bst->blend_shapes[i + 2]; bool erase = _float_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_precision_error); if (erase) { - tt->blend_shapes.remove_at(i + 1); + bst->blend_shapes.remove_at(i + 1); } else { i++; } } - if (tt->blend_shapes.size() == 2) { - if (abs(tt->blend_shapes[0].value - tt->blend_shapes[1].value) < p_allowed_precision_error) { - tt->blend_shapes.remove_at(1); + if (bst->blend_shapes.size() == 2) { + if (abs(bst->blend_shapes[0].value - bst->blend_shapes[1].value) < p_allowed_precision_error) { + bst->blend_shapes.remove_at(1); } } } @@ -4279,28 +4279,28 @@ void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_velocity void Animation::_value_track_optimize(int p_idx, real_t p_allowed_velocity_err, real_t p_allowed_angular_err, real_t p_allowed_precision_error) { ERR_FAIL_INDEX(p_idx, tracks.size()); ERR_FAIL_COND(tracks[p_idx]->type != TYPE_VALUE); - ValueTrack *tt = static_cast<ValueTrack *>(tracks[p_idx]); - if (tt->values.size() == 0) { + ValueTrack *vt = static_cast<ValueTrack *>(tracks[p_idx]); + if (vt->values.size() == 0) { return; } - Variant::Type type = tt->values[0].value.get_type(); + Variant::Type type = vt->values[0].value.get_type(); // Special case for angle interpolation. - bool is_using_angle = tt->interpolation == Animation::INTERPOLATION_LINEAR_ANGLE || tt->interpolation == Animation::INTERPOLATION_CUBIC_ANGLE; + bool is_using_angle = vt->interpolation == Animation::INTERPOLATION_LINEAR_ANGLE || vt->interpolation == Animation::INTERPOLATION_CUBIC_ANGLE; int i = 0; - while (i < tt->values.size() - 2) { + while (i < vt->values.size() - 2) { bool erase = false; switch (type) { case Variant::FLOAT: { TKey<float> t0; TKey<float> t1; TKey<float> t2; - t0.time = tt->values[i].time; - t1.time = tt->values[i + 1].time; - t2.time = tt->values[i + 2].time; - t0.value = tt->values[i].value; - t1.value = tt->values[i + 1].value; - t2.value = tt->values[i + 2].value; + t0.time = vt->values[i].time; + t1.time = vt->values[i + 1].time; + t2.time = vt->values[i + 2].time; + t0.value = vt->values[i].value; + t1.value = vt->values[i + 1].value; + t2.value = vt->values[i + 2].value; if (is_using_angle) { float diff1 = fmod(t1.value - t0.value, Math_TAU); t1.value = t0.value + fmod(2.0 * diff1, Math_TAU) - diff1; @@ -4316,36 +4316,36 @@ void Animation::_value_track_optimize(int p_idx, real_t p_allowed_velocity_err, TKey<Vector2> t0; TKey<Vector2> t1; TKey<Vector2> t2; - t0.time = tt->values[i].time; - t1.time = tt->values[i + 1].time; - t2.time = tt->values[i + 2].time; - t0.value = tt->values[i].value; - t1.value = tt->values[i + 1].value; - t2.value = tt->values[i + 2].value; + t0.time = vt->values[i].time; + t1.time = vt->values[i + 1].time; + t2.time = vt->values[i + 2].time; + t0.value = vt->values[i].value; + t1.value = vt->values[i + 1].value; + t2.value = vt->values[i + 2].value; erase = _vector2_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); } break; case Variant::VECTOR3: { TKey<Vector3> t0; TKey<Vector3> t1; TKey<Vector3> t2; - t0.time = tt->values[i].time; - t1.time = tt->values[i + 1].time; - t2.time = tt->values[i + 2].time; - t0.value = tt->values[i].value; - t1.value = tt->values[i + 1].value; - t2.value = tt->values[i + 2].value; + t0.time = vt->values[i].time; + t1.time = vt->values[i + 1].time; + t2.time = vt->values[i + 2].time; + t0.value = vt->values[i].value; + t1.value = vt->values[i + 1].value; + t2.value = vt->values[i + 2].value; erase = _vector3_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); } break; case Variant::QUATERNION: { TKey<Quaternion> t0; TKey<Quaternion> t1; TKey<Quaternion> t2; - t0.time = tt->values[i].time; - t1.time = tt->values[i + 1].time; - t2.time = tt->values[i + 2].time; - t0.value = tt->values[i].value; - t1.value = tt->values[i + 1].value; - t2.value = tt->values[i + 2].value; + t0.time = vt->values[i].time; + t1.time = vt->values[i + 1].time; + t2.time = vt->values[i + 2].time; + t0.value = vt->values[i].value; + t1.value = vt->values[i + 1].value; + t2.value = vt->values[i + 2].value; erase = _quaternion_track_optimize_key(t0, t1, t2, p_allowed_velocity_err, p_allowed_angular_err, p_allowed_precision_error); } break; default: { @@ -4353,18 +4353,18 @@ void Animation::_value_track_optimize(int p_idx, real_t p_allowed_velocity_err, } if (erase) { - tt->values.remove_at(i + 1); + vt->values.remove_at(i + 1); } else { i++; } } - if (tt->values.size() == 2) { + if (vt->values.size() == 2) { bool single_key = false; switch (type) { case Variant::FLOAT: { - float val_0 = tt->values[0].value; - float val_1 = tt->values[1].value; + float val_0 = vt->values[0].value; + float val_1 = vt->values[1].value; if (is_using_angle) { float diff1 = fmod(val_1 - val_0, Math_TAU); val_1 = val_0 + fmod(2.0 * diff1, Math_TAU) - diff1; @@ -4372,25 +4372,25 @@ void Animation::_value_track_optimize(int p_idx, real_t p_allowed_velocity_err, single_key = abs(val_0 - val_1) < p_allowed_precision_error; } break; case Variant::VECTOR2: { - Vector2 val_0 = tt->values[0].value; - Vector2 val_1 = tt->values[1].value; + Vector2 val_0 = vt->values[0].value; + Vector2 val_1 = vt->values[1].value; single_key = (val_0 - val_1).length() < p_allowed_precision_error; } break; case Variant::VECTOR3: { - Vector3 val_0 = tt->values[0].value; - Vector3 val_1 = tt->values[1].value; + Vector3 val_0 = vt->values[0].value; + Vector3 val_1 = vt->values[1].value; single_key = (val_0 - val_1).length() < p_allowed_precision_error; } break; case Variant::QUATERNION: { - Quaternion val_0 = tt->values[0].value; - Quaternion val_1 = tt->values[1].value; + Quaternion val_0 = vt->values[0].value; + Quaternion val_1 = vt->values[1].value; single_key = (val_0 - val_1).length() < p_allowed_precision_error; } break; default: { } break; } if (single_key) { - tt->values.remove_at(1); + vt->values.remove_at(1); } } } diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp index 5f725b2fbe..427d418551 100644 --- a/scene/resources/animation_library.cpp +++ b/scene/resources/animation_library.cpp @@ -85,7 +85,7 @@ bool AnimationLibrary::has_animation(const StringName &p_name) const { } Ref<Animation> AnimationLibrary::get_animation(const StringName &p_name) const { - ERR_FAIL_COND_V(!animations.has(p_name), Ref<Animation>()); + ERR_FAIL_COND_V_MSG(!animations.has(p_name), Ref<Animation>(), vformat("Animation not found: \"%s\".", p_name)); return animations[p_name]; } diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 5bfa1adfe5..410f35e597 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -35,6 +35,7 @@ #include "default_theme_icons.gen.h" #include "scene/resources/font.h" #include "scene/resources/theme.h" +#include "scene/theme/theme_db.h" #include "servers/text_server.h" #include "modules/modules_enabled.gen.h" // For svg. @@ -1101,18 +1102,11 @@ void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPos fill_default_theme(t, default_font, bold_font, bold_italics_font, italics_font, default_icon, default_style, default_scale); - Theme::set_default(t); - Theme::set_fallback_base_scale(default_scale); - Theme::set_fallback_icon(default_icon); - Theme::set_fallback_style(default_style); - Theme::set_fallback_font(default_font); - Theme::set_fallback_font_size(default_font_size * default_scale); -} + ThemeDB::get_singleton()->set_default_theme(t); -void clear_default_theme() { - Theme::set_project_default(nullptr); - Theme::set_default(nullptr); - Theme::set_fallback_icon(nullptr); - Theme::set_fallback_style(nullptr); - Theme::set_fallback_font(nullptr); + ThemeDB::get_singleton()->set_fallback_base_scale(default_scale); + ThemeDB::get_singleton()->set_fallback_icon(default_icon); + ThemeDB::get_singleton()->set_fallback_stylebox(default_style); + ThemeDB::get_singleton()->set_fallback_font(default_font); + ThemeDB::get_singleton()->set_fallback_font_size(default_font_size * default_scale); } diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h index 15be5e676f..003934ce90 100644 --- a/scene/resources/default_theme/default_theme.h +++ b/scene/resources/default_theme/default_theme.h @@ -37,6 +37,5 @@ const int default_font_size = 16; void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale); void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPositioning p_font_subpixel = TextServer::SUBPIXEL_POSITIONING_AUTO, TextServer::Hinting p_font_hinting = TextServer::HINTING_LIGHT, TextServer::FontAntialiasing p_font_antialiased = TextServer::FONT_ANTIALIASING_GRAY, bool p_font_msdf = false, bool p_font_generate_mipmaps = false); -void clear_default_theme(); #endif // DEFAULT_THEME_H diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index be57d9c965..8ec5678f0a 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -39,6 +39,7 @@ #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" #include "scene/resources/theme.h" +#include "scene/theme/theme_db.h" /*************************************************************************/ /* Font */ @@ -2552,13 +2553,13 @@ Ref<Font> FontVariation::_get_base_font_or_default() const { } // Check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { List<StringName> theme_types; - Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); @@ -2569,13 +2570,13 @@ Ref<Font> FontVariation::_get_base_font_or_default() const { } // Lastly, fall back on the items defined in the default Theme, if they exist. - if (Theme::get_default().is_valid()) { + if (ThemeDB::get_singleton()->get_default_theme().is_valid()) { List<StringName> theme_types; - Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); @@ -2585,7 +2586,7 @@ Ref<Font> FontVariation::_get_base_font_or_default() const { } // If they don't exist, use any type to return the default/empty value. - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); @@ -2859,13 +2860,13 @@ Ref<Font> SystemFont::_get_base_font_or_default() const { } // Check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { List<StringName> theme_types; - Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); @@ -2876,13 +2877,13 @@ Ref<Font> SystemFont::_get_base_font_or_default() const { } // Lastly, fall back on the items defined in the default Theme, if they exist. - if (Theme::get_default().is_valid()) { + if (ThemeDB::get_singleton()->get_default_theme().is_valid()) { List<StringName> theme_types; - Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); @@ -2892,7 +2893,7 @@ Ref<Font> SystemFont::_get_base_font_or_default() const { } // If they don't exist, use any type to return the default/empty value. - Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + Ref<Font> f = ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); if (f.is_valid()) { theme_font = f; theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<SystemFont *>(this)), &Font::_invalidate_rids), CONNECT_REFERENCE_COUNTED); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 0e1b18d584..33334801c3 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -279,25 +279,36 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref<Resource> res = value; if (res.is_valid()) { if (res->is_local_to_scene()) { - HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); - - if (E) { - value = E->value; + // In a situation where a local-to-scene resource is used in a child node of a non-editable instance, + // we need to avoid the parent scene from overriding the resource potentially also used in the root + // of the instantiated scene. That would to the instance having two different instances of the resource. + // Since at this point it's too late to propagate the resource instance in the parent scene to all the relevant + // nodes in the instance (and that would require very complex bookkepping), what we do instead is + // tampering the resource object already there with the values from the node in the parent scene and + // then tell this node to reference that resource. + if (n.instance >= 0) { + Ref<Resource> node_res = node->get(snames[nprops[j].name]); + if (node_res.is_valid()) { + node_res->copy_from(res); + node_res->configure_for_local_scene(node, resources_local_to_scene); + value = node_res; + } } else { + HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); Node *base = i == 0 ? node : ret_nodes[0]; - - if (p_edit_state == GEN_EDIT_STATE_MAIN || p_edit_state == GEN_EDIT_STATE_MAIN_INHERITED) { - //for the main scene, use the resource as is - res->configure_for_local_scene(base, resources_local_to_scene); - resources_local_to_scene[res] = res; - + if (E) { + value = E->value; } else { - //for instances, a copy must be made - Node *base2 = i == 0 ? node : ret_nodes[0]; - Ref<Resource> local_dupe = res->duplicate_for_local_scene(base2, resources_local_to_scene); - resources_local_to_scene[res] = local_dupe; - res = local_dupe; - value = local_dupe; + if (p_edit_state == GEN_EDIT_STATE_MAIN) { + //for the main scene, use the resource as is + res->configure_for_local_scene(base, resources_local_to_scene); + resources_local_to_scene[res] = res; + } else { + //for instances, a copy must be made + Ref<Resource> local_dupe = res->duplicate_for_local_scene(base, resources_local_to_scene); + resources_local_to_scene[res] = local_dupe; + value = local_dupe; + } } } //must make a copy, because this res is local to scene @@ -398,7 +409,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) { - E.value->setup_local_to_scene(); + if (E.value->get_local_scene() == ret_nodes[0]) { + E.value->setup_local_to_scene(); + } } //do connections diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index e993936350..fc5cf2a028 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -32,6 +32,7 @@ #include "core/core_string_names.h" #include "scene/resources/theme.h" +#include "scene/theme/theme_db.h" #include "servers/rendering_server.h" #include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/polypartition.h" @@ -2984,13 +2985,13 @@ Ref<Font> TextMesh::_get_font_or_default() const { } // Check the project-defined Theme resource. - if (Theme::get_project_default().is_valid()) { + if (ThemeDB::get_singleton()->get_project_theme().is_valid()) { List<StringName> theme_types; - Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_project_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - return Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_project_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + return ThemeDB::get_singleton()->get_project_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); } } } @@ -2998,17 +2999,17 @@ Ref<Font> TextMesh::_get_font_or_default() const { // Lastly, fall back on the items defined in the default Theme, if they exist. { List<StringName> theme_types; - Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); + ThemeDB::get_singleton()->get_default_theme()->get_type_dependencies(get_class_name(), StringName(), &theme_types); for (const StringName &E : theme_types) { - if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { - return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (ThemeDB::get_singleton()->get_default_theme()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + return ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); } } } // If they don't exist, use any type to return the default/empty value. - return Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + return ThemeDB::get_singleton()->get_default_theme()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); } void TextMesh::set_font_size(int p_size) { diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 3f6eec8497..3321392821 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -31,17 +31,7 @@ #include "theme.h" #include "core/string/print_string.h" - -// Universal Theme resources used when no other theme has the item. -Ref<Theme> Theme::default_theme; -Ref<Theme> Theme::project_default_theme; - -// Universal default values, final fallback for every theme. -float Theme::fallback_base_scale = 1.0; -Ref<Texture2D> Theme::fallback_icon; -Ref<StyleBox> Theme::fallback_style; -Ref<Font> Theme::fallback_font; -int Theme::fallback_font_size = 16; +#include "scene/theme/theme_db.h" // Dynamic properties. bool Theme::_set(const StringName &p_name, const Variant &p_value) { @@ -185,64 +175,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const { } } -// Universal fallback Theme resources. -Ref<Theme> Theme::get_default() { - return default_theme; -} - -void Theme::set_default(const Ref<Theme> &p_default) { - default_theme = p_default; -} - -Ref<Theme> Theme::get_project_default() { - return project_default_theme; -} - -void Theme::set_project_default(const Ref<Theme> &p_project_default) { - project_default_theme = p_project_default; -} - -// Universal fallback values for theme item types. -void Theme::set_fallback_base_scale(float p_base_scale) { - fallback_base_scale = p_base_scale; -} - -void Theme::set_fallback_icon(const Ref<Texture2D> &p_icon) { - fallback_icon = p_icon; -} - -void Theme::set_fallback_style(const Ref<StyleBox> &p_style) { - fallback_style = p_style; -} - -void Theme::set_fallback_font(const Ref<Font> &p_font) { - fallback_font = p_font; -} - -void Theme::set_fallback_font_size(int p_font_size) { - fallback_font_size = p_font_size; -} - -float Theme::get_fallback_base_scale() { - return fallback_base_scale; -} - -Ref<Texture2D> Theme::get_fallback_icon() { - return fallback_icon; -} - -Ref<StyleBox> Theme::get_fallback_style() { - return fallback_style; -} - -Ref<Font> Theme::get_fallback_font() { - return fallback_font; -} - -int Theme::get_fallback_font_size() { - return fallback_font_size; -} - +// Static helpers. bool Theme::is_valid_type_name(const String &p_name) { for (int i = 0; i < p_name.length(); i++) { if (!is_ascii_identifier_char(p_name[i])) { @@ -351,7 +284,7 @@ Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_the if (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { return icon_map[p_theme_type][p_name]; } else { - return fallback_icon; + return ThemeDB::get_singleton()->get_fallback_icon(); } } @@ -461,7 +394,7 @@ Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_ if (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { return style_map[p_theme_type][p_name]; } else { - return fallback_style; + return ThemeDB::get_singleton()->get_fallback_stylebox(); } } @@ -573,7 +506,7 @@ Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_ty } else if (has_default_font()) { return default_font; } else { - return fallback_font; + return ThemeDB::get_singleton()->get_fallback_font(); } } @@ -676,7 +609,7 @@ int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_typ } else if (has_default_font_size()) { return default_font_size; } else { - return fallback_font_size; + return ThemeDB::get_singleton()->get_fallback_font_size(); } } diff --git a/scene/resources/theme.h b/scene/resources/theme.h index a2aca5e61f..ed1dc7c938 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -102,17 +102,6 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; - // Universal Theme resources used when no other theme has the item. - static Ref<Theme> default_theme; - static Ref<Theme> project_default_theme; - - // Universal default values, final fallback for every theme. - static float fallback_base_scale; - static Ref<Texture2D> fallback_icon; - static Ref<StyleBox> fallback_style; - static Ref<Font> fallback_font; - static int fallback_font_size; - // Default values configurable for each individual theme. float default_base_scale = 0.0; Ref<Font> default_font; @@ -126,24 +115,6 @@ protected: virtual void reset_state() override; public: - static Ref<Theme> get_default(); - static void set_default(const Ref<Theme> &p_default); - - static Ref<Theme> get_project_default(); - static void set_project_default(const Ref<Theme> &p_project_default); - - static void set_fallback_base_scale(float p_base_scale); - static void set_fallback_icon(const Ref<Texture2D> &p_icon); - static void set_fallback_style(const Ref<StyleBox> &p_style); - static void set_fallback_font(const Ref<Font> &p_font); - static void set_fallback_font_size(int p_font_size); - - static float get_fallback_base_scale(); - static Ref<Texture2D> get_fallback_icon(); - static Ref<StyleBox> get_fallback_style(); - static Ref<Font> get_fallback_font(); - static int get_fallback_font_size(); - static bool is_valid_type_name(const String &p_name); static bool is_valid_item_name(const String &p_name); diff --git a/scene/theme/SCsub b/scene/theme/SCsub new file mode 100644 index 0000000000..fc61250247 --- /dev/null +++ b/scene/theme/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.scene_sources, "*.cpp") diff --git a/scene/theme/theme_db.cpp b/scene/theme/theme_db.cpp new file mode 100644 index 0000000000..d6e892cd93 --- /dev/null +++ b/scene/theme/theme_db.cpp @@ -0,0 +1,237 @@ +/*************************************************************************/ +/* theme_db.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "theme_db.h" + +#include "core/config/project_settings.h" +#include "core/io/resource_loader.h" +#include "scene/resources/default_theme/default_theme.h" +#include "scene/resources/font.h" +#include "scene/resources/style_box.h" +#include "scene/resources/texture.h" +#include "scene/resources/theme.h" +#include "servers/text_server.h" + +// Default engine theme creation and configuration. +void ThemeDB::initialize_theme() { + // Allow creating the default theme at a different scale to suit higher/lower base resolutions. + float default_theme_scale = GLOBAL_DEF("gui/theme/default_theme_scale", 1.0); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_theme_scale", PropertyInfo(Variant::FLOAT, "gui/theme/default_theme_scale", PROPERTY_HINT_RANGE, "0.5,8,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + String theme_path = GLOBAL_DEF_RST("gui/theme/custom", ""); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", ""); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + TextServer::FontAntialiasing font_antialiasing = (TextServer::FontAntialiasing)(int)GLOBAL_DEF_RST("gui/theme/default_font_antialiasing", 1); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiasing", PropertyInfo(Variant::INT, "gui/theme/default_font_antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + TextServer::Hinting font_hinting = (TextServer::Hinting)(int)GLOBAL_DEF_RST("gui/theme/default_font_hinting", TextServer::HINTING_LIGHT); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_hinting", PropertyInfo(Variant::INT, "gui/theme/default_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)GLOBAL_DEF_RST("gui/theme/default_font_subpixel_positioning", TextServer::SUBPIXEL_POSITIONING_AUTO); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_subpixel_positioning", PropertyInfo(Variant::INT, "gui/theme/default_font_subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + + const bool font_msdf = GLOBAL_DEF_RST("gui/theme/default_font_multichannel_signed_distance_field", false); + const bool font_generate_mipmaps = GLOBAL_DEF_RST("gui/theme/default_font_generate_mipmaps", false); + + GLOBAL_DEF_RST("gui/theme/lcd_subpixel_layout", 1); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/lcd_subpixel_layout", PropertyInfo(Variant::INT, "gui/theme/lcd_subpixel_layout", PROPERTY_HINT_ENUM, "Disabled,Horizontal RGB,Horizontal BGR,Vertical RGB,Vertical BGR")); + ProjectSettings::get_singleton()->set_restart_if_changed("gui/theme/lcd_subpixel_layout", false); + + Ref<Font> font; + if (!font_path.is_empty()) { + font = ResourceLoader::load(font_path); + if (!font.is_valid()) { + ERR_PRINT("Error loading custom font '" + font_path + "'"); + } + } + + // Always make the default theme to avoid invalid default font/icon/style in the given theme. + if (RenderingServer::get_singleton()) { + make_default_theme(default_theme_scale, font, font_subpixel_positioning, font_hinting, font_antialiasing, font_msdf, font_generate_mipmaps); + } + + if (!theme_path.is_empty()) { + Ref<Theme> theme = ResourceLoader::load(theme_path); + if (theme.is_valid()) { + set_project_theme(theme); + if (font.is_valid()) { + set_fallback_font(font); + } + } else { + ERR_PRINT("Error loading custom theme '" + theme_path + "'"); + } + } +} + +void ThemeDB::initialize_theme_noproject() { + if (RenderingServer::get_singleton()) { + make_default_theme(1.0, Ref<Font>()); + } +} + +// Universal fallback Theme resources. + +void ThemeDB::set_default_theme(const Ref<Theme> &p_default) { + default_theme = p_default; +} + +Ref<Theme> ThemeDB::get_default_theme() { + return default_theme; +} + +void ThemeDB::set_project_theme(const Ref<Theme> &p_project_default) { + project_theme = p_project_default; +} + +Ref<Theme> ThemeDB::get_project_theme() { + return project_theme; +} + +// Universal fallback values for theme item types. + +void ThemeDB::set_fallback_base_scale(float p_base_scale) { + if (fallback_base_scale == p_base_scale) { + return; + } + + fallback_base_scale = p_base_scale; + emit_signal(SNAME("fallback_changed")); +} + +float ThemeDB::get_fallback_base_scale() { + return fallback_base_scale; +} + +void ThemeDB::set_fallback_font(const Ref<Font> &p_font) { + if (fallback_font == p_font) { + return; + } + + fallback_font = p_font; + emit_signal(SNAME("fallback_changed")); +} + +Ref<Font> ThemeDB::get_fallback_font() { + return fallback_font; +} + +void ThemeDB::set_fallback_font_size(int p_font_size) { + if (fallback_font_size == p_font_size) { + return; + } + + fallback_font_size = p_font_size; + emit_signal(SNAME("fallback_changed")); +} + +int ThemeDB::get_fallback_font_size() { + return fallback_font_size; +} + +void ThemeDB::set_fallback_icon(const Ref<Texture2D> &p_icon) { + if (fallback_icon == p_icon) { + return; + } + + fallback_icon = p_icon; + emit_signal(SNAME("fallback_changed")); +} + +Ref<Texture2D> ThemeDB::get_fallback_icon() { + return fallback_icon; +} + +void ThemeDB::set_fallback_stylebox(const Ref<StyleBox> &p_stylebox) { + if (fallback_stylebox == p_stylebox) { + return; + } + + fallback_stylebox = p_stylebox; + emit_signal(SNAME("fallback_changed")); +} + +Ref<StyleBox> ThemeDB::get_fallback_stylebox() { + return fallback_stylebox; +} + +// Object methods. +void ThemeDB::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_default_theme"), &ThemeDB::get_default_theme); + ClassDB::bind_method(D_METHOD("get_project_theme"), &ThemeDB::get_project_theme); + + ClassDB::bind_method(D_METHOD("set_fallback_base_scale", "base_scale"), &ThemeDB::set_fallback_base_scale); + ClassDB::bind_method(D_METHOD("get_fallback_base_scale"), &ThemeDB::get_fallback_base_scale); + ClassDB::bind_method(D_METHOD("set_fallback_font", "font"), &ThemeDB::set_fallback_font); + ClassDB::bind_method(D_METHOD("get_fallback_font"), &ThemeDB::get_fallback_font); + ClassDB::bind_method(D_METHOD("set_fallback_font_size", "font_size"), &ThemeDB::set_fallback_font_size); + ClassDB::bind_method(D_METHOD("get_fallback_font_size"), &ThemeDB::get_fallback_font_size); + ClassDB::bind_method(D_METHOD("set_fallback_icon", "icon"), &ThemeDB::set_fallback_icon); + ClassDB::bind_method(D_METHOD("get_fallback_icon"), &ThemeDB::get_fallback_icon); + ClassDB::bind_method(D_METHOD("set_fallback_stylebox", "stylebox"), &ThemeDB::set_fallback_stylebox); + ClassDB::bind_method(D_METHOD("get_fallback_stylebox"), &ThemeDB::get_fallback_stylebox); + + ADD_GROUP("Fallback values", "fallback_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fallback_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_fallback_base_scale", "get_fallback_base_scale"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_font", PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_NONE), "set_fallback_font", "get_fallback_font"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fallback_font_size", PROPERTY_HINT_RANGE, "0,256,1,or_greater,suffix:px"), "set_fallback_font_size", "get_fallback_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_NONE), "set_fallback_icon", "get_fallback_icon"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_stylebox", PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_NONE), "set_fallback_stylebox", "get_fallback_stylebox"); + + ADD_SIGNAL(MethodInfo("fallback_changed")); +} + +// Memory management, reference, and initialization +ThemeDB *ThemeDB::singleton = nullptr; + +ThemeDB *ThemeDB::get_singleton() { + return singleton; +} + +ThemeDB::ThemeDB() { + singleton = this; + + // Universal default values, final fallback for every theme. + fallback_base_scale = 1.0; + fallback_font_size = 16; +} + +ThemeDB::~ThemeDB() { + default_theme.unref(); + project_theme.unref(); + + fallback_font.unref(); + fallback_icon.unref(); + fallback_stylebox.unref(); + + singleton = nullptr; +} diff --git a/scene/theme/theme_db.h b/scene/theme/theme_db.h new file mode 100644 index 0000000000..aace33d82c --- /dev/null +++ b/scene/theme/theme_db.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* theme_db.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef THEME_DB_H +#define THEME_DB_H + +#include "core/object/class_db.h" +#include "core/object/ref_counted.h" + +class Font; +class StyleBox; +class Texture2D; +class Theme; + +class ThemeDB : public Object { + GDCLASS(ThemeDB, Object); + + static ThemeDB *singleton; + + // Universal Theme resources used when no other theme has the item. + Ref<Theme> default_theme; + Ref<Theme> project_theme; + + // Universal default values, final fallback for every theme. + float fallback_base_scale; + Ref<Font> fallback_font; + int fallback_font_size; + Ref<Texture2D> fallback_icon; + Ref<StyleBox> fallback_stylebox; + +protected: + static void _bind_methods(); + +public: + void initialize_theme(); + void initialize_theme_noproject(); + + // Universal Theme resources + + void set_default_theme(const Ref<Theme> &p_default); + Ref<Theme> get_default_theme(); + + void set_project_theme(const Ref<Theme> &p_project_default); + Ref<Theme> get_project_theme(); + + // Universal default values. + + void set_fallback_base_scale(float p_base_scale); + float get_fallback_base_scale(); + + void set_fallback_font(const Ref<Font> &p_font); + Ref<Font> get_fallback_font(); + + void set_fallback_font_size(int p_font_size); + int get_fallback_font_size(); + + void set_fallback_icon(const Ref<Texture2D> &p_icon); + Ref<Texture2D> get_fallback_icon(); + + void set_fallback_stylebox(const Ref<StyleBox> &p_stylebox); + Ref<StyleBox> get_fallback_stylebox(); + + static ThemeDB *get_singleton(); + ThemeDB(); + ~ThemeDB(); +}; + +#endif // THEME_DB_H |