use crate::LayoutRect;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MousePosition {
pub column: i16,
pub row: i16,
pub space: CoordSpace,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CoordSpace {
Screen,
}
impl MousePosition {
pub fn is_inside(&self, area: LayoutRect) -> bool {
self.column >= area.x as i16
&& self.column < (area.x.saturating_add(i32::from(area.width))) as i16
&& self.row >= area.y as i16
&& self.row < (area.y.saturating_add(i32::from(area.height))) as i16
}
pub fn to_local(&self, area: LayoutRect) -> Option<(u16, u16)> {
if !self.is_inside(area) {
return None;
}
let local_col = self.column.saturating_sub(area.x as i16);
let local_row = self.row.saturating_sub(area.y as i16);
Some((local_col as u16, local_row as u16))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_inside_returns_true_for_contained_point() {
let area = LayoutRect {
x: 5,
y: 10,
width: 20,
height: 30,
};
let pos = MousePosition {
column: 10,
row: 15,
space: CoordSpace::Screen,
};
assert!(pos.is_inside(area));
}
#[test]
fn is_inside_returns_false_for_outside_point() {
let area = LayoutRect {
x: 5,
y: 10,
width: 20,
height: 30,
};
let pos = MousePosition {
column: 100,
row: 100,
space: CoordSpace::Screen,
};
assert!(!pos.is_inside(area));
}
#[test]
fn is_inside_handles_edge_boundary() {
let area = LayoutRect {
x: 5,
y: 10,
width: 20,
height: 30,
};
let pos = MousePosition {
column: 5,
row: 10,
space: CoordSpace::Screen,
};
assert!(pos.is_inside(area));
let pos = MousePosition {
column: 25,
row: 10,
space: CoordSpace::Screen,
};
assert!(!pos.is_inside(area));
let pos = MousePosition {
column: 5,
row: 40,
space: CoordSpace::Screen,
};
assert!(!pos.is_inside(area));
}
#[test]
fn to_local_returns_correct_offset() {
let area = LayoutRect {
x: 10,
y: 20,
width: 50,
height: 60,
};
let pos = MousePosition {
column: 25,
row: 35,
space: CoordSpace::Screen,
};
let local = pos.to_local(area).unwrap();
assert_eq!(local, (15, 15));
}
#[test]
fn to_local_returns_none_when_outside() {
let area = LayoutRect {
x: 10,
y: 20,
width: 50,
height: 60,
};
let pos = MousePosition {
column: 5,
row: 35,
space: CoordSpace::Screen,
};
assert!(pos.to_local(area).is_none());
}
#[test]
fn negative_coordinates_dont_underflow() {
let area = LayoutRect {
x: 10,
y: 20,
width: 50,
height: 60,
};
let pos = MousePosition {
column: -5,
row: -10,
space: CoordSpace::Screen,
};
assert!(!pos.is_inside(area));
assert!(pos.to_local(area).is_none());
}
}