diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index 5452807770..e8a5464aa2 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -33,7 +33,7 @@ [/csharp] [/codeblocks] See also [BaseButton] which contains common properties and methods associated with this node. - [b]Note:[/b] Buttons do not detect touch input and therefore don't support multitouch, since mouse emulation can only press one button at a given time. Use [TouchScreenButton] for buttons that trigger gameplay movement or actions. + [b]Note:[/b] Buttons support multitouch via touch input, allowing multiple buttons to be pressed at the same time. Otherwise, mouse input is used, limiting interaction to one button press at a time. https://godotengine.org/asset-library/asset/2712 diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 598670b58f..31bf964729 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -63,6 +63,45 @@ void BaseButton::gui_input(const Ref &p_event) { return; } + if (p_event->get_device() == InputEvent::DEVICE_ID_EMULATION) { + return; + } + + if (p_event->is_pressed()) { + status.device_id = p_event->get_device(); + } + + if (p_event->get_device() != status.device_id) { + return; + } + + Ref touch = p_event; + if (touch.is_valid()) { + if (status.touch_index == -1) { + if (touch->is_pressed()) { + status.touch_index = touch->get_index(); + status.press_attempt = true; + status.pressing_inside = has_point(touch->get_position()); + on_action_event(p_event); + } + } else if (touch->get_index() == status.touch_index) { + if (!touch->is_pressed()) { + status.touch_index = -1; + on_action_event(p_event); + } + } + } + + Ref drag = p_event; + if (drag.is_valid() && drag->get_index() == status.touch_index && status.press_attempt) { + bool last_press_inside = status.pressing_inside; + status.pressing_inside = has_point(drag->get_position()); + + if (last_press_inside != status.pressing_inside) { + queue_redraw(); + } + } + Ref mouse_button = p_event; bool ui_accept = p_event->is_action("ui_accept", true) && !p_event->is_echo(); @@ -154,6 +193,7 @@ void BaseButton::_notification(int p_what) { case NOTIFICATION_SCROLL_BEGIN: { if (status.press_attempt) { status.press_attempt = false; + status.touch_index = -1; queue_redraw(); } } break; @@ -165,6 +205,7 @@ void BaseButton::_notification(int p_what) { case NOTIFICATION_FOCUS_EXIT: { if (status.press_attempt) { status.press_attempt = false; + status.touch_index = -1; queue_redraw(); } else if (status.hovering) { queue_redraw(); @@ -187,6 +228,7 @@ void BaseButton::_notification(int p_what) { status.hovering = false; status.press_attempt = false; status.pressing_inside = false; + status.touch_index = -1; } break; } } @@ -205,8 +247,11 @@ void BaseButton::_toggled(bool p_pressed) { void BaseButton::on_action_event(Ref p_event) { Ref mouse_button = p_event; + Ref screen_touch = p_event; + Ref screen_drag = p_event; - if (p_event->is_pressed() && (mouse_button.is_null() || status.hovering)) { + bool is_accept_event = mouse_button.is_null() && screen_touch.is_null() && screen_drag.is_null(); + if (p_event->is_pressed() && (is_accept_event || status.hovering)) { status.press_attempt = true; status.pressing_inside = true; if (!status.pressed_down_with_focus) { diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index b089947c3f..c12f39e4ba 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -63,7 +63,8 @@ private: bool pressing_inside = false; bool pressed_down_with_focus = false; bool disabled = false; - + int touch_index = -1; + int device_id = InputEvent::DEVICE_ID_EMULATION; } status; Ref button_group;