working focus

This commit is contained in:
filipriec_vm
2026-01-11 16:03:04 +01:00
parent e3e2d64b2a
commit 1044003179
6 changed files with 138 additions and 2 deletions

View File

@@ -0,0 +1,57 @@
extern crate alloc;
use tui_orchestrator::focus::{FocusBuilder, FocusId, FocusManager, Focusable};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum FormElement {
Username,
Password,
RememberMe,
Submit,
}
struct FormPage {
username: alloc::string::String,
password: alloc::string::String,
remember: bool,
}
impl Focusable<FormElement> for FormPage {
fn focus_targets(&self) -> alloc::vec::Vec<FormElement> {
alloc::vec![
FormElement::Username,
FormElement::Password,
FormElement::RememberMe,
FormElement::Submit,
]
}
}
fn main() {
let form = FormPage {
username: alloc::string::String::new(),
password: alloc::string::String::new(),
remember: false,
};
let mut focus_manager = FocusManager::new();
focus_manager.set_targets(form.focus_targets());
println!("Current focus: {:?}", focus_manager.current());
focus_manager.next();
println!("After next: {:?}", focus_manager.current());
focus_manager.last();
println!("After last: {:?}", focus_manager.current());
println!("Is first: {}", focus_manager.is_first());
println!("Is last: {}", focus_manager.is_last());
println!("\n--- FocusBuilder Demo ---");
let builder = FocusBuilder::new()
.target(FormElement::Username)
.target(FormElement::Password)
.target(FormElement::Submit);
let targets = builder.build();
println!("Built targets: {:?}", targets);
}

26
src/focus/builder.rs Normal file
View File

@@ -0,0 +1,26 @@
#[derive(Debug, Clone, Default)]
pub struct FocusBuilder<F: super::FocusId> {
targets: alloc::vec::Vec<F>,
}
impl<F: super::FocusId> FocusBuilder<F> {
pub fn new() -> Self {
Self {
targets: alloc::vec::Vec::new(),
}
}
pub fn target(mut self, target: F) -> Self {
self.targets.push(target);
self
}
pub fn targets(mut self, targets: &[F]) -> Self {
self.targets.extend_from_slice(targets);
self
}
pub fn build(self) -> alloc::vec::Vec<F> {
self.targets
}
}

View File

@@ -1,5 +1,3 @@
// path_from_the_root: src/focus/id.rs // path_from_the_root: src/focus/id.rs
pub trait FocusId: Clone + PartialEq + Eq + core::hash::Hash {} pub trait FocusId: Clone + PartialEq + Eq + core::hash::Hash {}
impl<T: Clone + PartialEq + Eq + core::hash::Hash> FocusId for T {}

View File

@@ -133,4 +133,39 @@ impl<F: FocusId> FocusManager<F> {
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.targets.is_empty() self.targets.is_empty()
} }
pub fn current_index(&self) -> Option<usize> {
if self.targets.is_empty() {
None
} else {
Some(self.index)
}
}
pub fn wrap_next(&mut self) {
if !self.targets.is_empty() {
self.index = (self.index + 1) % self.targets.len();
}
}
pub fn wrap_prev(&mut self) {
if !self.targets.is_empty() {
self.index = if self.index == 0 {
self.targets.len() - 1
} else {
self.index - 1
};
}
}
pub fn is_first(&self) -> bool {
self.current_index() == Some(0)
}
pub fn is_last(&self) -> bool {
match self.current_index() {
Some(idx) => idx == self.targets.len().saturating_sub(1),
None => false,
}
}
} }

9
src/focus/mode.rs Normal file
View File

@@ -0,0 +1,9 @@
pub trait FocusModeHint<F: super::FocusId> {
fn focus_modes(&self) -> &[&'static str];
}
impl<F: super::FocusId> FocusModeHint<F> for super::FocusManager<F> {
fn focus_modes(&self) -> &[&'static str] {
&["general"]
}
}

11
src/focus/navigation.rs Normal file
View File

@@ -0,0 +1,11 @@
pub trait FocusNavigation<F: super::FocusId> {
type Error;
fn can_navigate_forward(&self, from: &F) -> bool;
fn can_navigate_backward(&self, from: &F) -> bool;
fn navigate_forward(&mut self) -> Result<Option<F>, Self::Error>;
fn navigate_backward(&mut self) -> Result<Option<F>, Self::Error>;
}