From 77c6864e7c772c5e228bc09fe40c2c0b8884386d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 2 Aug 2022 04:20:47 +0200 Subject: Implement `focus_next` operation ... as well as a `count_focusable` composable helper! --- native/src/widget/operation.rs | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'native') diff --git a/native/src/widget/operation.rs b/native/src/widget/operation.rs index 2cfba005..5a0f0c18 100644 --- a/native/src/widget/operation.rs +++ b/native/src/widget/operation.rs @@ -58,3 +58,96 @@ pub fn focus(target: Id) -> impl Operation { Focus { target } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct FocusCount { + focused: Option, + total: usize, +} + +pub fn count_focusable(f: fn(FocusCount) -> O) -> impl Operation +where + O: Operation + 'static, +{ + struct CountFocusable { + count: FocusCount, + next: fn(FocusCount) -> O, + } + + impl Operation for CountFocusable + where + O: Operation + 'static, + { + fn focusable( + &mut self, + state: &mut dyn state::Focusable, + _id: Option<&Id>, + ) { + if state.is_focused() { + self.count.focused = Some(self.count.total); + } + + self.count.total += 1; + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self) + } + + fn finish(&self) -> Outcome { + Outcome::Chain(Box::new((self.next)(self.count))) + } + } + + CountFocusable { + count: FocusCount::default(), + next: f, + } +} + +pub fn focus_next() -> impl Operation { + struct FocusNext { + count: FocusCount, + current: usize, + } + + impl Operation for FocusNext { + fn focusable( + &mut self, + state: &mut dyn state::Focusable, + _id: Option<&Id>, + ) { + if self.count.total == 0 { + return; + } + + match self.count.focused { + None if self.current == 0 => state.focus(), + Some(focused) if focused == self.current => state.unfocus(), + Some(focused) if focused + 1 == self.current => state.focus(), + Some(focused) + if focused == self.count.total - 1 && self.current == 0 => + { + state.focus() + } + _ => {} + } + + self.current += 1; + } + + fn container( + &mut self, + _id: Option<&Id>, + operate_on_children: &mut dyn FnMut(&mut dyn Operation), + ) { + operate_on_children(self) + } + } + + count_focusable(|count| FocusNext { count, current: 0 }) +} -- cgit