use crate::Selector; use crate::core::widget::operation::{ Focusable, Outcome, Scrollable, TextInput, }; use crate::core::widget::{Id, Operation}; use crate::core::{Rectangle, Vector}; use crate::target::Candidate; use std::any::Any; /// An [`Operation`] that runs the [`Selector`] and stops after /// the first [`Output`](Selector::Output) is produced. pub type Find = Finder>; /// An [`Operation`] that runs the [`Selector`] for the entire /// widget tree and aggregates all of its [`Output`](Selector::Output). pub type FindAll = Finder>; #[derive(Debug)] pub struct One where S: Selector, { selector: S, output: Option, } impl One where S: Selector, { pub fn new(selector: S) -> Self { Self { selector, output: None, } } } impl Strategy for One where S: Selector, S::Output: Clone, { type Output = Option; fn feed(&mut self, target: Candidate<'_>) { if let Some(output) = self.selector.select(target) { self.output = Some(output); } } fn is_done(&self) -> bool { self.output.is_some() } fn finish(&self) -> Self::Output { self.output.clone() } } #[derive(Debug)] pub struct All where S: Selector, { selector: S, outputs: Vec, } impl All where S: Selector, { pub fn new(selector: S) -> Self { Self { selector, outputs: Vec::new(), } } } impl Strategy for All where S: Selector, S::Output: Clone, { type Output = Vec; fn feed(&mut self, target: Candidate<'_>) { if let Some(output) = self.selector.select(target) { self.outputs.push(output); } } fn is_done(&self) -> bool { false } fn finish(&self) -> Self::Output { self.outputs.clone() } } pub trait Strategy { type Output; fn feed(&mut self, target: Candidate<'_>); fn is_done(&self) -> bool; fn finish(&self) -> Self::Output; } #[derive(Debug)] pub struct Finder { strategy: S, stack: Vec<(Rectangle, Vector)>, viewport: Rectangle, translation: Vector, } impl Finder { pub fn new(strategy: S) -> Self { Self { strategy, stack: vec![(Rectangle::INFINITE, Vector::ZERO)], viewport: Rectangle::INFINITE, translation: Vector::ZERO, } } } impl Operation for Finder where S: Strategy + Send, S::Output: Send, { fn traverse( &mut self, operate: &mut dyn FnMut(&mut dyn Operation), ) { if self.strategy.is_done() { return; } self.stack.push((self.viewport, self.translation)); operate(self); let _ = self.stack.pop(); let (viewport, translation) = self.stack.last().unwrap(); self.viewport = *viewport; self.translation = *translation; } fn container(&mut self, id: Option<&Id>, bounds: Rectangle) { if self.strategy.is_done() { return; } self.strategy.feed(Candidate::Container { id, bounds, visible_bounds: self .viewport .intersection(&(bounds + self.translation)), }); } fn focusable( &mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Focusable, ) { if self.strategy.is_done() { return; } self.strategy.feed(Candidate::Focusable { id, bounds, visible_bounds: self .viewport .intersection(&(bounds + self.translation)), state, }); } fn scrollable( &mut self, id: Option<&Id>, bounds: Rectangle, content_bounds: Rectangle, translation: Vector, state: &mut dyn Scrollable, ) { if self.strategy.is_done() { return; } let visible_bounds = self.viewport.intersection(&(bounds + self.translation)); self.strategy.feed(Candidate::Scrollable { id, bounds, visible_bounds, content_bounds, translation, state, }); self.translation = self.translation - translation; self.viewport = visible_bounds.unwrap_or_default(); } fn text_input( &mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn TextInput, ) { if self.strategy.is_done() { return; } self.strategy.feed(Candidate::TextInput { id, bounds, visible_bounds: self .viewport .intersection(&(bounds + self.translation)), state, }); } fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) { if self.strategy.is_done() { return; } self.strategy.feed(Candidate::Text { id, bounds, visible_bounds: self .viewport .intersection(&(bounds + self.translation)), content: text, }); } fn custom( &mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Any, ) { if self.strategy.is_done() { return; } self.strategy.feed(Candidate::Custom { id, bounds, visible_bounds: self .viewport .intersection(&(bounds + self.translation)), state, }); } fn finish(&self) -> Outcome { Outcome::Some(self.strategy.finish()) } }