use std::marker::PhantomData;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SeriesId(pub usize);
impl SeriesId {
pub const MAIN: SeriesId = SeriesId(0);
pub const VOLUME: SeriesId = SeriesId(1);
pub fn name(&self) -> &'static str {
match self.0 {
0 => "Main Series",
1 => "Volume",
_ => "Series",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ChartElementId {
Series(SeriesId),
OverlayIndicator(usize),
PaneIndicator(usize),
}
pub trait SelectableId: Copy + Eq + std::fmt::Debug {}
impl<T: Copy + Eq + std::fmt::Debug> SelectableId for T {}
#[derive(Clone, Debug)]
pub struct SelectionState<Id: SelectableId> {
pub selected: Option<Id>,
pub hovered: Option<Id>,
pub bar_idx: Option<usize>,
_phantom: PhantomData<Id>,
}
impl<Id: SelectableId> Default for SelectionState<Id> {
fn default() -> Self {
Self {
selected: None,
hovered: None,
bar_idx: None,
_phantom: PhantomData,
}
}
}
impl<Id: SelectableId> SelectionState<Id> {
pub fn new() -> Self {
Self::default()
}
pub fn select(&mut self, id: Id, bar_idx: Option<usize>) {
self.selected = Some(id);
self.bar_idx = bar_idx;
}
pub fn deselect(&mut self) {
self.selected = None;
self.bar_idx = None;
}
pub fn set_hovered(&mut self, id: Option<Id>) {
self.hovered = id;
}
pub fn is_selected(&self, id: Id) -> bool {
self.selected == Some(id)
}
pub fn is_hovered(&self, id: Id) -> bool {
self.hovered == Some(id)
}
pub fn has_selection(&self) -> bool {
self.selected.is_some()
}
pub fn selected_id(&self) -> Option<Id> {
self.selected
}
pub fn selected_bar(&self) -> Option<usize> {
self.bar_idx
}
pub fn is_empty(&self) -> bool {
self.selected.is_none() && self.hovered.is_none()
}
pub fn clear(&mut self) {
self.selected = None;
self.hovered = None;
self.bar_idx = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct TestId(usize);
#[test]
fn test_select_deselect() {
let mut state = SelectionState::<TestId>::new();
assert!(!state.has_selection());
state.select(TestId(1), Some(42));
assert!(state.has_selection());
assert!(state.is_selected(TestId(1)));
assert!(!state.is_selected(TestId(2)));
assert_eq!(state.selected_bar(), Some(42));
state.deselect();
assert!(!state.has_selection());
assert_eq!(state.selected_bar(), None);
}
#[test]
fn test_hover() {
let mut state = SelectionState::<TestId>::new();
assert!(!state.is_hovered(TestId(1)));
state.set_hovered(Some(TestId(1)));
assert!(state.is_hovered(TestId(1)));
assert!(!state.is_hovered(TestId(2)));
state.set_hovered(None);
assert!(!state.is_hovered(TestId(1)));
}
#[test]
fn test_chart_element_select_reselect_deselect() {
let mut state = SelectionState::<ChartElementId>::new();
assert!(!state.has_selection());
state.select(ChartElementId::PaneIndicator(2), Some(10));
assert_eq!(state.selected_id(), Some(ChartElementId::PaneIndicator(2)));
assert_eq!(state.selected_bar(), Some(10));
state.select(ChartElementId::Series(SeriesId::MAIN), Some(20));
assert_eq!(
state.selected_id(),
Some(ChartElementId::Series(SeriesId::MAIN))
);
assert!(!state.is_selected(ChartElementId::PaneIndicator(2)));
assert_eq!(state.selected_bar(), Some(20));
state.select(ChartElementId::OverlayIndicator(0), Some(5));
assert!(state.is_selected(ChartElementId::OverlayIndicator(0)));
state.deselect();
assert!(!state.has_selection());
assert_eq!(state.selected_id(), None);
assert_eq!(state.selected_bar(), None);
}
#[test]
fn test_chart_element_ids_are_distinct() {
let overlay = ChartElementId::OverlayIndicator(1);
let pane = ChartElementId::PaneIndicator(1);
let series = ChartElementId::Series(SeriesId(1));
assert_ne!(overlay, pane);
assert_ne!(overlay, series);
assert_ne!(pane, series);
}
}