#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum FocusState {
Unfocused,
Focused,
FocusVisible,
Disabled,
}
impl FocusState {
pub fn has_focus(self) -> bool {
matches!(self, Self::Focused | Self::FocusVisible)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct FocusTarget(String);
impl FocusTarget {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl AsRef<str> for FocusTarget {
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct FocusOrder(usize);
impl FocusOrder {
pub fn new(value: usize) -> Self {
Self(value)
}
pub fn value(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum FocusVisibility {
Auto,
Visible,
Hidden,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct TabIndex(i16);
impl TabIndex {
pub fn new(value: i16) -> Self {
Self(value)
}
pub fn value(self) -> i16 {
self.0
}
pub fn is_focusable(self) -> bool {
self.0 >= 0
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct FocusScope {
targets: Vec<FocusTarget>,
}
impl FocusScope {
pub fn new(targets: Vec<FocusTarget>) -> Self {
Self { targets }
}
pub fn targets(&self) -> &[FocusTarget] {
&self.targets
}
pub fn contains(&self, target: &FocusTarget) -> bool {
self.targets.contains(target)
}
}
#[cfg(test)]
mod tests {
use super::{FocusOrder, FocusScope, FocusState, FocusTarget, FocusVisibility, TabIndex};
#[test]
fn checks_focus_state_and_index_helpers() {
assert!(FocusState::FocusVisible.has_focus());
assert!(!FocusState::Disabled.has_focus());
assert!(TabIndex::new(0).is_focusable());
assert!(!TabIndex::new(-1).is_focusable());
assert_eq!(FocusOrder::new(2).value(), 2);
assert_eq!(FocusVisibility::Visible, FocusVisibility::Visible);
}
#[test]
fn creates_focus_scopes() {
let submit = FocusTarget::new("submit");
let cancel = FocusTarget::new("cancel");
let scope = FocusScope::new(vec![submit.clone()]);
assert!(scope.contains(&submit));
assert!(!scope.contains(&cancel));
assert_eq!(scope.targets().len(), 1);
}
}