diff options
Diffstat (limited to 'scene/gui')
-rw-r--r-- | scene/gui/control.cpp | 433 | ||||
-rw-r--r-- | scene/gui/control.h | 51 | ||||
-rw-r--r-- | scene/gui/file_dialog.cpp | 4 | ||||
-rw-r--r-- | scene/gui/graph_node.cpp | 2 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 1 | ||||
-rw-r--r-- | scene/gui/tree.cpp | 457 | ||||
-rw-r--r-- | scene/gui/tree.h | 99 |
7 files changed, 550 insertions, 497 deletions
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index ce5eef93aa..83abd02b40 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -168,6 +168,12 @@ Size2 Control::_edit_get_minimum_size() const { } #endif +void Control::accept_event() { + if (is_inside_tree()) { + get_viewport()->_gui_accept_event(); + } +} + void Control::set_custom_minimum_size(const Size2 &p_custom) { if (p_custom == data.custom_minimum_size) { return; @@ -765,32 +771,27 @@ Size2 Control::get_minimum_size() const { } template <class T> -bool Control::_find_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, T &r_ret, T (Theme::*get_func)(const StringName &, const StringName &) const, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type) { - // try with custom themes +T Control::get_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) { + ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified."); + + // First, look through each control or window node in the branch, until no valid parent can be found. + // For each control iterate through its inheritance chain and see if p_name exists in any of them. Control *theme_owner = p_theme_owner; Window *theme_owner_window = p_theme_owner_window; while (theme_owner || theme_owner_window) { - StringName class_name = p_node_type; - - while (class_name != StringName()) { - if (theme_owner && (theme_owner->data.theme.operator->()->*has_func)(p_name, class_name)) { - r_ret = (theme_owner->data.theme.operator->()->*get_func)(p_name, class_name); - return true; + for (List<StringName>::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()); } - if (theme_owner_window && (theme_owner_window->theme.operator->()->*has_func)(p_name, class_name)) { - r_ret = (theme_owner_window->theme.operator->()->*get_func)(p_name, class_name); - return true; + if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E->get())) { + return theme_owner_window->theme->get_theme_item(p_data_type, p_name, E->get()); } - - class_name = ClassDB::get_parent_class_nocheck(class_name); } Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { theme_owner = parent_c->data.theme_owner; theme_owner_window = parent_c->data.theme_owner_window; @@ -805,33 +806,47 @@ bool Control::_find_theme_item(Control *p_theme_owner, Window *p_theme_owner_win } } } - return false; + + // Secondly, check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + for (List<StringName>::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()); + } + } + } + + // Lastly, fall back on the items defined in the default Theme, if they exist. + for (List<StringName>::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]); } -bool Control::_has_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type) { - // try with custom themes +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) { + ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified."); + + // First, look through each control or window node in the branch, until no valid parent can be found. + // For each control iterate through its inheritance chain and see if p_name exists in any of them. Control *theme_owner = p_theme_owner; Window *theme_owner_window = p_theme_owner_window; while (theme_owner || theme_owner_window) { - StringName class_name = p_node_type; - - while (class_name != StringName()) { - if (theme_owner && (theme_owner->data.theme.operator->()->*has_func)(p_name, class_name)) { + for (List<StringName>::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; } - if (theme_owner_window && (theme_owner_window->theme.operator->()->*has_func)(p_name, class_name)) { + if (theme_owner_window && theme_owner_window->theme->has_theme_item(p_data_type, p_name, E->get())) { return true; } - - class_name = ClassDB::get_parent_class_nocheck(class_name); } Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent(); - Control *parent_c = Object::cast_to<Control>(parent); - if (parent_c) { theme_owner = parent_c->data.theme_owner; theme_owner_window = parent_c->data.theme_owner_window; @@ -846,179 +861,112 @@ bool Control::_has_theme_item(Control *p_theme_owner, Window *p_theme_owner_wind } } } - return false; -} -Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { - const Ref<Texture2D> *tex = data.icon_override.getptr(p_name); - if (tex) { - return *tex; + // Secondly, check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + for (List<StringName>::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; + } } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_icons(data.theme_owner, data.theme_owner_window, p_name, type); + // Lastly, fall back on the items defined in the default Theme, if they exist. + for (List<StringName>::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; } -Ref<Texture2D> Control::get_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Ref<Texture2D> icon; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, icon, &Theme::get_icon, &Theme::has_icon, p_name, p_node_type)) { - return icon; +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_custom_type) { + if (data.theme_custom_type != StringName()) { + p_list->push_back(data.theme_custom_type); + } + Theme::get_type_dependencies(get_class_name(), p_list); + } else { + Theme::get_type_dependencies(p_theme_type, p_list); } +} - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_icon(p_name, p_node_type)) { - return Theme::get_project_default()->get_icon(p_name, p_node_type); +Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { + const Ref<Texture2D> *tex = data.icon_override.getptr(p_name); + if (tex) { + return *tex; } } - return Theme::get_default()->get_icon(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<Ref<Texture2D>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); } -Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const Ref<StyleBox> *style = data.style_override.getptr(p_name); if (style) { return *style; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_styleboxs(data.theme_owner, data.theme_owner_window, p_name, type); -} - -Ref<StyleBox> Control::get_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Ref<StyleBox> stylebox; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, stylebox, &Theme::get_stylebox, &Theme::has_stylebox, p_name, p_node_type)) { - return stylebox; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_stylebox(p_name, p_node_type)) { - return Theme::get_project_default()->get_stylebox(p_name, p_node_type); - } - } - - return Theme::get_default()->get_stylebox(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<Ref<StyleBox>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } -Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const Ref<Font> *font = data.font_override.getptr(p_name); if (font) { return *font; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_fonts(data.theme_owner, data.theme_owner_window, p_name, type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<Ref<Font>>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); } -int Control::get_theme_font_size(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const int *font_size = data.font_size_override.getptr(p_name); if (font_size) { return *font_size; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_font_sizes(data.theme_owner, data.theme_owner_window, p_name, type); -} - -Ref<Font> Control::get_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Ref<Font> font; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, font, &Theme::get_font, &Theme::has_font, p_name, p_node_type)) { - return font; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font(p_name, p_node_type)) { - return Theme::get_project_default()->get_font(p_name, p_node_type); - } - } - - return Theme::get_default()->get_font(p_name, p_node_type); -} - -int Control::get_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - int font_size; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, font_size, &Theme::get_font_size, &Theme::has_font_size, p_name, p_node_type)) { - return font_size; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font_size(p_name, p_node_type)) { - return Theme::get_project_default()->get_font_size(p_name, p_node_type); - } - } - - return Theme::get_default()->get_font_size(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } -Color Control::get_theme_color(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const Color *color = data.color_override.getptr(p_name); if (color) { return *color; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_colors(data.theme_owner, data.theme_owner_window, p_name, type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<Color>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); } -Color Control::get_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - Color color; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, color, &Theme::get_color, &Theme::has_color, p_name, p_node_type)) { - return color; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_color(p_name, p_node_type)) { - return Theme::get_project_default()->get_color(p_name, p_node_type); - } - } - return Theme::get_default()->get_color(p_name, p_node_type); -} - -int Control::get_theme_constant(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { const int *constant = data.constant_override.getptr(p_name); if (constant) { return *constant; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return get_constants(data.theme_owner, data.theme_owner_window, p_name, type); -} - -int Control::get_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - int constant; - - if (_find_theme_item(p_theme_owner, p_theme_owner_window, constant, &Theme::get_constant, &Theme::has_constant, p_name, p_node_type)) { - return constant; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_constant(p_name, p_node_type)) { - return Theme::get_project_default()->get_constant(p_name, p_node_type); - } - } - return Theme::get_default()->get_constant(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return get_theme_item_in_types<int>(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } bool Control::has_theme_icon_override(const StringName &p_name) const { @@ -1051,154 +999,76 @@ bool Control::has_theme_constant_override(const StringName &p_name) const { return constant != nullptr; } -bool Control::has_theme_icon(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_icon_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_icons(data.theme_owner, data.theme_owner_window, p_name, type); -} - -bool Control::has_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_icon, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_color(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_icon(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_ICON, p_name, theme_types); } -bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_stylebox_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_styleboxs(data.theme_owner, data.theme_owner_window, p_name, type); -} - -bool Control::has_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_stylebox, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_stylebox(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_stylebox(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_STYLEBOX, p_name, theme_types); } -bool Control::has_theme_font(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_font_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_fonts(data.theme_owner, data.theme_owner_window, p_name, type); -} - -bool Control::has_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_font, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_font(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT, p_name, theme_types); } -bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_font_size_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_font_sizes(data.theme_owner, data.theme_owner_window, p_name, type); -} - -bool Control::has_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_font_size, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_font_size(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_font_size(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types); } -bool Control::has_theme_color(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_color_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_colors(data.theme_owner, data.theme_owner_window, p_name, type); -} - -bool Control::has_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_color, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_color(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_color(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_COLOR, p_name, theme_types); } -bool Control::has_theme_constant(const StringName &p_name, const StringName &p_node_type) const { - if (p_node_type == StringName() || p_node_type == get_class_name()) { +bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_custom_type) { if (has_theme_constant_override(p_name)) { return true; } } - StringName type = p_node_type ? p_node_type : get_class_name(); - - return has_constants(data.theme_owner, data.theme_owner_window, p_name, p_node_type); -} - -bool Control::has_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type) { - if (_has_theme_item(p_theme_owner, p_theme_owner_window, &Theme::has_constant, p_name, p_node_type)) { - return true; - } - - if (Theme::get_project_default().is_valid()) { - if (Theme::get_project_default()->has_constant(p_name, p_node_type)) { - return true; - } - } - return Theme::get_default()->has_constant(p_name, p_node_type); + List<StringName> theme_types; + _get_theme_type_dependencies(p_theme_type, &theme_types); + return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types); } Rect2 Control::get_parent_anchorable_rect() const { @@ -2171,16 +2041,19 @@ void Control::set_theme(const Ref<Theme> &p_theme) { } } -void Control::accept_event() { - if (is_inside_tree()) { - get_viewport()->_gui_accept_event(); - } -} - Ref<Theme> Control::get_theme() const { return data.theme; } +void Control::set_theme_custom_type(const StringName &p_theme_type) { + data.theme_custom_type = p_theme_type; + _propagate_theme_changed(this, data.theme_owner, data.theme_owner_window); +} + +StringName Control::get_theme_custom_type() const { + return data.theme_custom_type; +} + void Control::set_tooltip(const String &p_tooltip) { data.tooltip = p_tooltip; update_configuration_warnings(); @@ -2499,9 +2372,9 @@ bool Control::is_text_field() const { return false; } -Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_node_type, const Array &p_args, const String p_text) const { +Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const { Vector<Vector2i> ret; - switch (p_node_type) { + switch (p_theme_type) { case STRUCTURED_TEXT_URI: { int prev = 0; for (int i = 0; i < p_text.length(); i++) { @@ -2811,6 +2684,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Control::set_theme); ClassDB::bind_method(D_METHOD("get_theme"), &Control::get_theme); + ClassDB::bind_method(D_METHOD("set_theme_custom_type", "theme_type"), &Control::set_theme_custom_type); + ClassDB::bind_method(D_METHOD("get_theme_custom_type"), &Control::get_theme_custom_type); + ClassDB::bind_method(D_METHOD("add_theme_icon_override", "name", "texture"), &Control::add_theme_icon_override); ClassDB::bind_method(D_METHOD("add_theme_stylebox_override", "name", "stylebox"), &Control::add_theme_style_override); ClassDB::bind_method(D_METHOD("add_theme_font_override", "name", "font"), &Control::add_theme_font_override); @@ -2825,12 +2701,12 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_theme_color_override", "name"), &Control::remove_theme_color_override); ClassDB::bind_method(D_METHOD("remove_theme_constant_override", "name"), &Control::remove_theme_constant_override); - ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "node_type"), &Control::get_theme_icon, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "node_type"), &Control::get_theme_stylebox, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_font", "name", "node_type"), &Control::get_theme_font, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "node_type"), &Control::get_theme_font_size, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_color", "name", "node_type"), &Control::get_theme_color, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "node_type"), &Control::get_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Control::get_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Control::get_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font", "name", "theme_type"), &Control::get_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Control::get_theme_font_size, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_color", "name", "theme_type"), &Control::get_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Control::get_theme_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("has_theme_icon_override", "name"), &Control::has_theme_icon_override); ClassDB::bind_method(D_METHOD("has_theme_stylebox_override", "name"), &Control::has_theme_stylebox_override); @@ -2839,12 +2715,12 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("has_theme_color_override", "name"), &Control::has_theme_color_override); ClassDB::bind_method(D_METHOD("has_theme_constant_override", "name"), &Control::has_theme_constant_override); - ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "node_type"), &Control::has_theme_icon, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "node_type"), &Control::has_theme_stylebox, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_font", "name", "node_type"), &Control::has_theme_font, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "node_type"), &Control::has_theme_font_size, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_color", "name", "node_type"), &Control::has_theme_color, DEFVAL("")); - ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "node_type"), &Control::has_theme_constant, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Control::has_theme_icon, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Control::has_theme_stylebox, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font", "name", "theme_type"), &Control::has_theme_font, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Control::has_theme_font_size, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL("")); + ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_parent_control"), &Control::get_parent_control); @@ -2958,8 +2834,9 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); - ADD_GROUP("Theme", ""); + ADD_GROUP("Theme", "theme_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_custom_type"), "set_theme_custom_type", "get_theme_custom_type"); ADD_GROUP("", ""); BIND_ENUM_CONSTANT(FOCUS_NONE); diff --git a/scene/gui/control.h b/scene/gui/control.h index 1f397df589..a05025c32d 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -201,6 +201,8 @@ private: Ref<Theme> theme; Control *theme_owner = nullptr; Window *theme_owner_window = nullptr; + StringName theme_custom_type; + String tooltip; CursorShape default_cursor = CURSOR_ARROW; @@ -258,23 +260,9 @@ private: static void _propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign = true); template <class T> - _FORCE_INLINE_ static bool _find_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, T &, T (Theme::*get_func)(const StringName &, const StringName &) const, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type); - - _FORCE_INLINE_ static bool _has_theme_item(Control *p_theme_owner, Window *p_theme_owner_window, bool (Theme::*has_func)(const StringName &, const StringName &) const, const StringName &p_name, const StringName &p_node_type); - - static Ref<Texture2D> get_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static Ref<StyleBox> get_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static Ref<Font> get_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static int get_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static Color get_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static int get_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - - static bool has_icons(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_styleboxs(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_fonts(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_font_sizes(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_colors(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); - static bool has_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_node_type = StringName()); + static T get_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); + static bool 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); + _FORCE_INLINE_ void _get_theme_type_dependencies(const StringName &p_theme_type, List<StringName> *p_list) const; protected: virtual void add_child_notify(Node *p_child) override; @@ -282,7 +270,7 @@ protected: //virtual void _window_gui_input(InputEvent p_event); - virtual Vector<Vector2i> structured_text_parser(StructuredTextParser p_node_type, const Array &p_args, const String p_text) const; + virtual Vector<Vector2i> structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const; bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -415,6 +403,9 @@ public: void set_theme(const Ref<Theme> &p_theme); Ref<Theme> get_theme() const; + void set_theme_custom_type(const StringName &p_theme_type); + StringName get_theme_custom_type() const; + void set_h_size_flags(int p_flags); int get_h_size_flags() const; @@ -466,12 +457,12 @@ public: void remove_theme_color_override(const StringName &p_name); void remove_theme_constant_override(const StringName &p_name); - Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_node_type = StringName()) const; - Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_node_type = StringName()) const; - Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_node_type = StringName()) const; - int get_theme_font_size(const StringName &p_name, const StringName &p_node_type = StringName()) const; - Color get_theme_color(const StringName &p_name, const StringName &p_node_type = StringName()) const; - int get_theme_constant(const StringName &p_name, const StringName &p_node_type = StringName()) const; + Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + int get_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + Color get_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + int get_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; bool has_theme_icon_override(const StringName &p_name) const; bool has_theme_stylebox_override(const StringName &p_name) const; @@ -480,12 +471,12 @@ public: bool has_theme_color_override(const StringName &p_name) const; bool has_theme_constant_override(const StringName &p_name) const; - bool has_theme_icon(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_stylebox(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_font(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_font_size(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_color(const StringName &p_name, const StringName &p_node_type = StringName()) const; - bool has_theme_constant(const StringName &p_name, const StringName &p_node_type = StringName()) const; + bool has_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const; + bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const; /* TOOLTIP */ diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 350c77039f..806039d7ac 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -589,8 +589,8 @@ void FileDialog::update_file_list() { files.pop_front(); } - if (tree->get_root() && tree->get_root()->get_children() && tree->get_selected() == nullptr) { - tree->get_root()->get_children()->select(0); + if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { + tree->get_root()->get_first_child()->select(0); } } diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 7771970a22..1ca54a16ae 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -228,7 +228,7 @@ void GraphNode::_resort() { } stretch_avail += stretch_diff - sb->get_margin(SIDE_BOTTOM) - sb->get_margin(SIDE_TOP); //available stretch space. - /** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable + /** Second, pass successively to discard elements that can't be stretched, this will run while stretchable elements exist */ while (stretch_ratio_total > 0) { // first of all, don't even be here if no stretchable objects exist diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index ba98b3c931..e305c6210d 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -6853,6 +6853,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count); ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text); ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line); + ClassDB::bind_method(D_METHOD("get_visible_line_count"), &TextEdit::get_total_visible_rows); ClassDB::bind_method(D_METHOD("set_line", "line", "new_text"), &TextEdit::set_line); ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &TextEdit::set_structured_text_bidi_override); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 9e4c73679e..2f78736a12 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -47,36 +47,6 @@ #include <limits.h> -void TreeItem::move_to_top() { - if (!parent || parent->children == this) { - return; //already on top - } - TreeItem *prev = get_prev(); - prev->next = next; - next = parent->children; - parent->children = this; -} - -void TreeItem::move_to_bottom() { - if (!parent || !next) { - return; - } - - TreeItem *prev = get_prev(); - TreeItem *last = next; - while (last->next) { - last = last->next; - } - - if (prev) { - prev->next = next; - } else { - parent->children = next; - } - last->next = this; - next = nullptr; -} - Size2 TreeItem::Cell::get_icon_size() const { if (icon.is_null()) { return Size2(); @@ -118,6 +88,54 @@ void TreeItem::_cell_deselected(int p_cell) { tree->item_deselected(p_cell, this); } +void TreeItem::_change_tree(Tree *p_tree) { + if (p_tree == tree) { + return; + } + + TreeItem *c = first_child; + while (c) { + c->_change_tree(p_tree); + c = c->next; + } + + if (tree && tree->root == this) { + tree->root = nullptr; + } + + if (tree && tree->popup_edited_item == this) { + tree->popup_edited_item = nullptr; + tree->pressing_for_editor = false; + } + + if (tree && tree->cache.hover_item == this) { + tree->cache.hover_item = nullptr; + } + + if (tree && tree->selected_item == this) { + tree->selected_item = nullptr; + } + + if (tree && tree->drop_mode_over == this) { + tree->drop_mode_over = nullptr; + } + + if (tree && tree->single_select_defer == this) { + tree->single_select_defer = nullptr; + } + + if (tree && tree->edited_item == this) { + tree->edited_item = nullptr; + tree->pressing_for_editor = false; + } + + tree = p_tree; + + if (tree) { + cells.resize(tree->columns.size()); + } +} + /* cell mode */ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); @@ -427,20 +445,74 @@ int TreeItem::get_custom_minimum_height() const { return custom_min_height; } +/* Item manipulation */ + +TreeItem *TreeItem::create_child(int p_idx) { + TreeItem *ti = memnew(TreeItem(tree)); + if (tree) { + ti->cells.resize(tree->columns.size()); + } + + TreeItem *l_prev = nullptr; + TreeItem *c = first_child; + int idx = 0; + + while (c) { + if (idx++ == p_idx) { + c->prev = ti; + ti->next = c; + break; + } + l_prev = c; + c = c->next; + } + + if (l_prev) { + l_prev->next = ti; + ti->prev = l_prev; + if (!children_cache.is_empty()) { + if (ti->next) { + children_cache.insert(p_idx, ti); + } else { + children_cache.append(ti); + } + } + } else { + first_child = ti; + if (!children_cache.is_empty()) { + children_cache.insert(0, ti); + } + } + + ti->parent = this; + + return ti; +} + +Tree *TreeItem::get_tree() { + return tree; +} + TreeItem *TreeItem::get_next() { return next; } TreeItem *TreeItem::get_prev() { - if (!parent || parent->children == this) { - return nullptr; + if (prev) { + return prev; } - TreeItem *prev = parent->children; - while (prev && prev->next != this) { - prev = prev->next; + if (!parent || parent->first_child == this) { + return nullptr; + } + // This is an edge case + TreeItem *l_prev = parent->first_child; + while (l_prev && l_prev->next != this) { + l_prev = l_prev->next; } + prev = l_prev; + return prev; } @@ -448,8 +520,8 @@ TreeItem *TreeItem::get_parent() { return parent; } -TreeItem *TreeItem::get_children() { - return children; +TreeItem *TreeItem::get_first_child() { + return first_child; } TreeItem *TreeItem::get_prev_visible(bool p_wrap) { @@ -475,10 +547,10 @@ TreeItem *TreeItem::get_prev_visible(bool p_wrap) { } } else { current = prev; - while (!current->collapsed && current->children) { + while (!current->collapsed && current->first_child) { //go to the very end - current = current->children; + current = current->first_child; while (current->next) { current = current->next; } @@ -491,8 +563,8 @@ TreeItem *TreeItem::get_prev_visible(bool p_wrap) { TreeItem *TreeItem::get_next_visible(bool p_wrap) { TreeItem *current = this; - if (!current->collapsed && current->children) { - current = current->children; + if (!current->collapsed && current->first_child) { + current = current->first_child; } else if (current->next) { current = current->next; @@ -515,24 +587,136 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) { return current; } -void TreeItem::remove_child(TreeItem *p_item) { +TreeItem *TreeItem::get_child(int p_idx) { + _create_children_cache(); + ERR_FAIL_INDEX_V(p_idx, children_cache.size(), nullptr); + return children_cache.get(p_idx); +} + +int TreeItem::get_child_count() { + _create_children_cache(); + return children_cache.size(); +} + +Array TreeItem::get_children() { + int size = get_child_count(); + Array arr; + arr.resize(size); + for (int i = 0; i < size; i++) { + arr[i] = children_cache[i]; + } + + return arr; +} + +int TreeItem::get_index() { + int idx = 0; + TreeItem *c = this; + + while (c) { + c = c->get_prev(); + idx++; + } + return idx - 1; +} + +void TreeItem::move_before(TreeItem *p_item) { ERR_FAIL_NULL(p_item); - TreeItem **c = &children; + ERR_FAIL_COND(is_root); + ERR_FAIL_COND(!p_item->parent); - while (*c) { - if ((*c) == p_item) { - TreeItem *aux = *c; + if (p_item == this) { + return; + } - *c = (*c)->next; + TreeItem *p = p_item->parent; + while (p) { + ERR_FAIL_COND_MSG(p == this, "Can't move to a descendant"); + p = p->parent; + } - aux->parent = nullptr; - return; - } + Tree *old_tree = tree; + _unlink_from_tree(); + _change_tree(p_item->tree); + + parent = p_item->parent; + + TreeItem *item_prev = p_item->get_prev(); + if (item_prev) { + item_prev->next = this; + parent->children_cache.clear(); + } else { + parent->first_child = this; + parent->children_cache.insert(0, this); + } + + prev = item_prev; + next = p_item; + p_item->prev = this; + + if (old_tree && old_tree != tree) { + old_tree->update(); + } + + if (tree) { + tree->update(); + } +} + +void TreeItem::move_after(TreeItem *p_item) { + ERR_FAIL_NULL(p_item); + ERR_FAIL_COND(is_root); + ERR_FAIL_COND(!p_item->parent); + + if (p_item == this) { + return; + } - c = &(*c)->next; + TreeItem *p = p_item->parent; + while (p) { + ERR_FAIL_COND_MSG(p == this, "Can't move to a descendant"); + p = p->parent; } - ERR_FAIL(); + Tree *old_tree = tree; + _unlink_from_tree(); + _change_tree(p_item->tree); + + if (p_item->next) { + p_item->next->prev = this; + } + parent = p_item->parent; + prev = p_item; + next = p_item->next; + p_item->next = this; + + if (next) { + parent->children_cache.clear(); + } else { + parent->children_cache.append(this); + } + + if (old_tree && old_tree != tree) { + old_tree->update(); + } + + if (tree) { + tree->update(); + } +} + +void TreeItem::remove_child(TreeItem *p_item) { + ERR_FAIL_NULL(p_item); + ERR_FAIL_COND(p_item->parent != this); + + p_item->_unlink_from_tree(); + p_item->prev = nullptr; + p_item->next = nullptr; + p_item->parent = nullptr; + + if (tree) { + tree->update(); + } } void TreeItem::set_selectable(int p_column, bool p_selectable) { @@ -785,7 +969,7 @@ void recursive_call_aux(TreeItem *p_item, const StringName &p_method, const Vari return; } p_item->call(p_method, p_args, p_argcount, r_error); - TreeItem *c = p_item->get_children(); + TreeItem *c = p_item->get_first_child(); while (c) { recursive_call_aux(c, p_method, p_args, p_argcount, r_error); c = c->get_next(); @@ -855,16 +1039,6 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_minimum_height", "height"), &TreeItem::set_custom_minimum_height); ClassDB::bind_method(D_METHOD("get_custom_minimum_height"), &TreeItem::get_custom_minimum_height); - ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next); - ClassDB::bind_method(D_METHOD("get_prev"), &TreeItem::get_prev); - ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent); - ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children); - - ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false)); - - ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child); - ClassDB::bind_method(D_METHOD("set_selectable", "column", "selectable"), &TreeItem::set_selectable); ClassDB::bind_method(D_METHOD("is_selectable", "column"), &TreeItem::is_selectable); @@ -895,19 +1069,38 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_idx", "disabled"), &TreeItem::set_button_disabled); ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_idx"), &TreeItem::is_button_disabled); - ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right); - ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right); - ClassDB::bind_method(D_METHOD("set_tooltip", "column", "tooltip"), &TreeItem::set_tooltip); ClassDB::bind_method(D_METHOD("get_tooltip", "column"), &TreeItem::get_tooltip); ClassDB::bind_method(D_METHOD("set_text_align", "column", "text_align"), &TreeItem::set_text_align); ClassDB::bind_method(D_METHOD("get_text_align", "column"), &TreeItem::get_text_align); - ClassDB::bind_method(D_METHOD("move_to_top"), &TreeItem::move_to_top); - ClassDB::bind_method(D_METHOD("move_to_bottom"), &TreeItem::move_to_bottom); + + ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right); + ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right); ClassDB::bind_method(D_METHOD("set_disable_folding", "disable"), &TreeItem::set_disable_folding); ClassDB::bind_method(D_METHOD("is_folding_disabled"), &TreeItem::is_folding_disabled); + ClassDB::bind_method(D_METHOD("create_child", "idx"), &TreeItem::create_child, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("get_tree"), &TreeItem::get_tree); + + ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next); + ClassDB::bind_method(D_METHOD("get_prev"), &TreeItem::get_prev); + ClassDB::bind_method(D_METHOD("get_parent"), &TreeItem::get_parent); + ClassDB::bind_method(D_METHOD("get_first_child"), &TreeItem::get_first_child); + + ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false)); + + ClassDB::bind_method(D_METHOD("get_child", "idx"), &TreeItem::get_child); + ClassDB::bind_method(D_METHOD("get_child_count"), &TreeItem::get_child_count); + ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children); + ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index); + + ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::_move_before); + ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::_move_after); + + ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child); + { MethodInfo mi; mi.name = "call_recursive"; @@ -932,7 +1125,7 @@ void TreeItem::_bind_methods() { } void TreeItem::clear_children() { - TreeItem *c = children; + TreeItem *c = first_child; while (c) { TreeItem *aux = c; c = c->get_next(); @@ -940,56 +1133,18 @@ void TreeItem::clear_children() { memdelete(aux); } - children = nullptr; + first_child = nullptr; }; TreeItem::TreeItem(Tree *p_tree) { tree = p_tree; - collapsed = false; - disable_folding = false; - custom_min_height = 0; - - parent = nullptr; // parent item - next = nullptr; // next in list - children = nullptr; //child items } TreeItem::~TreeItem() { + _unlink_from_tree(); + prev = nullptr; clear_children(); - - if (parent) { - parent->remove_child(this); - } - - if (tree && tree->root == this) { - tree->root = nullptr; - } - - if (tree && tree->popup_edited_item == this) { - tree->popup_edited_item = nullptr; - tree->pressing_for_editor = false; - } - - if (tree && tree->cache.hover_item == this) { - tree->cache.hover_item = nullptr; - } - - if (tree && tree->selected_item == this) { - tree->selected_item = nullptr; - } - - if (tree && tree->drop_mode_over == this) { - tree->drop_mode_over = nullptr; - } - - if (tree && tree->single_select_defer == this) { - tree->single_select_defer = nullptr; - } - - if (tree && tree->edited_item == this) { - tree->edited_item = nullptr; - tree->pressing_for_editor = false; - } + _change_tree(nullptr); } /**********************************************/ @@ -1116,7 +1271,7 @@ int Tree::get_item_height(TreeItem *p_item) const { if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { height += get_item_height(c); @@ -1263,7 +1418,7 @@ void Tree::update_item_cache(TreeItem *p_item) { update_item_cell(p_item, i); } - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { update_item_cache(c); c = c->next; @@ -1430,7 +1585,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (drop_mode_flags && drop_mode_over == p_item) { Rect2 r = cell_rect; - bool has_parent = p_item->get_children() != nullptr; + bool has_parent = p_item->get_first_child() != nullptr; if (rtl) { r.position.x = get_size().width - r.position.x - r.size.x; } @@ -1626,7 +1781,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } - if (!p_item->disable_folding && !hide_folding && p_item->children) { //has children, draw the guide box + if (!p_item->disable_folding && !hide_folding && p_item->first_child) { //has children, draw the guide box Ref<Texture2D> arrow; @@ -1656,7 +1811,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; int prev_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y; @@ -1666,7 +1821,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int parent_ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin); Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs; - if (c->get_children() != nullptr) { + if (c->get_first_child() != nullptr) { root_pos -= Point2i(cache.arrow->get_width(), 0); } @@ -1723,8 +1878,8 @@ int Tree::_count_selected_items(TreeItem *p_from) const { } } - if (p_from->get_children()) { - count += _count_selected_items(p_from->get_children()); + if (p_from->get_first_child()) { + count += _count_selected_items(p_from->get_first_child()); } if (p_from->get_next()) { @@ -1812,7 +1967,7 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c *r_in_range = false; } - TreeItem *c = p_current->children; + TreeItem *c = p_current->first_child; while (c) { select_single_item(p_selected, c, p_col, p_prev, r_in_range, p_current->is_collapsed() || p_force_deselect); @@ -1839,7 +1994,6 @@ void Tree::_range_click_timeout() { click_handled = false; Ref<InputEventMouseButton> mb; mb.instance(); - ; propagate_mouse_activated = false; // done from outside, so signal handler can't clear the tree in the middle of emit (which is a common case) blocked++; @@ -1879,7 +2033,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool } if (!p_item->disable_folding && !hide_folding && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) { - if (p_item->children) { + if (p_item->first_child) { p_item->set_collapsed(!p_item->is_collapsed()); } @@ -1926,7 +2080,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool x -= cache.hseparation; } - if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_children()) { + if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) { p_item->set_collapsed(!p_item->is_collapsed()); return -1; //collapse/uncollapse because nothing can be done with item } @@ -2164,7 +2318,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool if (!p_item->collapsed) { /* if not collapsed, check the children */ - TreeItem *c = p_item->children; + TreeItem *c = p_item->first_child; while (c) { int child_h = propagate_mouse_event(new_pos, x_ofs, y_ofs, p_double_click, c, p_button, p_mod); @@ -2270,7 +2424,7 @@ void Tree::popup_select(int p_option) { void Tree::_go_left() { if (selected_col == 0) { - if (selected_item->get_children() != nullptr && !selected_item->is_collapsed()) { + if (selected_item->get_first_child() != nullptr && !selected_item->is_collapsed()) { selected_item->set_collapsed(true); } else { if (columns.size() == 1) { // goto parent with one column @@ -2298,7 +2452,7 @@ void Tree::_go_left() { void Tree::_go_right() { if (selected_col == (columns.size() - 1)) { - if (selected_item->get_children() != nullptr && selected_item->is_collapsed()) { + if (selected_item->get_first_child() != nullptr && selected_item->is_collapsed()) { selected_item->set_collapsed(false); } else if (selected_item->get_next_visible()) { selected_col = 0; @@ -2417,7 +2571,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } if (k.is_valid() && k->is_alt_pressed()) { selected_item->set_collapsed(false); - TreeItem *next = selected_item->get_children(); + TreeItem *next = selected_item->get_first_child(); while (next && next != selected_item->next) { next->set_collapsed(false); next = next->get_next_visible(); @@ -2436,7 +2590,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { if (k.is_valid() && k->is_alt_pressed()) { selected_item->set_collapsed(true); - TreeItem *next = selected_item->get_children(); + TreeItem *next = selected_item->get_first_child(); while (next && next != selected_item->next) { next->set_collapsed(true); next = next->get_next_visible(); @@ -2834,7 +2988,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { break; } } - if (!root || (!root->get_children() && hide_root)) { + if (!root || (!root->get_first_child() && hide_root)) { if (b->get_button_index() == MOUSE_BUTTON_RIGHT && allow_rmb_select) { emit_signal("empty_tree_rmb_selected", get_local_mouse_position()); } @@ -3269,38 +3423,15 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { TreeItem *ti = nullptr; if (p_parent) { - // Append or insert a new item to the given parent. - ti = memnew(TreeItem(this)); - ERR_FAIL_COND_V(!ti, nullptr); - ti->cells.resize(columns.size()); - - TreeItem *prev = nullptr; - TreeItem *c = p_parent->children; - int idx = 0; - - while (c) { - if (idx++ == p_idx) { - ti->next = c; - break; - } - prev = c; - c = c->next; - } - - if (prev) { - prev->next = ti; - } else { - p_parent->children = ti; - } - ti->parent = p_parent; - + ERR_FAIL_COND_V_MSG(p_parent->tree != this, nullptr, "A different tree owns the given parent"); + ti = p_parent->create_child(p_idx); } else { if (!root) { // No root exists, make the given item the new root. ti = memnew(TreeItem(this)); ERR_FAIL_COND_V(!ti, nullptr); ti->cells.resize(columns.size()); - + ti->is_root = true; root = ti; } else { // Root exists, append or insert to root. @@ -3321,8 +3452,8 @@ TreeItem *Tree::get_last_item() { while (last) { if (last->next) { last = last->next; - } else if (last->children) { - last = last->children; + } else if (last->first_child) { + last = last->first_child; } else { break; } @@ -3491,8 +3622,8 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { if (!p_item) { p_item = root; } else { - if (p_item->children) { - p_item = p_item->children; + if (p_item->first_child) { + p_item = p_item->first_child; } else if (p_item->next) { p_item = p_item->next; @@ -3561,7 +3692,7 @@ int Tree::get_column_width(int p_column) const { void Tree::propagate_set_columns(TreeItem *p_item) { p_item->cells.resize(columns.size()); - TreeItem *c = p_item->get_children(); + TreeItem *c = p_item->get_first_child(); while (c) { propagate_set_columns(c); c = c->next; @@ -3611,8 +3742,8 @@ int Tree::get_item_offset(TreeItem *p_item) const { ofs += cache.vseparation; } - if (it->children && !it->collapsed) { - it = it->children; + if (it->first_child && !it->collapsed) { + it = it->first_child; } else if (it->next) { it = it->next; @@ -3935,7 +4066,7 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_ return nullptr; // do not try children, it's collapsed } - TreeItem *n = p_item->get_children(); + TreeItem *n = p_item->get_first_child(); while (n) { int ch; TreeItem *r = _find_item_at_pos(n, pos, r_column, ch, section); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 6d36f0df7f..9dbfd93082 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -122,14 +122,18 @@ private: Vector<Cell> cells; - bool collapsed; // won't show children - bool disable_folding; - int custom_min_height; + bool collapsed = false; // won't show children + bool disable_folding = false; + int custom_min_height = 0; - TreeItem *parent; // parent item - TreeItem *next; // next in list - TreeItem *children; //child items - Tree *tree; //tree (for reference) + TreeItem *parent = nullptr; // parent item + TreeItem *prev = nullptr; // previous in list + TreeItem *next = nullptr; // next in list + TreeItem *first_child = nullptr; + + Vector<TreeItem *> children_cache; + bool is_root = false; // for tree root + Tree *tree; // tree (for reference) TreeItem(Tree *p_tree); @@ -138,9 +142,40 @@ private: void _cell_selected(int p_cell); void _cell_deselected(int p_cell); + void _change_tree(Tree *p_tree); + + _FORCE_INLINE_ void _create_children_cache() { + if (children_cache.is_empty()) { + TreeItem *c = first_child; + while (c) { + children_cache.append(c); + c = c->next; + } + } + } + + _FORCE_INLINE_ void _unlink_from_tree() { + TreeItem *p = get_prev(); + if (p) { + p->next = next; + } + if (next) { + next->prev = p; + } + if (parent) { + if (!parent->children_cache.is_empty()) { + parent->children_cache.remove(get_index()); + } + if (parent->first_child == this) { + parent->first_child = next; + } + } + } + protected: static void _bind_methods(); - //bind helpers + + // Bind helpers Dictionary _get_range_config(int p_column) { Dictionary d; double min = 0.0, max = 0.0, step = 0.0; @@ -156,6 +191,13 @@ protected: remove_child(Object::cast_to<TreeItem>(p_child)); } + void _move_before(Object *p_item) { + move_before(Object::cast_to<TreeItem>(p_item)); + } + void _move_after(Object *p_item) { + move_after(Object::cast_to<TreeItem>(p_item)); + } + Variant _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); public: @@ -234,16 +276,6 @@ public: void set_custom_minimum_height(int p_height); int get_custom_minimum_height() const; - TreeItem *get_prev(); - TreeItem *get_next(); - TreeItem *get_parent(); - TreeItem *get_children(); - - TreeItem *get_prev_visible(bool p_wrap = false); - TreeItem *get_next_visible(bool p_wrap = false); - - void remove_child(TreeItem *p_item); - void set_selectable(int p_column, bool p_selectable); bool is_selectable(int p_column) const; @@ -269,22 +301,43 @@ public: void set_tooltip(int p_column, const String &p_tooltip); String get_tooltip(int p_column) const; - void clear_children(); - void set_text_align(int p_column, TextAlign p_align); TextAlign get_text_align(int p_column) const; void set_expand_right(int p_column, bool p_enable); bool get_expand_right(int p_column) const; - void move_to_top(); - void move_to_bottom(); - void set_disable_folding(bool p_disable); bool is_folding_disabled() const; + /* Item manipulation */ + + TreeItem *create_child(int p_idx = -1); + + Tree *get_tree(); + + TreeItem *get_prev(); + TreeItem *get_next(); + TreeItem *get_parent(); + TreeItem *get_first_child(); + + TreeItem *get_prev_visible(bool p_wrap = false); + TreeItem *get_next_visible(bool p_wrap = false); + + TreeItem *get_child(int p_idx); + int get_child_count(); + Array get_children(); + int get_index(); + + void move_before(TreeItem *p_item); + void move_after(TreeItem *p_item); + + void remove_child(TreeItem *p_item); + void call_recursive(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); + void clear_children(); + ~TreeItem(); }; |