Add hover state to Tree items display
Implements https://github.com/godotengine/godot-proposals/issues/7160https://github.com/godotengine/godot-proposals/issues/7160
Since this is a bit less trivial than other hover additions, here are some explanations.
This change aims at improving the usability of the scene tree and all places where a Tree control is used. Moving the mouse above a tree item highlights it.
The highlight is also rendered on individual hovered icon buttons. In this case, the main row or cell it belongs to keeps its highlight in a "dimmed" version. This helps focusing on the icon while keeping the visual relationship with the piece of data the action is likely to impact.
Example with a slightly customized theme to illustrate a dense list:
Detail on the "dimmed hover" effect:
To make it more obvious, here it is with customized colors:
Projects can take advantage of the new styling possibilities. All new visual elements are customizable using appropriate theme resources.
Editor and default themes are adjusted to provide the additional theme resources. Other existing stuff is left untouched in this regard, so that there are no regressions on existing projects.
The Tree control is a complex widget, handling many possibilities of display. The display and original click handling algorithm is based on recursive traversal.
I implemented a direct, lighter way to find out which item is hovered that exactly matches, or at best attempt, the algorithm used to determine the clicked item, because mouse movemens happen more often.
This PR is exclusively adding support for hover feedback. It does not fix a few inconsistencies I found in click coordinates resolution, especially for the buttons attached to a cell (think the script, show/hide, etc... you see in the scene tree) with some combination of spacings. Those may be addressed in separate PRs. The goal here is that the hovered zones match the currently clickable zones.
A good part of the complexity is that there is an algorithm to move those icons towards the left in comparison to their original position in the cell if there is horizontal scrolling. This is what keeps the icon buttons visible in the scene tree at all times even if there is a long node name that overflows. The logic to compute that offset is a little complex and I had to replicate it. The said algorithm is not implemented in right-to-left display and some of the public methods that return the position of the in-cell buttons do not apply it - matter for separate issue reports.
This complex control has to be refactored in order to fix some positioning quirks. This waits for a more important upgrade where it is admissible to potentially break some themes.
For the changes covered in this PR, I reproduced in the hover code the way side buttons are positioned including the quirks. For example, you can see those giggling sideways if you scroll the inspector horizontally when names are longer. The original click detection is a recursive tree traversal; for the hover I found a simpler way without having to hook up to this recursivity. That is less execution complexity as well for an event that occurs more often than clicks. As some refactoring is planned for later, I opted of the simpler solution.
CC @passivestar as I believe you had Opinions™ on this type of hover/select UX ;)
CC @passivestar as I believe you had Opinions™ on this type of hover/select UX ;)
I like it, I think godot needs it. I actually wanted to work on this myself before I discovered this PR
I think I found a bug where it sometimes won't unhover when the mouse leaves the dock:
https://github.com/godotengine/godot/assets/60579014/f9e38469-ac2b-4cc4-8ee3-ce8fa449dee0
I think I found a bug where it sometimes won't unhover when the mouse leaves the dock:
@davthedev Would you have time to see if you can reproduce and fix that issue?
Sure! I will have more time to have a look in a couple days.
Tree seems to be getting an InputEventMouseMotion after NOTIFICATION_MOUSE_EXIT, causing cache.hover_item to be set again before the queued redraw, which causes the bug that I found
You could either delay nullifying cache.hover_item to make sure it's null before the redraw happens, or properly check that the mouse cursor is in the tree when we're setting things in gui_input
None of that seems ideal though and I find it weird that we're getting a mouse motion event after the mouse exit notification, is it the intended behavior in godot?
Also noted another couple bugs I should fix:
-
If the content is refreshed while hovering, the highlight is lost until the next mouse move. This happens on the "Distant" nodes tree while a scene is running.
-
Hover highlight is also visible during a drag-drop operation and can be misleading at that moment, as the one thing that should be highlighted during this operation is the drop target.
Just fixed the two bugs above. Changed the mouse over (or not) tracking logic to tentatively fix @passivestar hover feedback bug, relying on MOUSE_ENTER / _EXIT notifications. As I was not able to reproduce on my side, can you have a look?
As I was not able to reproduce on my side, can you have a look?
The bug is gone 🥳
I noticed two other things though:
- Hovering in the project manager is broken now? It uses a bright thin outline instead of whatever is was before
https://github.com/godotengine/godot/assets/60579014/0d0f5b35-f9ce-4727-8b56-21e6425061b1
- When hovering in a tree that has more than one main non-button column it highlights the right column, which doesn't always make sense semantically. In the shortcuts editor for example those buttons are related to the entire row, not just the rightmost column. When using trees in games it might not always make sense either
https://github.com/godotengine/godot/assets/60579014/e2578f49-6162-42ea-9ab5-5254a155049d
Perhaps when a tree has more than 2 main columns and you're hovering over a button it should highlight ALL of the columns or NONE of them, instead of the right one?
I noticed two other things though:
1. Hovering in the project manager is broken now? It uses a bright thin outline instead of whatever is was before
Looks like a broken editor theme. The thin grey outline is the default StyleBox when nothing is specified in the system theme; I will have a look. I rebased my month-old work with lots of commits in between, probable collateral damage...
2. When hovering in a tree that has more than one main non-button column it highlights the right column, which doesn't always make sense semantically. In the shortcuts editor for example those buttons are related to the entire row, not just the rightmost column. When using trees in games it might not always make sense either
Because there are multiple possible select modes in the Tree control that can be set on each instance: entire row or single cell. The view you are referring to is in single-cell mode, unlike the node tree. It works like a spreadsheet editor. I think it is up to this specific screen to work its way around instead by configuring the Tree control instance correctly.
Here, the cell is the actual binding, which you can edit using the dedicated buttons. Makes sense IMHO.
If some actions need to relate to the whole row, while keeping the separate cell selection possible, there are multiple options:
- Configure this screen in single-row selection mode - check UX as, for now, double-clicking the left cell unfolds the tree, and double-clicking the right cell opens the binding edit dialog.
- Make a separate third column for the buttons
- Add a setting to the Tree control mandating to highlight the whole row on specific cells / columns or buttons. Or a "select single cell but highlight whole row on buttons" selection mode
I guess my main concern is not the shortcut window but the fact that the last column will always be highlighted in this mode whenever somebody makes a game with a spreadsheet in it. Kind of random? It seems like checking if there's more than one column and not highlighting any column (only highlighting the button) in this mode would be a more sensible default imo