Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/core/mod_test.rs
StratoSDK / crates / strato-ui-core / src / core / mod_test.rs
1use std::{
2 cell::RefCell,
3 pin::Pin,
4 rc::Rc,
5 sync::atomic::AtomicBool,
6 sync::Arc,
7 task::{Context, Poll},
8};
9 
10use crate::r#async::Timer;
11use anyhow::Result;
12use futures_util::{stream, Stream};
13use parking_lot::Mutex;
14 
15use super::*;
16use crate::{
17 elements::*,
18 keymap::{macros::*, Keystroke},
19};
20 
21#[path = "transfer_view_tests.rs"]
22mod transfer_view_tests;
23 
24#[test]
25fn test_subscribe_and_emit_from_model() {
26 #[derive(Default)]
27 struct Model {
28 events: Vec<usize>,
29 }
30 
31 impl Entity for Model {
32 type Event = usize;
33 }
34 
35 App::test((), |mut app| async move {
36 let app = &mut app;
37 
38 let handle_1 = app.add_model(|_| Model::default());
39 let handle_2 = app.add_model(|_| Model::default());
40 let handle_2b = handle_2.clone();
41 
42 handle_1.update(app, |_, c| {
43 c.subscribe_to_model(&handle_2, move |model: &mut Model, event, c| {
44 model.events.push(*event);
45 
46 c.subscribe_to_model(&handle_2b, |model, event, _| {
47 model.events.push(*event * 2);
48 });
49 });
50 });
51 
52 handle_2.update(app, |_, c| c.emit(7));
53 handle_1.read(app, |model, _| assert_eq!(model.events, vec![7]));
54 
55 handle_2.update(app, |_, c| c.emit(5));
56 handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5]));
57 });
58}
59 
60#[test]
61fn test_observe_and_notify_from_model() {
62 #[derive(Default)]
63 struct Model {
64 count: usize,
65 events: Vec<usize>,
66 }
67 
68 impl Entity for Model {
69 type Event = ();
70 }
71 
72 App::test((), |mut app| async move {
73 let app = &mut app;
74 let handle_1 = app.add_model(|_| Model::default());
75 let handle_2 = app.add_model(|_| Model::default());
76 let handle_2b = handle_2.clone();
77 
78 handle_1.update(app, |_, c| {
79 c.observe(&handle_2, move |model, observed, c| {
80 model.events.push(observed.as_ref(c).count);
81 c.observe(&handle_2b, |model, observed, c| {
82 model.events.push(observed.as_ref(c).count * 2);
83 });
84 });
85 });
86 
87 handle_2.update(app, |model, c| {
88 model.count = 7;
89 c.notify()
90 });
91 handle_1.read(app, |model, _| assert_eq!(model.events, vec![7]));
92 
93 handle_2.update(app, |model, c| {
94 model.count = 5;
95 c.notify()
96 });
97 handle_1.read(app, |model, _| assert_eq!(model.events, vec![7, 10, 5]))
98 })
99}
100 
101#[test]
102fn test_observe_model_from_app() {
103 #[derive(Default)]
104 struct Model {
105 count: usize,
106 }
107 
108 impl Entity for Model {
109 type Event = ();
110 }
111 
112 App::test((), |mut app| async move {
113 let app = &mut app;
114 let handle_1 = app.add_model(|_| Model::default());
115 let handle_2 = app.add_model(|_| Model::default());
116 
117 let handle_1_clone = handle_1.clone();
118 app.update(|ctx| {
119 ctx.observe_model(&handle_2, move |model_2, ctx| {
120 let model_2_count = model_2.as_ref(ctx).count;
121 handle_1_clone.update(ctx, |model_1, c| {
122 // Set the count of model 1 to that of model 2.
123 model_1.count = model_2_count;
124 c.notify();
125 });
126 });
127 });
128 
129 handle_2.update(app, |model_2, c| {
130 model_2.count = 7;
131 c.notify()
132 });
133 
134 // Model 1's count should match that of model 2.
135 handle_1.read(app, |model_1, _| assert_eq!(model_1.count, 7));
136 })
137}
138 
139#[test]
140fn test_subscribe_to_model_from_app() {
141 #[derive(Default)]
142 struct Model {
143 val: usize,
144 }
145 
146 impl Entity for Model {
147 type Event = usize;
148 }
149 
150 App::test((), |mut app| async move {
151 let app: &mut App = &mut app;
152 let model = app.add_model(|_| Model::default());
153 
154 app.update(|ctx| {
155 ctx.subscribe_to_model(&model, move |model, event, ctx| {
156 model.update(ctx, |model, _ctx| {
157 model.val = *event;
158 })
159 });
160 });
161 
162 app.update(|ctx| {
163 model.update(ctx, |_view, ctx| {
164 ctx.emit(42);
165 })
166 });
167 
168 model.read(app, |model, _| assert_eq!(model.val, 42));
169 })
170}
171 
172#[test]
173fn test_subscribe_to_view_from_app() {
174 #[derive(Default)]
175 struct View {
176 val: usize,
177 }
178 
179 impl Entity for View {
180 type Event = usize;
181 }
182 
183 impl super::View for View {
184 fn render(&self, _: &AppContext) -> Box<dyn Element> {
185 Empty::new().finish()
186 }
187 
188 fn ui_name() -> &'static str {
189 "View"
190 }
191 }
192 
193 impl TypedActionView for View {
194 type Action = ();
195 }
196 
197 App::test((), |mut app| async move {
198 let app: &mut App = &mut app;
199 let (_, view) = app.add_window(WindowStyle::NotStealFocus, |_ctx| View::default());
200 
201 app.update(|ctx| {
202 ctx.subscribe_to_view(&view, move |view, event, ctx| {
203 view.update(ctx, |view, _ctx| {
204 view.val = *event;
205 })
206 });
207 });
208 
209 app.update(|ctx| {
210 view.update(ctx, |_view, ctx| {
211 ctx.emit(42);
212 })
213 });
214 
215 view.read(app, |view, _| assert_eq!(view.val, 42));
216 })
217}
218 
219#[test]
220fn test_subscribe_to_view_from_model() {
221 #[derive(Default)]
222 struct Model {
223 val: usize,
224 }
225 
226 impl Entity for Model {
227 type Event = ();
228 }
229 
230 #[derive(Default)]
231 struct View;
232 
233 impl Entity for View {
234 type Event = usize;
235 }
236 
237 impl super::View for View {
238 fn render(&self, _: &AppContext) -> Box<dyn Element> {
239 Empty::new().finish()
240 }
241 
242 fn ui_name() -> &'static str {
243 "View"
244 }
245 }
246 
247 impl TypedActionView for View {
248 type Action = ();
249 }
250 
251 App::test((), |mut app| async move {
252 let app = &mut app;
253 
254 let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |_ctx| View);
255 
256 let model_handle = app.add_model(|ctx| {
257 let model = Model::default();
258 ctx.subscribe_to_view(&view_handle, |model: &mut Model, event, _ctx| {
259 model.val = *event;
260 });
261 model
262 });
263 
264 app.update(|ctx| {
265 view_handle.update(ctx, |_view, ctx| {
266 ctx.emit(42);
267 })
268 });
269 
270 // The model value should match the emitted value if the subscription was successful.
271 model_handle.read(app, |model_1, _| assert_eq!(model_1.val, 42));
272 })
273}
274 
275#[test]
276fn test_spawn_from_model() {
277 #[derive(Default)]
278 struct Model {
279 count: usize,
280 }
281 
282 impl Entity for Model {
283 type Event = ();
284 }
285 
286 App::test((), |mut app| async move {
287 let handle = app.add_model(|_| Model::default());
288 handle
289 .update(&mut app, |_, c| {
290 c.spawn_local(async { 7 }, |model, output, _| {
291 model.count = output;
292 })
293 })
294 .await;
295 handle.read(&app, |model, _| assert_eq!(model.count, 7));
296 
297 let (tx, rx) = futures::channel::oneshot::channel();
298 handle.update(&mut app, |_, c| {
299 c.spawn(async { 14 }, move |model, output, _| {
300 model.count = output;
301 tx.send(()).unwrap();
302 })
303 });
304 
305 rx.await.unwrap();
306 handle.read(&app, |model, _| assert_eq!(model.count, 14));
307 });
308}
309 
310#[ignore]
311#[test]
312fn test_spawn_abortable_from_model() {
313 #[derive(Debug, Default, PartialEq)]
314 enum SpawnedOutcome {
315 #[default]
316 NotCompleted,
317 Aborted,
318 Resolved {
319 value: usize,
320 },
321 }
322 
323 #[derive(Default)]
324 struct Model {
325 spawned_outcome: SpawnedOutcome,
326 }
327 
328 impl Entity for Model {
329 type Event = ();
330 }
331 
332 App::test((), |mut app| async move {
333 let handle = app.add_model(|_| Model::default());
334 
335 let (tx, rx) = futures::channel::oneshot::channel();
336 handle.update(&mut app, |_, c| {
337 c.spawn_abortable(
338 async { 14 },
339 move |model, _, _| {
340 model.spawned_outcome = SpawnedOutcome::Resolved { value: 14 };
341 tx.send(()).unwrap();
342 },
343 |_, _| {},
344 )
345 });
346 rx.await.unwrap();
347 
348 // The future should be successfully resolved since we never called abort.
349 handle.read(&app, |model, _| {
350 assert_eq!(
351 model.spawned_outcome,
352 SpawnedOutcome::Resolved { value: 14 }
353 )
354 });
355 
356 let (tx, rx) = futures::channel::oneshot::channel();
357 handle.update(&mut app, |_, c| {
358 let spawned_future = c.spawn_abortable(
359 async { 14 },
360 move |_, _, _| {},
361 move |model, _| {
362 model.spawned_outcome = SpawnedOutcome::Aborted;
363 tx.send(()).unwrap();
364 },
365 );
366 
367 spawned_future.abort();
368 });
369 rx.await.unwrap();
370 
371 // The future should be successfully resolved since we never called abort.
372 handle.read(&app, |model, _| {
373 assert_eq!(model.spawned_outcome, SpawnedOutcome::Aborted)
374 });
375 });
376}
377 
378#[test]
379fn test_spawn_stream_local_from_model() {
380 #[derive(Default)]
381 struct Model {
382 events: Vec<Option<usize>>,
383 }
384 
385 impl Entity for Model {
386 type Event = ();
387 }
388 
389 App::test((), |mut app| async move {
390 let handle = app.add_model(|_| Model::default());
391 handle
392 .update(&mut app, |_, c| {
393 c.spawn_stream_local(
394 stream::iter(vec![1, 2, 3]),
395 |model, output, _| {
396 model.events.push(Some(output));
397 },
398 |model, _| {
399 model.events.push(None);
400 },
401 )
402 .into_future()
403 })
404 .await;
405 
406 handle.read(&app, |model, _| {
407 assert_eq!(model.events, [Some(1), Some(2), Some(3), None])
408 });
409 })
410}
411 
412#[test]
413fn test_global_action_from_model() {
414 struct Model;
415 
416 impl Entity for Model {
417 type Event = ();
418 }
419 
420 struct Argument(String);
421 
422 App::test((), |mut app| async move {
423 let handled = Rc::new(AtomicBool::new(false));
424 let handled_writer = handled.clone();
425 app.add_global_action("global_action", move |arg: &Argument, _| {
426 handled_writer.store(true, Ordering::SeqCst);
427 assert_eq!(arg.0, "global_argument");
428 });
429 
430 let model_handle = app.add_model(|_| Model);
431 
432 model_handle.update(&mut app, |_, ctx| {
433 ctx.dispatch_global_action("global_action", Argument("global_argument".into()));
434 });
435 
436 assert!(handled.load(Ordering::SeqCst));
437 })
438}
439 
440#[test]
441fn test_view_handles() {
442 struct View {
443 other: Option<ViewHandle<View>>,
444 events: Vec<String>,
445 }
446 
447 impl Entity for View {
448 type Event = usize;
449 }
450 
451 impl super::View for View {
452 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
453 Empty::new().finish()
454 }
455 
456 fn ui_name() -> &'static str {
457 "View"
458 }
459 }
460 
461 impl TypedActionView for View {
462 type Action = ();
463 }
464 
465 impl View {
466 fn new(other: Option<ViewHandle<View>>, ctx: &mut ViewContext<Self>) -> Self {
467 if let Some(other) = other.as_ref() {
468 ctx.subscribe_to_view(other, |me, _, event, _| {
469 me.events.push(format!("observed event {event}"));
470 });
471 }
472 Self {
473 other,
474 events: Vec::new(),
475 }
476 }
477 }
478 
479 App::test((), |mut app| async move {
480 let app = &mut app;
481 
482 let (window_id, _) = app.add_window(WindowStyle::NotStealFocus, |ctx| View::new(None, ctx));
483 let handle_1 = app.add_view(window_id, |ctx| View::new(None, ctx));
484 let handle_2 = app.add_view(window_id, |ctx| View::new(Some(handle_1.clone()), ctx));
485 app.read(|ctx| {
486 assert_eq!(ctx.windows[&window_id].views.len(), 3);
487 });
488 
489 handle_1.update(app, |view, ctx| {
490 view.events.push("updated".into());
491 ctx.emit(1);
492 ctx.emit(2);
493 });
494 handle_1.read(app, |view, _| {
495 assert_eq!(view.events, vec!["updated".to_string()]);
496 });
497 handle_2.read(app, |view, _| {
498 assert_eq!(
499 view.events,
500 vec![
501 "observed event 1".to_string(),
502 "observed event 2".to_string(),
503 ]
504 );
505 });
506 
507 handle_2.update(app, |view, _| {
508 drop(handle_1);
509 view.other.take();
510 });
511 
512 app.update(|ctx| {
513 assert_eq!(ctx.windows[&window_id].views.len(), 2);
514 assert!(ctx.subscriptions.is_empty());
515 assert!(ctx.observations.is_empty());
516 });
517 })
518}
519 
520#[test]
521fn test_view_handles_from_typed_action_views() {
522 struct View {
523 other: Option<ViewHandle<View>>,
524 events: Vec<String>,
525 }
526 
527 impl Entity for View {
528 type Event = usize;
529 }
530 
531 impl super::View for View {
532 fn render(&self, _: &AppContext) -> Box<dyn Element> {
533 Empty::new().finish()
534 }
535 
536 fn ui_name() -> &'static str {
537 "View"
538 }
539 }
540 
541 impl TypedActionView for View {
542 type Action = ();
543 }
544 
545 impl View {
546 fn new(other: Option<ViewHandle<View>>, ctx: &mut ViewContext<Self>) -> Self {
547 if let Some(other) = other.as_ref() {
548 ctx.subscribe_to_view(other, |me, _, event, _| {
549 me.events.push(format!("observed event {event}"));
550 });
551 }
552 Self {
553 other,
554 events: Vec::new(),
555 }
556 }
557 }
558 
559 App::test((), |mut app| async move {
560 let (window_id, _) = app.add_window(WindowStyle::NotStealFocus, |ctx| View::new(None, ctx));
561 
562 // Using a scoped block here to test the reference counting of ViewHandles
563 // We return the parent handle so that it remains in-scope, but allow the child
564 // handle to be dropped
565 let parent_handle = {
566 let child = app.add_typed_action_view(window_id, |ctx| View::new(None, ctx));
567 let parent =
568 app.add_typed_action_view(window_id, |ctx| View::new(Some(child.clone()), ctx));
569 app.read(|ctx| {
570 assert_eq!(ctx.windows[&window_id].views.len(), 3);
571 });
572 
573 child.update(&mut app, |view, ctx| {
574 view.events.push("updated".into());
575 ctx.emit(1);
576 ctx.emit(2);
577 });
578 
579 child.read(&app, |view, _| {
580 assert_eq!(view.events, vec!["updated".to_owned()]);
581 });
582 parent.read(&app, |view, _| {
583 assert_eq!(
584 view.events,
585 vec!["observed event 1".to_owned(), "observed event 2".to_owned()]
586 );
587 });
588 
589 // Return the parent handle, allowing the child handle to go out of scope
590 parent
591 };
592 
593 // Remove the child handle from the parent, which removes it completely
594 parent_handle.update(&mut app, |view, _| {
595 view.other.take();
596 });
597 
598 app.update(|ctx| {
599 assert_eq!(ctx.windows[&window_id].views.len(), 2);
600 assert!(ctx.subscriptions.is_empty());
601 assert!(ctx.observations.is_empty());
602 });
603 });
604}
605 
606#[test]
607fn test_global_action_from_view() {
608 struct View;
609 
610 impl Entity for View {
611 type Event = ();
612 }
613 
614 impl super::View for View {
615 fn render(&self, _: &AppContext) -> Box<dyn Element> {
616 Empty::new().finish()
617 }
618 
619 fn ui_name() -> &'static str {
620 "View"
621 }
622 }
623 
624 impl TypedActionView for View {
625 type Action = ();
626 }
627 
628 struct Argument(String);
629 
630 App::test((), |mut app| async move {
631 let handled = Rc::new(AtomicBool::new(false));
632 let handled_writer = handled.clone();
633 app.add_global_action("global_action", move |arg: &Argument, _| {
634 handled_writer.store(true, Ordering::SeqCst);
635 assert_eq!(arg.0, "global_argument");
636 });
637 
638 let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |_| View);
639 view_handle.update(&mut app, |_, ctx| {
640 ctx.dispatch_global_action("global_action", Argument("global_argument".into()));
641 });
642 
643 assert!(handled.load(Ordering::SeqCst));
644 });
645}
646 
647#[test]
648fn test_subscribe_and_emit_from_view() {
649 #[derive(Default)]
650 struct View {
651 events: Vec<usize>,
652 }
653 
654 impl Entity for View {
655 type Event = usize;
656 }
657 
658 impl super::View for View {
659 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
660 Empty::new().finish()
661 }
662 
663 fn ui_name() -> &'static str {
664 "View"
665 }
666 }
667 
668 impl TypedActionView for View {
669 type Action = ();
670 }
671 
672 struct Model;
673 
674 impl Entity for Model {
675 type Event = usize;
676 }
677 
678 App::test((), |mut app| async move {
679 let app = &mut app;
680 
681 let (window_id, handle_1) = app.add_window(WindowStyle::NotStealFocus, |_| View::default());
682 let handle_2 = app.add_view(window_id, |_| View::default());
683 let handle_2b = handle_2.clone();
684 let handle_3 = app.add_model(|_| Model);
685 
686 handle_1.update(app, |_, c| {
687 c.subscribe_to_view(&handle_2, move |me, _, event, c| {
688 me.events.push(*event);
689 
690 c.subscribe_to_view(&handle_2b, |me, _, event, _| {
691 me.events.push(*event * 2);
692 });
693 });
694 
695 c.subscribe_to_model(&handle_3, |me, _, event, _| {
696 me.events.push(*event);
697 })
698 });
699 
700 handle_2.update(app, |_, c| c.emit(7));
701 handle_1.read(app, |view, _| assert_eq!(view.events, vec![7]));
702 
703 handle_2.update(app, |_, c| c.emit(5));
704 handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5]));
705 
706 handle_3.update(app, |_, c| c.emit(9));
707 handle_1.read(app, |view, _| assert_eq!(view.events, vec![7, 10, 5, 9]));
708 })
709}
710 
711#[test]
712fn test_dropping_subscribers() {
713 struct View;
714 
715 impl Entity for View {
716 type Event = ();
717 }
718 
719 impl super::View for View {
720 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
721 Empty::new().finish()
722 }
723 
724 fn ui_name() -> &'static str {
725 "View"
726 }
727 }
728 
729 impl TypedActionView for View {
730 type Action = ();
731 }
732 
733 struct Model;
734 
735 impl Entity for Model {
736 type Event = ();
737 }
738 
739 App::test((), |mut app| async move {
740 let app = &mut app;
741 
742 let (window_id, _) = app.add_window(WindowStyle::NotStealFocus, |_| View);
743 let observing_view = app.add_view(window_id, |_| View);
744 let emitting_view = app.add_view(window_id, |_| View);
745 let observing_model = app.add_model(|_| Model);
746 let observed_model = app.add_model(|_| Model);
747 
748 observing_view.update(app, |_, ctx| {
749 ctx.subscribe_to_view(&emitting_view, |_, _, _, _| {});
750 ctx.subscribe_to_model(&observed_model, |_, _, _, _| {});
751 });
752 observing_model.update(app, |_, ctx| {
753 ctx.subscribe_to_model(&observed_model, |_, _, _| {});
754 });
755 
756 app.update(|_| {
757 drop(observing_view);
758 drop(observing_model);
759 });
760 
761 emitting_view.update(app, |_, ctx| ctx.emit(()));
762 observed_model.update(app, |_, ctx| ctx.emit(()));
763 })
764}
765 
766#[test]
767fn test_observe_and_notify_from_view() {
768 #[derive(Default)]
769 struct View {
770 events: Vec<usize>,
771 }
772 
773 impl Entity for View {
774 type Event = usize;
775 }
776 
777 impl super::View for View {
778 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
779 Empty::new().finish()
780 }
781 
782 fn ui_name() -> &'static str {
783 "View"
784 }
785 }
786 
787 impl TypedActionView for View {
788 type Action = ();
789 }
790 
791 #[derive(Default)]
792 struct Model {
793 count: usize,
794 }
795 
796 impl Entity for Model {
797 type Event = ();
798 }
799 
800 App::test((), |mut app| async move {
801 let app = &mut app;
802 let (_, view) = app.add_window(WindowStyle::NotStealFocus, |_| View::default());
803 let model = app.add_model(|_| Model::default());
804 
805 view.update(app, |_, c| {
806 c.observe(&model, |me, observed, c| {
807 me.events.push(observed.as_ref(c).count)
808 });
809 });
810 
811 model.update(app, |model, c| {
812 model.count = 11;
813 c.notify();
814 });
815 view.read(app, |view, _| assert_eq!(view.events, vec![11]));
816 })
817}
818 
819#[test]
820fn test_dropping_observers() {
821 struct View;
822 
823 impl Entity for View {
824 type Event = ();
825 }
826 
827 impl super::View for View {
828 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
829 Empty::new().finish()
830 }
831 
832 fn ui_name() -> &'static str {
833 "View"
834 }
835 }
836 
837 impl TypedActionView for View {
838 type Action = ();
839 }
840 
841 struct Model;
842 
843 impl Entity for Model {
844 type Event = ();
845 }
846 
847 App::test((), |mut app| async move {
848 let app = &mut app;
849 
850 let (window_id, _) = app.add_window(WindowStyle::NotStealFocus, |_| View);
851 let observing_view = app.add_view(window_id, |_| View);
852 let observing_model = app.add_model(|_| Model);
853 let observed_model = app.add_model(|_| Model);
854 
855 observing_view.update(app, |_, ctx| {
856 ctx.observe(&observed_model, |_, _, _| {});
857 });
858 observing_model.update(app, |_, ctx| {
859 ctx.observe(&observed_model, |_, _, _| {});
860 });
861 
862 app.update(|_| {
863 drop(observing_view);
864 drop(observing_model);
865 });
866 
867 observed_model.update(app, |_, ctx| ctx.notify());
868 })
869}
870 
871#[test]
872fn test_focus() {
873 #[derive(Default)]
874 struct View {
875 events: Vec<String>,
876 }
877 
878 impl Entity for View {
879 type Event = String;
880 }
881 
882 impl super::View for View {
883 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
884 Empty::new().finish()
885 }
886 
887 fn ui_name() -> &'static str {
888 "View"
889 }
890 
891 fn on_focus(&mut self, focus_ctx: &FocusContext, ctx: &mut ViewContext<Self>) {
892 if focus_ctx.is_self_focused() {
893 self.events.push("self focused".into());
894 ctx.emit("focused".into());
895 }
896 }
897 
898 fn on_blur(&mut self, blur_ctx: &BlurContext, ctx: &mut ViewContext<Self>) {
899 if blur_ctx.is_self_blurred() {
900 self.events.push("self blurred".into());
901 ctx.emit("blurred".into());
902 }
903 }
904 }
905 
906 impl TypedActionView for View {
907 type Action = ();
908 }
909 
910 App::test((), |mut app| async move {
911 let app = &mut app;
912 let (window_id, view_1) = app.add_window(WindowStyle::NotStealFocus, |_| View::default());
913 let view_2 = app.add_view(window_id, |_| View::default());
914 
915 view_1.update(app, |_, ctx| {
916 ctx.subscribe_to_view(&view_2, |view_1, _, event, _| {
917 view_1.events.push(format!("view 2 {event}"));
918 });
919 ctx.focus(&view_2);
920 });
921 
922 view_1.update(app, |_, ctx| {
923 ctx.focus(&view_1);
924 });
925 
926 view_1.read(app, |view_1, _| {
927 assert_eq!(
928 view_1.events,
929 [
930 "self focused".to_string(),
931 "self blurred".to_string(),
932 "view 2 focused".to_string(),
933 "self focused".to_string(),
934 "view 2 blurred".to_string(),
935 ],
936 );
937 });
938 
939 view_1.update(app, |view_1, ctx| {
940 view_1.events.clear();
941 ctx.focus(&view_2);
942 });
943 
944 // Return focus to root view if focused view is removed
945 view_1.update(app, |_, _| {
946 drop(view_2);
947 });
948 
949 app.read(|ctx| assert_eq!(ctx.focused_view_id(window_id), Some(view_1.id())));
950 
951 view_1.read(app, |view_1, _| {
952 assert_eq!(
953 view_1.events,
954 [
955 "self blurred".to_string(),
956 "view 2 focused".to_string(),
957 "self focused".to_string(),
958 ],
959 );
960 });
961 })
962}
963 
964struct NestedView {
965 children: Vec<ViewHandle<NestedView>>,
966 name: String,
967 events: Rc<RefCell<Vec<String>>>,
968 hide_children: bool,
969}
970 
971impl NestedView {
972 fn new(
973 name: String,
974 children: Vec<ViewHandle<NestedView>>,
975 events: Rc<RefCell<Vec<String>>>,
976 ) -> Self {
977 Self {
978 name,
979 events,
980 children,
981 hide_children: false,
982 }
983 }
984 
985 fn set_children(&mut self, children: Vec<ViewHandle<NestedView>>, ctx: &mut ViewContext<Self>) {
986 self.children = children;
987 ctx.notify();
988 }
989 
990 fn with_hide_children(mut self, hide_children: bool) -> Self {
991 self.hide_children = hide_children;
992 self
993 }
994}
995 
996impl Entity for NestedView {
997 type Event = String;
998}
999 
1000impl super::View for NestedView {
1001 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1002 if self.hide_children {
1003 Empty::new().finish()
1004 } else {
1005 Flex::column()
1006 .with_children(
1007 self.children
1008 .iter()
1009 .map(|child| ChildView::new(child).finish()),
1010 )
1011 .finish()
1012 }
1013 }
1014 
1015 fn ui_name() -> &'static str {
1016 "View"
1017 }
1018 
1019 fn on_focus(&mut self, focus_ctx: &FocusContext, _ctx: &mut ViewContext<Self>) {
1020 if focus_ctx.is_self_focused() {
1021 self.events
1022 .borrow_mut()
1023 .push(format!("{} self focused", self.name));
1024 } else {
1025 self.events
1026 .borrow_mut()
1027 .push(format!("{} child focused", self.name));
1028 }
1029 }
1030 
1031 fn on_blur(&mut self, blur_ctx: &BlurContext, ctx: &mut ViewContext<Self>) {
1032 if blur_ctx.is_self_blurred() {
1033 self.events
1034 .borrow_mut()
1035 .push(format!("{} self blurred", self.name));
1036 ctx.emit("blurred".into());
1037 } else {
1038 self.events
1039 .borrow_mut()
1040 .push(format!("{} child blurred", self.name));
1041 }
1042 }
1043}
1044 
1045impl TypedActionView for NestedView {
1046 type Action = ();
1047}
1048 
1049#[test]
1050fn test_nested_focus() {
1051 App::test((), |mut app| async move {
1052 // Test that focusing child views call the focus callbacks of the ancestor views.
1053 // View heirarchy
1054 // View 1
1055 // - View 2
1056 // - View 3
1057 // - View 4
1058 
1059 let events: Rc<RefCell<Vec<String>>> = Rc::new(RefCell::new(vec![]));
1060 let app = &mut app;
1061 let (window_id, view_1) = app.add_window(WindowStyle::NotStealFocus, |_| {
1062 NestedView::new("View 1".to_string(), vec![], events.clone())
1063 });
1064 let view_2 = app.add_view(window_id, |_| {
1065 NestedView::new("View 2".to_string(), vec![], events.clone())
1066 });
1067 let view_4 = app.add_view(window_id, |_| {
1068 NestedView::new("View 4".to_string(), vec![], events.clone())
1069 });
1070 let view_3 = app.add_view(window_id, |_| {
1071 NestedView::new("View 3".to_string(), vec![view_4.clone()], events.clone())
1072 });
1073 
1074 assert_eq!(events.take(), ["View 1 self focused".to_string(),],);
1075 
1076 view_1.update(app, |view_1, ctx| {
1077 view_1.set_children(vec![view_2.clone(), view_3.clone()], ctx);
1078 });
1079 
1080 view_1.update(app, |_view, ctx| {
1081 ctx.focus(&view_2);
1082 });
1083 
1084 view_1.update(app, |_, ctx| {
1085 ctx.focus(&view_1);
1086 });
1087 
1088 assert_eq!(
1089 events.take(),
1090 [
1091 "View 1 self blurred".to_string(),
1092 "View 2 self focused".to_string(),
1093 "View 1 child focused".to_string(),
1094 "View 2 self blurred".to_string(),
1095 "View 1 child blurred".to_string(),
1096 "View 1 self focused".to_string(),
1097 ],
1098 );
1099 
1100 view_4.update(app, |_, ctx| {
1101 ctx.focus(&view_4);
1102 });
1103 
1104 assert_eq!(
1105 events.take(),
1106 [
1107 "View 1 self blurred".to_string(),
1108 "View 4 self focused".to_string(),
1109 "View 3 child focused".to_string(),
1110 "View 1 child focused".to_string(),
1111 ],
1112 );
1113 });
1114}
1115 
1116#[test]
1117fn test_nested_focus_with_unrendered_view() {
1118 // Test that unrendered views that are created in the context of another view work.
1119 App::test((), |mut app| async move {
1120 // Test that focusing child views call the focus callbacks of the ancestor views.
1121 // In this case, the child view is not rendered, but because it was created by
1122 // ViewContext.add_typed_action_view, the parent view of it should still receive
1123 // focus and blur events.
1124 // View heirarchy
1125 // View 1
1126 // - View 2
1127 let events: Rc<RefCell<Vec<String>>> = Rc::new(RefCell::new(vec![]));
1128 let app = &mut app;
1129 let (_window_id, view_1) = app.add_window(WindowStyle::NotStealFocus, |ctx| {
1130 let view_2 = ctx.add_typed_action_view(|_ctx| {
1131 NestedView::new("View 2".to_string(), vec![], events.clone())
1132 });
1133 NestedView::new("View 1".to_string(), vec![view_2], events.clone())
1134 .with_hide_children(true)
1135 });
1136 
1137 assert_eq!(events.take(), ["View 1 self focused".to_string(),],);
1138 
1139 view_1.update(app, |view, ctx| {
1140 ctx.focus(&view.children[0]);
1141 });
1142 
1143 view_1.update(app, |_, ctx| {
1144 ctx.focus(&view_1);
1145 });
1146 
1147 assert_eq!(
1148 events.take(),
1149 [
1150 "View 1 self blurred".to_string(),
1151 "View 2 self focused".to_string(),
1152 "View 1 child focused".to_string(),
1153 "View 2 self blurred".to_string(),
1154 "View 1 child blurred".to_string(),
1155 "View 1 self focused".to_string(),
1156 ],
1157 );
1158 })
1159}
1160 
1161#[test]
1162fn test_spawn_stream_local_from_view() {
1163 #[derive(Default)]
1164 struct View {
1165 events: Vec<Option<usize>>,
1166 }
1167 
1168 impl Entity for View {
1169 type Event = ();
1170 }
1171 
1172 impl super::View for View {
1173 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1174 Empty::new().finish()
1175 }
1176 
1177 fn ui_name() -> &'static str {
1178 "View"
1179 }
1180 }
1181 
1182 impl TypedActionView for View {
1183 type Action = ();
1184 }
1185 
1186 App::test((), |mut app| async move {
1187 let (_, handle) = app.add_window(WindowStyle::NotStealFocus, |_| View::default());
1188 handle
1189 .update(&mut app, |_, c| {
1190 c.spawn_stream_local(
1191 stream::iter(vec![1, 2, 3]),
1192 |me, output, _| {
1193 me.events.push(Some(output));
1194 },
1195 |me, _| {
1196 me.events.push(None);
1197 },
1198 )
1199 .into_future()
1200 })
1201 .await;
1202 
1203 handle.read(&app, |view, _| {
1204 assert_eq!(view.events, [Some(1), Some(2), Some(3), None])
1205 });
1206 });
1207}
1208 
1209#[test]
1210#[ignore]
1211fn test_spawn_stream_local_from_view_await_after_closing_window() {
1212 #[derive(Default)]
1213 struct View {}
1214 
1215 impl Entity for View {
1216 type Event = ();
1217 }
1218 
1219 impl super::View for View {
1220 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1221 Empty::new().finish()
1222 }
1223 
1224 fn ui_name() -> &'static str {
1225 "View"
1226 }
1227 }
1228 
1229 impl TypedActionView for View {
1230 type Action = ();
1231 }
1232 
1233 struct TestState {
1234 closed_window: bool,
1235 done: bool,
1236 }
1237 
1238 struct YieldOnceOnClosingWindow {
1239 state: Arc<Mutex<TestState>>,
1240 }
1241 
1242 impl YieldOnceOnClosingWindow {
1243 fn new(state: Arc<Mutex<TestState>>) -> Self {
1244 YieldOnceOnClosingWindow { state }
1245 }
1246 }
1247 
1248 impl Stream for YieldOnceOnClosingWindow {
1249 type Item = ();
1250 
1251 fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<()>> {
1252 let state = self.state.lock();
1253 match *state {
1254 TestState {
1255 closed_window: true,
1256 done: false,
1257 } => Poll::Ready(Some(())),
1258 TestState { done: true, .. } => Poll::Ready(None),
1259 _ => Poll::Pending,
1260 }
1261 }
1262 }
1263 
1264 App::test((), |mut app| async move {
1265 let state = Arc::new(Mutex::new(TestState {
1266 closed_window: false,
1267 done: false,
1268 }));
1269 let (_, handle) = app.add_window(WindowStyle::NotStealFocus, |_| View::default());
1270 handle
1271 .update(&mut app, |_, c| {
1272 let ret = c
1273 .spawn_stream_local(
1274 YieldOnceOnClosingWindow::new(state.clone()),
1275 |_, _, _| {},
1276 |_, _| {},
1277 )
1278 .into_future();
1279 c.close_window();
1280 {
1281 let mut state = state.lock();
1282 state.closed_window = true;
1283 }
1284 ret
1285 })
1286 .await;
1287 });
1288}
1289 
1290#[test]
1291fn test_dispatch_action() {
1292 struct ViewA {
1293 id: usize,
1294 }
1295 
1296 impl Entity for ViewA {
1297 type Event = ();
1298 }
1299 
1300 impl View for ViewA {
1301 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1302 Empty::new().finish()
1303 }
1304 
1305 fn ui_name() -> &'static str {
1306 "View"
1307 }
1308 }
1309 
1310 impl TypedActionView for ViewA {
1311 type Action = ();
1312 }
1313 
1314 struct ViewB {
1315 id: usize,
1316 }
1317 
1318 impl Entity for ViewB {
1319 type Event = ();
1320 }
1321 
1322 impl View for ViewB {
1323 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1324 Empty::new().finish()
1325 }
1326 
1327 fn ui_name() -> &'static str {
1328 "View"
1329 }
1330 }
1331 
1332 struct ActionArg {
1333 foo: String,
1334 }
1335 
1336 App::test((), |mut app| async move {
1337 let actions = Rc::new(RefCell::new(Vec::new()));
1338 
1339 let actions_clone = actions.clone();
1340 app.add_global_action("action", move |_: &ActionArg, _: &mut AppContext| {
1341 actions_clone.borrow_mut().push("global a".to_string());
1342 });
1343 
1344 let actions_clone = actions.clone();
1345 app.add_global_action("action", move |_: &ActionArg, _: &mut AppContext| {
1346 actions_clone.borrow_mut().push("global b".to_string());
1347 });
1348 
1349 let actions_clone = actions.clone();
1350 app.add_action("action", move |view: &mut ViewA, arg: &ActionArg, _ctx| {
1351 assert_eq!(arg.foo, "bar");
1352 actions_clone.borrow_mut().push(format!("{} a", view.id));
1353 false
1354 });
1355 
1356 let actions_clone = actions.clone();
1357 app.add_action("action", move |view: &mut ViewA, _: &ActionArg, _ctx| {
1358 actions_clone.borrow_mut().push(format!("{} b", view.id));
1359 view.id == 1
1360 });
1361 
1362 let actions_clone = actions.clone();
1363 app.add_action("action", move |view: &mut ViewB, _: &ActionArg, _ctx| {
1364 actions_clone.borrow_mut().push(format!("{} c", view.id));
1365 false
1366 });
1367 
1368 let actions_clone = actions.clone();
1369 app.add_action("action", move |view: &mut ViewB, _: &ActionArg, _ctx| {
1370 actions_clone.borrow_mut().push(format!("{} d", view.id));
1371 false
1372 });
1373 
1374 let (window_id, view_1) = app.add_window(WindowStyle::NotStealFocus, |_| ViewA { id: 1 });
1375 let view_2 = app.add_view(window_id, |_| ViewB { id: 2 });
1376 let view_3 = app.add_view(window_id, |_| ViewA { id: 3 });
1377 let view_4 = app.add_view(window_id, |_| ViewB { id: 4 });
1378 
1379 app.dispatch_action(
1380 window_id,
1381 &[view_1.id(), view_2.id(), view_3.id(), view_4.id()],
1382 "action",
1383 ActionArg { foo: "bar".into() },
1384 );
1385 
1386 assert_eq!(
1387 *actions.borrow(),
1388 vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "1 b", "1 a", "global b", "global a"]
1389 );
1390 
1391 // Remove view_1, which doesn't propagate the action.
1392 actions.borrow_mut().clear();
1393 app.dispatch_action(
1394 window_id,
1395 &[view_2.id(), view_3.id(), view_4.id()],
1396 "action",
1397 ActionArg { foo: "bar".into() },
1398 );
1399 
1400 assert_eq!(
1401 *actions.borrow(),
1402 vec!["4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "global b", "global a"]
1403 );
1404 
1405 actions.borrow_mut().clear();
1406 let actions_clone = actions.clone();
1407 app.add_action("action", move |view: &mut ViewB, _: &ActionArg, _ctx| {
1408 actions_clone.borrow_mut().push(format!("{} f", view.id));
1409 true
1410 });
1411 
1412 app.dispatch_action(
1413 window_id,
1414 &[view_1.id(), view_2.id(), view_3.id(), view_4.id()],
1415 "action",
1416 ActionArg { foo: "bar".into() },
1417 );
1418 
1419 // Ensure the action is only fired on the bottom-most view.
1420 assert_eq!(
1421 *actions.borrow(),
1422 vec!["4 f", "4 d", "4 c", "global b", "global a"]
1423 );
1424 })
1425}
1426 
1427#[test]
1428fn test_dispatch_typed_action() {
1429 struct ViewA {
1430 handled: Vec<String>,
1431 }
1432 
1433 impl ViewA {
1434 fn new() -> Self {
1435 Self {
1436 handled: Vec::new(),
1437 }
1438 }
1439 }
1440 
1441 impl Entity for ViewA {
1442 type Event = ();
1443 }
1444 
1445 impl View for ViewA {
1446 fn render(&self, _: &AppContext) -> Box<dyn Element> {
1447 Empty::new().finish()
1448 }
1449 
1450 fn ui_name() -> &'static str {
1451 "ViewA"
1452 }
1453 }
1454 
1455 #[derive(Debug)]
1456 struct ViewActionA(String);
1457 
1458 impl TypedActionView for ViewA {
1459 type Action = ViewActionA;
1460 
1461 fn handle_action(&mut self, action: &ViewActionA, _: &mut ViewContext<Self>) {
1462 self.handled.push(format!("Handling action: {}", action.0));
1463 }
1464 }
1465 
1466 struct ViewB {
1467 handled: Vec<String>,
1468 }
1469 
1470 impl ViewB {
1471 fn new() -> Self {
1472 Self {
1473 handled: Vec::new(),
1474 }
1475 }
1476 }
1477 
1478 impl Entity for ViewB {
1479 type Event = ();
1480 }
1481 
1482 impl View for ViewB {
1483 fn render(&self, _: &AppContext) -> Box<dyn Element> {
1484 Empty::new().finish()
1485 }
1486 
1487 fn ui_name() -> &'static str {
1488 "ViewB"
1489 }
1490 }
1491 
1492 #[derive(Debug)]
1493 struct ViewActionB(usize);
1494 
1495 impl TypedActionView for ViewB {
1496 type Action = ViewActionB;
1497 
1498 fn handle_action(&mut self, action: &ViewActionB, _: &mut ViewContext<Self>) {
1499 self.handled.push(format!("Handling action: {}", action.0));
1500 }
1501 }
1502 
1503 App::test((), |mut app| async move {
1504 let (window_id, root_view) = app.add_window(WindowStyle::NotStealFocus, |_| ViewA::new());
1505 
1506 // First dispatch a typed action with no handlers and make sure we don't leave pending
1507 // flushes in the wrong state
1508 app.dispatch_typed_action(
1509 window_id,
1510 &[root_view.id()],
1511 &ViewActionA("Hello world!".into()),
1512 );
1513 
1514 app.update(|app| {
1515 // Pending fluses is 1, not 0 because we are in an update closure
1516 assert_eq!(1, app.pending_flushes());
1517 });
1518 
1519 let parent_view = app.add_typed_action_view(window_id, |_| ViewA::new());
1520 let child_view = app.add_typed_action_view(window_id, |_| ViewB::new());
1521 let grandchild_view = app.add_typed_action_view(window_id, |_| ViewA::new());
1522 
1523 // Dispatching `ViewActionA` should be handled by the lowest instance of ViewA
1524 app.dispatch_typed_action(
1525 window_id,
1526 &[
1527 root_view.id(),
1528 parent_view.id(),
1529 child_view.id(),
1530 grandchild_view.id(),
1531 ],
1532 &ViewActionA("Hello world!".into()),
1533 );
1534 
1535 // Only grandchild_view should have a record of handling the action
1536 grandchild_view.read(&app, |view, _| {
1537 assert_eq!(view.handled, ["Handling action: Hello world!"]);
1538 });
1539 child_view.read(&app, |view, _| {
1540 assert!(view.handled.is_empty());
1541 });
1542 parent_view.read(&app, |view, _| {
1543 assert!(view.handled.is_empty());
1544 });
1545 
1546 // Dispatching `ViewActionB` should be handled by the only instance of ViewB
1547 app.dispatch_typed_action(
1548 window_id,
1549 &[
1550 root_view.id(),
1551 parent_view.id(),
1552 child_view.id(),
1553 grandchild_view.id(),
1554 ],
1555 &ViewActionB(10),
1556 );
1557 
1558 child_view.read(&app, |view, _| {
1559 assert_eq!(view.handled, ["Handling action: 10"],);
1560 });
1561 
1562 // Dispatching `ViewActionA` without grandchild_view should be handled by parent_view
1563 app.dispatch_typed_action(
1564 window_id,
1565 &[root_view.id(), parent_view.id(), child_view.id()],
1566 &ViewActionA("Goodbye!".into()),
1567 );
1568 
1569 parent_view.read(&app, |view, _| {
1570 assert_eq!(view.handled, ["Handling action: Goodbye!"],);
1571 });
1572 });
1573}
1574 
1575#[test]
1576fn test_dispatch_close_window_action() {
1577 struct ViewA;
1578 
1579 impl Entity for ViewA {
1580 type Event = ();
1581 }
1582 
1583 impl View for ViewA {
1584 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1585 Empty::new().finish()
1586 }
1587 
1588 fn ui_name() -> &'static str {
1589 "View"
1590 }
1591 }
1592 
1593 impl TypedActionView for ViewA {
1594 type Action = ();
1595 }
1596 
1597 App::test((), |mut app| async move {
1598 app.add_action("close_window_action", move |_: &mut ViewA, _: &(), ctx| {
1599 ctx.close_window();
1600 true
1601 });
1602 
1603 let (window_id, view_1) = app.add_window(WindowStyle::NotStealFocus, |_| ViewA);
1604 app.dispatch_action(window_id, &[view_1.id()], "close_window_action", ());
1605 })
1606}
1607 
1608#[test]
1609fn test_dispatch_keystroke() -> Result<()> {
1610 struct View {
1611 id: usize,
1612 keymap_context: keymap::Context,
1613 handled_action: bool,
1614 }
1615 
1616 impl Entity for View {
1617 type Event = ();
1618 }
1619 
1620 impl super::View for View {
1621 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1622 Empty::new().finish()
1623 }
1624 
1625 fn ui_name() -> &'static str {
1626 "View"
1627 }
1628 
1629 fn keymap_context(&self, _: &AppContext) -> keymap::Context {
1630 self.keymap_context.clone()
1631 }
1632 }
1633 
1634 #[derive(Debug)]
1635 struct Action(String);
1636 
1637 impl super::TypedActionView for View {
1638 type Action = Action;
1639 
1640 fn handle_action(&mut self, action: &Action, _: &mut ViewContext<Self>) {
1641 self.handled_action = true;
1642 assert_eq!(self.id, 2);
1643 assert_eq!(action.0, "a");
1644 }
1645 }
1646 
1647 impl View {
1648 fn new(id: usize) -> Self {
1649 View {
1650 id,
1651 keymap_context: keymap::Context::default(),
1652 handled_action: false,
1653 }
1654 }
1655 }
1656 
1657 App::test((), |mut app| async move {
1658 let mut view_1 = View::new(1);
1659 let mut view_2 = View::new(2);
1660 let mut view_3 = View::new(3);
1661 view_1.keymap_context.set.insert("a");
1662 view_2.keymap_context.set.insert("b");
1663 view_3.keymap_context.set.insert("c");
1664 
1665 let (window_id, view_1) = app.add_window(WindowStyle::NotStealFocus, |_| view_1);
1666 let view_2 = app.add_typed_action_view(window_id, |_| view_2);
1667 let view_3 = app.add_typed_action_view(window_id, |_| view_3);
1668 
1669 // This keymap's only binding dispatches the action on View 2, because only that view
1670 // will have "b" in its context
1671 let binding = keymap::FixedBinding::new("a", Action("a".into()), id!("b"));
1672 app.update(|ctx| ctx.register_fixed_bindings(vec![binding]));
1673 
1674 app.dispatch_keystroke(
1675 window_id,
1676 &[view_1.id(), view_2.id(), view_3.id()],
1677 &Keystroke::parse("a")?,
1678 false,
1679 )?;
1680 
1681 let handled_action = view_2.read(&app, |view, _| view.handled_action);
1682 
1683 assert!(handled_action);
1684 Ok(())
1685 })
1686}
1687 
1688#[test]
1689fn test_dispatch_keystroke_typed_action() -> Result<()> {
1690 struct View {
1691 id: usize,
1692 keymap_context: keymap::Context,
1693 }
1694 
1695 impl Entity for View {
1696 type Event = ();
1697 }
1698 
1699 impl super::View for View {
1700 fn render(&self, _: &AppContext) -> Box<dyn Element> {
1701 Empty::new().finish()
1702 }
1703 
1704 fn ui_name() -> &'static str {
1705 "View"
1706 }
1707 
1708 fn keymap_context(&self, _: &AppContext) -> keymap::Context {
1709 self.keymap_context.clone()
1710 }
1711 }
1712 
1713 #[derive(Debug)]
1714 struct Action(String);
1715 
1716 impl TypedActionView for View {
1717 type Action = Action;
1718 
1719 fn handle_action(&mut self, action: &Action, _: &mut ViewContext<Self>) {
1720 assert_eq!(self.id, 2);
1721 assert_eq!(action.0, "a");
1722 }
1723 }
1724 
1725 impl View {
1726 fn new(id: usize, context: &'static str) -> Self {
1727 let mut instance = View {
1728 id,
1729 keymap_context: Default::default(),
1730 };
1731 
1732 instance.keymap_context.set.insert(context);
1733 
1734 instance
1735 }
1736 }
1737 
1738 App::test((), |mut app| async move {
1739 let (window_id, view_1) = app.add_window(WindowStyle::NotStealFocus, |_| View::new(1, "a"));
1740 let view_2 = app.add_typed_action_view(window_id, |_| View::new(2, "b"));
1741 let view_3 = app.add_typed_action_view(window_id, |_| View::new(3, "c"));
1742 
1743 // This keymap's only binding dispatches the action on View 2, because only that view
1744 // will have "b" in its context
1745 let binding = keymap::FixedBinding::new("a", Action("a".into()), id!("b"));
1746 app.update(|ctx| ctx.register_fixed_bindings(vec![binding]));
1747 
1748 let handled = app.dispatch_keystroke(
1749 window_id,
1750 &[view_1.id(), view_2.id(), view_3.id()],
1751 &Keystroke::parse("a")?,
1752 false,
1753 )?;
1754 
1755 assert!(handled);
1756 Ok(())
1757 })
1758}
1759 
1760#[test]
1761fn test_dispatch_custom_action_triggers_typed_action() -> Result<()> {
1762 struct View {
1763 id: usize,
1764 keymap_context: keymap::Context,
1765 action_count: usize,
1766 keydown_count: Rc<RefCell<usize>>,
1767 }
1768 
1769 impl Entity for View {
1770 type Event = ();
1771 }
1772 
1773 impl super::View for View {
1774 fn render(&self, _: &AppContext) -> Box<dyn Element> {
1775 let keydown_count = self.keydown_count.clone();
1776 EventHandler::new(Empty::new().finish())
1777 .on_keydown(move |_, _, _| {
1778 *keydown_count.borrow_mut() += 1;
1779 DispatchEventResult::StopPropagation
1780 })
1781 .finish()
1782 }
1783 
1784 fn ui_name() -> &'static str {
1785 "View"
1786 }
1787 
1788 fn keymap_context(&self, _: &AppContext) -> keymap::Context {
1789 self.keymap_context.clone()
1790 }
1791 }
1792 
1793 #[derive(Debug)]
1794 struct Action(String);
1795 
1796 impl TypedActionView for View {
1797 type Action = Action;
1798 
1799 fn handle_action(&mut self, action: &Action, _: &mut ViewContext<Self>) {
1800 assert_eq!(self.id, 1);
1801 assert_eq!(action.0, "a");
1802 self.action_count += 1;
1803 }
1804 }
1805 
1806 impl View {
1807 fn new(id: usize, context: &'static str) -> Self {
1808 let mut instance = View {
1809 id,
1810 keymap_context: Default::default(),
1811 action_count: 0,
1812 keydown_count: Rc::new(RefCell::new(0)),
1813 };
1814 
1815 instance.keymap_context.set.insert(context);
1816 instance
1817 }
1818 }
1819 
1820 App::test((), |mut app| async move {
1821 let (window_id, view_1) = app.add_window(WindowStyle::NotStealFocus, |_| View::new(1, "a"));
1822 let custom_tag = 123_isize;
1823 let binding = keymap::FixedBinding::custom(
1824 custom_tag,
1825 Action("a".into()),
1826 "test custom action",
1827 id!("a"),
1828 );
1829 app.update(|ctx| {
1830 ctx.register_default_keystroke_triggers_for_custom_actions(|_| {
1831 Some(Keystroke::parse("ctrl-1").expect("failed to parse keystroke"))
1832 });
1833 ctx.register_fixed_bindings(vec![binding]);
1834 });
1835 view_1.update(&mut app, |_, ctx| {
1836 ctx.focus_self();
1837 });
1838 app.dispatch_custom_action(custom_tag, window_id);
1839 assert_eq!(view_1.read(&app, |view, _| view.action_count), 1);
1840 assert_eq!(view_1.read(&app, |view, _| *view.keydown_count.borrow()), 0);
1841 
1842 app.update(|ctx| {
1843 ctx.disable_key_bindings(window_id);
1844 });
1845 app.dispatch_custom_action(custom_tag, window_id);
1846 assert_eq!(view_1.read(&app, |view, _| view.action_count), 1);
1847 assert_eq!(view_1.read(&app, |view, _| *view.keydown_count.borrow()), 1);
1848 
1849 app.update(|ctx| {
1850 ctx.enable_key_bindings(window_id);
1851 });
1852 app.dispatch_custom_action(custom_tag, window_id);
1853 assert_eq!(view_1.read(&app, |view, _| view.action_count), 2);
1854 assert_eq!(view_1.read(&app, |view, _| *view.keydown_count.borrow()), 1);
1855 
1856 Ok(())
1857 })
1858}
1859 
1860#[test]
1861fn test_ui_and_window_updates() {
1862 struct View {
1863 count: usize,
1864 }
1865 
1866 impl Entity for View {
1867 type Event = ();
1868 }
1869 
1870 impl super::View for View {
1871 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1872 Empty::new().finish()
1873 }
1874 
1875 fn ui_name() -> &'static str {
1876 "View"
1877 }
1878 }
1879 
1880 impl TypedActionView for View {
1881 type Action = ();
1882 }
1883 
1884 App::test((), |mut app| async move {
1885 let (window_id, _) = app.add_window(WindowStyle::NotStealFocus, |_| View { count: 3 });
1886 let view_1 = app.add_view(window_id, |_| View { count: 1 });
1887 let view_2 = app.add_view(window_id, |_| View { count: 2 });
1888 
1889 // Ensure that registering for UI updates after mutating the app still gives us all the
1890 // updates.
1891 
1892 let window_invalidations = Rc::new(RefCell::new(Vec::new()));
1893 let window_invalidations_ = window_invalidations.clone();
1894 app.on_window_invalidated(window_id, move |window_id, ctx| {
1895 window_invalidations_
1896 .borrow_mut()
1897 .push(ctx.take_all_invalidations_for_window(window_id))
1898 });
1899 
1900 let view_2_id = view_2.id();
1901 view_1.update(&mut app, |view, ctx| {
1902 view.count = 7;
1903 ctx.notify();
1904 drop(view_2);
1905 });
1906 
1907 let invalidation = window_invalidations.borrow_mut().drain(..).next().unwrap();
1908 assert_eq!(invalidation.updated.len(), 1);
1909 assert!(invalidation.updated.contains(&view_1.id()));
1910 assert_eq!(invalidation.removed.len(), 1);
1911 assert!(invalidation.removed.contains(&view_2_id));
1912 
1913 let view_3 = view_1.update(&mut app, |_, ctx| ctx.add_view(|_| View { count: 8 }));
1914 
1915 let invalidation = window_invalidations.borrow_mut().drain(..).next().unwrap();
1916 assert_eq!(invalidation.updated.len(), 1);
1917 assert!(invalidation.updated.contains(&view_3.id()));
1918 assert!(invalidation.removed.is_empty());
1919 
1920 let (tx, rx) = futures::channel::oneshot::channel();
1921 view_3.update(&mut app, move |_, ctx| {
1922 ctx.spawn(async { 9 }, move |me, output, ctx| {
1923 tx.send(()).unwrap();
1924 me.count = output;
1925 ctx.notify();
1926 })
1927 });
1928 
1929 rx.await.unwrap();
1930 
1931 let invalidation = window_invalidations.borrow_mut().drain(..).next().unwrap();
1932 assert_eq!(invalidation.updated.len(), 1);
1933 assert!(invalidation.updated.contains(&view_3.id()));
1934 assert!(invalidation.removed.is_empty());
1935 });
1936}
1937 
1938#[test]
1939fn test_finish_pending_tasks() {
1940 struct View;
1941 
1942 impl Entity for View {
1943 type Event = ();
1944 }
1945 
1946 impl super::View for View {
1947 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
1948 Empty::new().finish()
1949 }
1950 
1951 fn ui_name() -> &'static str {
1952 "View"
1953 }
1954 }
1955 
1956 impl TypedActionView for View {
1957 type Action = ();
1958 }
1959 
1960 struct Model;
1961 
1962 impl Entity for Model {
1963 type Event = ();
1964 }
1965 
1966 App::test((), |mut app| async move {
1967 let model = app.add_model(|_| Model);
1968 let (_, view) = app.add_window(WindowStyle::NotStealFocus, |_| View);
1969 
1970 model.update(&mut app, |_, ctx| {
1971 let _ = ctx.spawn(async {}, |_, _, _| {});
1972 let _ = ctx.spawn(async {}, |_, _, _| {});
1973 let _ = ctx.spawn_stream_local(
1974 futures::stream::iter(vec![1, 2, 3]),
1975 |_, _, _| {},
1976 |_, _| {},
1977 );
1978 });
1979 
1980 view.update(&mut app, |_, ctx| {
1981 let _ = ctx.spawn(async {}, |_, _, _| {});
1982 let _ = ctx.spawn(async {}, |_, _, _| {});
1983 let _ = ctx.spawn_stream_local(
1984 futures::stream::iter(vec![1, 2, 3]),
1985 |_, _, _| {},
1986 |_, _| {},
1987 );
1988 });
1989 
1990 app.update(|ctx| {
1991 assert!(!ctx.task_callbacks.is_empty());
1992 });
1993 app.finish_pending_tasks().await;
1994 app.update(|ctx| {
1995 assert!(ctx.task_callbacks.is_empty());
1996 });
1997 app.finish_pending_tasks().await; // Don't block if there are no tasks
1998 });
1999}
2000 
2001#[test]
2002fn test_key_bindings_for_view() {
2003 use keymap::FixedBinding;
2004 struct ViewA;
2005 struct ViewB;
2006 impl Entity for ViewA {
2007 type Event = ();
2008 }
2009 impl Entity for ViewB {
2010 type Event = ();
2011 }
2012 impl View for ViewA {
2013 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2014 Empty::new().finish()
2015 }
2016 
2017 fn ui_name() -> &'static str {
2018 "ViewA"
2019 }
2020 }
2021 impl View for ViewB {
2022 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2023 Empty::new().finish()
2024 }
2025 
2026 fn ui_name() -> &'static str {
2027 "ViewB"
2028 }
2029 }
2030 
2031 struct Container {
2032 child_a: ViewHandle<ViewA>,
2033 child_b: ViewHandle<ViewB>,
2034 }
2035 impl Entity for Container {
2036 type Event = ();
2037 }
2038 impl View for Container {
2039 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2040 let mut stack = Stack::new();
2041 stack.add_child(ChildView::new(&self.child_a).finish());
2042 stack.add_child(ChildView::new(&self.child_b).finish());
2043 
2044 stack.finish()
2045 }
2046 fn ui_name() -> &'static str {
2047 "Container"
2048 }
2049 }
2050 impl Container {
2051 fn new(ctx: &mut ViewContext<Self>) -> Self {
2052 let child_a = ctx.add_view(|_| ViewA);
2053 let child_b = ctx.add_view(|_| ViewB);
2054 
2055 Self { child_a, child_b }
2056 }
2057 }
2058 
2059 struct Root {
2060 child: ViewHandle<Container>,
2061 }
2062 impl Entity for Root {
2063 type Event = ();
2064 }
2065 impl View for Root {
2066 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2067 ChildView::new(&self.child).finish()
2068 }
2069 fn ui_name() -> &'static str {
2070 "Root"
2071 }
2072 }
2073 
2074 impl TypedActionView for Root {
2075 type Action = ();
2076 }
2077 
2078 impl Root {
2079 fn new(ctx: &mut ViewContext<Self>) -> Self {
2080 let child = ctx.add_view(Container::new);
2081 
2082 Self { child }
2083 }
2084 }
2085 
2086 #[derive(Debug)]
2087 enum Action {
2088 A,
2089 EmptyRoot,
2090 BContainer,
2091 BLeaf,
2092 C,
2093 EmptyB,
2094 }
2095 
2096 App::test((), |mut app| async move {
2097 app.update(|ctx| {
2098 ctx.register_fixed_bindings(vec![
2099 FixedBinding::new("a", Action::A, id!("Root")),
2100 FixedBinding::empty("description", Action::EmptyRoot, id!("Root")),
2101 FixedBinding::new("b", Action::BContainer, id!("Container")),
2102 FixedBinding::new("b", Action::BLeaf, id!("ViewA")),
2103 FixedBinding::new("c", Action::C, id!("ViewB")),
2104 FixedBinding::empty("other description", Action::EmptyB, id!("ViewB")),
2105 ]);
2106 });
2107 
2108 /*
2109 Builds a View Hierarchy that looks like (with bound keys in parentheses)
2110 
2111 Root (a)
2112 |
2113 Container (b)
2114 | |
2115 (b) ViewA ViewB (c)
2116 */
2117 
2118 let (window_id, _) = app.add_window(WindowStyle::NotStealFocus, Root::new);
2119 let view_id_a = app.views_of_type::<ViewA>(window_id).unwrap()[0].id();
2120 let view_id_b = app.views_of_type::<ViewB>(window_id).unwrap()[0].id();
2121 
2122 // Force an update so the child view hierarchy is processed
2123 app.views_of_type::<Root>(window_id).unwrap()[0].update(&mut app, |_, ctx| ctx.notify());
2124 
2125 app.update(|ctx| {
2126 // Binding actions available to ViewA should be 'A', 'BLeaf', and 'EmptyRoot', since
2127 // the key binding on 'b' overlaps between ViewA and Container, we should only get the
2128 // highest precedence one ('BLeaf')
2129 let bindings_a = ctx
2130 .key_bindings_for_view(window_id, view_id_a)
2131 .into_iter()
2132 .map(|binding| format!("{:?}", binding.action))
2133 .collect::<Vec<_>>();
2134 
2135 assert_eq!(bindings_a, ["BLeaf", "EmptyRoot", "A"]);
2136 
2137 // Binding actions available to ViewB should be 'A', 'EmptyRoot', 'BContainer', 'C',
2138 // and 'EmptyB', as the only binding overlaps are empty, which aren't treated as
2139 // overlapping each other
2140 let bindings_b = ctx
2141 .key_bindings_for_view(window_id, view_id_b)
2142 .into_iter()
2143 .map(|binding| format!("{:?}", binding.action))
2144 .collect::<Vec<_>>();
2145 
2146 assert_eq!(bindings_b, ["EmptyB", "C", "BContainer", "EmptyRoot", "A"]);
2147 });
2148 });
2149}
2150 
2151#[test]
2152fn test_await_spawned_future() {
2153 #[derive(Default)]
2154 struct Model {
2155 received_spawn_callback: bool,
2156 }
2157 
2158 impl Entity for Model {
2159 type Event = ();
2160 }
2161 
2162 App::test((), |mut app| async move {
2163 let model = app.add_model(|_| Model::default());
2164 
2165 model
2166 .update(&mut app, |_, ctx| {
2167 let future_handle = ctx.spawn(
2168 async move { Timer::after(Duration::from_millis(10)).await },
2169 |model, _, _| {
2170 model.received_spawn_callback = true;
2171 },
2172 );
2173 
2174 ctx.await_spawned_future(future_handle.future_id())
2175 })
2176 .await;
2177 
2178 // Assert that the callback was executed. We do this via a model because we aren't
2179 // guaranteed that the callback will run if the future is awaited.
2180 model.read(&app, |model, _| {
2181 assert!(model.received_spawn_callback);
2182 });
2183 });
2184}
2185 
2186#[test]
2187fn test_editable_binding_getters() {
2188 use keymap::EditableBinding;
2189 struct ViewA;
2190 struct ViewB;
2191 struct Container {
2192 child_a: ViewHandle<ViewA>,
2193 child_b: ViewHandle<ViewB>,
2194 }
2195 struct Root {
2196 child: ViewHandle<Container>,
2197 }
2198 impl Entity for ViewA {
2199 type Event = ();
2200 }
2201 impl Entity for ViewB {
2202 type Event = ();
2203 }
2204 impl Entity for Container {
2205 type Event = ();
2206 }
2207 impl Entity for Root {
2208 type Event = ();
2209 }
2210 impl View for ViewA {
2211 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2212 Empty::new().finish()
2213 }
2214 fn ui_name() -> &'static str {
2215 "ViewA"
2216 }
2217 }
2218 impl View for ViewB {
2219 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2220 Empty::new().finish()
2221 }
2222 fn ui_name() -> &'static str {
2223 "ViewB"
2224 }
2225 }
2226 impl View for Container {
2227 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2228 Stack::new()
2229 .with_child(ChildView::new(&self.child_a).finish())
2230 .with_child(ChildView::new(&self.child_b).finish())
2231 .finish()
2232 }
2233 fn ui_name() -> &'static str {
2234 "Container"
2235 }
2236 }
2237 impl Container {
2238 fn new(ctx: &mut ViewContext<Self>) -> Self {
2239 let child_a = ctx.add_view(|_| ViewA);
2240 let child_b = ctx.add_view(|_| ViewB);
2241 
2242 Self { child_a, child_b }
2243 }
2244 }
2245 impl View for Root {
2246 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2247 ChildView::new(&self.child).finish()
2248 }
2249 fn ui_name() -> &'static str {
2250 "Root"
2251 }
2252 }
2253 
2254 impl TypedActionView for Root {
2255 type Action = ();
2256 }
2257 
2258 impl Root {
2259 fn new(ctx: &mut ViewContext<Self>) -> Self {
2260 let child = ctx.add_view(Container::new);
2261 
2262 Self { child }
2263 }
2264 }
2265 #[derive(Debug)]
2266 struct Action(#[allow(dead_code)] String);
2267 
2268 App::test((), |mut app| async move {
2269 use crate::keymap::macros::*;
2270 app.update(|ctx| {
2271 ctx.register_editable_bindings([
2272 EditableBinding::new("a", "Action a", Action("a".into()))
2273 .with_context_predicate(id!("Root")),
2274 EditableBinding::new("b-container", "Action b in Container", Action("b".into()))
2275 .with_context_predicate(id!("Container")),
2276 EditableBinding::new("b-leaf", "Action b in Leaf", Action("b".into()))
2277 .with_context_predicate(id!("ViewA")),
2278 EditableBinding::new("c", "Action c", Action("c".into()))
2279 .with_context_predicate(id!("ViewB")),
2280 ]);
2281 });
2282 
2283 /*
2284 Builds a View Hierarchy that looks like (with bound keys in parentheses)
2285 
2286 Root (a)
2287 |
2288 Container (b)
2289 | |
2290 (b) ViewA ViewB (c)
2291 */
2292 
2293 let (window_id, _) = app.add_window(WindowStyle::NotStealFocus, Root::new);
2294 let view_id_a = app.views_of_type::<ViewA>(window_id).unwrap()[0].id();
2295 let view_id_b = app.views_of_type::<ViewB>(window_id).unwrap()[0].id();
2296 
2297 // Force an update so the child view hierarchy is processed
2298 app.views_of_type::<Root>(window_id).unwrap()[0].update(&mut app, |_, ctx| ctx.notify());
2299 
2300 // Editable Bindings available to ViewA should be 'a', 'b-container', and 'b-leaf'
2301 // since those are all tied to context ViewA or its ancestors
2302 app.update(|ctx| {
2303 let actions_a = ctx
2304 .editable_bindings_for_view(window_id, view_id_a)
2305 .into_iter()
2306 .map(|action| action.name.to_owned())
2307 .collect::<Vec<_>>();
2308 assert_eq!(actions_a, ["b-leaf", "b-container", "a"]);
2309 
2310 // Editable bindings available to ViewB should be 'a', 'b-container', and 'c' since
2311 // those are all tied to ViewB or its ancestors
2312 let actions_b = ctx
2313 .editable_bindings_for_view(window_id, view_id_b)
2314 .into_iter()
2315 .map(|action| action.name.to_owned())
2316 .collect::<Vec<_>>();
2317 assert_eq!(actions_b, ["c", "b-container", "a"]);
2318 
2319 // Calling `editable_bindings` should get _all_ editable bindings
2320 let all_actions = ctx
2321 .editable_bindings()
2322 .map(|action| action.name.to_owned())
2323 .collect::<Vec<_>>();
2324 assert_eq!(all_actions, ["c", "b-leaf", "b-container", "a"]);
2325 });
2326 });
2327}
2328 
2329#[test]
2330fn test_unsubscribe_from_model_inside_callback() {
2331 #[derive(Default)]
2332 struct SubscriberModel {
2333 events: Vec<usize>,
2334 }
2335 
2336 impl Entity for SubscriberModel {
2337 type Event = ();
2338 }
2339 
2340 struct EmitterModel;
2341 
2342 impl Entity for EmitterModel {
2343 type Event = usize;
2344 }
2345 
2346 App::test((), |mut app| async move {
2347 let emitter = app.add_model(|_| EmitterModel);
2348 let emitter_clone = emitter.clone();
2349 
2350 let subscriber = app.add_model(|_| SubscriberModel::default());
2351 
2352 // Subscribe, and unsubscribe from inside the callback.
2353 subscriber.update(&mut app, |_, ctx| {
2354 let emitter_for_unsubscribe = emitter_clone.clone();
2355 ctx.subscribe_to_model(&emitter_clone, move |model, event, ctx| {
2356 model.events.push(*event);
2357 // Unsubscribe from inside the callback.
2358 ctx.unsubscribe_from_model(&emitter_for_unsubscribe);
2359 });
2360 });
2361 
2362 // First emit should trigger the callback.
2363 emitter.update(&mut app, |_, ctx| ctx.emit(1));
2364 subscriber.read(&app, |model, _| {
2365 assert_eq!(model.events, vec![1], "first event should be received");
2366 });
2367 
2368 // Second emit should NOT trigger the callback, since we unsubscribed.
2369 emitter.update(&mut app, |_, ctx| ctx.emit(2));
2370 subscriber.read(&app, |model, _| {
2371 assert_eq!(
2372 model.events,
2373 vec![1],
2374 "second event should NOT be received after unsubscribe"
2375 );
2376 });
2377 });
2378}
2379 
2380#[test]
2381fn test_unsubscribe_from_model_inside_callback_with_multiple_subscriptions() {
2382 // This test verifies that when there are multiple subscriptions from A to B,
2383 // and one callback calls unsubscribe_from_model:
2384 // 1. All callbacks for the CURRENT event still fire (unsubscribe affects future events).
2385 // 2. No callbacks fire for FUTURE events.
2386 
2387 #[derive(Default)]
2388 struct SubscriberModel {
2389 events: Vec<&'static str>,
2390 }
2391 
2392 impl Entity for SubscriberModel {
2393 type Event = ();
2394 }
2395 
2396 struct EmitterModel;
2397 
2398 impl Entity for EmitterModel {
2399 type Event = ();
2400 }
2401 
2402 App::test((), |mut app| async move {
2403 let emitter = app.add_model(|_| EmitterModel);
2404 let emitter_clone1 = emitter.clone();
2405 let emitter_clone2 = emitter.clone();
2406 
2407 let subscriber = app.add_model(|_| SubscriberModel::default());
2408 
2409 // Add two subscriptions from subscriber to emitter.
2410 subscriber.update(&mut app, |_, ctx| {
2411 // First subscription: will call unsubscribe.
2412 let emitter_for_unsubscribe = emitter_clone1.clone();
2413 ctx.subscribe_to_model(&emitter_clone1, move |model, _, ctx| {
2414 model.events.push("first");
2415 ctx.unsubscribe_from_model(&emitter_for_unsubscribe);
2416 });
2417 
2418 // Second subscription: should still be called for this event.
2419 ctx.subscribe_to_model(&emitter_clone2, move |model, _, _| {
2420 model.events.push("second");
2421 });
2422 });
2423 
2424 // Emit an event. BOTH callbacks should fire for the current event.
2425 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2426 subscriber.read(&app, |model, _| {
2427 assert_eq!(
2428 model.events,
2429 vec!["first", "second"],
2430 "all existing callbacks should fire for the current event"
2431 );
2432 });
2433 
2434 // Emit again. Neither callback should fire since we unsubscribed.
2435 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2436 subscriber.read(&app, |model, _| {
2437 assert_eq!(
2438 model.events,
2439 vec!["first", "second"],
2440 "no callbacks should be invoked for future events after unsubscribe"
2441 );
2442 });
2443 });
2444}
2445 
2446#[test]
2447fn test_unsubscribe_then_resubscribe_from_model_inside_callback_keeps_new_subscription() {
2448 #[derive(Default)]
2449 struct SubscriberModel {
2450 events: Vec<&'static str>,
2451 }
2452 
2453 impl Entity for SubscriberModel {
2454 type Event = ();
2455 }
2456 
2457 struct EmitterModel;
2458 
2459 impl Entity for EmitterModel {
2460 type Event = ();
2461 }
2462 
2463 App::test((), |mut app| async move {
2464 let emitter = app.add_model(|_| EmitterModel);
2465 let emitter_clone = emitter.clone();
2466 
2467 let subscriber = app.add_model(|_| SubscriberModel::default());
2468 
2469 subscriber.update(&mut app, |_, ctx| {
2470 let emitter_for_unsubscribe = emitter_clone.clone();
2471 let emitter_for_resubscribe = emitter_clone.clone();
2472 
2473 ctx.subscribe_to_model(&emitter_clone, move |model, _, ctx| {
2474 model.events.push("old");
2475 
2476 ctx.unsubscribe_from_model(&emitter_for_unsubscribe);
2477 
2478 ctx.subscribe_to_model(&emitter_for_resubscribe, |model, _, _| {
2479 model.events.push("new");
2480 });
2481 });
2482 });
2483 
2484 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2485 subscriber.read(&app, |model, _| {
2486 assert_eq!(
2487 model.events,
2488 vec!["old"],
2489 "The new subscription should not fire for the current event."
2490 );
2491 });
2492 
2493 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2494 subscriber.read(&app, |model, _| {
2495 assert_eq!(
2496 model.events,
2497 vec!["old", "new"],
2498 "The new subscription should survive unsubscribe-then-resubscribe inside a callback."
2499 );
2500 });
2501 });
2502}
2503 
2504#[test]
2505fn test_subscribe_then_unsubscribe_from_model_inside_callback_drops_new_subscription() {
2506 #[derive(Default)]
2507 struct SubscriberModel {
2508 events: Vec<&'static str>,
2509 }
2510 
2511 impl Entity for SubscriberModel {
2512 type Event = ();
2513 }
2514 
2515 struct EmitterModel;
2516 
2517 impl Entity for EmitterModel {
2518 type Event = ();
2519 }
2520 
2521 App::test((), |mut app| async move {
2522 let emitter = app.add_model(|_| EmitterModel);
2523 let emitter_clone = emitter.clone();
2524 
2525 let subscriber = app.add_model(|_| SubscriberModel::default());
2526 
2527 subscriber.update(&mut app, |_, ctx| {
2528 let emitter_for_subscribe = emitter_clone.clone();
2529 let emitter_for_unsubscribe = emitter_clone.clone();
2530 
2531 ctx.subscribe_to_model(&emitter_clone, move |model, _, ctx| {
2532 model.events.push("old");
2533 
2534 ctx.subscribe_to_model(&emitter_for_subscribe, |model, _, _| {
2535 model.events.push("new");
2536 });
2537 
2538 ctx.unsubscribe_from_model(&emitter_for_unsubscribe);
2539 });
2540 });
2541 
2542 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2543 subscriber.read(&app, |model, _| {
2544 assert_eq!(
2545 model.events,
2546 vec!["old"],
2547 "The new subscription should not fire for the current event."
2548 );
2549 });
2550 
2551 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2552 subscriber.read(&app, |model, _| {
2553 assert_eq!(
2554 model.events,
2555 vec!["old"],
2556 "The new subscription should be removed when subscribe-then-unsubscribe happens in a callback."
2557 );
2558 });
2559 });
2560}
2561 
2562#[test]
2563fn test_view_unsubscribe_to_model_inside_callback() {
2564 #[derive(Default)]
2565 struct SubscriberView {
2566 events: Vec<usize>,
2567 }
2568 
2569 impl Entity for SubscriberView {
2570 type Event = ();
2571 }
2572 
2573 impl View for SubscriberView {
2574 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2575 Empty::new().finish()
2576 }
2577 
2578 fn ui_name() -> &'static str {
2579 "SubscriberView"
2580 }
2581 }
2582 
2583 impl TypedActionView for SubscriberView {
2584 type Action = ();
2585 }
2586 
2587 struct EmitterModel;
2588 
2589 impl Entity for EmitterModel {
2590 type Event = usize;
2591 }
2592 
2593 App::test((), |mut app| async move {
2594 let emitter = app.add_model(|_| EmitterModel);
2595 let emitter_clone = emitter.clone();
2596 
2597 let (_, subscriber) =
2598 app.add_window(WindowStyle::NotStealFocus, |_| SubscriberView::default());
2599 
2600 // Subscribe, and unsubscribe from inside the callback.
2601 subscriber.update(&mut app, |_, ctx| {
2602 let emitter_for_unsubscribe = emitter_clone.clone();
2603 ctx.subscribe_to_model(&emitter_clone, move |view, _, event, ctx| {
2604 view.events.push(*event);
2605 // Unsubscribe from inside the callback.
2606 ctx.unsubscribe_to_model(&emitter_for_unsubscribe);
2607 });
2608 });
2609 
2610 // First emit should trigger the callback.
2611 emitter.update(&mut app, |_, ctx| ctx.emit(1));
2612 subscriber.read(&app, |view, _| {
2613 assert_eq!(view.events, vec![1], "first event should be received");
2614 });
2615 
2616 // Second emit should NOT trigger the callback, since we unsubscribed.
2617 emitter.update(&mut app, |_, ctx| ctx.emit(2));
2618 subscriber.read(&app, |view, _| {
2619 assert_eq!(
2620 view.events,
2621 vec![1],
2622 "second event should NOT be received after unsubscribe"
2623 );
2624 });
2625 });
2626}
2627 
2628#[test]
2629fn test_view_unsubscribe_to_model_inside_callback_with_multiple_subscriptions() {
2630 // This test verifies that when there are multiple subscriptions from a view to a model,
2631 // and one callback calls unsubscribe_to_model:
2632 // 1. All callbacks for the CURRENT event still fire (unsubscribe affects future events).
2633 // 2. No callbacks fire for FUTURE events.
2634 
2635 #[derive(Default)]
2636 struct SubscriberView {
2637 events: Vec<&'static str>,
2638 }
2639 
2640 impl Entity for SubscriberView {
2641 type Event = ();
2642 }
2643 
2644 impl View for SubscriberView {
2645 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2646 Empty::new().finish()
2647 }
2648 
2649 fn ui_name() -> &'static str {
2650 "SubscriberView"
2651 }
2652 }
2653 
2654 impl TypedActionView for SubscriberView {
2655 type Action = ();
2656 }
2657 
2658 struct EmitterModel;
2659 
2660 impl Entity for EmitterModel {
2661 type Event = ();
2662 }
2663 
2664 App::test((), |mut app| async move {
2665 let emitter = app.add_model(|_| EmitterModel);
2666 let emitter_clone1 = emitter.clone();
2667 let emitter_clone2 = emitter.clone();
2668 
2669 let (_, subscriber) =
2670 app.add_window(WindowStyle::NotStealFocus, |_| SubscriberView::default());
2671 
2672 // Add two subscriptions from subscriber view to emitter model.
2673 subscriber.update(&mut app, |_, ctx| {
2674 // First subscription: will call unsubscribe.
2675 let emitter_for_unsubscribe = emitter_clone1.clone();
2676 ctx.subscribe_to_model(&emitter_clone1, move |view, _, _, ctx| {
2677 view.events.push("first");
2678 ctx.unsubscribe_to_model(&emitter_for_unsubscribe);
2679 });
2680 
2681 // Second subscription: should still be called for this event.
2682 ctx.subscribe_to_model(&emitter_clone2, move |view, _, _, _| {
2683 view.events.push("second");
2684 });
2685 });
2686 
2687 // Emit an event. BOTH callbacks should fire for the current event.
2688 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2689 subscriber.read(&app, |view, _| {
2690 assert_eq!(
2691 view.events,
2692 vec!["first", "second"],
2693 "all existing callbacks should fire for the current event"
2694 );
2695 });
2696 
2697 // Emit again. Neither callback should fire since we unsubscribed.
2698 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2699 subscriber.read(&app, |view, _| {
2700 assert_eq!(
2701 view.events,
2702 vec!["first", "second"],
2703 "no callbacks should be invoked for future events after unsubscribe"
2704 );
2705 });
2706 });
2707}
2708 
2709#[test]
2710fn test_view_unsubscribe_then_resubscribe_to_model_inside_callback_keeps_new_subscription() {
2711 #[derive(Default)]
2712 struct SubscriberView {
2713 events: Vec<&'static str>,
2714 }
2715 
2716 impl Entity for SubscriberView {
2717 type Event = ();
2718 }
2719 
2720 impl View for SubscriberView {
2721 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2722 Empty::new().finish()
2723 }
2724 
2725 fn ui_name() -> &'static str {
2726 "SubscriberView"
2727 }
2728 }
2729 
2730 impl TypedActionView for SubscriberView {
2731 type Action = ();
2732 }
2733 
2734 struct EmitterModel;
2735 
2736 impl Entity for EmitterModel {
2737 type Event = ();
2738 }
2739 
2740 App::test((), |mut app| async move {
2741 let emitter = app.add_model(|_| EmitterModel);
2742 let emitter_clone = emitter.clone();
2743 
2744 let (_, subscriber) =
2745 app.add_window(WindowStyle::NotStealFocus, |_| SubscriberView::default());
2746 
2747 subscriber.update(&mut app, |_, ctx| {
2748 let emitter_for_unsubscribe = emitter_clone.clone();
2749 let emitter_for_resubscribe = emitter_clone.clone();
2750 
2751 ctx.subscribe_to_model(&emitter_clone, move |view, _, _, ctx| {
2752 view.events.push("old");
2753 
2754 ctx.unsubscribe_to_model(&emitter_for_unsubscribe);
2755 
2756 ctx.subscribe_to_model(&emitter_for_resubscribe, |view, _, _, _| {
2757 view.events.push("new");
2758 });
2759 });
2760 });
2761 
2762 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2763 subscriber.read(&app, |view, _| {
2764 assert_eq!(
2765 view.events,
2766 vec!["old"],
2767 "The new subscription should not fire for the current event."
2768 );
2769 });
2770 
2771 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2772 subscriber.read(&app, |view, _| {
2773 assert_eq!(
2774 view.events,
2775 vec!["old", "new"],
2776 "The new subscription should survive unsubscribe-then-resubscribe inside a callback."
2777 );
2778 });
2779 });
2780}
2781 
2782#[test]
2783fn test_view_subscribe_then_unsubscribe_to_model_inside_callback_drops_new_subscription() {
2784 #[derive(Default)]
2785 struct SubscriberView {
2786 events: Vec<&'static str>,
2787 }
2788 
2789 impl Entity for SubscriberView {
2790 type Event = ();
2791 }
2792 
2793 impl View for SubscriberView {
2794 fn render(&self, _: &AppContext) -> Box<dyn Element> {
2795 Empty::new().finish()
2796 }
2797 
2798 fn ui_name() -> &'static str {
2799 "SubscriberView"
2800 }
2801 }
2802 
2803 impl TypedActionView for SubscriberView {
2804 type Action = ();
2805 }
2806 
2807 struct EmitterModel;
2808 
2809 impl Entity for EmitterModel {
2810 type Event = ();
2811 }
2812 
2813 App::test((), |mut app| async move {
2814 let emitter = app.add_model(|_| EmitterModel);
2815 let emitter_clone = emitter.clone();
2816 
2817 let (_, subscriber) =
2818 app.add_window(WindowStyle::NotStealFocus, |_| SubscriberView::default());
2819 
2820 subscriber.update(&mut app, |_, ctx| {
2821 let emitter_for_subscribe = emitter_clone.clone();
2822 let emitter_for_unsubscribe = emitter_clone.clone();
2823 
2824 ctx.subscribe_to_model(&emitter_clone, move |view, _, _, ctx| {
2825 view.events.push("old");
2826 
2827 ctx.subscribe_to_model(&emitter_for_subscribe, |view, _, _, _| {
2828 view.events.push("new");
2829 });
2830 
2831 ctx.unsubscribe_to_model(&emitter_for_unsubscribe);
2832 });
2833 });
2834 
2835 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2836 subscriber.read(&app, |view, _| {
2837 assert_eq!(
2838 view.events,
2839 vec!["old"],
2840 "The new subscription should not fire for the current event."
2841 );
2842 });
2843 
2844 emitter.update(&mut app, |_, ctx| ctx.emit(()));
2845 subscriber.read(&app, |view, _| {
2846 assert_eq!(
2847 view.events,
2848 vec!["old"],
2849 "The new subscription should be removed when subscribe-then-unsubscribe happens in a callback."
2850 );
2851 });
2852 });
2853}
2854