diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 9d6e4c62fe9..317cffc13e0 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -42,6 +42,13 @@ Rect2 TabContainer::_get_tab_rect() const { return rect; } +TabContainer::CachedTab &TabContainer::get_pending_tab(int p_idx) const { + if (p_idx >= pending_tabs.size()) { + pending_tabs.resize(p_idx + 1); + } + return pending_tabs.write[p_idx]; +} + int TabContainer::_get_tab_height() const { int height = 0; if (tabs_visible && get_tab_count() > 0) { @@ -133,6 +140,37 @@ void TabContainer::gui_input(const Ref &p_event) { } } +void TabContainer::_get_property_list(List *p_list) const { + List properties; + property_helper.get_property_list(&properties); + + for (PropertyInfo &info : properties) { + int index; + if (info.name.ends_with("title") && property_helper.is_property_valid(info.name, &index)) { + Control *tab_control = get_tab_control(index); + ERR_FAIL_NULL(tab_control); + if (get_tab_title(index) == tab_control->get_name()) { + // Don't store default title. + info.usage &= ~PROPERTY_USAGE_STORAGE; + } + } + p_list->push_back(info); + } +} + +bool TabContainer::_property_get_revert(const StringName &p_name, Variant &r_property) const { + const String sname = p_name; + + int index; + if (sname.ends_with("title") && property_helper.is_property_valid(sname, &index)) { + Control *tab_control = get_tab_control(index); + ERR_FAIL_NULL_V(tab_control, false); + r_property = String(tab_control->get_name()); + return true; + } + return property_helper.property_get_revert(p_name, r_property); +} + void TabContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ACCESSIBILITY_INVALIDATE: { @@ -186,7 +224,21 @@ void TabContainer::_notification(int p_what) { } } break; - case NOTIFICATION_READY: + case NOTIFICATION_READY: { + for (int i = 0; i < pending_tabs.size(); i++) { + const CachedTab &tab = pending_tabs[i]; + if (tab.has_title) { + set_tab_title(i, tab.title); + } + set_tab_icon(i, tab.icon); + set_tab_disabled(i, tab.disabled); + set_tab_hidden(i, tab.hidden); + } + pending_tabs.clear(); + + [[fallthrough]]; + } + case NOTIFICATION_RESIZED: { _update_margins(); } break; @@ -580,9 +632,7 @@ void TabContainer::_refresh_tab_indices() { void TabContainer::_refresh_tab_names() { Vector controls = _get_tab_controls(); for (int i = 0; i < controls.size(); i++) { - if (!controls[i]->has_meta("_tab_name") && String(controls[i]->get_name()) != get_tab_title(i)) { - tab_bar->set_tab_title(i, controls[i]->get_name()); - } + tab_bar->set_tab_title(i, controls[i]->get_meta("_tab_name", controls[i]->get_name())); } } @@ -599,7 +649,7 @@ void TabContainer::add_child_notify(Node *p_child) { } c->hide(); - tab_bar->add_tab(p_child->get_name()); + tab_bar->add_tab(p_child->get_meta("_tab_name", p_child->get_name())); c->set_meta("_tab_index", tab_bar->get_tab_count() - 1); _update_margins(); @@ -615,6 +665,7 @@ void TabContainer::add_child_notify(Node *p_child) { if (!is_inside_tree()) { callable_mp(this, &TabContainer::_repaint).call_deferred(); } + notify_property_list_changed(); } void TabContainer::move_child_notify(Node *p_child) { @@ -631,6 +682,7 @@ void TabContainer::move_child_notify(Node *p_child) { _refresh_tab_indices(); queue_accessibility_update(); + notify_property_list_changed(); } void TabContainer::remove_child_notify(Node *p_child) { @@ -675,6 +727,7 @@ void TabContainer::remove_child_notify(Node *p_child) { if (!is_inside_tree()) { callable_mp(this, &TabContainer::_repaint).call_deferred(); } + notify_property_list_changed(); } TabBar *TabContainer::get_tab_bar() const { @@ -828,6 +881,12 @@ bool TabContainer::is_all_tabs_in_front() const { void TabContainer::set_tab_title(int p_tab, const String &p_title) { Control *child = get_tab_control(p_tab); + if (!child && !is_ready()) { + CachedTab &tab = get_pending_tab(p_tab); + tab.title = p_title; + tab.has_title = true; + return; + } ERR_FAIL_NULL(child); if (tab_bar->get_tab_title(p_tab) == p_title) { @@ -859,6 +918,12 @@ String TabContainer::get_tab_tooltip(int p_tab) const { } void TabContainer::set_tab_icon(int p_tab, const Ref &p_icon) { + Control *child = get_tab_control(p_tab); + if (!child && !is_ready()) { + get_pending_tab(p_tab).icon = p_icon; + return; + } + if (tab_bar->get_tab_icon(p_tab) == p_icon) { return; } @@ -891,6 +956,12 @@ int TabContainer::get_tab_icon_max_width(int p_tab) const { } void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) { + Control *child = get_tab_control(p_tab); + if (!child && !is_ready()) { + get_pending_tab(p_tab).disabled = p_disabled; + return; + } + if (tab_bar->is_tab_disabled(p_tab) == p_disabled) { return; } @@ -909,6 +980,10 @@ bool TabContainer::is_tab_disabled(int p_tab) const { void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) { Control *child = get_tab_control(p_tab); + if (!child && !is_ready()) { + get_pending_tab(p_tab).hidden = p_hidden; + return; + } ERR_FAIL_NULL(child); if (tab_bar->is_tab_hidden(p_tab) == p_hidden) { @@ -1090,6 +1165,7 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("are_tabs_visible"), &TabContainer::are_tabs_visible); ClassDB::bind_method(D_METHOD("set_all_tabs_in_front", "is_front"), &TabContainer::set_all_tabs_in_front); ClassDB::bind_method(D_METHOD("is_all_tabs_in_front"), &TabContainer::is_all_tabs_in_front); + ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &TabContainer::set_tab_title); ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabContainer::get_tab_title); ClassDB::bind_method(D_METHOD("set_tab_tooltip", "tab_idx", "tooltip"), &TabContainer::set_tab_tooltip); @@ -1106,6 +1182,7 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_metadata", "tab_idx"), &TabContainer::get_tab_metadata); ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabContainer::set_tab_button_icon); ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabContainer::get_tab_button_icon); + ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point); ClassDB::bind_method(D_METHOD("get_tab_idx_from_control", "control"), &TabContainer::get_tab_idx_from_control); ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup); @@ -1190,6 +1267,16 @@ void TabContainer::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT, TabContainer, tab_font, "font"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT_SIZE, TabContainer, tab_font_size, "font_size"); BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, outline_size); + + CachedTab defaults; + + base_property_helper.set_prefix("tab_"); + base_property_helper.set_array_length_getter(&TabContainer::get_tab_count); + base_property_helper.register_property(PropertyInfo(Variant::STRING, "title"), defaults.title, &TabContainer::set_tab_title, &TabContainer::get_tab_title); + base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &TabContainer::set_tab_icon, &TabContainer::get_tab_icon); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &TabContainer::set_tab_disabled, &TabContainer::is_tab_disabled); + base_property_helper.register_property(PropertyInfo(Variant::BOOL, "hidden"), defaults.hidden, &TabContainer::set_tab_hidden, &TabContainer::is_tab_hidden); + PropertyListHelper::register_base_helper(&base_property_helper); } TabContainer::TabContainer() { @@ -1204,6 +1291,8 @@ TabContainer::TabContainer() { tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected)); tab_bar->connect("tab_button_pressed", callable_mp(this, &TabContainer::_on_tab_button_pressed)); tab_bar->connect("active_tab_rearranged", callable_mp(this, &TabContainer::_on_active_tab_rearranged)); - connect(SceneStringName(mouse_exited), callable_mp(this, &TabContainer::_on_mouse_exited)); + + property_helper.setup_for_instance(base_property_helper, this); + property_helper.enable_out_of_bounds_assign(); } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 66ece7c76b9..e418de35f7c 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -33,6 +33,7 @@ #include "scene/gui/container.h" #include "scene/gui/popup.h" #include "scene/gui/tab_bar.h" +#include "scene/property_list_helper.h" class TabContainer : public Container { GDCLASS(TabContainer, Container); @@ -101,6 +102,19 @@ private: Ref tab_font; int tab_font_size; } theme_cache; + struct CachedTab { + bool has_title = false; + String title; + Ref icon; + bool disabled = false; + bool hidden = false; + }; + + static inline PropertyListHelper base_property_helper; + PropertyListHelper property_helper; + + mutable Vector pending_tabs; + CachedTab &get_pending_tab(int p_idx) const; HashMap tab_panels; @@ -130,6 +144,12 @@ private: protected: virtual void gui_input(const Ref &p_event) override; + bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); } + bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); } + void _get_property_list(List *p_list) const; + bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _notification(int p_what); virtual void add_child_notify(Node *p_child) override; virtual void move_child_notify(Node *p_child) override; diff --git a/scene/property_list_helper.cpp b/scene/property_list_helper.cpp index c6c21e0dba3..315c602e613 100644 --- a/scene/property_list_helper.cpp +++ b/scene/property_list_helper.cpp @@ -43,7 +43,7 @@ void PropertyListHelper::register_base_helper(PropertyListHelper *p_helper) { // base_helpers.push_back(p_helper); } -const PropertyListHelper::Property *PropertyListHelper::_get_property(const String &p_property, int *r_index) const { +const PropertyListHelper::Property *PropertyListHelper::_get_property(const String &p_property, int *r_index, bool p_allow_oob) const { const Vector components = p_property.rsplit("/", true, 1); if (components.size() < 2 || !components[0].begins_with(prefix)) { return nullptr; @@ -55,7 +55,7 @@ const PropertyListHelper::Property *PropertyListHelper::_get_property(const Stri } int index = index_string.to_int(); - if (index < 0 || index >= _call_array_length_getter()) { + if (index < 0 || (!p_allow_oob && index >= _call_array_length_getter())) { return nullptr; } @@ -165,7 +165,7 @@ bool PropertyListHelper::property_get_value(const String &p_property, Variant &r bool PropertyListHelper::property_set_value(const String &p_property, const Variant &p_value) const { int index; - const Property *property = _get_property(p_property, &index); + const Property *property = _get_property(p_property, &index, allow_oob_assign); if (property) { _call_setter(property->setter, index, p_value); diff --git a/scene/property_list_helper.h b/scene/property_list_helper.h index 17709f993ce..1af04b9fa12 100644 --- a/scene/property_list_helper.h +++ b/scene/property_list_helper.h @@ -48,7 +48,9 @@ class PropertyListHelper { HashMap property_list; Object *object = nullptr; - const Property *_get_property(const String &p_property, int *r_index) const; + bool allow_oob_assign = false; + + const Property *_get_property(const String &p_property, int *r_index, bool p_allow_oob = false) const; void _call_setter(const MethodBind *p_setter, int p_index, const Variant &p_value) const; Variant _call_getter(const Property *p_property, int p_index) const; int _call_array_length_getter() const; @@ -87,5 +89,7 @@ public: bool property_can_revert(const String &p_property) const; bool property_get_revert(const String &p_property, Variant &r_value) const; + void enable_out_of_bounds_assign() { allow_oob_assign = true; } + void clear(); };