working focus
This commit is contained in:
57
examples/focus_advanced.rs
Normal file
57
examples/focus_advanced.rs
Normal 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
26
src/focus/builder.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {}
|
|
||||||
|
|||||||
@@ -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
9
src/focus/mode.rs
Normal 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
11
src/focus/navigation.rs
Normal 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>;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user