use crate::*; #[derive(Clone, Copy, Debug, PartialEq)] pub enum FocusState { Exited(T), Focused(T), Entered(T), } impl FocusState { pub fn inner (&self) -> T { match self { Self::Exited(inner) => *inner, Self::Focused(inner) => *inner, Self::Entered(inner) => *inner, } } pub fn set_inner (&mut self, inner: T) { *self = match self { Self::Exited(_) => Self::Exited(inner), Self::Focused(_) => Self::Focused(inner), Self::Entered(_) => Self::Entered(inner), } } pub fn is_exited (&self) -> bool { if let Self::Exited(_) = self { true } else { false } } pub fn is_focused (&self) -> bool { if let Self::Focused(_) = self { true } else { false } } pub fn is_entered (&self) -> bool { if let Self::Entered(_) = self { true } else { false } } pub fn to_exited (&mut self) { *self = Self::Exited(self.inner()) } pub fn to_focused (&mut self) { *self = Self::Focused(self.inner()) } pub fn to_entered (&mut self) { *self = Self::Entered(self.inner()) } } #[derive(Copy, Clone, PartialEq, Debug)] pub enum FocusCommand { Next, Prev, Up, Down, Left, Right, Enter, Exit } impl Command for FocusCommand { fn execute (self, state: &mut F) -> Perhaps { use FocusCommand::*; match self { Next => { state.focus_next(); }, Prev => { state.focus_prev(); }, Up => { state.focus_up(); }, Down => { state.focus_down(); }, Left => { state.focus_left(); }, Right => { state.focus_right(); }, Enter => { state.focus_enter(); }, Exit => { state.focus_exit(); }, _ => {} } Ok(None) } } /// Trait for things that have ordered focusable subparts. pub trait HasFocus { /// Type that identifies of focused item. type Item: Copy + PartialEq + Debug; /// Get the currently focused item. fn focused (&self) -> Self::Item; /// Focus the next item. fn focus_next (&mut self); /// Focus the previous item. fn focus_prev (&mut self); /// Loop forward until a specific item is focused. fn focus (&mut self, target: Self::Item) { while self.focused() != target { self.focus_next() } } /// Enter the focused item fn focus_enter (&mut self) {} /// Exit the focused item fn focus_exit (&mut self) {} /// Return the focused item, if any fn focus_entered (&self) -> Option { None } } /// Trait for things that implement directional focus. pub trait FocusGrid { type Item: Copy + PartialEq + Debug; fn focus_layout (&self) -> &[&[Self::Item]]; fn focus_cursor (&self) -> (usize, usize); fn focus_cursor_mut (&mut self) -> &mut (usize, usize); fn focus_update (&mut self) {} fn focus_up (&mut self) { let layout = self.focus_layout(); let (x, y) = self.focus_cursor(); let next_y = if y == 0 { layout.len().saturating_sub(1) } else { y - 1 }; let next_x = if layout[y].len() == layout[next_y].len() { x } else { ((x as f32 / layout[y].len() as f32) * layout[next_y].len() as f32) as usize }; *self.focus_cursor_mut() = (next_x, next_y); self.focus_update(); } fn focus_down (&mut self) { let layout = self.focus_layout(); let (x, y) = self.focus_cursor(); let next_y = if y >= layout.len().saturating_sub(1) { 0 } else { y + 1 }; let next_x = if layout[y].len() == layout[next_y].len() { x } else { ((x as f32 / layout[y].len() as f32) * layout[next_y].len() as f32) as usize }; *self.focus_cursor_mut() = (next_x, next_y); self.focus_update(); } fn focus_left (&mut self) { let layout = self.focus_layout(); let (x, y) = self.focus_cursor(); let next_x = if x == 0 { layout[y].len().saturating_sub(1) } else { x - 1 }; *self.focus_cursor_mut() = (next_x, y); self.focus_update(); } fn focus_right (&mut self) { let layout = self.focus_layout(); let (x, y) = self.focus_cursor(); let next_x = if x >= layout[y].len().saturating_sub(1) { 0 } else { x + 1 }; *self.focus_cursor_mut() = (next_x, y); self.focus_update(); } } impl HasFocus for U where T: Copy + PartialEq + Debug, U: FocusGrid { type Item = T; fn focused (&self) -> Self::Item { let (x, y) = self.focus_cursor(); self.focus_layout()[y][x] } fn focus_next (&mut self) { let current = self.focused(); let (x, y) = self.focus_cursor(); if x < self.focus_layout()[y].len().saturating_sub(1) { self.focus_right(); } else { self.focus_down(); self.focus_cursor_mut().0 = 0; } if self.focused() == current { // FIXME: prevent infinite loop self.focus_next() } self.focus_exit(); self.focus_update(); } fn focus_prev (&mut self) { let current = self.focused(); let (x, _) = self.focus_cursor(); if x > 0 { self.focus_left(); } else { self.focus_up(); let (_, y) = self.focus_cursor(); let next_x = self.focus_layout()[y].len().saturating_sub(1); self.focus_cursor_mut().0 = next_x; } if self.focused() == current { // FIXME: prevent infinite loop self.focus_prev() } self.focus_exit(); self.focus_update(); } }