Refactor and remove excessive calls of NOTIFICATION_THEME_CHANGED

This commit is contained in:
Aaron Record 2022-07-08 13:29:36 -06:00
parent 6d196c1ce3
commit 74eb2a70bd
7 changed files with 170 additions and 182 deletions

View File

@ -1145,6 +1145,7 @@
</signal> </signal>
<signal name="theme_changed"> <signal name="theme_changed">
<description> <description>
Emitted when the [constant NOTIFICATION_THEME_CHANGED] notification is sent.
</description> </description>
</signal> </signal>
</signals> </signals>
@ -1174,7 +1175,12 @@
Sent when the node loses focus. Sent when the node loses focus.
</constant> </constant>
<constant name="NOTIFICATION_THEME_CHANGED" value="45"> <constant name="NOTIFICATION_THEME_CHANGED" value="45">
Sent when the node's [member theme] changes, right before Godot redraws the control. Happens when you call one of the [code]add_theme_*_override[/code] methods. Sent when the node needs to refresh its theme items. This happens in one of the following cases:
- The [member theme] property is changed on this node or any of its ancestors.
- The [member theme_type_variation] property is changed on this node.
- One of the node's theme property overrides is changed.
- The node enters the scene tree.
[b]Note:[/b] As an optimization, this notification won't be sent from changes that occur while this node is outside of the scene tree. Instead, all of the theme item updates can be applied at once when the node enters the scene tree.
</constant> </constant>
<constant name="NOTIFICATION_SCROLL_BEGIN" value="47"> <constant name="NOTIFICATION_SCROLL_BEGIN" value="47">
Sent when this node is inside a [ScrollContainer] which has begun being scrolled. Sent when this node is inside a [ScrollContainer] which has begun being scrolled.

View File

@ -448,7 +448,7 @@
</signal> </signal>
<signal name="theme_changed"> <signal name="theme_changed">
<description> <description>
Emitted when the [member theme] is modified or changed to another [Theme]. Emitted when the [constant NOTIFICATION_THEME_CHANGED] notification is sent.
</description> </description>
</signal> </signal>
<signal name="visibility_changed"> <signal name="visibility_changed">
@ -467,6 +467,13 @@
<constant name="NOTIFICATION_VISIBILITY_CHANGED" value="30"> <constant name="NOTIFICATION_VISIBILITY_CHANGED" value="30">
Emitted when [Window]'s visibility changes, right before [signal visibility_changed]. Emitted when [Window]'s visibility changes, right before [signal visibility_changed].
</constant> </constant>
<constant name="NOTIFICATION_THEME_CHANGED" value="32">
Sent when the node needs to refresh its theme items. This happens in one of the following cases:
- The [member theme] property is changed on this node or any of its ancestors.
- The [member theme_type_variation] property is changed on this node.
- The node enters the scene tree.
[b]Note:[/b] As an optimization, this notification won't be sent from changes that occur while this node is outside of the scene tree. Instead, all of the theme item updates can be applied at once when the node enters the scene tree.
</constant>
<constant name="MODE_WINDOWED" value="0" enum="Mode"> <constant name="MODE_WINDOWED" value="0" enum="Mode">
Windowed mode, i.e. [Window] doesn't occupy whole screen (unless set to the size of the screen). Windowed mode, i.e. [Window] doesn't occupy whole screen (unless set to the size of the screen).
</constant> </constant>

View File

@ -252,6 +252,11 @@ void EditorLog::_rebuild_log() {
} }
void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
if (!is_inside_tree()) {
// The log will be built all at once when it enters the tree and has its theme items.
return;
}
// Only add the message to the log if it passes the filters. // Only add the message to the log if it passes the filters.
bool filter_active = type_filter_map[p_message.type]->is_active(); bool filter_active = type_filter_map[p_message.type]->is_active();
String search_text = search_box->get_text(); String search_text = search_box->get_text();

View File

@ -252,36 +252,36 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
if (name.begins_with("theme_override_icons/")) { if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1); String dname = name.get_slicec('/', 1);
if (data.icon_override.has(dname)) { if (data.icon_override.has(dname)) {
data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.icon_override.erase(dname); data.icon_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED); _notify_theme_override_changed();
} else if (name.begins_with("theme_override_styles/")) { } else if (name.begins_with("theme_override_styles/")) {
String dname = name.get_slicec('/', 1); String dname = name.get_slicec('/', 1);
if (data.style_override.has(dname)) { if (data.style_override.has(dname)) {
data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.style_override.erase(dname); data.style_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED); _notify_theme_override_changed();
} else if (name.begins_with("theme_override_fonts/")) { } else if (name.begins_with("theme_override_fonts/")) {
String dname = name.get_slicec('/', 1); String dname = name.get_slicec('/', 1);
if (data.font_override.has(dname)) { if (data.font_override.has(dname)) {
data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.font_override.erase(dname); data.font_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED); _notify_theme_override_changed();
} else if (name.begins_with("theme_override_font_sizes/")) { } else if (name.begins_with("theme_override_font_sizes/")) {
String dname = name.get_slicec('/', 1); String dname = name.get_slicec('/', 1);
data.font_size_override.erase(dname); data.font_size_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED); _notify_theme_override_changed();
} else if (name.begins_with("theme_override_colors/")) { } else if (name.begins_with("theme_override_colors/")) {
String dname = name.get_slicec('/', 1); String dname = name.get_slicec('/', 1);
data.color_override.erase(dname); data.color_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED); _notify_theme_override_changed();
} else if (name.begins_with("theme_override_constants/")) { } else if (name.begins_with("theme_override_constants/")) {
String dname = name.get_slicec('/', 1); String dname = name.get_slicec('/', 1);
data.constant_override.erase(dname); data.constant_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED); _notify_theme_override_changed();
} else { } else {
return false; return false;
} }
@ -2260,62 +2260,62 @@ bool Control::is_clipping_contents() {
// Theming. // Theming.
void Control::_propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign) { void Control::_propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_notify, bool p_assign) {
Control *c = Object::cast_to<Control>(p_at); Control *c = Object::cast_to<Control>(p_at);
if (c && c != p_owner && c->data.theme.is_valid()) { // has a theme, this can't be propagated
return;
}
Window *w = c == nullptr ? Object::cast_to<Window>(p_at) : nullptr; Window *w = c == nullptr ? Object::cast_to<Window>(p_at) : nullptr;
if (w && w != p_owner_window && w->theme.is_valid()) { // has a theme, this can't be propagated if (!c && !w) {
// Theme inheritance chains are broken by nodes that aren't Control or Window.
return; return;
} }
for (int i = 0; i < p_at->get_child_count(); i++) { bool assign = p_assign;
CanvasItem *child = Object::cast_to<CanvasItem>(p_at->get_child(i)); if (c) {
if (child) { if (c != p_owner && c->data.theme.is_valid()) {
_propagate_theme_changed(child, p_owner, p_owner_window, p_assign); // Has a theme, so we don't want to change the theme owner,
} else { // but we still want to propagate in case this child has theme items
Window *window = Object::cast_to<Window>(p_at->get_child(i)); // it inherits from the theme this node uses.
if (window) { // See https://github.com/godotengine/godot/issues/62844.
_propagate_theme_changed(window, p_owner, p_owner_window, p_assign); assign = false;
}
}
} }
if (c) { if (assign) {
if (p_assign) {
c->data.theme_owner = p_owner; c->data.theme_owner = p_owner;
c->data.theme_owner_window = p_owner_window; c->data.theme_owner_window = p_owner_window;
} }
if (p_notify) {
c->notification(Control::NOTIFICATION_THEME_CHANGED); c->notification(Control::NOTIFICATION_THEME_CHANGED);
c->emit_signal(SceneStringNames::get_singleton()->theme_changed); }
} else if (w) {
if (w != p_owner_window && w->theme.is_valid()) {
// Same as above.
assign = false;
} }
if (w) { if (assign) {
if (p_assign) {
w->theme_owner = p_owner; w->theme_owner = p_owner;
w->theme_owner_window = p_owner_window; w->theme_owner_window = p_owner_window;
} }
if (p_notify) {
w->notification(Window::NOTIFICATION_THEME_CHANGED); w->notification(Window::NOTIFICATION_THEME_CHANGED);
w->emit_signal(SceneStringNames::get_singleton()->theme_changed); }
}
for (int i = 0; i < p_at->get_child_count(); i++) {
_propagate_theme_changed(p_at->get_child(i), p_owner, p_owner_window, p_notify, assign);
} }
} }
void Control::_theme_changed() { void Control::_theme_changed() {
_propagate_theme_changed(this, this, nullptr, false); if (is_inside_tree()) {
_propagate_theme_changed(this, this, nullptr, true, false);
}
} }
void Control::_theme_property_override_changed() { void Control::_notify_theme_override_changed() {
notification(NOTIFICATION_THEME_CHANGED); if (!data.bulk_theme_override && is_inside_tree()) {
emit_signal(SceneStringNames::get_singleton()->theme_changed);
update_minimum_size(); // Overrides are likely to affect minimum size.
}
void Control::_notify_theme_changed() {
if (!data.bulk_theme_override) {
notification(NOTIFICATION_THEME_CHANGED); notification(NOTIFICATION_THEME_CHANGED);
} }
} }
@ -2339,28 +2339,25 @@ void Control::set_theme(const Ref<Theme> &p_theme) {
} }
data.theme = p_theme; data.theme = p_theme;
if (!p_theme.is_null()) { if (data.theme.is_valid()) {
data.theme_owner = this; _propagate_theme_changed(this, this, nullptr, is_inside_tree(), true);
data.theme_owner_window = nullptr; data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), CONNECT_DEFERRED);
_propagate_theme_changed(this, this, nullptr); return;
} else { }
Control *parent_c = Object::cast_to<Control>(get_parent());
Control *parent_c = Object::cast_to<Control>(get_parent());
if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) {
Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window); _propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window, is_inside_tree(), true);
} else { return;
}
Window *parent_w = cast_to<Window>(get_parent()); Window *parent_w = cast_to<Window>(get_parent());
if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) { if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) {
Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window); _propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window, is_inside_tree(), true);
} else { return;
Control::_propagate_theme_changed(this, nullptr, nullptr);
}
}
} }
if (data.theme.is_valid()) { _propagate_theme_changed(this, nullptr, nullptr, is_inside_tree(), true);
data.theme->connect("changed", callable_mp(this, &Control::_theme_changed), CONNECT_DEFERRED);
}
} }
Ref<Theme> Control::get_theme() const { Ref<Theme> Control::get_theme() const {
@ -2372,7 +2369,9 @@ void Control::set_theme_type_variation(const StringName &p_theme_type) {
return; return;
} }
data.theme_type_variation = p_theme_type; data.theme_type_variation = p_theme_type;
_propagate_theme_changed(this, data.theme_owner, data.theme_owner_window); if (is_inside_tree()) {
notification(NOTIFICATION_THEME_CHANGED);
}
} }
StringName Control::get_theme_type_variation() const { StringName Control::get_theme_type_variation() const {
@ -2697,93 +2696,93 @@ void Control::add_theme_icon_override(const StringName &p_name, const Ref<Textur
ERR_FAIL_COND(!p_icon.is_valid()); ERR_FAIL_COND(!p_icon.is_valid());
if (data.icon_override.has(p_name)) { if (data.icon_override.has(p_name)) {
data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.icon_override[p_name] = p_icon; data.icon_override[p_name] = p_icon;
data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), CONNECT_REFERENCE_COUNTED); data.icon_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) { void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
ERR_FAIL_COND(!p_style.is_valid()); ERR_FAIL_COND(!p_style.is_valid());
if (data.style_override.has(p_name)) { if (data.style_override.has(p_name)) {
data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.style_override[p_name] = p_style; data.style_override[p_name] = p_style;
data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), CONNECT_REFERENCE_COUNTED); data.style_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) { void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) {
ERR_FAIL_COND(!p_font.is_valid()); ERR_FAIL_COND(!p_font.is_valid());
if (data.font_override.has(p_name)) { if (data.font_override.has(p_name)) {
data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.font_override[p_name] = p_font; data.font_override[p_name] = p_font;
data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_theme_property_override_changed), CONNECT_REFERENCE_COUNTED); data.font_override[p_name]->connect("changed", callable_mp(this, &Control::_notify_theme_override_changed), CONNECT_REFERENCE_COUNTED);
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) { void Control::add_theme_font_size_override(const StringName &p_name, int p_font_size) {
data.font_size_override[p_name] = p_font_size; data.font_size_override[p_name] = p_font_size;
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) { void Control::add_theme_color_override(const StringName &p_name, const Color &p_color) {
data.color_override[p_name] = p_color; data.color_override[p_name] = p_color;
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::add_theme_constant_override(const StringName &p_name, int p_constant) { void Control::add_theme_constant_override(const StringName &p_name, int p_constant) {
data.constant_override[p_name] = p_constant; data.constant_override[p_name] = p_constant;
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::remove_theme_icon_override(const StringName &p_name) { void Control::remove_theme_icon_override(const StringName &p_name) {
if (data.icon_override.has(p_name)) { if (data.icon_override.has(p_name)) {
data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.icon_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.icon_override.erase(p_name); data.icon_override.erase(p_name);
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::remove_theme_style_override(const StringName &p_name) { void Control::remove_theme_style_override(const StringName &p_name) {
if (data.style_override.has(p_name)) { if (data.style_override.has(p_name)) {
data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.style_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.style_override.erase(p_name); data.style_override.erase(p_name);
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::remove_theme_font_override(const StringName &p_name) { void Control::remove_theme_font_override(const StringName &p_name) {
if (data.font_override.has(p_name)) { if (data.font_override.has(p_name)) {
data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); data.font_override[p_name]->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
data.font_override.erase(p_name); data.font_override.erase(p_name);
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::remove_theme_font_size_override(const StringName &p_name) { void Control::remove_theme_font_size_override(const StringName &p_name) {
data.font_size_override.erase(p_name); data.font_size_override.erase(p_name);
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::remove_theme_color_override(const StringName &p_name) { void Control::remove_theme_color_override(const StringName &p_name) {
data.color_override.erase(p_name); data.color_override.erase(p_name);
_notify_theme_changed(); _notify_theme_override_changed();
} }
void Control::remove_theme_constant_override(const StringName &p_name) { void Control::remove_theme_constant_override(const StringName &p_name) {
data.constant_override.erase(p_name); data.constant_override.erase(p_name);
_notify_theme_changed(); _notify_theme_override_changed();
} }
bool Control::has_theme_icon_override(const StringName &p_name) const { bool Control::has_theme_icon_override(const StringName &p_name) const {
@ -2981,7 +2980,7 @@ void Control::end_bulk_theme_override() {
ERR_FAIL_COND(!data.bulk_theme_override); ERR_FAIL_COND(!data.bulk_theme_override);
data.bulk_theme_override = false; data.bulk_theme_override = false;
_notify_theme_changed(); _notify_theme_override_changed();
} }
// Internationalization. // Internationalization.
@ -3087,37 +3086,26 @@ Control *Control::make_custom_tooltip(const String &p_text) const {
// Base object overrides. // Base object overrides.
void Control::add_child_notify(Node *p_child) { void Control::add_child_notify(Node *p_child) {
Control *child_c = Object::cast_to<Control>(p_child); // We propagate when this node uses a custom theme, so it can pass it on to its children.
if (data.theme_owner || data.theme_owner_window) {
if (child_c && child_c->data.theme.is_null() && (data.theme_owner || data.theme_owner_window)) { // `p_notify` is false here as `NOTIFICATION_THEME_CHANGED` will be handled by `NOTIFICATION_ENTER_TREE`.
_propagate_theme_changed(child_c, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff _propagate_theme_changed(p_child, data.theme_owner, data.theme_owner_window, false, true);
}
Window *child_w = Object::cast_to<Window>(p_child);
if (child_w && child_w->theme.is_null() && (data.theme_owner || data.theme_owner_window)) {
_propagate_theme_changed(child_w, data.theme_owner, data.theme_owner_window); //need to propagate here, since many controls may require setting up stuff
} }
} }
void Control::remove_child_notify(Node *p_child) { void Control::remove_child_notify(Node *p_child) {
Control *child_c = Object::cast_to<Control>(p_child); // If the removed child isn't inheriting any theme items through this node, then there's no need to propagate.
if (data.theme_owner || data.theme_owner_window) {
if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) { _propagate_theme_changed(p_child, nullptr, nullptr, false, true);
_propagate_theme_changed(child_c, nullptr, nullptr);
}
Window *child_w = Object::cast_to<Window>(p_child);
if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) {
_propagate_theme_changed(child_w, nullptr, nullptr);
} }
} }
void Control::_notification(int p_notification) { void Control::_notification(int p_notification) {
switch (p_notification) { switch (p_notification) {
case NOTIFICATION_ENTER_TREE: { case NOTIFICATION_ENTER_TREE: {
_invalidate_theme_cache(); // Need to defer here, because theme owner information might be set in
// add_child_notify, which doesn't get called until right after this.
call_deferred(SNAME("notification"), NOTIFICATION_THEME_CHANGED);
} break; } break;
case NOTIFICATION_POST_ENTER_TREE: { case NOTIFICATION_POST_ENTER_TREE: {
@ -3142,18 +3130,6 @@ void Control::_notification(int p_notification) {
data.parent_window = Object::cast_to<Window>(get_parent()); data.parent_window = Object::cast_to<Window>(get_parent());
data.is_rtl_dirty = true; data.is_rtl_dirty = true;
if (data.theme.is_null()) {
if (data.parent && (data.parent->data.theme_owner || data.parent->data.theme_owner_window)) {
data.theme_owner = data.parent->data.theme_owner;
data.theme_owner_window = data.parent->data.theme_owner_window;
notification(NOTIFICATION_THEME_CHANGED);
} else if (data.parent_window && (data.parent_window->theme_owner || data.parent_window->theme_owner_window)) {
data.theme_owner = data.parent_window->theme_owner;
data.theme_owner_window = data.parent_window->theme_owner_window;
notification(NOTIFICATION_THEME_CHANGED);
}
}
CanvasItem *node = this; CanvasItem *node = this;
bool has_parent_control = false; bool has_parent_control = false;
@ -3257,6 +3233,7 @@ void Control::_notification(int p_notification) {
} break; } break;
case NOTIFICATION_THEME_CHANGED: { case NOTIFICATION_THEME_CHANGED: {
emit_signal(SceneStringNames::get_singleton()->theme_changed);
_invalidate_theme_cache(); _invalidate_theme_cache();
update_minimum_size(); update_minimum_size();
update(); update();
@ -3626,13 +3603,13 @@ void Control::_bind_methods() {
Control::~Control() { Control::~Control() {
// Resources need to be disconnected. // Resources need to be disconnected.
for (KeyValue<StringName, Ref<Texture2D>> &E : data.icon_override) { for (KeyValue<StringName, Ref<Texture2D>> &E : data.icon_override) {
E.value->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
for (KeyValue<StringName, Ref<StyleBox>> &E : data.style_override) { for (KeyValue<StringName, Ref<StyleBox>> &E : data.style_override) {
E.value->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
for (KeyValue<StringName, Ref<Font>> &E : data.font_override) { for (KeyValue<StringName, Ref<Font>> &E : data.font_override) {
E.value->disconnect("changed", callable_mp(this, &Control::_theme_property_override_changed)); E.value->disconnect("changed", callable_mp(this, &Control::_notify_theme_override_changed));
} }
// Then override maps can be simply cleared. // Then override maps can be simply cleared.

View File

@ -300,11 +300,10 @@ private:
// Theming. // Theming.
void _theme_changed(); void _theme_changed();
void _theme_property_override_changed(); void _notify_theme_override_changed();
void _notify_theme_changed();
void _invalidate_theme_cache(); void _invalidate_theme_cache();
static void _propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_assign = true); static void _propagate_theme_changed(Node *p_at, Control *p_owner, Window *p_owner_window, bool p_notify, bool p_assign);
template <class T> template <class T>
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 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);

View File

@ -848,21 +848,13 @@ void Window::_notification(int p_what) {
RS::get_singleton()->viewport_set_active(get_viewport_rid(), true); RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
} }
if (theme.is_null()) { // Need to defer here, because theme owner information might be set in
Control *parent_c = cast_to<Control>(get_parent()); // add_child_notify, which doesn't get called until right after this.
if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { call_deferred(SNAME("notification"), NOTIFICATION_THEME_CHANGED);
theme_owner = parent_c->data.theme_owner; } break;
theme_owner_window = parent_c->data.theme_owner_window;
notification(NOTIFICATION_THEME_CHANGED); case NOTIFICATION_THEME_CHANGED: {
} else { emit_signal(SceneStringNames::get_singleton()->theme_changed);
Window *parent_w = cast_to<Window>(get_parent());
if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) {
theme_owner = parent_w->theme_owner;
theme_owner_window = parent_w->theme_owner_window;
notification(NOTIFICATION_THEME_CHANGED);
}
}
}
} break; } break;
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
@ -1250,16 +1242,10 @@ Rect2i Window::get_usable_parent_rect() const {
} }
void Window::add_child_notify(Node *p_child) { void Window::add_child_notify(Node *p_child) {
Control *child_c = Object::cast_to<Control>(p_child); // We propagate when this node uses a custom theme, so it can pass it on to its children.
if (theme_owner || theme_owner_window) {
if (child_c && child_c->data.theme.is_null() && (theme_owner || theme_owner_window)) { // `p_notify` is false here as `NOTIFICATION_THEME_CHANGED` will be handled by `NOTIFICATION_ENTER_TREE`.
Control::_propagate_theme_changed(child_c, theme_owner, theme_owner_window); //need to propagate here, since many controls may require setting up stuff Control::_propagate_theme_changed(this, theme_owner, theme_owner_window, false, true);
}
Window *child_w = Object::cast_to<Window>(p_child);
if (child_w && child_w->theme.is_null() && (theme_owner || theme_owner_window)) {
Control::_propagate_theme_changed(child_w, theme_owner, theme_owner_window); //need to propagate here, since many controls may require setting up stuff
} }
if (is_inside_tree() && wrap_controls) { if (is_inside_tree() && wrap_controls) {
@ -1268,16 +1254,9 @@ void Window::add_child_notify(Node *p_child) {
} }
void Window::remove_child_notify(Node *p_child) { void Window::remove_child_notify(Node *p_child) {
Control *child_c = Object::cast_to<Control>(p_child); // If the removed child isn't inheriting any theme items through this node, then there's no need to propagate.
if (theme_owner || theme_owner_window) {
if (child_c && (child_c->data.theme_owner || child_c->data.theme_owner_window) && child_c->data.theme.is_null()) { Control::_propagate_theme_changed(this, nullptr, nullptr, false, true);
Control::_propagate_theme_changed(child_c, nullptr, nullptr);
}
Window *child_w = Object::cast_to<Window>(p_child);
if (child_w && (child_w->theme_owner || child_w->theme_owner_window) && child_w->theme.is_null()) {
Control::_propagate_theme_changed(child_w, nullptr, nullptr);
} }
if (is_inside_tree() && wrap_controls) { if (is_inside_tree() && wrap_controls) {
@ -1290,34 +1269,47 @@ void Window::set_theme(const Ref<Theme> &p_theme) {
return; return;
} }
theme = p_theme; if (theme.is_valid()) {
theme->disconnect("changed", callable_mp(this, &Window::_theme_changed));
}
if (!p_theme.is_null()) { theme = p_theme;
theme_owner = nullptr; if (theme.is_valid()) {
theme_owner_window = this; Control::_propagate_theme_changed(this, nullptr, this, is_inside_tree(), true);
Control::_propagate_theme_changed(this, nullptr, this); theme->connect("changed", callable_mp(this, &Window::_theme_changed), CONNECT_DEFERRED);
} else { return;
Control *parent_c = cast_to<Control>(get_parent()); }
Control *parent_c = Object::cast_to<Control>(get_parent());
if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) { if (parent_c && (parent_c->data.theme_owner || parent_c->data.theme_owner_window)) {
Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window); Control::_propagate_theme_changed(this, parent_c->data.theme_owner, parent_c->data.theme_owner_window, is_inside_tree(), true);
} else { return;
}
Window *parent_w = cast_to<Window>(get_parent()); Window *parent_w = cast_to<Window>(get_parent());
if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) { if (parent_w && (parent_w->theme_owner || parent_w->theme_owner_window)) {
Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window); Control::_propagate_theme_changed(this, parent_w->theme_owner, parent_w->theme_owner_window, is_inside_tree(), true);
} else { return;
Control::_propagate_theme_changed(this, nullptr, nullptr);
}
}
} }
Control::_propagate_theme_changed(this, nullptr, nullptr, is_inside_tree(), true);
} }
Ref<Theme> Window::get_theme() const { Ref<Theme> Window::get_theme() const {
return theme; return theme;
} }
void Window::_theme_changed() {
if (is_inside_tree()) {
Control::_propagate_theme_changed(this, nullptr, this, true, false);
}
}
void Window::set_theme_type_variation(const StringName &p_theme_type) { void Window::set_theme_type_variation(const StringName &p_theme_type) {
theme_type_variation = p_theme_type; theme_type_variation = p_theme_type;
Control::_propagate_theme_changed(this, theme_owner, theme_owner_window); if (is_inside_tree()) {
notification(NOTIFICATION_THEME_CHANGED);
}
} }
StringName Window::get_theme_type_variation() const { StringName Window::get_theme_type_variation() const {
@ -1712,6 +1704,7 @@ void Window::_bind_methods() {
ADD_SIGNAL(MethodInfo("theme_changed")); ADD_SIGNAL(MethodInfo("theme_changed"));
BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
BIND_CONSTANT(NOTIFICATION_THEME_CHANGED);
BIND_ENUM_CONSTANT(MODE_WINDOWED); BIND_ENUM_CONSTANT(MODE_WINDOWED);
BIND_ENUM_CONSTANT(MODE_MINIMIZED); BIND_ENUM_CONSTANT(MODE_MINIMIZED);

View File

@ -253,6 +253,7 @@ public:
void set_theme(const Ref<Theme> &p_theme); void set_theme(const Ref<Theme> &p_theme);
Ref<Theme> get_theme() const; Ref<Theme> get_theme() const;
void _theme_changed();
void set_theme_type_variation(const StringName &p_theme_type); void set_theme_type_variation(const StringName &p_theme_type);
StringName get_theme_type_variation() const; StringName get_theme_type_variation() const;