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