Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a lifecycle method for manual theme item caching to Control #65156

Merged

Conversation

YuriSizov
Copy link
Contributor

@YuriSizov YuriSizov commented Aug 31, 2022

Join me on a journey towards more organized utilization of theme items! In Part 1 we're adding the framework for controls and cover most controls with updated and, hopefully, improved code to utilize said framework.


As we're trying to improve the theme propagation logic and theme utilization in built-in controls and the editor, @AaronRecord made #62845, which unearthed some issues with how some UI elements are currently written. Namely, a significant amount of editor UI elements don't react correctly to theme changes which results in broken visuals. Before #62845 it was hard to spot those issues, as they would only fail to update on theme changes (so when a user messes with editor settings, which is fairly rare). After that PR, the issues became apparent as we no longer have theme immediately upon NOTIFICATION_ENTER_TREE, which allowed those controls to behave seemingly correct.

On top of that, we have a longstanding code quality issue. The code accessing theme items is disconnected and all over the place. As mentioned above, sometimes controls don't implement handlers for the necessary system events (like NOTIFICATION_THEME_CHANGED). Sometimes code is excessively run during each draw call, or at other times where no update to theme happened and thus the complicated execution path is unnecessary. I've partially addressed it with the dumb theme item cache in #64314, so performance-wise we're probably in a good spot.

Still, theme items excessively rely on strings, which results in hard-to-maintain error-prone code. I am slowly moving towards solving this completely with ThemeDB (godotengine/godot-proposals#4486), but that work is still very much in progress. In the meantime my plan to improve code quality, is as follows:

  • Add to Control and Window a virtual method that would be called automatically at necessary points in time to generate the "not-so-dumb" cache. The cache structure remains a responsibility of each individual control or window class, but for consistency I'm proposing to create ThemeCache struct named theme_cache.
  • Make each control and window class utilize it, move all get_theme_* calls to that method and use class properties instead throughout the rest of the methods.
  • Implement the same logic for editor controls/widgets. At least the ones directly extending control types.
  • Patch the remaining editor widgets with some other ad-hoc caching.

This removes the need for contributors to think about when is it correct to update theme items, as there is now a method for it. This also reduces the amount of theme related strings in the codebase, keeping them only in the specific place of each class.
For editor widgets this hopefully makes it harder to write code that would not implement theme support properly, removing the issues uncovered by #62845.


This PR is only the first one of several.

  • It adds Control::_update_theme_item_cache() and adds calls to it in the appropriate places to ensure items are valid and up to date.
  • It implements the method for most of control nodes.
  • Cleans up some bad theme item accesses.

I've omitted CodeEdit and TextEdit for now, because they already have some sort of cache and I wanted to push this as soon as possible. Tree is in a similar position, but I've already committed to it today, so it was converted to the new system.

I've also omitted RichTextLabel, as it's big and I don't want to break it right now. The system is optional, so it doesn't really matter if everything is implemented at the same time.

And I've also omitted GraphEdit/GraphNode and ColorPicker/ColorPickerButton/ColorPresetButton, as those have refactoring PRs in works, and I don't want to introduce major conflicts for now (#61414 and #62910).


I will continue tomorrow with Window-based nodes and the reported editor widgets. There are around 3k calls to get_theme_* in our codebase, so help is appreciated (if we decide to merge this, of course). Poke me on RC if you want to take a part of this work upon yourself.

@YuriSizov YuriSizov force-pushed the control-customizable-cache-p1 branch 2 times, most recently from 1479f6e to 98de0f7 Compare September 1, 2022 09:57
Copy link
Member

@bruvzg bruvzg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not tested all the controls, only looked through the code. Everything looks good.

scene/gui/button.cpp Show resolved Hide resolved
scene/gui/tree.cpp Outdated Show resolved Hide resolved
Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks excellent. I only checked thoroughly down to Label, but I expect the rest follows the same logic. Tested locally and didn't spot any obvious regression in the editor.

scene/gui/box_container.cpp Show resolved Hide resolved
Comment on lines 120 to +121
if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) {
style = get_theme_stylebox(SNAME("normal_mirrored"));
style = theme_cache.normal_mirrored;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see most of this code uses has_theme_* checks before fetching the theme items, but the new cache doesn't.
Can that lead to issues?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks are kind of iffy to begin with, we normally don't code controls like that. Issues this should not create, after all the cache would simply host default values for each data type. But these has_theme_* calls are potentially excessive, same as get_theme_* calls were, and we aren't using any cache for them. I think we could simply use the cached values, or at least the dumb cache.

But I've decided to get back to this later.

scene/gui/check_box.cpp Show resolved Hide resolved

theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
theme_cache.check_v_adjust = get_theme_constant(SNAME("check_v_adjust"));
theme_cache.normal_style = get_theme_stylebox(SNAME("normal"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistant with Button and the actual theme item name it should likely be normal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or the other way around maybe, the Button ones could get _style suffix. I suppose you're already planning to harmonize all this and make theme item names more explicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer for the cache items to be explicit at least, so that the code that uses it is clear. Admittedly, in this PR I didn't update everything to a better style, but that's in part because indeed I can do that while harmonizing the names.

scene/gui/check_button.cpp Show resolved Hide resolved
@akien-mga akien-mga merged commit dcd7456 into godotengine:master Sep 1, 2022
@akien-mga
Copy link
Member

Thanks!

@YuriSizov YuriSizov deleted the control-customizable-cache-p1 branch September 1, 2022 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants