godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.98k stars 21.16k forks source link

vslider _gui_input(event: InputEvent) reports old value #47322

Open wyattbiker opened 3 years ago

wyattbiker commented 3 years ago

Godot version: Godot Engine v3.2.4.rc4.official - https://godotengine.org OpenGL ES 3.0 Renderer: NVIDIA GeForce GT 650M OpenGL Engine OpenGL ES Batching: ON Mac OS X Catalina 10.15.7

Issue description: VSlider reports old value of slider in _gui_input(event: InputEvent) signal

Steps to reproduce:

extends VSlider

#This signal is called 1st
func _on_VSlider_gui_input(event: InputEvent) -> void:
    if (event.is_pressed() and event.button_index == BUTTON_LEFT):  
        print("\nClicked")
        print("_gui_input: ",value) # Reports the old slider value

#This signal is called 2nd
func _on_VSlider_value_changed(value: float) -> void:
    print("_value_changed: ",value) # Correctly reports the current slider value

When VSlider is clicked _gui_input() method reports the original old value, while _value_changed()reports the correct new value.

Example Output:

Clicked
_gui_input: 0
_value_changed: 6

Clicked
_gui_input: 6
_value_changed: 1

Clicked
_gui_input: 1
_value_changed: 9

Makes the _gui_input() not as useful.

Thanks!

jmb462 commented 3 years ago

As a workaround, you can yield for a frame in gui_input callback method.


func _on_VSlider_gui_input(event: InputEvent) -> void:
    if (event.is_pressed() and event.button_index == BUTTON_LEFT):  
        yield(get_tree(),"idle_frame")
        print("\nClicked")
        print("_gui_input: ",value) # Reports the old slider value```
Calinou commented 3 years ago

Does this occur with other kinds of GUI controls such as CheckBox?

This may actually be intended behavior, but I'm not 100% sure. Sometimes, you actually want to read the old value of a control. Maybe it should just be documented.

wyattbiker commented 3 years ago
yield(get_tree(),"idle_frame")

@jmb462 Good workaround as long as you don't mind value_changed() being completed before gui_input().

@Calinou As per your suggestion CheckBox as well as SpinBox have same behavior as VSlider. Would be nice to understand if it's a use case and document it with workaround and leave alone. I assume so much software been written around it.

In my case I was using gui_input() to emit a signal to other scenes that used that control. So it was providing the 'old' value to them.

Nukiloco commented 3 years ago

I think it wouldn't really make any sense to keep the old value on _gui_input(). If I needed the old value, I would of just made a variable called var last_value and set it to whatever value comes through _gui_input() or _value_changed().

WhalesState commented 1 month ago

The Control node first calls the virtual function Control._gui_input, allowing you to manually handle input before it's processed internally.

void Control::_call_gui_input(const Ref<InputEvent> &p_event) {
    if (p_event->get_device() != InputEvent::DEVICE_ID_INTERNAL) {
        emit_signal(SceneStringName(gui_input), p_event); // Signal should be first, so it's possible to override an event (and then accept it).
    }
    if (!is_inside_tree() || get_viewport()->is_input_handled()) {
        return; // Input was handled, abort.
    }

    if (p_event->get_device() != InputEvent::DEVICE_ID_INTERNAL) {
        GDVIRTUAL_CALL(_gui_input, p_event);
    }
    if (!is_inside_tree() || get_viewport()->is_input_handled()) {
        return; // Input was handled, abort.
    }
    gui_input(p_event);
}