1use crate::{Id, InputState, Key, WidgetRects, hit_test, id, input_state, memory};
4
5use self::{hit_test::WidgetHits, id::IdSet, input_state::PointerEvent, memory::InteractionState};
6
7#[derive(Clone, Default)]
13pub struct InteractionSnapshot {
14 pub clicked: Option<Id>,
16
17 pub long_touched: Option<Id>,
20
21 pub drag_started: Option<Id>,
25
26 pub dragged: Option<Id>,
35
36 pub drag_stopped: Option<Id>,
41
42 pub hovered: IdSet,
50
51 pub contains_pointer: IdSet,
57}
58
59impl InteractionSnapshot {
60 pub fn ui(&self, ui: &mut crate::Ui) {
61 let Self {
62 clicked,
63 long_touched,
64 drag_started,
65 dragged,
66 drag_stopped,
67 hovered,
68 contains_pointer,
69 } = self;
70
71 fn id_ui<'a>(ui: &mut crate::Ui, widgets: impl IntoIterator<Item = &'a Id>) {
72 for id in widgets {
73 ui.label(id.short_debug_format());
74 }
75 }
76
77 crate::Grid::new("interaction").show(ui, |ui| {
78 ui.label("clicked");
79 id_ui(ui, clicked);
80 ui.end_row();
81
82 ui.label("long_touched");
83 id_ui(ui, long_touched);
84 ui.end_row();
85
86 ui.label("drag_started");
87 id_ui(ui, drag_started);
88 ui.end_row();
89
90 ui.label("dragged");
91 id_ui(ui, dragged);
92 ui.end_row();
93
94 ui.label("drag_stopped");
95 id_ui(ui, drag_stopped);
96 ui.end_row();
97
98 ui.label("hovered");
99 id_ui(ui, hovered);
100 ui.end_row();
101
102 ui.label("contains_pointer");
103 id_ui(ui, contains_pointer);
104 ui.end_row();
105 });
106 }
107}
108
109pub(crate) fn interact(
110 prev_snapshot: &InteractionSnapshot,
111 widgets: &WidgetRects,
112 hits: &WidgetHits,
113 input: &InputState,
114 interaction: &mut InteractionState,
115) -> InteractionSnapshot {
116 profiling::function_scope!();
117
118 if let Some(id) = interaction.potential_click_id
119 && !widgets.contains(id)
120 {
121 interaction.potential_click_id = None;
123 }
124 if let Some(id) = interaction.potential_drag_id
125 && !widgets.contains(id)
126 {
127 }
132
133 let mut clicked = None;
134 let mut dragged = prev_snapshot.dragged;
135 let mut long_touched = None;
136
137 if input.key_pressed(Key::Escape) {
138 dragged = None;
140 interaction.potential_drag_id = None;
141 }
142
143 if input.is_long_touch() {
144 if let Some(widget) = interaction
146 .potential_click_id
147 .and_then(|id| widgets.get(id))
148 {
149 dragged = None;
150 clicked = Some(widget.id);
151 long_touched = Some(widget.id);
152 interaction.potential_click_id = None;
153 interaction.potential_drag_id = None;
154 }
155 }
156
157 for pointer_event in &input.pointer.pointer_events {
159 match pointer_event {
160 PointerEvent::Moved(_) => {}
161
162 PointerEvent::Pressed { .. } => {
163 if interaction.potential_click_id.is_none() {
165 interaction.potential_click_id = hits.click.map(|w| w.id);
166 }
167
168 if interaction.potential_drag_id.is_none() {
170 interaction.potential_drag_id = hits.drag.map(|w| w.id);
171 }
172 }
173
174 PointerEvent::Released { click, button: _ } => {
175 if click.is_some()
176 && !input.pointer.is_decidedly_dragging()
177 && let Some(widget) = interaction
178 .potential_click_id
179 .and_then(|id| widgets.get(id))
180 {
181 clicked = Some(widget.id);
182 }
183
184 interaction.potential_drag_id = None;
185 interaction.potential_click_id = None;
186 dragged = None;
187 }
188 }
189 }
190
191 if dragged.is_none() {
192 if let Some(widget) = interaction.potential_drag_id.and_then(|id| widgets.get(id))
194 && widget.enabled
195 {
196 let is_dragged = if widget.sense.senses_click() && widget.sense.senses_drag() {
197 input.pointer.is_decidedly_dragging()
201 } else {
202 widget.sense.senses_drag()
204 };
205
206 if is_dragged {
207 dragged = Some(widget.id);
208 }
209 }
210 }
211
212 if !input.pointer.could_any_button_be_click() {
213 interaction.potential_click_id = None;
214 }
215
216 if !input.pointer.any_down() || input.pointer.latest_pos().is_none() {
217 interaction.potential_click_id = None;
218 interaction.potential_drag_id = None;
219 }
220
221 let drag_changed = dragged != prev_snapshot.dragged;
224 let drag_stopped = drag_changed.then_some(prev_snapshot.dragged).flatten();
225 let drag_started = drag_changed.then_some(dragged).flatten();
226
227 let contains_pointer: IdSet = hits
236 .contains_pointer
237 .iter()
238 .chain(&hits.click)
239 .chain(&hits.drag)
240 .map(|w| w.id)
241 .collect();
242
243 let hovered = if clicked.is_some() || dragged.is_some() || long_touched.is_some() {
244 clicked
246 .iter()
247 .chain(&dragged)
248 .chain(&long_touched)
249 .copied()
250 .collect()
251 } else {
252 let order = |id| hits.close.iter().position(|w| w.id == id);
266
267 let click_order = hits.click.and_then(|w| order(w.id)).unwrap_or(0);
268 let drag_order = hits.drag.and_then(|w| order(w.id)).unwrap_or(0);
269 let top_interactive_order = click_order.max(drag_order);
270
271 let mut hovered: IdSet = hits.click.iter().chain(&hits.drag).map(|w| w.id).collect();
272
273 for w in &hits.contains_pointer {
274 let is_interactive = w.sense.senses_click() || w.sense.senses_drag();
275 if is_interactive {
276 } else {
279 let is_on_top_of_the_interactive_widget =
280 top_interactive_order <= order(w.id).unwrap_or(0);
281 if is_on_top_of_the_interactive_widget {
282 hovered.insert(w.id);
283 }
284 }
285 }
286
287 hovered
288 };
289
290 InteractionSnapshot {
291 clicked,
292 long_touched,
293 drag_started,
294 dragged,
295 drag_stopped,
296 hovered,
297 contains_pointer,
298 }
299}