touch-selection 0.2.0

Selection data structure intended for touch and single button devices
Documentation
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()
    }
}