Godot 3.0.5
I've been several times in the situation where I wanted to animate a GUI window while it opens or closes. The problem is, when the animation play, I don't want the window or any of its children to be interactive. If it was, an unfortunate double-click could trigger a child control during the animation, leading to unwanted game state.
Disabling all widgets in the window would be messy, because you might need that state set for other reasons and it changes how controls look.
Then, so far I've been solving this problem by placing an occluder control on top of it to block mouse clicks, but I feel it's not as easy as it should be, and it has complications for example when the parent is a container (would mess with anchors, requiring additional setup).
Another case is when doing an animated pause menu. With a blocking control on top of the window, I cannot get input to work in the game until the animation completely hides it (because I am correctly using _unhandled_input), but in that case I want game input to start working immediately as if the menu was ignoring all input.
It also doesn't take care of keyboard input, although a script could clear the focus.
I tried changing mouse_filter, but this one only affects the control and not its children. I would like to basically have that working with the following options:
An important missing feature.
The method should also change the children to a _disabled_ theme look, e.g. a _Button_ should appear "gray" as if .disabled=true
I would not expect it to look different during an animation, though. I just want them to not respond to input. I also thought about disabling input globally, but that would be extremely intrusive and would not work for widgets using only a local region of the screen.
We need the same feature for different needs. Your is an animation, mine is disabling a _PanelContainer_ that is not needed until a certain condition is met. That's why I also need a "gray" look on the controls. I guess there's the need for two different methods?
I've been using two different methods in my own projects to achieve this.
One is to loop through the nodes and set their mouse_filter to Control.MOUSE_FILTER_IGNORE in _ready (or to set them in the editor) as this will stop them responding to clicks and other mouse events - without triggering the nodes disabled state, then use a function track on the animation to call a method that loops through the children and set them back to Control.MOUSE_FILTER_STOP.
If it is only certain nodes you want to disable/enable then you can use grouping or list them in an array to loop through.
The second, which I prefer only because it can serve a second purpose, is to have a Container (in my case ColorRect) which you add to the top of the scene which will have a mouse_filter of Control.MOUSE_FILTER_STOP. This prevents mouse events going through to the elements underneath and can be transparent, which (again with a function track called method) you can remove (or move/resize to not cover your elements, or change its mouse_filter to Control.MOUSE_FILTER_IGNORE). My other use for this is you can give it a color with alpha Color(0,0,0,0.4) which would dim the current scene (as well as prevent events) when overlaying a popup scene on top.
I've been using the second method so far, but as I described it has some edge cases.
Also none of this fixes keyboard events, but I guess that requires changing the key focus.
I probably wasn't clear in my description, I trigger the blocking container from a global script (in which they get created when needed and destroyed when not used) so I can place it at any layer in the scene tree (between game and menu, over a menu) at any size and color.
I also use a variable onready var __IsReady = false in a base script all scenes extend from, which gets checked in _input/_unhandled_input (and around any Input checks in _process) - This could be an enum for finer control. For some scenes this will get set to true in _ready but it can also be set in the same function that triggers the occluding control or post onscreen animation. I think the documents talk about using get_tree().paused/pause_mode for input blocking but I found that didn't disable Control nodes, and it blocks _process which we might not want.
Using both of these I can add a blocking control over a main menu scene when 'options' is clicked and prevent input to anything but the popup it creates. Then I can also add a blocking control over the popup and remove the one from beneath the menu and using __IsReady to change how my _input (and _unhandled_input) works so input and mouse are passed to the bottom scene before the menu animates away (which is what I think you're talking about achieving?)..
I tried to avoid going global or script-level inheritance everywhere, mostly because it's not a paradigm I'm comfortable with anymore (got stuck too often with the diamond problem and it's harder to reuse the functionality) and that would not fix local controls that don't mean to block the whole screen. So currently I have a locally blocking control added in a component-style way on each dialog and each UI that needs it, but coming up with this was not easy as I found many edge cases.
So yeah I have found similar workarounds to make this work, but I opened the issue to explain that case since it could be useful to have a feature in engine that helps accomplishing it, if doable.
I'm guessing a solution (to the intitial request, rather than the work around - which I use too) would be adding an Inherit mode to mouse_filter?
Unsure on the feasability (impact on speed/processing) but: adding MOUSE_FILTER_INHERIT to MouseFilter in 'scene/gui/control.h' (and bind it in '/scene/gui/control.cpp') which could then be checked on _gui_ calls in 'scene/main/viewport.cpp' (the same place other Control::MOUSE_FILTER_* flags are checked). This would probably need propagating the same way pause_mode is handled in 'scene/main/node.cpp' (set_pause_mode -> _propagate_pause_owner) but for set_mouse_filter in 'scene/gui/control.cpp'
There might need to be some type/class checks as only things that inherit from control have mouse_filter (or add it to node - as it would get ignored by anything not inheriting from control).
I would start/test this but I have never managed to get source to compile on windows, I will give it another go though.
@GlenIku The problem is that keyboard input would still work. Also, this won't change the children controls look. This is why there should be _disable()_ and _enable()_ methods that take care of both input and look.
It's an obvious feature in my view: you do panel.disable() and all the children controls get grayed out recursively.
@char0xff the original request says
Disabling all widgets in the window would be messy, because you might need that state set for other reasons and it changes how controls look
Which is why I was thinking they wanted to disable mouse events without changing appearance (which your recursive disabling would do as it would be setting disabled).
I do like your recursive enable/disable idea though, and even though this can be done in gdscript, I guess it would be nice to know the speed of recursivly going for n in get_children baked in vs written in gdscript.
Edit: Just had a look and some gui elements use editable whilst others use disabled, so more type checks?
Curious if there has been any updates regarding this feature? I鈥檓 looking for a clean way of disabling/enabling keyboard input.
Curious if there has been any updates regarding this feature? I鈥檓 looking for a clean way of disabling/enabling keyboard input.
This has been driving me crazy recently. Surely there is a way to achieve this?
I'm using a transparent color rectangle named 'Disabled' which covers the panel and a property of the panel named 'disabled';
var disabled setget _disable
.....
.....
.....
func _disable(state: bool):
$Disabled.visible = state
disabled = state
var focussed: Control = $Panel/ButtonOK.get_focus_owner()
if focussed: focussed.release_focus()
Setting the panels disabled property controls the panel state. I just use any panel control to get the current control with focus and release it - seems to work OK so far.
Feature and improvement proposals for the Godot Engine are now being discussed and reviewed in a dedicated Godot Improvement Proposals (GIP) (godotengine/godot-proposals) issue tracker. The GIP tracker has a detailed issue template designed so that proposals include all the relevant information to start a productive discussion and help the community assess the validity of the proposal for the engine.
The main (godotengine/godot) tracker is now solely dedicated to bug reports and Pull Requests, enabling contributors to have a better focus on bug fixing work. Therefore, we are now closing all older feature proposals on the main issue tracker.
If you are interested in this feature proposal, please open a new proposal on the GIP tracker following the given issue template (after checking that it doesn't exist already). Be sure to reference this closed issue if it includes any relevant discussion (which you are also encouraged to summarize in the new proposal). Thanks in advance!
Most helpful comment
Curious if there has been any updates regarding this feature? I鈥檓 looking for a clean way of disabling/enabling keyboard input.