mirror of
https://github.com/godotengine/godot.git
synced 2025-12-07 13:49:54 +00:00
Allow to customize TabContainer tabs in editor
This commit is contained in:
parent
ccf414ecb4
commit
646ac8494b
4 changed files with 123 additions and 10 deletions
|
|
@ -42,6 +42,13 @@ Rect2 TabContainer::_get_tab_rect() const {
|
||||||
return rect;
|
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 TabContainer::_get_tab_height() const {
|
||||||
int height = 0;
|
int height = 0;
|
||||||
if (tabs_visible && get_tab_count() > 0) {
|
if (tabs_visible && get_tab_count() > 0) {
|
||||||
|
|
@ -133,6 +140,37 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TabContainer::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||||
|
List<PropertyInfo> 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) {
|
void TabContainer::_notification(int p_what) {
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
|
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
|
||||||
|
|
@ -186,7 +224,21 @@ void TabContainer::_notification(int p_what) {
|
||||||
}
|
}
|
||||||
} break;
|
} 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: {
|
case NOTIFICATION_RESIZED: {
|
||||||
_update_margins();
|
_update_margins();
|
||||||
} break;
|
} break;
|
||||||
|
|
@ -580,9 +632,7 @@ void TabContainer::_refresh_tab_indices() {
|
||||||
void TabContainer::_refresh_tab_names() {
|
void TabContainer::_refresh_tab_names() {
|
||||||
Vector<Control *> controls = _get_tab_controls();
|
Vector<Control *> controls = _get_tab_controls();
|
||||||
for (int i = 0; i < controls.size(); i++) {
|
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_meta("_tab_name", controls[i]->get_name()));
|
||||||
tab_bar->set_tab_title(i, controls[i]->get_name());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -599,7 +649,7 @@ void TabContainer::add_child_notify(Node *p_child) {
|
||||||
}
|
}
|
||||||
c->hide();
|
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);
|
c->set_meta("_tab_index", tab_bar->get_tab_count() - 1);
|
||||||
|
|
||||||
_update_margins();
|
_update_margins();
|
||||||
|
|
@ -615,6 +665,7 @@ void TabContainer::add_child_notify(Node *p_child) {
|
||||||
if (!is_inside_tree()) {
|
if (!is_inside_tree()) {
|
||||||
callable_mp(this, &TabContainer::_repaint).call_deferred();
|
callable_mp(this, &TabContainer::_repaint).call_deferred();
|
||||||
}
|
}
|
||||||
|
notify_property_list_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabContainer::move_child_notify(Node *p_child) {
|
void TabContainer::move_child_notify(Node *p_child) {
|
||||||
|
|
@ -631,6 +682,7 @@ void TabContainer::move_child_notify(Node *p_child) {
|
||||||
|
|
||||||
_refresh_tab_indices();
|
_refresh_tab_indices();
|
||||||
queue_accessibility_update();
|
queue_accessibility_update();
|
||||||
|
notify_property_list_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabContainer::remove_child_notify(Node *p_child) {
|
void TabContainer::remove_child_notify(Node *p_child) {
|
||||||
|
|
@ -675,6 +727,7 @@ void TabContainer::remove_child_notify(Node *p_child) {
|
||||||
if (!is_inside_tree()) {
|
if (!is_inside_tree()) {
|
||||||
callable_mp(this, &TabContainer::_repaint).call_deferred();
|
callable_mp(this, &TabContainer::_repaint).call_deferred();
|
||||||
}
|
}
|
||||||
|
notify_property_list_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
TabBar *TabContainer::get_tab_bar() const {
|
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) {
|
void TabContainer::set_tab_title(int p_tab, const String &p_title) {
|
||||||
Control *child = get_tab_control(p_tab);
|
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);
|
ERR_FAIL_NULL(child);
|
||||||
|
|
||||||
if (tab_bar->get_tab_title(p_tab) == p_title) {
|
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<Texture2D> &p_icon) {
|
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) {
|
if (tab_bar->get_tab_icon(p_tab) == p_icon) {
|
||||||
return;
|
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) {
|
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) {
|
if (tab_bar->is_tab_disabled(p_tab) == p_disabled) {
|
||||||
return;
|
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) {
|
void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
|
||||||
Control *child = get_tab_control(p_tab);
|
Control *child = get_tab_control(p_tab);
|
||||||
|
if (!child && !is_ready()) {
|
||||||
|
get_pending_tab(p_tab).hidden = p_hidden;
|
||||||
|
return;
|
||||||
|
}
|
||||||
ERR_FAIL_NULL(child);
|
ERR_FAIL_NULL(child);
|
||||||
|
|
||||||
if (tab_bar->is_tab_hidden(p_tab) == p_hidden) {
|
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("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("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("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("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("get_tab_title", "tab_idx"), &TabContainer::get_tab_title);
|
||||||
ClassDB::bind_method(D_METHOD("set_tab_tooltip", "tab_idx", "tooltip"), &TabContainer::set_tab_tooltip);
|
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("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("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_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_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("get_tab_idx_from_control", "control"), &TabContainer::get_tab_idx_from_control);
|
||||||
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
|
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, TabContainer, tab_font, "font");
|
||||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT_SIZE, TabContainer, tab_font_size, "font_size");
|
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);
|
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() {
|
TabContainer::TabContainer() {
|
||||||
|
|
@ -1204,6 +1291,8 @@ TabContainer::TabContainer() {
|
||||||
tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected));
|
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("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));
|
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));
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
#include "scene/gui/container.h"
|
#include "scene/gui/container.h"
|
||||||
#include "scene/gui/popup.h"
|
#include "scene/gui/popup.h"
|
||||||
#include "scene/gui/tab_bar.h"
|
#include "scene/gui/tab_bar.h"
|
||||||
|
#include "scene/property_list_helper.h"
|
||||||
|
|
||||||
class TabContainer : public Container {
|
class TabContainer : public Container {
|
||||||
GDCLASS(TabContainer, Container);
|
GDCLASS(TabContainer, Container);
|
||||||
|
|
@ -101,6 +102,19 @@ private:
|
||||||
Ref<Font> tab_font;
|
Ref<Font> tab_font;
|
||||||
int tab_font_size;
|
int tab_font_size;
|
||||||
} theme_cache;
|
} theme_cache;
|
||||||
|
struct CachedTab {
|
||||||
|
bool has_title = false;
|
||||||
|
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;
|
||||||
|
|
||||||
HashMap<Node *, RID> tab_panels;
|
HashMap<Node *, RID> tab_panels;
|
||||||
|
|
||||||
|
|
@ -130,6 +144,12 @@ private:
|
||||||
protected:
|
protected:
|
||||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
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;
|
||||||
|
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);
|
void _notification(int p_what);
|
||||||
virtual void add_child_notify(Node *p_child) override;
|
virtual void add_child_notify(Node *p_child) override;
|
||||||
virtual void move_child_notify(Node *p_child) override;
|
virtual void move_child_notify(Node *p_child) override;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ void PropertyListHelper::register_base_helper(PropertyListHelper *p_helper) { //
|
||||||
base_helpers.push_back(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<String> components = p_property.rsplit("/", true, 1);
|
const Vector<String> components = p_property.rsplit("/", true, 1);
|
||||||
if (components.size() < 2 || !components[0].begins_with(prefix)) {
|
if (components.size() < 2 || !components[0].begins_with(prefix)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -55,7 +55,7 @@ const PropertyListHelper::Property *PropertyListHelper::_get_property(const Stri
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = index_string.to_int();
|
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;
|
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 {
|
bool PropertyListHelper::property_set_value(const String &p_property, const Variant &p_value) const {
|
||||||
int index;
|
int index;
|
||||||
const Property *property = _get_property(p_property, &index);
|
const Property *property = _get_property(p_property, &index, allow_oob_assign);
|
||||||
|
|
||||||
if (property) {
|
if (property) {
|
||||||
_call_setter(property->setter, index, p_value);
|
_call_setter(property->setter, index, p_value);
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,9 @@ class PropertyListHelper {
|
||||||
HashMap<String, Property> property_list;
|
HashMap<String, Property> property_list;
|
||||||
Object *object = nullptr;
|
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;
|
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;
|
Variant _call_getter(const Property *p_property, int p_index) const;
|
||||||
int _call_array_length_getter() const;
|
int _call_array_length_getter() const;
|
||||||
|
|
@ -87,5 +89,7 @@ public:
|
||||||
bool property_can_revert(const String &p_property) const;
|
bool property_can_revert(const String &p_property) const;
|
||||||
bool property_get_revert(const String &p_property, Variant &r_value) 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();
|
void clear();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue