floem/views/
dyn_container.rs1use std::any::Any;
2
3use floem_reactive::{as_child_of_current_scope, create_updater, Scope};
4
5use crate::{
6 animate::RepeatMode,
7 context::UpdateCx,
8 view::{AnyView, View},
9 IntoView, ViewId,
10};
11
12#[macro_export]
13macro_rules! dyn_view {
14 ($signal:ident, $new:ident => $body:expr) => {
15 dyn_container(
16 move || {
17 use floem::reactive::SignalGet;
18 $signal.get()
19 },
20 move |$new| $body,
21 )
22 };
23
24 ($signal:ident => $body:expr) => {
25 dyn_container(
26 move || {
27 use floem::reactive::SignalGet;
28 $signal.get()
29 },
30 move |$signal| $body,
31 )
32 };
33}
34
35type ChildFn<T> = dyn Fn(T) -> (AnyView, Scope);
36
37pub struct DynamicContainer<T: 'static> {
39 id: ViewId,
40 child_id: ViewId,
41 child_scope: Scope,
42 child_fn: Box<ChildFn<T>>,
43 next_val_state: Option<(T, ViewId, Scope)>,
44 num_started_animations: u16,
45}
46
47pub fn dyn_container<CF: Fn(T) -> IV + 'static, T: 'static, IV: IntoView>(
93 update_view: impl Fn() -> T + 'static,
94 child_fn: CF,
95) -> DynamicContainer<T> {
96 let id = ViewId::new();
97
98 let initial = create_updater(update_view, move |new_state| {
99 id.update_state(DynMessage::Val(Box::new(new_state)));
100 });
101
102 let child_fn = Box::new(as_child_of_current_scope(move |e| child_fn(e).into_any()));
103 let (child, child_scope) = child_fn(initial);
104 let child_id = child.id();
105 id.set_children([child]);
106 DynamicContainer {
107 id,
108 child_scope,
109 child_id,
110 child_fn,
111 next_val_state: None,
112 num_started_animations: 0,
113 }
114}
115enum DynMessage {
116 Val(Box<dyn Any>),
117 CompletedAnimation,
118}
119
120impl<T: 'static> View for DynamicContainer<T> {
121 fn id(&self) -> ViewId {
122 self.id
123 }
124
125 fn debug_name(&self) -> std::borrow::Cow<'static, str> {
126 "Dynamic Container".into()
127 }
128
129 fn update(&mut self, cx: &mut UpdateCx, state: Box<dyn Any>) {
130 if let Ok(message) = state.downcast::<DynMessage>() {
131 match *message {
132 DynMessage::Val(val) => {
133 if let Ok(val) = val.downcast::<T>() {
134 self.new_val(cx, *val);
135 }
136 }
137 DynMessage::CompletedAnimation => {
138 self.num_started_animations = self.num_started_animations.saturating_sub(1);
139 if self.num_started_animations == 0 {
140 let next_val_state = self
141 .next_val_state
142 .take()
143 .expect("when waiting for animations the next value will be stored and all message effects should have been dropped by dropping the child id if another value was sent before the animations finished");
144 self.swap_val(cx, next_val_state);
145 }
146 }
147 }
148 }
149 }
150}
151impl<T> DynamicContainer<T> {
152 fn new_val(&mut self, cx: &mut UpdateCx, val: T) {
153 let id = self.id;
154
155 let old_child_scope = self.child_scope;
156 let old_child_id = self.child_id;
157
158 if self.num_started_animations > 0 {
159 let next_state = self
161 .next_val_state
162 .take()
163 .expect("valid when waiting for animations");
164 self.swap_val(cx, next_state);
166 self.num_started_animations = 0;
167 }
168
169 self.num_started_animations =
170 animations_recursive_on_remove(id, old_child_id, old_child_scope);
171
172 let next_state = (val, old_child_id, old_child_scope);
173 if self.num_started_animations == 0 {
174 self.swap_val(cx, next_state);
176 } else {
177 self.next_val_state = Some(next_state);
178 }
179 }
180
181 fn swap_val(&mut self, cx: &mut UpdateCx, next_val_state: (T, ViewId, Scope)) {
182 let (val, old_child_id, old_child_scope) = next_val_state;
183 let (new_child, new_child_scope) = (self.child_fn)(val);
184 self.child_id = new_child.id();
185 self.id.set_children([new_child]);
186 self.child_scope = new_child_scope;
187 cx.app_state_mut().remove_view(old_child_id);
188 old_child_scope.dispose();
189 animations_recursive_on_create(self.child_id);
190 self.id.request_all();
191 }
192}
193
194fn animations_recursive_on_remove(id: ViewId, child_id: ViewId, child_scope: Scope) -> u16 {
195 let mut wait_for = 0;
196 let state = child_id.state();
197 let mut state = state.borrow_mut();
198 let animations = &mut state.animations.stack;
199 let mut request_style = false;
200 for anim in animations {
201 if anim.run_on_remove && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
202 anim.reverse_mut();
203 request_style = true;
204 wait_for += 1;
205 let trigger = anim.on_visual_complete;
206 child_scope.create_updater(
207 move || trigger.track(),
208 move |_| {
209 id.update_state(DynMessage::CompletedAnimation);
210 },
211 );
212 }
213 }
214 drop(state);
215 if request_style {
216 child_id.request_style();
217 }
218
219 child_id
220 .children()
221 .into_iter()
222 .fold(wait_for, |acc, child_id| {
223 acc + animations_recursive_on_remove(id, child_id, child_scope)
224 })
225}
226fn animations_recursive_on_create(child_id: ViewId) {
227 let state = child_id.state();
228 let mut state = state.borrow_mut();
229 let animations = &mut state.animations.stack;
230 let mut request_style = false;
231 for anim in animations {
232 if anim.run_on_create && !matches!(anim.repeat_mode, RepeatMode::LoopForever) {
233 anim.start_mut();
234 request_style = true;
235 }
236 }
237 drop(state);
238 if request_style {
239 child_id.request_style();
240 }
241
242 child_id
243 .children()
244 .into_iter()
245 .for_each(animations_recursive_on_create);
246}