cranpose_ui/
mouse_input.rs1use std::{
4 cell::RefCell,
5 rc::{Rc, Weak},
6};
7
8use cranpose_core::{RuntimeHandle, current_runtime_handle};
9use cranpose_ui_graphics::Point;
10use smallvec::SmallVec;
11
12use crate::render_state::{AppContext, require_current_app_context};
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum MouseInputTarget {
17 Primary,
19 Window(u64),
21}
22
23#[doc(hidden)]
25#[derive(Clone, Copy)]
26pub struct MouseMoveRequest {
27 pub target: MouseInputTarget,
28 pub position: Point,
29}
30
31#[derive(Default)]
32pub(crate) struct MouseInputQueue(RefCell<SmallVec<[MouseMoveRequest; 2]>>);
33
34impl MouseInputQueue {
35 fn push(&self, request: MouseMoveRequest) {
36 let mut pending = self.0.borrow_mut();
37 if let Some(previous) = pending
38 .iter_mut()
39 .find(|item| item.target == request.target)
40 {
41 *previous = request;
42 } else {
43 pending.push(request);
44 }
45 }
46
47 pub(crate) fn take(&self) -> Option<MouseMoveRequest> {
48 let mut pending = self.0.borrow_mut();
49 if pending.is_empty() {
50 None
51 } else {
52 Some(pending.remove(0))
53 }
54 }
55
56 pub(crate) fn len(&self) -> usize {
57 self.0.borrow().len()
58 }
59}
60
61#[derive(Clone)]
67pub struct MouseInput {
68 context: Weak<AppContext>,
69 runtime: Option<RuntimeHandle>,
70}
71
72impl MouseInput {
73 pub fn move_to(&self, target: MouseInputTarget, position: Point) {
77 if !position.x.is_finite() || !position.y.is_finite() {
78 return;
79 }
80 if let Some(context) = self.context.upgrade() {
81 context
82 .mouse_input()
83 .push(MouseMoveRequest { target, position });
84 if let Some(runtime) = &self.runtime {
85 runtime.schedule();
86 }
87 }
88 }
89}
90
91pub fn local_mouse_input() -> MouseInput {
94 MouseInput {
95 context: Rc::downgrade(&require_current_app_context("mouse input")),
96 runtime: current_runtime_handle(),
97 }
98}