Skip to content

Commit

Permalink
Allow to customize TabContainer tabs in editor
Browse files Browse the repository at this point in the history
  • Loading branch information
KoBeWi committed Apr 18, 2024
1 parent 2543d19 commit a914e07
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 4 deletions.
75 changes: 74 additions & 1 deletion scene/gui/tab_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
#include "scene/gui/texture_rect.h"
#include "scene/theme/theme_db.h"

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) {
Expand Down Expand Up @@ -143,6 +150,19 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
}
}

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)) {
Vector<Control *> controls = _get_tab_controls();
ERR_FAIL_INDEX_V(index, controls.size(), false);

Check failure on line 159 in scene/gui/tab_container.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

the following warning is treated as an error

Check warning on line 159 in scene/gui/tab_container.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Editor (target=editor, tests=yes)

potentially uninitialized local variable 'index' used

Check failure on line 159 in scene/gui/tab_container.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release)

the following warning is treated as an error

Check warning on line 159 in scene/gui/tab_container.cpp

View workflow job for this annotation

GitHub Actions / 🏁 Windows / Template (target=template_release)

potentially uninitialized local variable 'index' used
r_property = String(controls[index]->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_ENTER_TREE: {
Expand All @@ -159,7 +179,19 @@ 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];
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;
Expand Down Expand Up @@ -544,6 +576,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) {
Expand All @@ -559,6 +592,7 @@ void TabContainer::move_child_notify(Node *p_child) {
}

_refresh_tab_indices();
notify_property_list_changed();
}

void TabContainer::remove_child_notify(Node *p_child) {
Expand Down Expand Up @@ -597,6 +631,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 {
Expand Down Expand Up @@ -749,6 +784,10 @@ 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()) {
get_pending_tab(p_tab).title = p_title;
return;
}
ERR_FAIL_NULL(child);

if (tab_bar->get_tab_title(p_tab) == p_title) {
Expand All @@ -773,7 +812,19 @@ String TabContainer::get_tab_title(int p_tab) const {
return tab_bar->get_tab_title(p_tab);
}

bool TabContainer::tab_has_title(int p_tab) const {
Control *child = get_tab_control(p_tab);
ERR_FAIL_COND_V(!child, false);
return child->has_meta("_tab_name");
}

void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &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;
}
Expand All @@ -789,6 +840,12 @@ Ref<Texture2D> TabContainer::get_tab_icon(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;
}
Expand All @@ -807,6 +864,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) {
Expand Down Expand Up @@ -978,6 +1039,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_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon);
Expand All @@ -990,6 +1052,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);
Expand Down Expand Up @@ -1063,6 +1126,14 @@ 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.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);
}

TabContainer::TabContainer() {
Expand All @@ -1077,5 +1148,7 @@ TabContainer::TabContainer() {
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));

property_helper.setup_for_instance(base_property_helper, this);

connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited));
}
21 changes: 21 additions & 0 deletions scene/gui/tab_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,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);
Expand Down Expand Up @@ -97,6 +98,19 @@ class TabContainer : public Container {
int tab_font_size;
} theme_cache;

struct CachedTab {
String title;
Ref<Texture2D> icon;
bool disabled = false;
bool hidden = false;
};

static inline PropertyListHelper base_property_helper;
PropertyListHelper property_helper;

mutable Vector<CachedTab> pending_tabs;
CachedTab &get_pending_tab(int p_idx) const;

int _get_tab_height() const;
Vector<Control *> _get_tab_controls() const;
void _on_theme_changed();
Expand All @@ -122,6 +136,12 @@ class TabContainer : public Container {
protected:
virtual void gui_input(const Ref<InputEvent> &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<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list, get_tab_count()); }
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;
Expand Down Expand Up @@ -154,6 +174,7 @@ class TabContainer : public Container {

void set_tab_title(int p_tab, const String &p_title);
String get_tab_title(int p_tab) const;
bool tab_has_title(int p_tab) const;

void set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_tab_icon(int p_tab) const;
Expand Down
6 changes: 3 additions & 3 deletions scene/property_list_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ void PropertyListHelper::get_property_list(List<PropertyInfo> *p_list, int p_cou
const Property &property = E.value;

PropertyInfo info = property.info;
if (_call_getter(&property, i) == property.default_value) {
info.name = vformat("%s%d/%s", prefix, i, info.name);

if (_call_getter(&property, i) == object->property_get_revert(info.name)) {
info.usage &= (~PROPERTY_USAGE_STORAGE);
}

info.name = vformat("%s%d/%s", prefix, i, info.name);
p_list->push_back(info);
}
}
Expand Down

0 comments on commit a914e07

Please sign in to comment.