1use std::cell::{Cell, RefCell};
50use std::rc::Rc;
51
52use floem::kurbo::Vec2;
53use floem::prelude::*;
54use floem::views::scroll::ScrollChanged;
55use floem::views::{Decorators, Stack};
56
57pub use floem::headless::*;
59
60pub use floem::ViewId;
62
63pub mod prelude {
65 pub use super::{ClickTracker, PointerCaptureTracker, ScrollTracker, layer, layers};
66 pub use floem::ViewId;
67 pub use floem::event::PointerId;
68 pub use floem::headless::*;
69 pub use floem::prelude::*;
70 pub use floem::views::{Container, Decorators, Empty, Scroll, Stack};
71}
72
73#[derive(Clone, Default)]
88pub struct ClickTracker {
89 clicks: Rc<RefCell<Vec<Option<String>>>>,
90 count: Rc<Cell<usize>>,
91 double_clicks: Rc<RefCell<Vec<Option<String>>>>,
92 double_click_count: Rc<Cell<usize>>,
93 secondary_clicks: Rc<RefCell<Vec<Option<String>>>>,
94 secondary_click_count: Rc<Cell<usize>>,
95}
96
97impl ClickTracker {
98 pub fn new() -> Self {
100 Self::default()
101 }
102
103 pub fn track<V: IntoView>(&self, view: V) -> impl IntoView + use<V> {
107 let clicks = self.clicks.clone();
108 let count = self.count.clone();
109 view.into_view().action(move || {
110 clicks.borrow_mut().push(None);
111 count.set(count.get() + 1);
112 })
113 }
114
115 pub fn track_named<V: IntoView>(&self, name: &str, view: V) -> impl IntoView + use<V> {
119 let clicks = self.clicks.clone();
120 let count = self.count.clone();
121 let name = name.to_string();
122 view.into_view().action(move || {
123 clicks.borrow_mut().push(Some(name.clone()));
124 count.set(count.get() + 1);
125 })
126 }
127
128 pub fn track_named_cont<V: IntoView>(&self, name: &str, view: V) -> impl IntoView + use<V> {
132 let clicks = self.clicks.clone();
133 let count = self.count.clone();
134 let name = name.to_string();
135 view.into_view()
136 .on_event_cont(listener::Click, move |_, _| {
137 clicks.borrow_mut().push(Some(name.clone()));
138 count.set(count.get() + 1);
139 })
140 }
141
142 pub fn was_clicked(&self) -> bool {
144 self.count.get() > 0
145 }
146
147 pub fn click_count(&self) -> usize {
149 self.count.get()
150 }
151
152 pub fn clicked(&self) -> Vec<Option<String>> {
156 self.clicks.borrow().clone()
157 }
158
159 pub fn clicked_names(&self) -> Vec<String> {
161 self.clicks
162 .borrow()
163 .iter()
164 .filter_map(|n| n.clone())
165 .collect()
166 }
167
168 pub fn reset(&self) {
170 self.clicks.borrow_mut().clear();
171 self.count.set(0);
172 self.double_clicks.borrow_mut().clear();
173 self.double_click_count.set(0);
174 self.secondary_clicks.borrow_mut().clear();
175 self.secondary_click_count.set(0);
176 }
177
178 pub fn track_double_click<V: IntoView>(&self, name: &str, view: V) -> impl IntoView + use<V> {
182 let clicks = self.double_clicks.clone();
183 let count = self.double_click_count.clone();
184 let name = name.to_string();
185 view.into_view()
186 .on_event_stop(listener::DoubleClick, move |_, _| {
187 clicks.borrow_mut().push(Some(name.clone()));
188 count.set(count.get() + 1);
189 })
190 }
191
192 pub fn double_click_count(&self) -> usize {
194 self.double_click_count.get()
195 }
196
197 pub fn double_clicked_names(&self) -> Vec<String> {
199 self.double_clicks
200 .borrow()
201 .iter()
202 .filter_map(|n| n.clone())
203 .collect()
204 }
205
206 pub fn track_secondary_click<V: IntoView>(
210 &self,
211 name: &str,
212 view: V,
213 ) -> impl IntoView + use<V> {
214 let clicks = self.secondary_clicks.clone();
215 let count = self.secondary_click_count.clone();
216 let name = name.to_string();
217 view.into_view()
218 .on_event_stop(listener::SecondaryClick, move |_, _| {
219 clicks.borrow_mut().push(Some(name.clone()));
220 count.set(count.get() + 1);
221 })
222 }
223
224 pub fn secondary_click_count(&self) -> usize {
226 self.secondary_click_count.get()
227 }
228
229 pub fn secondary_clicked_names(&self) -> Vec<String> {
231 self.secondary_clicks
232 .borrow()
233 .iter()
234 .filter_map(|n| n.clone())
235 .collect()
236 }
237}
238
239pub fn layer(view: impl IntoView) -> impl IntoView {
249 view.into_view()
250 .style(|s| s.absolute().inset(0.0).size_full())
251}
252
253pub fn layers<VT: ViewTuple + 'static>(children: VT) -> impl IntoView {
267 let children_iter = children
269 .into_views()
270 .into_iter()
271 .map(|v| v.style(|s| s.absolute().inset(0.0).size_full()));
272
273 Stack::from_iter(children_iter).style(|s| s.size_full())
274}
275
276pub use floem::kurbo::{Point, Rect};
297
298#[derive(Clone, Default)]
299pub struct ScrollTracker {
300 offsets: Rc<RefCell<Vec<Vec2>>>,
301}
302
303impl ScrollTracker {
304 pub fn new() -> Self {
306 Self::default()
307 }
308
309 pub fn track(&self, scroll: floem::views::Scroll) -> floem::views::Scroll {
311 let viewports = self.offsets.clone();
312 scroll.on_event_stop(ScrollChanged::listener(), move |_cx, state| {
313 viewports.borrow_mut().push(state.offset);
314 })
315 }
316
317 pub fn has_scrolled(&self) -> bool {
319 !self.offsets.borrow().is_empty()
320 }
321
322 pub fn scroll_count(&self) -> usize {
324 self.offsets.borrow().len()
325 }
326
327 pub fn last_offset(&self) -> Option<Vec2> {
329 self.offsets.borrow().last().copied()
330 }
331
332 pub fn offsets(&self) -> Vec<Vec2> {
334 self.offsets.borrow().clone()
335 }
336
337 pub fn scroll_position(&self) -> Option<Point> {
339 self.last_offset().map(|v| v.to_point())
340 }
341
342 pub fn reset(&self) {
344 self.offsets.borrow_mut().clear();
345 }
346}
347
348type PointerEventLog = Rc<RefCell<Vec<(String, Option<floem::event::PointerId>)>>>;
350
351#[derive(Clone, Default)]
366pub struct PointerCaptureTracker {
367 gained_captures: Rc<RefCell<Vec<(String, floem::event::PointerId)>>>,
368 lost_captures: Rc<RefCell<Vec<(String, floem::event::PointerId)>>>,
369 pointer_downs: PointerEventLog,
370 pointer_moves: PointerEventLog,
371 pointer_ups: PointerEventLog,
372}
373
374impl PointerCaptureTracker {
375 pub fn new() -> Self {
377 Self::default()
378 }
379
380 pub fn track<V: IntoView>(&self, name: &str, view: V) -> impl IntoView + use<V> {
382 let got_captures = self.gained_captures.clone();
383 let lost_captures = self.lost_captures.clone();
384 let pointer_downs = self.pointer_downs.clone();
385 let pointer_moves = self.pointer_moves.clone();
386 let pointer_ups = self.pointer_ups.clone();
387 let name = name.to_string();
388
389 let name_got = name.clone();
390 let name_lost = name.clone();
391 let name_down = name.clone();
392 let name_move = name.clone();
393 let name_up = name.clone();
394
395 view.into_view()
396 .on_event(listener::GainedPointerCapture, move |_cx, drag_token| {
397 got_captures
398 .borrow_mut()
399 .push((name_got.clone(), drag_token.pointer_id()));
400 floem::event::EventPropagation::Continue
401 })
402 .on_event(listener::LostPointerCapture, move |_cx, pointer_id| {
403 lost_captures
404 .borrow_mut()
405 .push((name_lost.clone(), *pointer_id));
406 floem::event::EventPropagation::Continue
407 })
408 .on_event(listener::PointerDown, move |_cx, pe| {
409 pointer_downs
410 .borrow_mut()
411 .push((name_down.clone(), pe.pointer.pointer_id));
412 floem::event::EventPropagation::Continue
413 })
414 .on_event(listener::PointerMove, move |_cx, pu| {
415 pointer_moves
416 .borrow_mut()
417 .push((name_move.clone(), pu.pointer.pointer_id));
418 floem::event::EventPropagation::Continue
419 })
420 .on_event(listener::PointerUp, move |_cx, pe| {
421 pointer_ups
422 .borrow_mut()
423 .push((name_up.clone(), pe.pointer.pointer_id));
424 floem::event::EventPropagation::Continue
425 })
426 }
427
428 pub fn gained_capture_count(&self) -> usize {
430 self.gained_captures.borrow().len()
431 }
432
433 pub fn lost_capture_count(&self) -> usize {
435 self.lost_captures.borrow().len()
436 }
437
438 pub fn got_capture_names(&self) -> Vec<String> {
440 self.gained_captures
441 .borrow()
442 .iter()
443 .map(|(name, _)| name.clone())
444 .collect()
445 }
446
447 pub fn lost_capture_names(&self) -> Vec<String> {
449 self.lost_captures
450 .borrow()
451 .iter()
452 .map(|(name, _)| name.clone())
453 .collect()
454 }
455
456 pub fn pointer_down_names(&self) -> Vec<String> {
458 self.pointer_downs
459 .borrow()
460 .iter()
461 .map(|(name, _)| name.clone())
462 .collect()
463 }
464
465 pub fn pointer_move_names(&self) -> Vec<String> {
467 self.pointer_moves
468 .borrow()
469 .iter()
470 .map(|(name, _)| name.clone())
471 .collect()
472 }
473
474 pub fn pointer_up_names(&self) -> Vec<String> {
476 self.pointer_ups
477 .borrow()
478 .iter()
479 .map(|(name, _)| name.clone())
480 .collect()
481 }
482
483 pub fn reset(&self) {
485 self.gained_captures.borrow_mut().clear();
486 self.lost_captures.borrow_mut().clear();
487 self.pointer_downs.borrow_mut().clear();
488 self.pointer_moves.borrow_mut().clear();
489 self.pointer_ups.borrow_mut().clear();
490 }
491}