use super::PanelRect;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum DropZone {
Center,
Left,
Right,
Up,
Down,
}
impl DropZone {
pub fn preview_rect(&self, container_rect: PanelRect) -> PanelRect {
match self {
DropZone::Center => container_rect,
DropZone::Left => PanelRect {
x: container_rect.x,
y: container_rect.y,
width: container_rect.width * 0.5,
height: container_rect.height,
},
DropZone::Right => PanelRect {
x: container_rect.x + container_rect.width * 0.5,
y: container_rect.y,
width: container_rect.width * 0.5,
height: container_rect.height,
},
DropZone::Up => PanelRect {
x: container_rect.x,
y: container_rect.y,
width: container_rect.width,
height: container_rect.height * 0.5,
},
DropZone::Down => PanelRect {
x: container_rect.x,
y: container_rect.y + container_rect.height * 0.5,
width: container_rect.width,
height: container_rect.height * 0.5,
},
}
}
}
pub struct DropZoneDetector {
edge_threshold: f32,
split_threshold: f32,
}
impl DropZoneDetector {
pub fn new() -> Self {
Self {
edge_threshold: 0.10,
split_threshold: 0.33,
}
}
pub fn with_thresholds(edge_threshold: f32, split_threshold: f32) -> Self {
Self {
edge_threshold,
split_threshold,
}
}
pub fn detect(&self, x: f32, y: f32, rect: PanelRect) -> DropZone {
let width = rect.width;
let height = rect.height;
let rel_x = x - rect.x;
let rel_y = y - rect.y;
let edge_x = width * self.edge_threshold;
let edge_y = height * self.edge_threshold;
if rel_x > edge_x && rel_x < width - edge_x && rel_y > edge_y && rel_y < height - edge_y {
return DropZone::Center;
}
let split_x = width * self.split_threshold;
if rel_x < split_x {
DropZone::Left
} else if rel_x > width - split_x {
DropZone::Right
} else if rel_y < height / 2.0 {
DropZone::Up
} else {
DropZone::Down
}
}
}
impl Default for DropZoneDetector {
fn default() -> Self {
Self::new()
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum CompassZone {
Center,
Left,
Right,
Up,
Down,
}
impl From<DropZone> for CompassZone {
fn from(zone: DropZone) -> Self {
match zone {
DropZone::Center => CompassZone::Center,
DropZone::Left => CompassZone::Left,
DropZone::Right => CompassZone::Right,
DropZone::Up => CompassZone::Up,
DropZone::Down => CompassZone::Down,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_drop_zone_center() {
let detector = DropZoneDetector::new();
let rect = PanelRect::new(0.0, 0.0, 100.0, 100.0);
let zone = detector.detect(50.0, 50.0, rect);
assert_eq!(zone, DropZone::Center);
}
#[test]
fn test_drop_zone_edges() {
let detector = DropZoneDetector::new();
let rect = PanelRect::new(0.0, 0.0, 100.0, 100.0);
let zone = detector.detect(5.0, 50.0, rect);
assert_eq!(zone, DropZone::Left);
let zone = detector.detect(95.0, 50.0, rect);
assert_eq!(zone, DropZone::Right);
let zone = detector.detect(50.0, 5.0, rect);
assert_eq!(zone, DropZone::Up);
let zone = detector.detect(50.0, 95.0, rect);
assert_eq!(zone, DropZone::Down);
}
#[test]
fn test_drop_zone_preview_rect() {
let rect = PanelRect::new(0.0, 0.0, 100.0, 100.0);
let preview = DropZone::Left.preview_rect(rect);
assert_eq!(preview.width, 50.0);
assert_eq!(preview.height, 100.0);
assert_eq!(preview.x, 0.0);
let preview = DropZone::Up.preview_rect(rect);
assert_eq!(preview.width, 100.0);
assert_eq!(preview.height, 50.0);
assert_eq!(preview.y, 0.0);
let preview = DropZone::Center.preview_rect(rect);
assert_eq!(preview, rect);
}
#[test]
fn test_compass_zone_conversion() {
assert_eq!(CompassZone::from(DropZone::Center), CompassZone::Center);
assert_eq!(CompassZone::from(DropZone::Left), CompassZone::Left);
assert_eq!(CompassZone::from(DropZone::Right), CompassZone::Right);
assert_eq!(CompassZone::from(DropZone::Up), CompassZone::Up);
assert_eq!(CompassZone::from(DropZone::Down), CompassZone::Down);
}
}