From 58319564fa8c169aa9036225461ae2e1b859c6d4 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Tue, 30 Nov 2021 23:29:18 +0300 Subject: [PATCH] Unify theme item lookup in Controls and respect default font --- doc/classes/Theme.xml | 2 +- scene/gui/control.cpp | 435 ++++++++++++-------------------------- scene/gui/control.h | 5 + scene/resources/theme.cpp | 13 +- scene/resources/theme.h | 1 + 5 files changed, 149 insertions(+), 307 deletions(-) diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index 7a7d7631c46c..f47ea0312e56 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -126,7 +126,7 @@ - Returns the [Font] at [code]name[/code] if the theme has [code]node_type[/code]. + Returns the [Font] at [code]name[/code] if the theme has [code]node_type[/code]. If such item does not exist and [member default_font] is set on the theme, the default font will be returned. diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 2d0dabf17c0d..808b72deaed8 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -641,6 +641,7 @@ bool Control::clips_input() const { } return false; } + bool Control::has_point(const Point2 &p_point) const { if (get_script_instance()) { Variant v = p_point; @@ -705,6 +706,7 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const return false; } + void Control::drop_data(const Point2 &p_point, const Variant &p_data) { if (data.drag_owner) { Object *obj = ObjectDB::get_instance(data.drag_owner); @@ -763,46 +765,109 @@ Size2 Control::get_minimum_size() const { return Size2(); } -Ref Control::get_icon(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { - const Ref *tex = data.icon_override.getptr(p_name); - if (tex) { - return *tex; - } - } - - StringName type = p_theme_type ? p_theme_type : get_class_name(); +template +T Control::get_theme_item_in_types(Control *p_theme_owner, Theme::DataType p_data_type, const StringName &p_name, List p_theme_types) { + ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified."); - // try with custom themes - Control *theme_owner = data.theme_owner; + // First, look through each control node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + Control *theme_owner = p_theme_owner; while (theme_owner) { - StringName class_name = type; + // For each theme resource check the theme types provided and see if p_name exists with any of them. + for (List::Element *E = p_theme_types.front(); E; E = E->next()) { + if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E->get())) { + return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E->get()); + } + } - while (class_name != StringName()) { - if (theme_owner->data.theme->has_icon(p_name, class_name)) { - return theme_owner->data.theme->get_icon(p_name, class_name); + Control *parent_c = Object::cast_to(theme_owner->get_parent()); + if (parent_c) { + theme_owner = parent_c->data.theme_owner; + } else { + theme_owner = nullptr; + } + } + + // Secondly, check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + for (List::Element *E = p_theme_types.front(); E; E = E->next()) { + if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E->get())) { + return Theme::get_project_default()->get_theme_item(p_data_type, p_name, E->get()); } + } + } - class_name = ClassDB::get_parent_class_nocheck(class_name); + // Lastly, fall back on the items defined in the default Theme, if they exist. + for (List::Element *E = p_theme_types.front(); E; E = E->next()) { + if (Theme::get_default()->has_theme_item(p_data_type, p_name, E->get())) { + return Theme::get_default()->get_theme_item(p_data_type, p_name, E->get()); } + } + // 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]); +} - Control *parent = Object::cast_to(theme_owner->get_parent()); +bool Control::has_theme_item_in_types(Control *p_theme_owner, Theme::DataType p_data_type, const StringName &p_name, List p_theme_types) { + ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified."); - if (parent) { - theme_owner = parent->data.theme_owner; + // First, look through each control node in the branch, until no valid parent can be found. + // Only nodes with a theme resource attached are considered. + Control *theme_owner = p_theme_owner; + + while (theme_owner) { + // For each theme resource check the theme types provided and see if p_name exists with any of them. + for (List::Element *E = p_theme_types.front(); E; E = E->next()) { + if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E->get())) { + return true; + } + } + + Control *parent_c = Object::cast_to(theme_owner->get_parent()); + if (parent_c) { + theme_owner = parent_c->data.theme_owner; } else { theme_owner = nullptr; } } + // Secondly, check the project-defined Theme resource. if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_icon(p_name, type)) { - return Theme::get_project_default()->get_icon(p_name, type); + for (List::Element *E = p_theme_types.front(); E; E = E->next()) { + if (Theme::get_project_default()->has_theme_item(p_data_type, p_name, E->get())) { + return true; + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + for (List::Element *E = p_theme_types.front(); E; E = E->next()) { + if (Theme::get_default()->has_theme_item(p_data_type, p_name, E->get())) { + return true; + } + } + return false; +} + +void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List *p_list) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + Theme::get_default()->get_type_dependencies(get_class_name(), p_list); + } else { + Theme::get_default()->get_type_dependencies(p_theme_type, p_list); + } +} + +Ref Control::get_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + const Ref *tex = data.icon_override.getptr(p_name); + if (tex) { + return *tex; } } - return Theme::get_default()->get_icon(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types>(data.theme_owner, Theme::DATA_TYPE_ICON, p_name, theme_types); } Ref Control::get_shader(const StringName &p_name, const StringName &p_theme_type) const { @@ -855,46 +920,11 @@ Ref Control::get_stylebox(const StringName &p_name, const StringName & } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - StringName class_name = type; - - while (theme_owner) { - while (class_name != StringName()) { - if (theme_owner->data.theme->has_stylebox(p_name, class_name)) { - return theme_owner->data.theme->get_stylebox(p_name, class_name); - } - - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - class_name = type; - - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - while (class_name != StringName()) { - if (Theme::get_project_default().is_valid() && Theme::get_project_default()->has_stylebox(p_name, type)) { - return Theme::get_project_default()->get_stylebox(p_name, type); - } - - if (Theme::get_default()->has_stylebox(p_name, class_name)) { - return Theme::get_default()->get_stylebox(p_name, class_name); - } - - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - return Theme::get_default()->get_stylebox(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types>(data.theme_owner, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } + Ref Control::get_font(const StringName &p_name, const StringName &p_theme_type) const { if (p_theme_type == StringName() || p_theme_type == get_class_name()) { const Ref *font = data.font_override.getptr(p_name); @@ -903,36 +933,11 @@ Ref Control::get_font(const StringName &p_name, const StringName &p_theme_ } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - while (theme_owner) { - StringName class_name = type; - - while (class_name != StringName()) { - if (theme_owner->data.theme->has_font(p_name, class_name)) { - return theme_owner->data.theme->get_font(p_name, class_name); - } - - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - if (theme_owner->data.theme->get_default_theme_font().is_valid()) { - return theme_owner->data.theme->get_default_theme_font(); - } - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - return Theme::get_default()->get_font(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types>(data.theme_owner, Theme::DATA_TYPE_FONT, p_name, theme_types); } + Color Control::get_color(const StringName &p_name, const StringName &p_theme_type) const { if (p_theme_type == StringName() || p_theme_type == get_class_name()) { const Color *color = data.color_override.getptr(p_name); @@ -941,37 +946,9 @@ Color Control::get_color(const StringName &p_name, const StringName &p_theme_typ } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - while (theme_owner) { - StringName class_name = type; - - while (class_name != StringName()) { - if (theme_owner->data.theme->has_color(p_name, class_name)) { - return theme_owner->data.theme->get_color(p_name, class_name); - } - - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_color(p_name, type)) { - return Theme::get_project_default()->get_color(p_name, type); - } - } - return Theme::get_default()->get_color(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types(data.theme_owner, Theme::DATA_TYPE_COLOR, p_name, theme_types); } int Control::get_constant(const StringName &p_name, const StringName &p_theme_type) const { @@ -982,37 +959,9 @@ int Control::get_constant(const StringName &p_name, const StringName &p_theme_ty } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - while (theme_owner) { - StringName class_name = type; - - while (class_name != StringName()) { - if (theme_owner->data.theme->has_constant(p_name, class_name)) { - return theme_owner->data.theme->get_constant(p_name, class_name); - } - - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_constant(p_name, type)) { - return Theme::get_project_default()->get_constant(p_name, type); - } - } - return Theme::get_default()->get_constant(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types(data.theme_owner, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } bool Control::has_icon_override(const StringName &p_name) const { @@ -1052,36 +1001,9 @@ bool Control::has_icon(const StringName &p_name, const StringName &p_theme_type) } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - while (theme_owner) { - StringName class_name = type; - - while (class_name != StringName()) { - if (theme_owner->data.theme->has_icon(p_name, class_name)) { - return true; - } - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_icon(p_name, type)) { - return true; - } - } - return Theme::get_default()->has_icon(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, Theme::DATA_TYPE_ICON, p_name, theme_types); } bool Control::has_shader(const StringName &p_name, const StringName &p_theme_type) const { @@ -1122,6 +1044,7 @@ bool Control::has_shader(const StringName &p_name, const StringName &p_theme_typ } return Theme::get_default()->has_shader(p_name, type); } + bool Control::has_stylebox(const StringName &p_name, const StringName &p_theme_type) const { if (p_theme_type == StringName() || p_theme_type == get_class_name()) { if (has_stylebox_override(p_name)) { @@ -1129,37 +1052,11 @@ bool Control::has_stylebox(const StringName &p_name, const StringName &p_theme_t } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - while (theme_owner) { - StringName class_name = type; - - while (class_name != StringName()) { - if (theme_owner->data.theme->has_stylebox(p_name, class_name)) { - return true; - } - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_stylebox(p_name, type)) { - return true; - } - } - return Theme::get_default()->has_stylebox(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } + bool Control::has_font(const StringName &p_name, const StringName &p_theme_type) const { if (p_theme_type == StringName() || p_theme_type == get_class_name()) { if (has_font_override(p_name)) { @@ -1167,36 +1064,9 @@ bool Control::has_font(const StringName &p_name, const StringName &p_theme_type) } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - while (theme_owner) { - StringName class_name = type; - - while (class_name != StringName()) { - if (theme_owner->data.theme->has_font(p_name, class_name)) { - return true; - } - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font(p_name, type)) { - return true; - } - } - return Theme::get_default()->has_font(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, Theme::DATA_TYPE_FONT, p_name, theme_types); } bool Control::has_color(const StringName &p_name, const StringName &p_theme_type) const { @@ -1206,36 +1076,9 @@ bool Control::has_color(const StringName &p_name, const StringName &p_theme_type } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - while (theme_owner) { - StringName class_name = type; - - while (class_name != StringName()) { - if (theme_owner->data.theme->has_color(p_name, class_name)) { - return true; - } - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_color(p_name, type)) { - return true; - } - } - return Theme::get_default()->has_color(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, Theme::DATA_TYPE_COLOR, p_name, theme_types); } bool Control::has_constant(const StringName &p_name, const StringName &p_theme_type) const { @@ -1245,40 +1088,13 @@ bool Control::has_constant(const StringName &p_name, const StringName &p_theme_t } } - StringName type = p_theme_type ? p_theme_type : get_class_name(); - - // try with custom themes - Control *theme_owner = data.theme_owner; - - while (theme_owner) { - StringName class_name = type; - - while (class_name != StringName()) { - if (theme_owner->data.theme->has_constant(p_name, class_name)) { - return true; - } - class_name = ClassDB::get_parent_class_nocheck(class_name); - } - - Control *parent = Object::cast_to(theme_owner->get_parent()); - - if (parent) { - theme_owner = parent->data.theme_owner; - } else { - theme_owner = nullptr; - } - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_constant(p_name, type)) { - return true; - } - } - return Theme::get_default()->has_constant(p_name, type); + List theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } Ref Control::get_theme_default_font() const { - // First, look through each control or window node in the branch, until no valid parent can be found. + // First, look through each control node in the branch, until no valid parent can be found. // Only nodes with a theme resource attached are considered. // For each theme resource see if their assigned theme has the default value defined and valid. Control *theme_owner = data.theme_owner; @@ -1288,8 +1104,7 @@ Ref Control::get_theme_default_font() const { return theme_owner->data.theme->get_default_theme_font(); } - Node *parent = theme_owner->get_parent(); - Control *parent_c = Object::cast_to(parent); + Control *parent_c = Object::cast_to(theme_owner->get_parent()); if (parent_c) { theme_owner = parent_c->data.theme_owner; } else { @@ -1725,6 +1540,7 @@ float Control::get_margin(Margin p_margin) const { Size2 Control::get_begin() const { return Size2(data.margin[0], data.margin[1]); } + Size2 Control::get_end() const { return Size2(data.margin[2], data.margin[3]); } @@ -1869,6 +1685,7 @@ void Control::add_shader_override(const StringName &p_name, const Ref &p } notification(NOTIFICATION_THEME_CHANGED); } + void Control::add_style_override(const StringName &p_name, const Ref &p_style) { if (data.style_override.has(p_name)) { data.style_override[p_name]->disconnect("changed", this, "_override_changed"); @@ -1902,10 +1719,12 @@ void Control::add_font_override(const StringName &p_name, const Ref &p_fon } notification(NOTIFICATION_THEME_CHANGED); } + void Control::add_color_override(const StringName &p_name, const Color &p_color) { data.color_override[p_name] = p_color; notification(NOTIFICATION_THEME_CHANGED); } + void Control::add_constant_override(const StringName &p_name, int p_constant) { data.constant_override[p_name] = p_constant; notification(NOTIFICATION_THEME_CHANGED); @@ -2148,6 +1967,7 @@ Control *Control::find_prev_valid_focus() const { Control::FocusMode Control::get_focus_mode() const { return data.focus_mode; } + bool Control::has_focus() const { return is_inside_tree() && get_viewport()->_gui_control_has_focus(this); } @@ -2284,6 +2104,7 @@ void Control::set_tooltip(const String &p_tooltip) { String Control::get_tooltip(const Point2 &p_pos) const { return data.tooltip; } + Control *Control::make_custom_tooltip(const String &p_text) const { if (get_script_instance()) { return const_cast(this)->call("_make_custom_tooltip", p_text); @@ -2300,6 +2121,7 @@ void Control::set_default_cursor_shape(CursorShape p_shape) { Control::CursorShape Control::get_default_cursor_shape() const { return data.default_cursor; } + Control::CursorShape Control::get_cursor_shape(const Point2 &p_pos) const { return data.default_cursor; } @@ -2494,6 +2316,7 @@ void Control::set_h_size_flags(int p_flags) { int Control::get_h_size_flags() const { return data.h_size_flags; } + void Control::set_v_size_flags(int p_flags) { if (data.v_size_flags == p_flags) { return; @@ -2634,6 +2457,7 @@ void Control::set_scale(const Vector2 &p_scale) { _notify_transform(); _change_notify("rect_scale"); } + Vector2 Control::get_scale() const { return data.scale; } @@ -2743,6 +2567,7 @@ void Control::set_v_grow_direction(GrowDirection p_direction) { data.v_grow = p_direction; _size_changed(); } + Control::GrowDirection Control::get_v_grow_direction() const { return data.v_grow; } diff --git a/scene/gui/control.h b/scene/gui/control.h index d095239a39fe..8073c8805cdb 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -239,6 +239,11 @@ class Control : public CanvasItem { void _update_minimum_size_cache(); + template + static T get_theme_item_in_types(Control *p_theme_owner, Theme::DataType p_data_type, const StringName &p_name, List p_theme_types); + static bool has_theme_item_in_types(Control *p_theme_owner, Theme::DataType p_data_type, const StringName &p_name, List p_theme_types); + _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List *p_list) const; + protected: virtual void add_child_notify(Node *p_child); virtual void remove_child_notify(Node *p_child); diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index f30df692fddd..b6cf520fa61e 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -470,7 +470,7 @@ Ref Theme::get_font(const StringName &p_name, const StringName &p_node_typ } bool Theme::has_font(const StringName &p_name, const StringName &p_node_type) const { - return (font_map.has(p_node_type) && font_map[p_node_type].has(p_name) && font_map[p_node_type][p_name].is_valid()); + return ((font_map.has(p_node_type) && font_map[p_node_type].has(p_name) && font_map[p_node_type][p_name].is_valid()) || has_default_theme_font()); } bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_node_type) const { @@ -924,6 +924,17 @@ void Theme::get_type_list(List *p_list) const { } } +void Theme::get_type_dependencies(const StringName &p_base_type, List *p_list) { + ERR_FAIL_NULL(p_list); + + // Build the dependency chain using native class hierarchy. + StringName class_name = p_base_type; + while (class_name != StringName()) { + p_list->push_back(class_name); + class_name = ClassDB::get_parent_class_nocheck(class_name); + } +} + // Internal methods for getting lists as a Vector of String (compatible with public API). PoolVector Theme::_get_icon_list(const String &p_node_type) const { PoolVector ilret; diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 31de21f80908..efaa1406ef04 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -189,6 +189,7 @@ class Theme : public Resource { void get_theme_item_types(DataType p_data_type, List *p_list) const; void get_type_list(List *p_list) const; + void get_type_dependencies(const StringName &p_base_type, List *p_list); void copy_default_theme(); void copy_theme(const Ref &p_other);