Allow Controls to automatically release focus when clicked off of
Describe the project you are working on
An application containing many Line/TextEdits and the like.
Describe the problem or limitation you are having in your project
Controls do not release focus when clicked outside of, and manually connecting to mouse_exited() on every one is not reasonable.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
Add an focus_release_on_outside_click option to Control to enable whether it should automatically release focus.
Alternatively, add a new value for focus_mode to enable this behavior.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
On mouse click, the currently focused node releases focus if the click was outside of its boundaries.
If this enhancement will not be used often, can it be worked around with a few lines of script?
class_name FocusAutoReleaseControl extends Control
func _input(event):
if not self.has_focus(): return
if not event is InputEventMouseButton: return
if not event.pressed: return
var control_rect = self.get_rect()
control_rect.position = Vector2.ZERO
var local_rect = control_rect
if local_rect.has_point(get_local_mouse_position()): return
self.release_focus()
However, this only works if the given Control does not need to be extended by another script, as GDScript lacks multiple inheritance.
Is there a reason why this should be core and not an add-on in the asset library?
See above.
Generally this is not going to be desirable. As it leaves a user without the ability to regain focus again.
For example, in most software I am familiar with, clicking on "nothing" may visually hide focus. But focus will continue from that element if the user starts using the arrow keys to navigate.
In game, I would not want to lose focus of a menu element if I click somewhere else. And I would expect focus to continue operating even if I do, and then try to change it using the arrow keys.
Consider that you can achieve the same behavior by placing a plain Control node as an invisible background, with mouse mode set to pass, and enable focus grabbing on that Control mode.
This means that if the user clicks "nothing", they will actually click on and grab focus with this control node. Which doesn't actually do anything with said focus afterwards. This also gives you one concrete place from which you can tell if the user has clicked "nothing".
You can also turn you example code into a generic component. Extend Node instead of Control, connect to the parents _gui_input() signal.
For example, in most software I am familiar with, clicking on "nothing" may visually hide focus. But focus will continue from that element if the user starts using the arrow keys to navigate.
- See also https://github.com/godotengine/godot-proposals/issues/2011.
We could do something similar once that proposal is implemented.
For example, in most software I am familiar with, clicking on "nothing" may visually hide focus. But focus will continue from that element if the user starts using the arrow keys to navigate.
Fair. However, in the case of a text box, clicking on nothing also cancels editing if it was taking place. This is a change in functionality, not just visuals. #2011 would not suffice. [EDIT: After trying more things out I did find a few exceptions that does not cancel editing, only hiding the cursor. However, with most software I have, this behavior is the exception.]
No such "focus-limbo" state is possible with Line/TextEdits currently.
Consider that you can achieve the same behavior by placing a plain Control node as an invisible background, with mouse mode set to pass, and enable focus grabbing on that Control mode.
Could you explain this in more detail? I tried it and could not get it to work as you describe.
Any news on this? No option for automatic release of focus is the only thing that's been bothering me with this system.
Place a control node behind your UI which acquires focus and does nothing, same effect. There's reason why this should not be a feature.
Could you explain this in more detail? I tried it and could not get it to work as you describe.
A plain Control node. Focus enabled. Put behind all your other UI. There's nothing else to it.
A plain Control node. Focus enabled. Put behind all your other UI. There's nothing else to it.
This does not work if you click on a control that has focus mode None and mouse filter Stop. That control will prevent clicking on the background control and focus will still present on the old control
My suggestion:
Add the focus_release_on_outside_click checkbox OP mentioned.
If it's true and you click outside this control, the focus should be removed from this control (or set to the clicked control).
The Scene could keep a reference to the last focused control. If you press a focus changing button (tab etc.), the last focused control should be focused again.
If the last focused control does not exist (anymore), focus the first node that can be focused.
Would that be possible?
This would also partially fix a problem i have.
I have a menu where i don't want to see any focused control as long as i click them with my mouse. When i press tab etc. for the first time, i want a control to take focus, but release the focus again when i click with my mouse.
I would need a focus mode All except Click for this though.
Please see my comments on a related engine-bug about UI elements not consuming key released events. https://github.com/godotengine/godot/issues/43701
I have a workaround for Godot specifically, but its cumbersome and unintuitive behavior in my opinion.
- Make your root control node FULL RECT anchor and set its focus mode to ALL
- Check
gui_get_focus_owner()at the top of your_unhandled_input
This lets you filter all input to the game if any HUD element happens to be focused.
func _unhandled_input(event: InputEvent) -> void:
if get_viewport().gui_get_focus_owner() != null:
return
What I found is that Godot is inconsistent in regard to this specific ticket with other engines like Unity3D. In Unity the InputField will automatically lose focus when you click away, like back onto the game. Interestingly, in regard to the other linked ticket, InputField does not consume any keyboard input so the cube and InputField both respond to keyboard events.
Here is a Unity3D specific code I added to my test project that shows Unity has the desired behavior that Godot doesn't.
If I click on the InputField and then space, I see the "exists" messsage and when I click back on the game and press space again I see the "null" message as expected.
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown("space"))
{
print("space key was pressed");
GameObject currentSelectedGameObject = EventSystem.current.currentSelectedGameObject;
if (currentSelectedGameObject == null) {
print("currentSelectedGameObject was null");
} else {
print("currentSelectedGameObject exists");
}
}
}
Generally this is not going to be desirable. As it leaves a user without the ability to regain focus again.
In web browsers, when you press Tab after clicking outside of something, the first interactable item on the page located after your click position will be focused, so that's not really an issue. (Shift + Tab focuses on the first interactable item before your click position instead.)
This may be complicated to implement in Godot though, so hiding visual focus only as you mentioned is probably the way to go as a first step.