use crate::Rectangle;
use crate::widget::Id;
use crate::widget::operation::focusable::mark_focus_dirty;
use crate::widget::operation::{self, Focusable, Operation, Outcome};
#[derive(Debug, Clone, Copy, Default)]
struct ScanResult {
auto_focus_index: Option<usize>,
total: usize,
focused_index: Option<usize>,
}
pub fn focus_auto<T>() -> impl Operation<T>
where
T: Send + 'static,
{
operation::then(scan(), apply)
}
fn scan() -> impl Operation<ScanResult> {
struct Scan {
result: ScanResult,
mark_next: bool,
}
impl Operation<ScanResult> for Scan {
fn auto_focusable(&mut self, _id: Option<&Id>, _bounds: Rectangle) {
self.mark_next = true;
}
fn focusable(&mut self, _id: Option<&Id>, _bounds: Rectangle, state: &mut dyn Focusable) {
if state.is_focused() {
self.result.focused_index = Some(self.result.total);
}
if self.mark_next && self.result.auto_focus_index.is_none() {
self.result.auto_focus_index = Some(self.result.total);
self.mark_next = false;
}
self.result.total += 1;
}
fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<ScanResult>)) {
operate(self);
}
fn finish(&self) -> Outcome<ScanResult> {
if let Some(target) = self.result.auto_focus_index {
if self.result.focused_index == Some(target) {
log::debug!(
"[focus_auto] auto-focus target at index {} is already focused — skipping",
target,
);
return Outcome::None;
}
} else {
if let Some(idx) = self.result.focused_index {
log::debug!(
"[focus_auto] no auto-focus target; widget at index {} already focused — skipping",
idx,
);
return Outcome::None;
}
}
log::debug!(
"[focus_auto] scan done — target={:?}, focused={:?}, total={}",
self.result.auto_focus_index,
self.result.focused_index,
self.result.total
);
Outcome::Some(self.result)
}
}
Scan {
result: ScanResult::default(),
mark_next: false,
}
}
fn apply<T>(result: ScanResult) -> impl Operation<T> {
struct Apply {
target: usize,
has_target: bool,
current: usize,
}
impl<T> Operation<T> for Apply {
fn focusable(&mut self, _id: Option<&Id>, _bounds: Rectangle, state: &mut dyn Focusable) {
if self.has_target && self.current == self.target {
log::debug!("[focus_auto] focusing widget at index {}", self.current);
state.focus();
mark_focus_dirty();
} else {
state.unfocus();
}
self.current += 1;
}
fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
operate(self);
}
}
Apply {
target: result.auto_focus_index.unwrap_or(0),
has_target: result.total > 0,
current: 0,
}
}