#include "theme_owner.h"
#include "scene/gui/control.h"
#include "scene/main/window.h"
#include "scene/theme/theme_db.h"
void ThemeOwner::set_owner_node(Node *p_node) {
owner_control = nullptr;
owner_window = nullptr;
Control *c = Object::cast_to<Control>(p_node);
if (c) {
owner_control = c;
return;
}
Window *w = Object::cast_to<Window>(p_node);
if (w) {
owner_window = w;
return;
}
}
Node *ThemeOwner::get_owner_node() const {
if (owner_control) {
return owner_control;
} else if (owner_window) {
return owner_window;
}
return nullptr;
}
bool ThemeOwner::has_owner_node() const {
return bool(owner_control || owner_window);
}
void ThemeOwner::set_owner_context(ThemeContext *p_context, bool p_propagate) {
ThemeContext *default_context = ThemeDB::get_singleton()->get_default_theme_context();
if (owner_context && owner_context->is_connected(CoreStringName(changed), callable_mp(this, &ThemeOwner::_owner_context_changed))) {
owner_context->disconnect(CoreStringName(changed), callable_mp(this, &ThemeOwner::_owner_context_changed));
} else if (default_context->is_connected(CoreStringName(changed), callable_mp(this, &ThemeOwner::_owner_context_changed))) {
default_context->disconnect(CoreStringName(changed), callable_mp(this, &ThemeOwner::_owner_context_changed));
}
owner_context = p_context;
if (owner_context) {
owner_context->connect(CoreStringName(changed), callable_mp(this, &ThemeOwner::_owner_context_changed));
} else {
default_context->connect(CoreStringName(changed), callable_mp(this, &ThemeOwner::_owner_context_changed));
}
if (p_propagate) {
_owner_context_changed();
}
}
void ThemeOwner::_owner_context_changed() {
if (!holder->is_inside_tree()) {
return;
}
Control *c = Object::cast_to<Control>(holder);
Window *w = c == nullptr ? Object::cast_to<Window>(holder) : nullptr;
if (c) {
c->notification(Control::NOTIFICATION_THEME_CHANGED);
} else if (w) {
w->notification(Window::NOTIFICATION_THEME_CHANGED);
}
}
ThemeContext *ThemeOwner::_get_active_owner_context() const {
if (owner_context) {
return owner_context;
}
return ThemeDB::get_singleton()->get_default_theme_context();
}
void ThemeOwner::assign_theme_on_parented(Node *p_for_node) {
Node *parent = p_for_node->get_parent();
Control *parent_c = Object::cast_to<Control>(parent);
if (parent_c && parent_c->has_theme_owner_node()) {
propagate_theme_changed(p_for_node, parent_c->get_theme_owner_node(), false, true);
} else {
Window *parent_w = Object::cast_to<Window>(parent);
if (parent_w && parent_w->has_theme_owner_node()) {
propagate_theme_changed(p_for_node, parent_w->get_theme_owner_node(), false, true);
}
}
}
void ThemeOwner::clear_theme_on_unparented(Node *p_for_node) {
Node *parent = p_for_node->get_parent();
Control *parent_c = Object::cast_to<Control>(parent);
if (parent_c && parent_c->has_theme_owner_node()) {
propagate_theme_changed(p_for_node, nullptr, false, true);
} else {
Window *parent_w = Object::cast_to<Window>(parent);
if (parent_w && parent_w->has_theme_owner_node()) {
propagate_theme_changed(p_for_node, nullptr, false, true);
}
}
}
void ThemeOwner::propagate_theme_changed(Node *p_to_node, Node *p_owner_node, bool p_notify, bool p_assign) {
Control *c = Object::cast_to<Control>(p_to_node);
Window *w = c == nullptr ? Object::cast_to<Window>(p_to_node) : nullptr;
if (!c && !w) {
return;
}
bool assign = p_assign;
if (c) {
if (c != p_owner_node && c->get_theme().is_valid()) {
assign = false;
}
if (assign) {
c->set_theme_owner_node(p_owner_node);
}
if (p_notify) {
c->notification(Control::NOTIFICATION_THEME_CHANGED);
}
} else if (w) {
if (w != p_owner_node && w->get_theme().is_valid()) {
assign = false;
}
if (assign) {
w->set_theme_owner_node(p_owner_node);
}
if (p_notify) {
w->notification(Window::NOTIFICATION_THEME_CHANGED);
}
}
for (int i = 0; i < p_to_node->get_child_count(); i++) {
propagate_theme_changed(p_to_node->get_child(i), p_owner_node, p_notify, assign);
}
}
void ThemeOwner::get_theme_type_dependencies(const Node *p_for_node, const StringName &p_theme_type, Vector<StringName> &r_result) const {
const Control *for_c = Object::cast_to<Control>(p_for_node);
const Window *for_w = Object::cast_to<Window>(p_for_node);
ERR_FAIL_COND_MSG(!for_c && !for_w, "Only Control and Window nodes and derivatives can be polled for theming.");
StringName type_name = p_for_node->get_class_name();
StringName type_variation;
if (for_c) {
type_variation = for_c->get_theme_type_variation();
} else if (for_w) {
type_variation = for_w->get_theme_type_variation();
}
if (p_theme_type == StringName() || p_theme_type == type_name || p_theme_type == type_variation) {
Node *owner_node = get_owner_node();
while (owner_node) {
Ref<Theme> owner_theme = _get_owner_node_theme(owner_node);
if (owner_theme.is_valid() && owner_theme->get_type_variation_base(type_variation) != StringName()) {
owner_theme->get_type_dependencies(type_name, type_variation, r_result);
return;
}
owner_node = _get_next_owner_node(owner_node);
}
ThemeContext *global_context = _get_active_owner_context();
for (const Ref<Theme> &theme : global_context->get_themes()) {
if (theme.is_valid() && theme->get_type_variation_base(type_variation) != StringName()) {
theme->get_type_dependencies(type_name, type_variation, r_result);
return;
}
}
ThemeDB::get_singleton()->get_native_type_dependencies(type_name, r_result);
return;
}
ThemeDB::get_singleton()->get_native_type_dependencies(p_theme_type, r_result);
}
Variant ThemeOwner::get_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, const Vector<StringName> &p_theme_types) {
ERR_FAIL_COND_V_MSG(p_theme_types.is_empty(), Variant(), "At least one theme type must be specified.");
Node *owner_node = get_owner_node();
while (owner_node) {
for (const StringName &E : p_theme_types) {
Ref<Theme> owner_theme = _get_owner_node_theme(owner_node);
if (owner_theme.is_valid() && owner_theme->has_theme_item(p_data_type, p_name, E)) {
return owner_theme->get_theme_item(p_data_type, p_name, E);
}
}
owner_node = _get_next_owner_node(owner_node);
}
ThemeContext *global_context = _get_active_owner_context();
for (const Ref<Theme> &theme : global_context->get_themes()) {
if (theme.is_valid()) {
for (const StringName &E : p_theme_types) {
if (theme->has_theme_item(p_data_type, p_name, E)) {
return theme->get_theme_item(p_data_type, p_name, E);
}
}
}
}
return global_context->get_fallback_theme()->get_theme_item(p_data_type, p_name, StringName());
}
bool ThemeOwner::has_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, const Vector<StringName> &p_theme_types) {
ERR_FAIL_COND_V_MSG(p_theme_types.is_empty(), false, "At least one theme type must be specified.");
Node *owner_node = get_owner_node();
while (owner_node) {
for (const StringName &E : p_theme_types) {
Ref<Theme> owner_theme = _get_owner_node_theme(owner_node);
if (owner_theme.is_valid() && owner_theme->has_theme_item(p_data_type, p_name, E)) {
return true;
}
}
owner_node = _get_next_owner_node(owner_node);
}
ThemeContext *global_context = _get_active_owner_context();
for (const Ref<Theme> &theme : global_context->get_themes()) {
if (theme.is_valid()) {
for (const StringName &E : p_theme_types) {
if (theme->has_theme_item(p_data_type, p_name, E)) {
return true;
}
}
}
}
return false;
}
float ThemeOwner::get_theme_default_base_scale() {
Node *owner_node = get_owner_node();
while (owner_node) {
Ref<Theme> owner_theme = _get_owner_node_theme(owner_node);
if (owner_theme.is_valid() && owner_theme->has_default_base_scale()) {
return owner_theme->get_default_base_scale();
}
owner_node = _get_next_owner_node(owner_node);
}
ThemeContext *global_context = _get_active_owner_context();
for (const Ref<Theme> &theme : global_context->get_themes()) {
if (theme.is_valid()) {
if (theme->has_default_base_scale()) {
return theme->get_default_base_scale();
}
}
}
return ThemeDB::get_singleton()->get_fallback_base_scale();
}
Ref<Font> ThemeOwner::get_theme_default_font() {
Node *owner_node = get_owner_node();
while (owner_node) {
Ref<Theme> owner_theme = _get_owner_node_theme(owner_node);
if (owner_theme.is_valid() && owner_theme->has_default_font()) {
return owner_theme->get_default_font();
}
owner_node = _get_next_owner_node(owner_node);
}
ThemeContext *global_context = _get_active_owner_context();
for (const Ref<Theme> &theme : global_context->get_themes()) {
if (theme.is_valid()) {
if (theme->has_default_font()) {
return theme->get_default_font();
}
}
}
return ThemeDB::get_singleton()->get_fallback_font();
}
int ThemeOwner::get_theme_default_font_size() {
Node *owner_node = get_owner_node();
while (owner_node) {
Ref<Theme> owner_theme = _get_owner_node_theme(owner_node);
if (owner_theme.is_valid() && owner_theme->has_default_font_size()) {
return owner_theme->get_default_font_size();
}
owner_node = _get_next_owner_node(owner_node);
}
ThemeContext *global_context = _get_active_owner_context();
for (const Ref<Theme> &theme : global_context->get_themes()) {
if (theme.is_valid()) {
if (theme->has_default_font_size()) {
return theme->get_default_font_size();
}
}
}
return ThemeDB::get_singleton()->get_fallback_font_size();
}
Ref<Theme> ThemeOwner::_get_owner_node_theme(Node *p_owner_node) const {
const Control *owner_c = Object::cast_to<Control>(p_owner_node);
if (owner_c) {
return owner_c->get_theme();
}
const Window *owner_w = Object::cast_to<Window>(p_owner_node);
if (owner_w) {
return owner_w->get_theme();
}
return Ref<Theme>();
}
Node *ThemeOwner::_get_next_owner_node(Node *p_from_node) const {
Node *parent = p_from_node->get_parent();
Control *parent_c = Object::cast_to<Control>(parent);
if (parent_c) {
return parent_c->get_theme_owner_node();
} else {
Window *parent_w = Object::cast_to<Window>(parent);
if (parent_w) {
return parent_w->get_theme_owner_node();
}
}
return nullptr;
}