use std::{
collections::{BTreeSet, HashSet},
hash::Hash,
};
pub trait Selector {
type Selectable: Selectable<Position = Self::Position>;
type Position: Copy;
fn moved(&self, moved: Self::Position, cursor: Self::Position) -> bool;
fn stop(
&self,
selectable: &mut Self::Selectable,
index: <Self::Selectable as Selectable>::Index,
);
fn add(
&self,
selectable: &mut Self::Selectable,
pos: Self::Position,
from: Option<<Self::Selectable as Selectable>::Index>,
) -> <Self::Selectable as Selectable>::Index {
selectable.add(pos, from)
}
fn get(
&self,
selectable: &Self::Selectable,
pos: Self::Position,
) -> Option<<Self::Selectable as Selectable>::Index> {
selectable.get(pos)
}
fn connect(
&self,
selectable: &mut Self::Selectable,
index: <Self::Selectable as Selectable>::Index,
selected: &[<Self::Selectable as Selectable>::Index],
) {
selectable.connect(index, selected)
}
fn special(
&self,
selectable: &mut Self::Selectable,
index: <Self::Selectable as Selectable>::Index,
selected: &[<Self::Selectable as Selectable>::Index],
) {
selectable.connect(index, selected)
}
}
pub trait Selectable {
type Position: Copy;
type Index;
fn add(&mut self, pos: Self::Position, from: Option<Self::Index>) -> Self::Index;
fn get(&self, pos: Self::Position) -> Option<Self::Index>;
fn connect(&mut self, _index: Self::Index, _selected: &[Self::Index]) {}
fn special(&mut self, _index: Self::Index, _selected: &[Self::Index]) {}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Action {
Connect,
Move,
}
pub struct ObjectGrabState<I> {
pub index: I,
pub action: Action,
}
pub enum GrabState<I> {
Object(ObjectGrabState<I>),
Empty,
}
pub struct GrabInfo<P, I> {
pub pos: P,
pub state: GrabState<I>,
pub time: f32,
pub moved: Option<P>,
created: bool,
}
impl<P: Copy, I: Copy> GrabInfo<P, I> {
fn new(pos: P, state: GrabState<I>, cursor: P) -> Self {
Self {
pos,
state,
time: 0.0,
moved: Some(cursor),
created: false,
}
}
pub fn hold<S: Selector<Position = P>>(&mut self, selector: &S, selectable: &mut S::Selectable)
where
S::Selectable: Selectable<Index = I>,
{
use GrabState::*;
match &mut self.state {
Empty => {
self.created = true;
let index = selector.add(selectable, self.pos, None);
self.state = Object(ObjectGrabState {
index,
action: Action::Connect,
});
selector.stop(selectable, index);
}
Object(ObjectGrabState { action, .. }) => *action = Action::Connect,
}
}
}
pub trait SelectionSet {
type Index;
fn to_vec(&self) -> Vec<Self::Index>;
fn contains(&self, index: &Self::Index) -> bool;
fn insert(&mut self, index: Self::Index);
fn remove(&mut self, index: &Self::Index);
fn clear(&mut self);
fn single(&mut self, index: Self::Index) {
self.clear();
self.insert(index);
}
}
impl<I: Clone + Eq + Hash> SelectionSet for HashSet<I> {
type Index = I;
fn to_vec(&self) -> Vec<I> {
self.iter().cloned().collect()
}
fn contains(&self, index: &I) -> bool {
self.contains(index)
}
fn insert(&mut self, index: I) {
self.insert(index);
}
fn remove(&mut self, index: &I) {
self.remove(index);
}
fn clear(&mut self) {
self.clear()
}
fn single(&mut self, index: I) {
self.clear();
self.insert(index);
}
}
impl<I: Clone + Ord> SelectionSet for BTreeSet<I> {
type Index = I;
fn to_vec(&self) -> Vec<I> {
self.iter().cloned().collect()
}
fn contains(&self, index: &I) -> bool {
self.contains(index)
}
fn insert(&mut self, index: I) {
self.insert(index);
}
fn remove(&mut self, index: &I) {
self.remove(index);
}
fn clear(&mut self) {
self.clear()
}
fn single(&mut self, index: I) {
self.clear();
self.insert(index);
}
}
#[derive(Default)]
pub struct Selection<P, C: SelectionSet> {
pub selected: C,
pub grab: Option<GrabInfo<P, C::Index>>,
}
impl<P: Copy, C: SelectionSet> Selection<P, C>
where
C::Index: Copy,
{
pub fn new(selected: C) -> Self {
Self {
selected,
grab: None,
}
}
pub fn press<S: Selector<Position = P>>(
&mut self,
pos: P,
cursor: P,
selector: &S,
selectable: &S::Selectable,
) where
S::Selectable: Selectable<Index = C::Index>,
{
use GrabState::*;
self.grab = Some(GrabInfo::new(
pos,
if let Some(index) = selector.get(selectable, pos) {
Object(ObjectGrabState {
index,
action: Action::Move,
})
} else {
Empty
},
cursor,
));
}
pub fn release<S: Selector<Position = P>>(
&mut self,
pos: P,
selector: &S,
selectable: &mut S::Selectable,
) where
S::Selectable: Selectable<Index = C::Index>,
{
let Some(mut grab_info) = self.grab.take() else {
return;
};
let GrabState::Object(ObjectGrabState {
action,
index: event,
}) = grab_info.state
else {
if grab_info.moved.is_some() {
self.selected.clear();
}
return;
};
for index in self.selected.to_vec() {
selector.stop(selectable, index);
}
use Action::*;
match action {
Connect => {
if grab_info.moved.is_none() {
let index = selector.get(selectable, pos).unwrap_or_else(|| {
let index = selector.add(selectable, pos, Some(event));
selector.stop(selectable, index);
index
});
selector.connect(selectable, index, &self.selected.to_vec());
self.selected.single(index);
} else if self.selected.contains(&event) {
self.selected.single(event);
} else if grab_info.created {
grab_info.created = false;
self.selected.insert(event);
} else {
selector.special(selectable, event, &self.selected.to_vec());
}
}
Move => {
if grab_info.moved.is_none() {
return;
}
if self.selected.contains(&event) {
self.selected.remove(&event);
} else {
self.selected.insert(event);
}
}
}
}
pub fn on_moved<S: Selector<Position = P>>(&mut self, cursor: P, selector: &S) {
let Some(grab_info) = &mut self.grab else {
return;
};
let &Some(moved) = &grab_info.moved else {
return;
};
if !selector.moved(moved, cursor) {
return;
}
grab_info.created = false;
grab_info.moved = None;
let &GrabState::Object(ObjectGrabState { index, .. }) = &grab_info.state else {
return;
};
if !self.selected.contains(&index) {
self.selected.single(index);
}
}
}
impl<P, S: SelectionSet + IntoIterator> IntoIterator for Selection<P, S> {
type Item = <S as IntoIterator>::Item;
type IntoIter = <S as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.selected.into_iter()
}
}
impl<'a, P, S: SelectionSet> IntoIterator for &'a Selection<P, S>
where
&'a S: IntoIterator,
{
type Item = <&'a S as IntoIterator>::Item;
type IntoIter = <&'a S as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.selected.into_iter()
}
}
impl<P, S: SelectionSet> Selection<P, S> {
pub fn iter<'a>(&'a self) -> <&'a S as IntoIterator>::IntoIter
where
&'a S: IntoIterator,
{
self.into_iter()
}
}