godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

Add a function to get all Control nodes at a specific position #8408

Open popcar2 opened 10 months ago

popcar2 commented 10 months ago

Describe the project you are working on

I'm creating an OS-like interface in Godot. I want to show a context menu when the player right clicks the mouse. Depending on what the player right clicks, it would show different options in the context menu.

The context menu is an autoload and I'd prefer if all the right click handling code was inside of it.

Describe the problem or limitation you are having in your project

There is no way to get the Control node(s) that the mouse right clicked. A Control node can only know when it got right clicked itself, but there's no way to globally know which Control nodes in general were clicked.

For example if I want a folder, taskbar, and window to show different context menus when right clicked, I would have to write code in each one of them to handle the right click then signal it to my context menu autoload. This isn't very intuitive or scalable.

I also needed this in other tasks, such as being able to click anywhere to de-select a folder, a very common feature in user interfaces.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

A global function to get the Control nodes under the mouse while respecting mouse_filter rules.

It could also be a more generic function to get Control nodes at any position (where I can then give it get_global_mouse_position()).

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

func _input(event):
    if event is InputEventMouseButton and event.button_index == 2:
        var mouse_pos = get_global_mouse_position()
        var control_nodes = get_control_nodes_at_pos(mouse_pos)
        for node in control_nodes:
            # Do something here

If this enhancement will not be used often, can it be worked around with a few lines of script?

It's hackier than a usual workaround, but I added a component that handles right clicking of each node in a specific group. This isn't intuitive or performant.

Is there a reason why this should be core and not an add-on in the asset library?

Knowing what you clicked and haven't clicked is commonly needed when developing advanced UI. I commonly see people ask questions like "How can I tell which Control node I clicked?" and "How can I know when the user has clicked OUTSIDE of a button?", there isn't a simple way of doing this without workarounds.

A popular workaround is creating a Rect2 the same size and position as a Control node and seeing if the mouse event is inside it, but this doesn't respect mouse_filter so you'd have to implement code checking if the mouse is over it anyways.

In conclusion, I think being able to know which Control nodes got clicked and which ones weren't is very useful when building complex UI. Being able to get Control nodes at a given position is a good, generic way of creating this mechanic.

Spartan322 commented 8 months ago

Why not at least expose Viewport::gui_find_control?

Zireael07 commented 8 months ago

I think it's what was exposed in #85966 https://github.com/godotengine/godot/pull/85966 ?

AThousandShips commented 8 months ago

No it's what's being exposed in:

Spartan322 commented 8 months ago

There is one case about this I can think in which you have multiple overlapping controls, this only gets somewhat solved by that to be fair, but gui_find_control would be preferred in most cases anyway.

pc9098 commented 3 weeks ago

Right now I have multiple overlapping controls, this solution would be best for me