use crate::util::for_coord_in_line;
use crate::{BreakingAction, Component, SharedState, UpdateInfo};
use crossterm::event::{Event, MouseEvent, MouseEventKind};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct MouseInfo {
pub last_mouse_pos: (usize, usize),
pub left_mouse_down: bool,
pub right_mouse_down: bool,
pub middle_mouse_down: bool,
}
#[derive(Default, Debug, PartialEq)]
pub struct MousePressedInfo {
pub left: bool,
pub right: bool,
pub middle: bool,
}
impl MousePressedInfo {
pub fn any(&self) -> bool {
self.left || self.right || self.middle
}
}
pub struct MouseEvents {
events: Vec<MouseInfo>,
has_new_this_frame: bool,
}
impl MouseEvents {
pub fn new() -> Self {
Self {
events: vec![],
has_new_this_frame: false,
}
}
pub fn push(&mut self, event: MouseInfo) {
self.events.push(event);
}
pub fn has_new_this_frame(&self) -> bool {
self.has_new_this_frame
}
pub fn for_each_linerp_only_fresh(&self, f: impl FnMut(MouseInfo)) {
if !self.has_new_this_frame {
return;
}
self.for_each_linerp_sticky(f);
}
pub fn for_each_linerp_sticky(&self, mut f: impl FnMut(MouseInfo)) {
if self.events.len() < 2 {
self.events.first().map(|mi| f(*mi));
return;
}
f(*self.events.first().unwrap());
for i in 0..self.events.len() - 1 {
MouseTrackerComponent::smooth_two_updates(
true,
self.events[i],
self.events[i + 1],
&mut f,
);
}
}
}
pub struct MouseTrackerComponent {
last_mouse_info: MouseInfo,
did_press_left: bool,
did_press_right: bool,
did_press_middle: bool,
mouse_events: MouseEvents,
}
impl MouseTrackerComponent {
pub fn new() -> Self {
Self {
last_mouse_info: MouseInfo::default(),
did_press_left: false,
did_press_right: false,
did_press_middle: false,
mouse_events: MouseEvents::new(),
}
}
pub fn update_mouse_info(event: MouseEvent, mouse_info: &mut MouseInfo) {
mouse_info.last_mouse_pos = (event.column as usize, event.row as usize);
let (button, down) = match event {
MouseEvent {
kind: MouseEventKind::Down(button),
..
} => (button, true),
MouseEvent {
kind: MouseEventKind::Up(button),
..
} => (button, false),
_ => return,
};
match button {
crossterm::event::MouseButton::Left => {
mouse_info.left_mouse_down = down;
}
crossterm::event::MouseButton::Right => {
mouse_info.right_mouse_down = down;
}
crossterm::event::MouseButton::Middle => {
mouse_info.middle_mouse_down = down;
}
}
}
pub fn smooth_two_updates(
exclude_start: bool,
first: MouseInfo,
second: MouseInfo,
mut f: impl FnMut(MouseInfo),
) {
let start_x = first.last_mouse_pos.0 as i64;
let start_y = first.last_mouse_pos.1 as i64;
let end_x = second.last_mouse_pos.0 as i64;
let end_y = second.last_mouse_pos.1 as i64;
for_coord_in_line(exclude_start, (start_x, start_y), (end_x, end_y), |x, y| {
let mi = if (x, y) == (end_x, end_y) {
second
} else {
first
};
f(MouseInfo {
last_mouse_pos: (x as usize, y as usize),
left_mouse_down: mi.left_mouse_down,
right_mouse_down: mi.right_mouse_down,
middle_mouse_down: mi.middle_mouse_down,
});
});
}
}
impl<S> Component<S> for MouseTrackerComponent {
fn on_event(
&mut self,
event: Event,
shared_state: &mut SharedState<S>,
) -> Option<BreakingAction> {
if let Event::Mouse(event) = event {
Self::update_mouse_info(event, &mut self.last_mouse_info);
self.mouse_events.push(self.last_mouse_info);
self.mouse_events.has_new_this_frame = true;
match event {
MouseEvent {
kind: MouseEventKind::Down(button),
..
} => match button {
crossterm::event::MouseButton::Left => {
self.did_press_left = true;
}
crossterm::event::MouseButton::Right => {
self.did_press_right = true;
}
crossterm::event::MouseButton::Middle => {
self.did_press_middle = true;
}
},
_ => {}
}
}
None
}
fn update(&mut self, update_info: UpdateInfo, shared_state: &mut SharedState<S>) {
shared_state.mouse_info = self.last_mouse_info;
shared_state.mouse_pressed.right = self.did_press_right;
shared_state.mouse_pressed.left = self.did_press_left;
shared_state.mouse_pressed.middle = self.did_press_middle;
std::mem::swap(&mut self.mouse_events, &mut shared_state.mouse_events);
self.mouse_events.events.clear();
self.mouse_events.push(self.last_mouse_info);
self.mouse_events.has_new_this_frame = false;
self.did_press_left = false;
self.did_press_right = false;
self.did_press_middle = false;
}
}