1use crate::Selector;
2use crate::core::widget::operation::accessible::Accessible;
3use crate::core::widget::operation::{Focusable, Outcome, Scrollable, TextInput};
4use crate::core::widget::{Id, Operation};
5use crate::core::{Rectangle, Vector};
6use crate::target::Candidate;
7
8use std::any::Any;
9
10pub type Find<S> = Finder<One<S>>;
13
14pub type FindAll<S> = Finder<All<S>>;
17
18#[derive(Debug)]
19pub struct One<S>
20where
21 S: Selector,
22{
23 selector: S,
24 output: Option<S::Output>,
25}
26
27impl<S> One<S>
28where
29 S: Selector,
30{
31 pub fn new(selector: S) -> Self {
32 Self {
33 selector,
34 output: None,
35 }
36 }
37}
38
39impl<S> Strategy for One<S>
40where
41 S: Selector,
42 S::Output: Clone,
43{
44 type Output = Option<S::Output>;
45
46 fn feed(&mut self, target: Candidate<'_>) {
47 if let Some(output) = self.selector.select(target) {
48 self.output = Some(output);
49 }
50 }
51
52 fn is_done(&self) -> bool {
53 self.output.is_some()
54 }
55
56 fn finish(&self) -> Self::Output {
57 self.output.clone()
58 }
59}
60
61#[derive(Debug)]
62pub struct All<S>
63where
64 S: Selector,
65{
66 selector: S,
67 outputs: Vec<S::Output>,
68}
69
70impl<S> All<S>
71where
72 S: Selector,
73{
74 pub fn new(selector: S) -> Self {
75 Self {
76 selector,
77 outputs: Vec::new(),
78 }
79 }
80}
81
82impl<S> Strategy for All<S>
83where
84 S: Selector,
85 S::Output: Clone,
86{
87 type Output = Vec<S::Output>;
88
89 fn feed(&mut self, target: Candidate<'_>) {
90 if let Some(output) = self.selector.select(target) {
91 self.outputs.push(output);
92 }
93 }
94
95 fn is_done(&self) -> bool {
96 false
97 }
98
99 fn finish(&self) -> Self::Output {
100 self.outputs.clone()
101 }
102}
103
104pub trait Strategy {
105 type Output;
106
107 fn feed(&mut self, target: Candidate<'_>);
108
109 fn is_done(&self) -> bool;
110
111 fn finish(&self) -> Self::Output;
112}
113
114#[derive(Debug)]
115pub struct Finder<S> {
116 strategy: S,
117 stack: Vec<(Rectangle, Vector)>,
118 viewport: Rectangle,
119 translation: Vector,
120}
121
122impl<S> Finder<S> {
123 pub fn new(strategy: S) -> Self {
124 Self {
125 strategy,
126 stack: vec![(Rectangle::INFINITE, Vector::ZERO)],
127 viewport: Rectangle::INFINITE,
128 translation: Vector::ZERO,
129 }
130 }
131}
132
133impl<S> Operation<S::Output> for Finder<S>
134where
135 S: Strategy + Send,
136 S::Output: Send,
137{
138 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<S::Output>)) {
139 if self.strategy.is_done() {
140 return;
141 }
142
143 self.stack.push((self.viewport, self.translation));
144 operate(self);
145 let _ = self.stack.pop();
146
147 let (viewport, translation) = self.stack.last().unwrap();
148 self.viewport = *viewport;
149 self.translation = *translation;
150 }
151
152 fn container(&mut self, id: Option<&Id>, bounds: Rectangle) {
153 if self.strategy.is_done() {
154 return;
155 }
156
157 self.strategy.feed(Candidate::Container {
158 id,
159 bounds,
160 visible_bounds: self.viewport.intersection(&(bounds + self.translation)),
161 });
162 }
163
164 fn focusable(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Focusable) {
165 if self.strategy.is_done() {
166 return;
167 }
168
169 self.strategy.feed(Candidate::Focusable {
170 id,
171 bounds,
172 visible_bounds: self.viewport.intersection(&(bounds + self.translation)),
173 state,
174 });
175 }
176
177 fn scrollable(
178 &mut self,
179 id: Option<&Id>,
180 bounds: Rectangle,
181 content_bounds: Rectangle,
182 translation: Vector,
183 state: &mut dyn Scrollable,
184 ) {
185 if self.strategy.is_done() {
186 return;
187 }
188
189 let visible_bounds = self.viewport.intersection(&(bounds + self.translation));
190
191 self.strategy.feed(Candidate::Scrollable {
192 id,
193 bounds,
194 visible_bounds,
195 content_bounds,
196 translation,
197 state,
198 });
199
200 self.translation -= translation;
201 self.viewport = visible_bounds.unwrap_or_default();
202 }
203
204 fn text_input(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn TextInput) {
205 if self.strategy.is_done() {
206 return;
207 }
208
209 self.strategy.feed(Candidate::TextInput {
210 id,
211 bounds,
212 visible_bounds: self.viewport.intersection(&(bounds + self.translation)),
213 state,
214 });
215 }
216
217 fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
218 if self.strategy.is_done() {
219 return;
220 }
221
222 self.strategy.feed(Candidate::Text {
223 id,
224 bounds,
225 visible_bounds: self.viewport.intersection(&(bounds + self.translation)),
226 content: text,
227 });
228 }
229
230 fn accessible(&mut self, id: Option<&Id>, bounds: Rectangle, accessible: &Accessible<'_>) {
231 if self.strategy.is_done() {
232 return;
233 }
234
235 self.strategy.feed(Candidate::Accessible {
236 id,
237 bounds,
238 visible_bounds: self.viewport.intersection(&(bounds + self.translation)),
239 accessible,
240 });
241 }
242
243 fn custom(&mut self, id: Option<&Id>, bounds: Rectangle, state: &mut dyn Any) {
244 if self.strategy.is_done() {
245 return;
246 }
247
248 self.strategy.feed(Candidate::Custom {
249 id,
250 bounds,
251 visible_bounds: self.viewport.intersection(&(bounds + self.translation)),
252 state,
253 });
254 }
255
256 fn finish(&self) -> Outcome<S::Output> {
257 Outcome::Some(self.strategy.finish())
258 }
259}