use std::collections::HashSet;
use blinc_core::layer::{Point, Rect};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum CanvasTool {
#[default]
Pan,
Select,
}
#[derive(Clone, Debug, Default)]
pub struct SelectionState {
pub selected: HashSet<String>,
pub marquee: Option<MarqueeState>,
}
#[derive(Clone, Debug)]
pub struct MarqueeState {
pub anchor: Point,
pub current: Point,
pub additive: bool,
pub base_selection: HashSet<String>,
}
impl MarqueeState {
pub fn rect(&self) -> Rect {
Rect::from_points(self.anchor, self.current)
}
}
#[derive(Clone, Debug)]
pub struct SelectionChangeEvent {
pub selected: HashSet<String>,
pub added: HashSet<String>,
pub removed: HashSet<String>,
}
impl SelectionState {
pub fn new() -> Self {
Self::default()
}
pub fn is_selected(&self, id: &str) -> bool {
self.selected.contains(id)
}
pub fn clear(&mut self) {
self.selected.clear();
}
pub fn select_single(&mut self, id: String) {
self.selected.clear();
self.selected.insert(id);
}
pub fn toggle(&mut self, id: &str) {
if self.selected.contains(id) {
self.selected.remove(id);
} else {
self.selected.insert(id.to_string());
}
}
pub fn add(&mut self, id: String) {
self.selected.insert(id);
}
pub fn replace(&mut self, ids: HashSet<String>) {
self.selected = ids;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_empty() {
let sel = SelectionState::new();
assert!(sel.selected.is_empty());
assert!(sel.marquee.is_none());
}
#[test]
fn test_select_single() {
let mut sel = SelectionState::new();
sel.select_single("a".into());
assert!(sel.is_selected("a"));
assert_eq!(sel.selected.len(), 1);
sel.select_single("b".into());
assert!(!sel.is_selected("a"));
assert!(sel.is_selected("b"));
assert_eq!(sel.selected.len(), 1);
}
#[test]
fn test_toggle() {
let mut sel = SelectionState::new();
sel.toggle("a");
assert!(sel.is_selected("a"));
sel.toggle("a");
assert!(!sel.is_selected("a"));
}
#[test]
fn test_add_and_clear() {
let mut sel = SelectionState::new();
sel.add("a".into());
sel.add("b".into());
sel.add("c".into());
assert_eq!(sel.selected.len(), 3);
sel.clear();
assert!(sel.selected.is_empty());
}
#[test]
fn test_replace() {
let mut sel = SelectionState::new();
sel.add("old".into());
let mut new_set = HashSet::new();
new_set.insert("x".into());
new_set.insert("y".into());
sel.replace(new_set);
assert!(!sel.is_selected("old"));
assert!(sel.is_selected("x"));
assert!(sel.is_selected("y"));
}
#[test]
fn test_marquee_rect() {
let marquee = MarqueeState {
anchor: Point::new(100.0, 200.0),
current: Point::new(50.0, 300.0),
additive: false,
base_selection: HashSet::new(),
};
let r = marquee.rect();
assert!((r.x() - 50.0).abs() < 1e-6);
assert!((r.y() - 200.0).abs() < 1e-6);
assert!((r.width() - 50.0).abs() < 1e-6);
assert!((r.height() - 100.0).abs() < 1e-6);
}
#[test]
fn test_tool_default() {
assert_eq!(CanvasTool::default(), CanvasTool::Pan);
}
}