godot icon indicating copy to clipboard operation
godot copied to clipboard

Buttons respond to input when set to PROCESS_MODE_DISABLED

Open SpaceAttorney opened this issue 2 years ago • 4 comments

Godot version

4.1.3.stable

System information

Godot v4.1.3.stable - Windows 10.0.22621 - Vulkan (Compatibility) - NVIDIA T500 (NVIDIA; 31.0.15.4601) - 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz (8 Threads)

Issue description

As I understand it, setting a node's process mode to Node.PROCESS_MODE_DISABLED should cause it to behave as if the scene tree were paused. This behavior is inconsistent in the case of Buttons. When the scene tree is paused, Buttons fail to respond to any input as expected; however, when Buttons are disabled via Node.process_mode, they continue to display active hover and focus behavior. This may be the case for other interactable Controls as well, I haven't tested.

Steps to reproduce

  • Create any scene with a Button
  • Set its process mode to "Disabled" in the Node settings
  • Run the test scene and observe its behavior

Minimal reproduction project

N/A

SpaceAttorney avatar Nov 10 '23 07:11 SpaceAttorney

related to #41487

Sauermann avatar Nov 10 '23 08:11 Sauermann

Processing and input processing are different things. Buttons receive input events through "_gui_input" and there is no direct way to disable that. The inputs for that come from mouse or keyboard events while the control is focused. if you wish for your buttons to not receive input, you can arrange all pausable buttons in a layer and create a overlay on top of them which captures all clicks. if you wish you can even add blur or some color+opacity to this click blocker. also, upon pausing you may wish to store the focused control, and restore focus upon unpausing.

darthLeviN avatar Nov 11 '23 22:11 darthLeviN

Disabling nodes via process mode does stop callbacks to _input() and _unhandled_input() though. I don't see why _gui_input() specifically should be an exception to that.

SpaceAttorney avatar Nov 11 '23 23:11 SpaceAttorney

To add to the discussion: I am trying to find a proper way to disable all interactions for a certain element.

For example:

  • A grid of buttons that when any one clicked brings up a popup.
  • While the popup is open, the buttons in the background should not respond to hover or focussing (e.g. via keyboard tabbing)
  • I don't want to disable all buttons manually as this is prone to errors

In web development for example there is the "inert" property that tells the browser to ignore all events for a node and its children but neither process_mode disabled nor mouse_filter ignore seem to function in such way.

@darthLeviN's approach sounds interesting (thanks for sharing ❤️) but I'd assume would still allow for keyboard tabbing? It seems kinda hacky (at least coming from web development).

stephanbogner avatar Aug 17 '24 21:08 stephanbogner

To add to the discussion: I am trying to find a proper way to disable all interactions for a certain element.

For example:

  • A grid of buttons that when any one clicked brings up a popup.
  • While the popup is open, the buttons in the background should not respond to hover or focussing (e.g. via keyboard tabbing)
  • I don't want to disable all buttons manually as this is prone to errors

In web development for example there is the "inert" property that tells the browser to ignore all events for a node and its children but neither process_mode disabled nor mouse_filter ignore seem to function in such way.

@darthLeviN's approach sounds interesting (thanks for sharing ❤️) but I'd assume would still allow for keyboard tabbing? It seems kinda hacky (at least coming from web development).

You can also use propagate_call. add a script to the controls you want. the script needs to have func custom_set_input_enabled(p_enabled: bool) defined and call propagate_call(&"custom_set_input_enabled", [true/false]) on the parent node and it will search through the node recursively, and for every node that has the method, it will call the method. i just have to add that this won't update the newly added nodes so you need to be careful with that. it only updates the nodes that are already there when you call the function.

If you want to go a step further and have multilayered control here and stop propagation at certain nodes, you will need to use a Autoload with a utility method that works similar to propagate_call but stops if it runs into a specific node group.

here is the implementaiton :

func layered_propagate_call(p_root: Node, p_func: StringName, p_args: Array, p_stopgroup := &"stop_propagation"):
    if root.has_method(p_func):
        root.callv(p_func, p_args)
    for child in root.get_children():
        if not child.is_in_group(p_stopgroup):
            layered_propagate_call(child, p_func, p_args, p_stopgroup)
        elif child.has_method(p_func):
            child.callv(p_func, p_args)

You can then add the other root nodes to the "stop_propagation" group to make different layers of input

Another solution is to use propagate_call with a different function :

var input_blockers := {}
func custom_add_input_blocker(node):
    if not input_blockers.has(node):
        input_blockers[node] = true
        node.tree_exiting.connect(_input_blocker_exiting_tree.bind(node), CONNECT_ONE_SHOT)
        refresh_input_mode()
func custom_remove_input_blocker(node):
    if input_blockers.has(node):
        input_blockers.erase(node)
        node.tree_exiting.disconnect(_input_blocker_exiting_tree)
        refresh_input_mode()
func _input_blocker_exiting_tree(node):
    input_blockers.erase(node)
    refresh_input_mode()
func refresh_input_mode():
    if input_blockers.is_empty():
        # override to enabled input
        pass
    else:
        # override to disabled input
        pass

This will allow you to add input blockers recursively by calling custom_remove_input_blocker and custom_add_input_blcoker with the help of propagate_call

darthLeviN avatar Aug 19 '24 17:08 darthLeviN

Reproduced (with much surprise) in v4.4.1.stable.official [49a5bc7b6], see attached project.

button-when-paused.tgz

bennbollay avatar Aug 21 '25 03:08 bennbollay