1use iced_core::{
2 Rectangle,
3 widget::{Id, Operation},
4};
5use iced_runtime::{Task, task::widget as operate};
6use std::{hash::Hash, marker::PhantomData};
7
8use crate::widgets::{self, NavigatorState};
9
10fn clear_history_op<T, Key>(target: Option<Id>) -> impl Operation<T>
11where
12 Key: 'static + Eq + Hash + Clone + Send,
13{
14 struct ClearHistory<Key> {
15 target: Option<Id>,
16 p: PhantomData<Key>,
17 }
18
19 impl<T, Key> Operation<T> for ClearHistory<Key>
20 where
21 Key: 'static + Eq + Hash + Clone + Send,
22 {
23 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
24 operate(self)
25 }
26
27 fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
28 #[cfg(feature = "stack")]
29 if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
30 value.request_update();
31
32 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
33 return;
34 }
35
36 value.clear_history();
37 }
38
39 #[cfg(feature = "tabs")]
40 if let Some(value) = state.downcast_mut::<widgets::tabs_navigator::State<Key>>() {
41 value.request_update();
42
43 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
44 return;
45 }
46
47 value.clear_history();
48 }
49
50 #[cfg(feature = "drawer")]
51 if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
52 value.request_update();
53
54 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
55 return;
56 }
57
58 value.clear_history();
59 }
60 }
61 }
62
63 ClearHistory {
64 target,
65 p: PhantomData::<Key>,
66 }
67}
68
69fn pop_history_op<T, Key>(target: Option<Id>) -> impl Operation<T>
70where
71 Key: 'static + Eq + Hash + Clone + Send,
72{
73 struct PopHistory<Key> {
74 target: Option<Id>,
75 p: PhantomData<Key>,
76 }
77
78 impl<T, Key> Operation<T> for PopHistory<Key>
79 where
80 Key: 'static + Eq + Hash + Clone + Send,
81 {
82 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
83 operate(self)
84 }
85
86 fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
87 #[cfg(feature = "stack")]
88 if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
89 value.request_update();
90
91 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
92 return;
93 }
94
95 value.pop_history();
96 }
97
98 #[cfg(feature = "tabs")]
99 if let Some(value) = state.downcast_mut::<widgets::tabs_navigator::State<Key>>() {
100 value.request_update();
101
102 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
103 return;
104 }
105
106 value.pop_history();
107 }
108
109 #[cfg(feature = "drawer")]
110 if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
111 value.request_update();
112
113 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
114 return;
115 }
116
117 value.pop_history();
118 }
119 }
120 }
121
122 PopHistory {
123 target,
124 p: PhantomData::<Key>,
125 }
126}
127
128fn go_back_op<T, Key>(target: Option<Id>) -> impl Operation<T>
129where
130 Key: 'static + Eq + Hash + Clone + Send,
131{
132 struct GoBack<Key> {
133 target: Option<Id>,
134 p: PhantomData<Key>,
135 }
136
137 impl<T, Key> Operation<T> for GoBack<Key>
138 where
139 Key: 'static + Eq + Hash + Clone + Send,
140 {
141 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
142 operate(self)
143 }
144
145 fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
146 #[cfg(feature = "stack")]
147 if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
148 value.request_update();
149
150 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
151 return;
152 }
153
154 value.go_back();
155 }
156
157 #[cfg(feature = "tabs")]
158 if let Some(value) = state.downcast_mut::<widgets::tabs_navigator::State<Key>>() {
159 value.request_update();
160
161 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
162 return;
163 }
164
165 value.go_back();
166 }
167
168 #[cfg(feature = "drawer")]
169 if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
170 value.request_update();
171
172 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
173 return;
174 }
175
176 value.go_back();
177 }
178 }
179 }
180
181 GoBack {
182 target,
183 p: PhantomData::<Key>,
184 }
185}
186
187fn navigate_op<T, Key>(page: Key, target: Option<Id>) -> impl Operation<T>
188where
189 Key: 'static + Eq + Hash + Clone + Send,
190{
191 struct Navigate<Key> {
192 target: Option<Id>,
193 page: Option<Key>,
194 }
195
196 impl<T, Key> Operation<T> for Navigate<Key>
197 where
198 Key: 'static + Eq + Hash + Clone + Send,
199 {
200 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
201 operate(self)
202 }
203
204 fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
205 #[cfg(feature = "stack")]
206 if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
207 value.request_update();
208
209 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
210 return;
211 }
212
213 value.navigate(self.page.take().unwrap());
214 }
215
216 #[cfg(feature = "tabs")]
217 if let Some(value) = state.downcast_mut::<widgets::tabs_navigator::State<Key>>() {
218 value.request_update();
219
220 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
221 return;
222 }
223
224 value.navigate(self.page.take().unwrap());
225 }
226
227 #[cfg(feature = "drawer")]
228 if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
229 value.request_update();
230
231 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
232 return;
233 }
234
235 value.navigate(self.page.take().unwrap());
236 }
237 }
238 }
239
240 Navigate {
241 target,
242 page: Some(page),
243 }
244}
245
246#[cfg(feature = "drawer")]
247fn open_drawer_op<T, Key>(target: Option<Id>) -> impl Operation<T>
248where
249 Key: 'static + Eq + Hash + Clone + Send,
250{
251 struct OpenDrawer<Key> {
252 target: Option<Id>,
253 p: PhantomData<Key>,
254 }
255
256 impl<T, Key> Operation<T> for OpenDrawer<Key>
257 where
258 Key: 'static + Eq + Hash + Clone + Send,
259 {
260 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
261 operate(self)
262 }
263
264 fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
265 if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
266 value.request_update();
267
268 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
269 return;
270 }
271
272 value.open_drawer();
273 }
274 }
275 }
276
277 OpenDrawer {
278 target,
279 p: PhantomData::<Key>,
280 }
281}
282
283#[cfg(feature = "drawer")]
284fn close_drawer_op<T, Key>(target: Option<Id>) -> impl Operation<T>
285where
286 Key: 'static + Eq + Hash + Clone + Send,
287{
288 struct CloseDrawer<Key> {
289 target: Option<Id>,
290 p: PhantomData<Key>,
291 }
292
293 impl<T, Key> Operation<T> for CloseDrawer<Key>
294 where
295 Key: 'static + Eq + Hash + Clone + Send,
296 {
297 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
298 operate(self)
299 }
300
301 fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
302 if let Some(value) = state.downcast_mut::<widgets::drawer_navigator::State<Key>>() {
303 value.request_update();
304
305 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
306 return;
307 }
308
309 value.close_drawer();
310 }
311 }
312 }
313
314 CloseDrawer {
315 target,
316 p: PhantomData::<Key>,
317 }
318}
319
320#[cfg(feature = "stack")]
321fn push_op<T, Key>(page: Key, target: Option<Id>) -> impl Operation<T>
322where
323 Key: 'static + Eq + Hash + Clone + Send,
324{
325 struct Push<Key> {
326 target: Option<Id>,
327 page: Option<Key>,
328 }
329
330 impl<T, Key> Operation<T> for Push<Key>
331 where
332 Key: 'static + Eq + Hash + Clone + Send,
333 {
334 fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation<T>)) {
335 operate(self)
336 }
337
338 fn custom(&mut self, id: Option<&Id>, _bounds: Rectangle, state: &mut dyn std::any::Any) {
339 if let Some(value) = state.downcast_mut::<widgets::stack_navigator::State<Key>>() {
340 value.request_update();
341
342 if id.is_some_and(|id| self.target.as_ref().is_some_and(|target| target != id)) {
343 return;
344 }
345
346 value.push(self.page.take().unwrap());
347 }
348 }
349 }
350
351 Push {
352 target,
353 page: Some(page),
354 }
355}
356
357pub fn navigate<T, P>(page: P) -> Task<T>
358where
359 P: 'static + Eq + Hash + Clone + Send,
360 T: 'static + Send,
361{
362 operate(navigate_op::<T, P>(page, None))
363}
364
365pub fn navigate_by_id<T, P>(page: P, target: Id) -> Task<T>
366where
367 P: 'static + Eq + Hash + Clone + Send,
368 T: 'static + Send,
369{
370 operate(navigate_op::<T, P>(page, Some(target)))
371}
372
373pub fn go_back<T, P>() -> Task<T>
374where
375 P: 'static + Eq + Hash + Clone + Send,
376 T: 'static + Send,
377{
378 operate(go_back_op::<T, P>(None))
379}
380
381pub fn go_back_by_id<T, P>(target: Id) -> Task<T>
382where
383 P: 'static + Eq + Hash + Clone + Send,
384 T: 'static + Send,
385{
386 operate(go_back_op::<T, P>(Some(target)))
387}
388
389pub fn clear_history<T, P>() -> Task<T>
390where
391 P: 'static + Eq + Hash + Clone + Send,
392 T: 'static + Send,
393{
394 operate(clear_history_op::<T, P>(None))
395}
396
397pub fn clear_history_by_id<T, P>(target: Id) -> Task<T>
398where
399 P: 'static + Eq + Hash + Clone + Send,
400 T: 'static + Send,
401{
402 operate(clear_history_op::<T, P>(Some(target)))
403}
404
405pub fn pop_history<T, P>() -> Task<T>
406where
407 P: 'static + Eq + Hash + Clone + Send,
408 T: 'static + Send,
409{
410 operate(pop_history_op::<T, P>(None))
411}
412
413pub fn pop_history_by_id<T, P>(target: Id) -> Task<T>
414where
415 P: 'static + Eq + Hash + Clone + Send,
416 T: 'static + Send,
417{
418 operate(pop_history_op::<T, P>(Some(target)))
419}
420
421#[cfg(feature = "drawer")]
422pub fn open_drawer<T, P>() -> Task<T>
423where
424 P: 'static + Eq + Hash + Clone + Send,
425 T: 'static + Send,
426{
427 operate(open_drawer_op::<T, P>(None))
428}
429
430#[cfg(feature = "drawer")]
431pub fn open_drawer_by_id<T, P>(target: Id) -> Task<T>
432where
433 P: 'static + Eq + Hash + Clone + Send,
434 T: 'static + Send,
435{
436 operate(open_drawer_op::<T, P>(Some(target)))
437}
438
439#[cfg(feature = "drawer")]
440pub fn close_drawer<T, P>() -> Task<T>
441where
442 P: 'static + Eq + Hash + Clone + Send,
443 T: 'static + Send,
444{
445 operate(close_drawer_op::<T, P>(None))
446}
447
448#[cfg(feature = "drawer")]
449pub fn close_drawer_by_id<T, P>(target: Id) -> Task<T>
450where
451 P: 'static + Eq + Hash + Clone + Send,
452 T: 'static + Send,
453{
454 operate(close_drawer_op::<T, P>(Some(target)))
455}
456
457#[cfg(feature = "stack")]
458pub fn push<T, P>(page: P) -> Task<T>
459where
460 P: 'static + Eq + Hash + Clone + Send,
461 T: 'static + Send,
462{
463 operate(push_op::<T, P>(page, None))
464}
465
466#[cfg(feature = "stack")]
467pub fn push_by_id<T, P>(page: P, target: Id) -> Task<T>
468where
469 P: 'static + Eq + Hash + Clone + Send,
470 T: 'static + Send,
471{
472 operate(push_op::<T, P>(page, Some(target)))
473}