use crate::widget_id::WidgetId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FocusDirection {
Next,
Previous,
Up,
Down,
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FocusEvent {
Gained(WidgetId),
Lost(WidgetId),
Changed {
from: Option<WidgetId>,
to: Option<WidgetId>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FocusPolicy {
Focusable,
ClickFocusable,
#[default]
NotFocusable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FocusScopeId(pub u64);
impl FocusScopeId {
pub const ROOT: Self = Self(0);
pub fn new(id: u64) -> Self {
Self(id)
}
}
#[derive(Debug, Clone)]
struct FocusEntry {
widget_id: WidgetId,
policy: FocusPolicy,
scope: FocusScopeId,
tab_index: i32, }
pub struct FocusManager {
entries: Vec<FocusEntry>,
focused: Option<usize>, active_scope: FocusScopeId,
events: Vec<FocusEvent>,
}
impl FocusManager {
pub fn new() -> Self {
Self {
entries: Vec::new(),
focused: None,
active_scope: FocusScopeId::ROOT,
events: Vec::new(),
}
}
pub fn register(&mut self, widget_id: WidgetId) {
self.register_with_policy(widget_id, FocusPolicy::Focusable);
}
pub fn register_with_policy(&mut self, widget_id: WidgetId, policy: FocusPolicy) {
self.register_with_details(widget_id, policy, FocusScopeId::ROOT, 0);
}
pub fn register_with_details(
&mut self,
widget_id: WidgetId,
policy: FocusPolicy,
scope: FocusScopeId,
tab_index: i32,
) {
if let Some(entry) = self.entries.iter_mut().find(|e| e.widget_id == widget_id) {
entry.policy = policy;
entry.scope = scope;
entry.tab_index = tab_index;
} else {
self.entries.push(FocusEntry {
widget_id,
policy,
scope,
tab_index,
});
}
}
pub fn unregister(&mut self, widget_id: WidgetId) {
if let Some(index) = self.entries.iter().position(|e| e.widget_id == widget_id) {
self.entries.remove(index);
if self.focused == Some(index) {
self.focused = None;
self.events.push(FocusEvent::Lost(widget_id));
} else if let Some(focused_idx) = self.focused {
if focused_idx > index {
self.focused = Some(focused_idx - 1);
}
}
}
}
pub fn clear(&mut self) {
if let Some(old_focus) = self.focused.and_then(|idx| self.entries.get(idx)) {
self.events.push(FocusEvent::Lost(old_focus.widget_id));
}
self.entries.clear();
self.focused = None;
}
pub fn focused(&self) -> Option<WidgetId> {
self.focused
.and_then(|idx| self.entries.get(idx))
.map(|e| e.widget_id)
}
pub fn set_focus(&mut self, widget_id: WidgetId) -> bool {
if let Some(index) = self.entries.iter().position(|e| e.widget_id == widget_id) {
let old_focus = self
.focused
.and_then(|idx| self.entries.get(idx).map(|e| e.widget_id));
if old_focus == Some(widget_id) {
return true; }
self.focused = Some(index);
if let Some(old_id) = old_focus {
self.events.push(FocusEvent::Lost(old_id));
}
self.events.push(FocusEvent::Gained(widget_id));
self.events.push(FocusEvent::Changed {
from: old_focus,
to: Some(widget_id),
});
true
} else {
false
}
}
pub fn clear_focus(&mut self) {
if let Some(old_focus) = self.focused.and_then(|idx| self.entries.get(idx)) {
let old_id = old_focus.widget_id;
self.focused = None;
self.events.push(FocusEvent::Lost(old_id));
self.events.push(FocusEvent::Changed {
from: Some(old_id),
to: None,
});
}
}
pub fn focus_next(&mut self) {
self.navigate(FocusDirection::Next);
}
pub fn focus_previous(&mut self) {
self.navigate(FocusDirection::Previous);
}
pub fn navigate(&mut self, direction: FocusDirection) {
if self.entries.is_empty() {
return;
}
let current_idx = self.focused.unwrap_or(0);
let next_idx = match direction {
FocusDirection::Next => self.find_next_focusable(current_idx),
FocusDirection::Previous => self.find_previous_focusable(current_idx),
FocusDirection::Down | FocusDirection::Right => self.find_next_focusable(current_idx),
FocusDirection::Up | FocusDirection::Left => self.find_previous_focusable(current_idx),
};
if let Some(next_idx) = next_idx {
let next_widget = self.entries[next_idx].widget_id;
self.set_focus(next_widget);
}
}
fn find_next_focusable(&self, current_idx: usize) -> Option<usize> {
let count = self.entries.len();
for i in 1..=count {
let idx = (current_idx + i) % count;
if self.can_focus(idx) {
return Some(idx);
}
}
None
}
fn find_previous_focusable(&self, current_idx: usize) -> Option<usize> {
let count = self.entries.len();
for i in 1..=count {
let idx = (current_idx + count - i) % count;
if self.can_focus(idx) {
return Some(idx);
}
}
None
}
fn can_focus(&self, index: usize) -> bool {
if let Some(entry) = self.entries.get(index) {
if entry.scope != self.active_scope {
return false;
}
match entry.policy {
FocusPolicy::Focusable => entry.tab_index >= 0,
FocusPolicy::ClickFocusable => false, FocusPolicy::NotFocusable => false,
}
} else {
false
}
}
pub fn set_scope(&mut self, scope: FocusScopeId) {
self.active_scope = scope;
if let Some(idx) = self.focused
&& let Some(entry) = self.entries.get(idx)
&& entry.scope != scope
{
self.clear_focus();
}
}
pub fn scope(&self) -> FocusScopeId {
self.active_scope
}
pub fn pop_events(&mut self) -> Vec<FocusEvent> {
std::mem::take(&mut self.events)
}
pub fn is_focused(&self, widget_id: WidgetId) -> bool {
self.focused() == Some(widget_id)
}
pub fn widget_count(&self) -> usize {
self.entries.len()
}
pub fn focusable_count(&self) -> usize {
self.entries
.iter()
.enumerate()
.filter(|(idx, _)| self.can_focus(*idx))
.count()
}
}
impl Default for FocusManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_focus_manager_new() {
let manager = FocusManager::new();
assert_eq!(manager.widget_count(), 0);
assert_eq!(manager.focused(), None);
}
#[test]
fn test_register_and_focus() {
let mut manager = FocusManager::new();
let id = WidgetId::from_raw(1);
manager.register(id);
assert_eq!(manager.widget_count(), 1);
let result = manager.set_focus(id);
assert!(result);
assert_eq!(manager.focused(), Some(id));
}
#[test]
fn test_focus_next() {
let mut manager = FocusManager::new();
let id1 = WidgetId::from_raw(1);
let id2 = WidgetId::from_raw(2);
let id3 = WidgetId::from_raw(3);
manager.register(id1);
manager.register(id2);
manager.register(id3);
manager.set_focus(id1);
assert_eq!(manager.focused(), Some(id1));
manager.focus_next();
assert_eq!(manager.focused(), Some(id2));
manager.focus_next();
assert_eq!(manager.focused(), Some(id3));
manager.focus_next();
assert_eq!(manager.focused(), Some(id1));
}
#[test]
fn test_focus_previous() {
let mut manager = FocusManager::new();
let id1 = WidgetId::from_raw(1);
let id2 = WidgetId::from_raw(2);
let id3 = WidgetId::from_raw(3);
manager.register(id1);
manager.register(id2);
manager.register(id3);
manager.set_focus(id3);
assert_eq!(manager.focused(), Some(id3));
manager.focus_previous();
assert_eq!(manager.focused(), Some(id2));
manager.focus_previous();
assert_eq!(manager.focused(), Some(id1));
manager.focus_previous();
assert_eq!(manager.focused(), Some(id3));
}
#[test]
fn test_clear_focus() {
let mut manager = FocusManager::new();
let id = WidgetId::from_raw(1);
manager.register(id);
manager.set_focus(id);
assert_eq!(manager.focused(), Some(id));
manager.clear_focus();
assert_eq!(manager.focused(), None);
}
#[test]
fn test_unregister() {
let mut manager = FocusManager::new();
let id1 = WidgetId::from_raw(1);
let id2 = WidgetId::from_raw(2);
manager.register(id1);
manager.register(id2);
manager.set_focus(id1);
manager.unregister(id1);
assert_eq!(manager.widget_count(), 1);
assert_eq!(manager.focused(), None);
}
#[test]
fn test_focus_policy() {
let mut manager = FocusManager::new();
let id1 = WidgetId::from_raw(1);
let id2 = WidgetId::from_raw(2);
let id3 = WidgetId::from_raw(3);
manager.register_with_policy(id1, FocusPolicy::Focusable);
manager.register_with_policy(id2, FocusPolicy::NotFocusable);
manager.register_with_policy(id3, FocusPolicy::Focusable);
manager.set_focus(id1);
manager.focus_next();
assert_eq!(manager.focused(), Some(id3));
}
#[test]
fn test_focus_scope() {
let mut manager = FocusManager::new();
let id1 = WidgetId::from_raw(1);
let id2 = WidgetId::from_raw(2);
let modal_scope = FocusScopeId::new(1);
manager.register_with_details(id1, FocusPolicy::Focusable, FocusScopeId::ROOT, 0);
manager.register_with_details(id2, FocusPolicy::Focusable, modal_scope, 0);
manager.set_focus(id1);
assert_eq!(manager.focused(), Some(id1));
manager.set_scope(modal_scope);
assert_eq!(manager.focused(), None);
manager.set_focus(id2);
assert_eq!(manager.focused(), Some(id2));
}
#[test]
fn test_focus_events() {
let mut manager = FocusManager::new();
let id1 = WidgetId::from_raw(1);
let id2 = WidgetId::from_raw(2);
manager.register(id1);
manager.register(id2);
manager.set_focus(id1);
let events = manager.pop_events();
assert_eq!(events.len(), 2);
manager.set_focus(id2);
let events = manager.pop_events();
assert_eq!(events.len(), 3);
let events = manager.pop_events();
assert_eq!(events.len(), 0);
}
}