use std::{
cell::RefCell,
rc::{Rc, Weak},
};
use cranpose_core::{NodeId, RuntimeHandle, current_runtime_handle};
use cranpose_ui_graphics::Point;
use smallvec::SmallVec;
use crate::render_state::{AppContext, require_current_app_context};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MouseInputTarget {
Primary,
Window(NodeId),
}
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct MouseMoveRequest {
pub target: MouseInputTarget,
pub position: Point,
}
#[derive(Default)]
pub(crate) struct MouseInputQueue(RefCell<SmallVec<[MouseMoveRequest; 2]>>);
impl MouseInputQueue {
fn push(&self, request: MouseMoveRequest) {
let mut pending = self.0.borrow_mut();
if let Some(previous) = pending
.iter_mut()
.find(|item| item.target == request.target)
{
*previous = request;
} else {
pending.push(request);
}
}
pub(crate) fn take(&self) -> Option<MouseMoveRequest> {
let mut pending = self.0.borrow_mut();
if pending.is_empty() {
None
} else {
Some(pending.remove(0))
}
}
pub(crate) fn len(&self) -> usize {
self.0.borrow().len()
}
}
#[derive(Clone)]
pub struct MouseInput {
context: Weak<AppContext>,
runtime: Option<RuntimeHandle>,
}
impl MouseInput {
pub fn move_to(&self, target: MouseInputTarget, position: Point) {
if !position.x.is_finite() || !position.y.is_finite() {
return;
}
if let Some(context) = self.context.upgrade() {
context
.mouse_input()
.push(MouseMoveRequest { target, position });
if let Some(runtime) = &self.runtime {
runtime.schedule();
}
}
}
}
pub fn local_mouse_input() -> MouseInput {
MouseInput {
context: Rc::downgrade(&require_current_app_context("mouse input")),
runtime: current_runtime_handle(),
}
}