use super::focus_group::FocusGroup;
use crate::types::WidgetId;
#[derive(Debug, Clone)]
pub struct FocusRing {
items: Vec<WidgetId>,
group: FocusGroup,
}
impl FocusRing {
pub fn new(ids: Vec<impl Into<WidgetId>>) -> Self {
let items: Vec<WidgetId> = ids.into_iter().map(Into::into).collect();
let mut group = FocusGroup::new(items.len());
if !items.is_empty() {
group.set_active(Some(0));
}
Self { items, group }
}
pub fn advance(&mut self) {
if self.group.active().is_some() {
self.group.cycle(1);
}
}
pub fn retreat(&mut self) {
if self.group.active().is_some() {
self.group.cycle(-1);
}
}
pub fn set(&mut self, id: &WidgetId) {
if let Some(idx) = self.items.iter().position(|item| item == id) {
self.group.set_active(Some(idx));
}
}
pub fn clear(&mut self) {
self.group.set_active(None);
}
pub fn focus_first(&mut self) {
if !self.items.is_empty() {
self.group.set_active(Some(0));
}
}
pub fn current(&self) -> Option<&WidgetId> {
self.group.active().and_then(|i| self.items.get(i))
}
pub fn current_index(&self) -> Option<usize> {
self.group.active()
}
pub fn is_focused(&self, id: &WidgetId) -> bool {
self.current() == Some(id)
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn items(&self) -> &[WidgetId] {
&self.items
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_focuses_first() {
let ring = FocusRing::new(vec!["a", "b", "c"]);
assert_eq!(ring.current(), Some(&WidgetId::new("a")));
assert_eq!(ring.current_index(), Some(0));
}
#[test]
fn empty_ring() {
let ring = FocusRing::new(Vec::<&str>::new());
assert_eq!(ring.current(), None);
assert!(ring.is_empty());
}
#[test]
fn advance_wraps() {
let mut ring = FocusRing::new(vec!["a", "b", "c"]);
ring.advance();
assert_eq!(ring.current(), Some(&WidgetId::new("b")));
ring.advance();
assert_eq!(ring.current(), Some(&WidgetId::new("c")));
ring.advance();
assert_eq!(ring.current(), Some(&WidgetId::new("a")));
}
#[test]
fn retreat_wraps() {
let mut ring = FocusRing::new(vec!["a", "b", "c"]);
ring.retreat();
assert_eq!(ring.current(), Some(&WidgetId::new("c")));
ring.retreat();
assert_eq!(ring.current(), Some(&WidgetId::new("b")));
}
#[test]
fn set_by_id() {
let mut ring = FocusRing::new(vec!["a", "b", "c"]);
ring.set(&WidgetId::new("c"));
assert_eq!(ring.current_index(), Some(2));
assert!(ring.is_focused(&WidgetId::new("c")));
assert!(!ring.is_focused(&WidgetId::new("a")));
}
#[test]
fn set_unknown_id_is_noop() {
let mut ring = FocusRing::new(vec!["a", "b"]);
ring.set(&WidgetId::new("z"));
assert_eq!(ring.current(), Some(&WidgetId::new("a")));
}
#[test]
fn clear_and_refocus() {
let mut ring = FocusRing::new(vec!["a", "b"]);
ring.clear();
assert_eq!(ring.current(), None);
ring.advance(); assert_eq!(ring.current(), None);
ring.focus_first();
assert_eq!(ring.current(), Some(&WidgetId::new("a")));
}
#[test]
fn advance_after_set() {
let mut ring = FocusRing::new(vec!["a", "b", "c"]);
ring.set(&WidgetId::new("b"));
ring.advance();
assert_eq!(ring.current(), Some(&WidgetId::new("c")));
}
#[test]
fn single_item_ring() {
let mut ring = FocusRing::new(vec!["only"]);
assert_eq!(ring.current(), Some(&WidgetId::new("only")));
ring.advance();
assert_eq!(ring.current(), Some(&WidgetId::new("only")));
ring.retreat();
assert_eq!(ring.current(), Some(&WidgetId::new("only")));
}
}