egui icon indicating copy to clipboard operation
egui copied to clipboard

🧞 `egui_winit::State::handle_event.consumed` is `false` on touch input where `true` would be expected

Open tosti007 opened this issue 2 years ago • 1 comments

Context

I am using egui_winit to pass winit events to egui. The egui ui is drawn on top of another texture using a custom (non-public) render backend. The events, when not handled by egui (i.e. when anywhere else is clicked/dragged), are passed along to another system. This entire system is running on an Android device, so all pointer input is done through Touch.

Description

A winit::WindowEvent::Touch has four possible phases: Started, Moved, Ended, and Cancelled. The issue I am having is in two of them.

TouchPhase::Started

The consumed value is based on Context::wants_pointer_input, however since during this event, there is no known mouse position, as the event isn't passed to egui::Context::begin_frame yet. This results in the wants_pointer_input to always be false, thus this event is never consumed.

TouchPhase::Moved

In the previous TouchPhase::Started event, the actual mouse position is set. However, egui_winit uses the Context::is_using_pointer value for the consumed state. The value emitted by this function becomes true when there is either a known clicked or dragged Widget Id. However, these values aren't set immediately, instead are set after a few frames delay, causing the first few events to be unconsumed.

TouchPhase::Ended | TouchPhase::Cancelled

These two phases currently use wants_pointer_input, however I feel like the more semantically correct choice would be to use is_using_pointer instead, as it's the last event in the chain.

Current workaround

To ensure the event doesn't accidentally fall through, I am currently using basically the following workaround:

let consumed = state.on_event(&ctx, event).consumed;
let actually_consumed = match event {
    WindowEvent::Touch(Touch { phase, .. }) => match phase {
        TouchPhase::Started => true,
        TouchPhase::Moved => consumed || ctx.input(|i| i.pointer.interact_pos().is_none()),
        TouchPhase::Ended | TouchPhase::Cancelled => false,
    },
    _ => consumed,
};

tosti007 avatar Aug 07 '23 11:08 tosti007

I ran into the same problem but on mouse input. i am using bevy, so fetching input using Input<MouseButton>

it goes something like this.

wants_pointer_input -> true
is_pointer_over_area -> true
Input<MouseButton>::just_pressed(MouseButton::Left) -> false
Input<MouseButton>::pressed(MouseButton::Left) -> false

wants_pointer_input -> false (should have been true)
is_pointer_over_area -> true
Input<MouseButton>::just_pressed(MouseButton::Left) -> true
Input<MouseButton>::pressed(MouseButton::Left) -> true

wants_pointer_input -> true
is_pointer_over_area -> true
Input<MouseButton>::just_pressed(MouseButton::Left) -> false
Input<MouseButton>::pressed(MouseButton::Left) -> true

thrombe avatar Dec 25 '23 23:12 thrombe