1use std::any::Any;
45use std::cell::{Cell, RefCell};
46use std::rc::Rc;
47
48use frust_core::{
49 AnyView, BoxConstraints, BuildCtx, ChangeFlags, ChildPod, EventCtx, EventResult, InputEvent,
50 LayoutCtx, PaintCtx, PaintScene, SemanticsCtx, View, Widget, any,
51};
52use kurbo::{Point, Size};
53
54type Requests = Rc<Cell<u32>>;
56
57thread_local! {
58 static ROW_SCOPES: RefCell<Vec<Requests>> = const { RefCell::new(Vec::new()) };
61}
62
63struct ScopeGuard;
66
67impl Drop for ScopeGuard {
68 fn drop(&mut self) {
69 ROW_SCOPES.with(|scopes| {
70 scopes.borrow_mut().pop();
71 });
72 }
73}
74
75fn with_row_scope<R>(requests: &Requests, f: impl FnOnce() -> R) -> R {
77 ROW_SCOPES.with(|scopes| scopes.borrow_mut().push(Rc::clone(requests)));
78 let _guard = ScopeGuard;
79 f()
80}
81
82pub struct KeepAliveHandle {
85 requests: Requests,
86 held: bool,
87}
88
89impl KeepAliveHandle {
90 pub fn acquire() -> Option<Self> {
95 ROW_SCOPES
96 .with(|scopes| scopes.borrow().last().cloned())
97 .map(|requests| Self {
98 requests,
99 held: false,
100 })
101 }
102
103 pub fn set(&mut self, keep_alive: bool) {
106 if keep_alive == self.held {
107 return;
108 }
109 self.held = keep_alive;
110 let count = self.requests.get();
111 self.requests.set(if keep_alive {
112 count.saturating_add(1)
113 } else {
114 count.saturating_sub(1)
115 });
116 }
117
118 pub fn is_held(&self) -> bool {
120 self.held
121 }
122}
123
124impl Drop for KeepAliveHandle {
125 fn drop(&mut self) {
126 self.set(false);
127 }
128}
129
130pub fn keep_alive<State: 'static, V: View<State>>(child: V) -> KeepAliveView<State> {
146 KeepAliveView {
147 child: any(child),
148 enabled: true,
149 }
150}
151
152pub struct KeepAliveView<State: 'static> {
154 child: AnyView<State>,
155 enabled: bool,
156}
157
158impl<State: 'static> KeepAliveView<State> {
159 pub fn enabled(mut self, enabled: bool) -> Self {
162 self.enabled = enabled;
163 self
164 }
165}
166
167pub struct KeepAliveWidget {
170 child: ChildPod,
171 handle: Option<KeepAliveHandle>,
172}
173
174impl<State: 'static> View<State> for KeepAliveView<State> {
175 type Element = KeepAliveWidget;
176
177 fn build(&self, ctx: &mut BuildCtx<'_>) -> KeepAliveWidget {
178 let mut handle = KeepAliveHandle::acquire();
179 if let Some(handle) = handle.as_mut() {
180 handle.set(self.enabled);
181 }
182 KeepAliveWidget {
183 child: crate::authoring::build_child(&self.child, ctx),
184 handle,
185 }
186 }
187
188 fn rebuild(
189 &self,
190 prev: &Self,
191 element: &mut KeepAliveWidget,
192 ctx: &mut BuildCtx<'_>,
193 ) -> ChangeFlags {
194 if let Some(handle) = element.handle.as_mut() {
195 handle.set(self.enabled);
196 }
197 crate::authoring::rebuild_child(&prev.child, &self.child, &mut element.child, ctx)
198 }
199
200 fn teardown(&self, element: &mut KeepAliveWidget, ctx: &mut BuildCtx<'_>) {
201 if let Some(handle) = element.handle.as_mut() {
202 handle.set(false);
203 }
204 crate::authoring::teardown_child(&self.child, &mut element.child, ctx);
205 }
206}
207
208impl Widget for KeepAliveWidget {
209 fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
210 let size = self.child.layout_child(ctx, bc);
211 self.child.set_origin(Point::ZERO);
212 size
213 }
214
215 fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut dyn PaintScene) {
216 self.child.paint_child(ctx, scene);
217 }
218
219 fn event(&mut self, ctx: &mut EventCtx, event: &InputEvent) -> EventResult {
220 crate::authoring::route_event_single(&mut self.child, ctx, event)
221 }
222
223 fn semantics(&self, ctx: &mut SemanticsCtx) {
224 self.child.semantics_child(ctx);
225 }
226
227 crate::authoring::visit_children!(child);
228}
229
230pub(crate) struct RowScope<V> {
235 child: V,
236}
237
238impl<V> RowScope<V> {
239 pub(crate) fn new(child: V) -> Self {
240 Self { child }
241 }
242}
243
244pub(crate) struct RowScopeWidget {
247 child: ChildPod,
248 requests: Requests,
249}
250
251impl<State: 'static, V: View<State>> View<State> for RowScope<V> {
252 type Element = RowScopeWidget;
253
254 fn build(&self, ctx: &mut BuildCtx<'_>) -> RowScopeWidget {
255 let requests = Requests::default();
256 let child = with_row_scope(&requests, || {
257 crate::authoring::build_child(&self.child, ctx)
258 });
259 RowScopeWidget { child, requests }
260 }
261
262 fn rebuild(
263 &self,
264 prev: &Self,
265 element: &mut RowScopeWidget,
266 ctx: &mut BuildCtx<'_>,
267 ) -> ChangeFlags {
268 let requests = Rc::clone(&element.requests);
269 with_row_scope(&requests, || {
270 crate::authoring::rebuild_child(&prev.child, &self.child, &mut element.child, ctx)
271 })
272 }
273
274 fn teardown(&self, element: &mut RowScopeWidget, ctx: &mut BuildCtx<'_>) {
275 crate::authoring::teardown_child(&self.child, &mut element.child, ctx);
276 }
277}
278
279impl Widget for RowScopeWidget {
280 fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints) -> Size {
281 let size = self.child.layout_child(ctx, bc);
282 self.child.set_origin(Point::ZERO);
283 size
284 }
285
286 fn paint(&mut self, ctx: &mut PaintCtx, scene: &mut dyn PaintScene) {
287 self.child.paint_child(ctx, scene);
288 }
289
290 fn event(&mut self, ctx: &mut EventCtx, event: &InputEvent) -> EventResult {
291 crate::authoring::route_event_single(&mut self.child, ctx, event)
292 }
293
294 fn semantics(&self, ctx: &mut SemanticsCtx) {
295 self.child.semantics_child(ctx);
296 }
297
298 crate::authoring::visit_children!(child);
299}
300
301pub(crate) fn row_wants_keep_alive(pod: &ChildPod) -> bool {
304 let widget: &dyn Any = pod.widget();
305 let scope = match widget.downcast_ref::<Box<dyn Widget>>() {
306 Some(boxed) => {
307 let inner: &dyn Any = &**boxed;
308 inner.downcast_ref::<RowScopeWidget>()
309 }
310 None => widget.downcast_ref::<RowScopeWidget>(),
311 };
312 scope.is_some_and(|scope| scope.requests.get() > 0)
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn a_handle_binds_to_the_innermost_row_and_counts_once() {
321 assert!(KeepAliveHandle::acquire().is_none(), "no row, no handle");
322 let outer = Requests::default();
323 let inner = Requests::default();
324 let (mut a, mut b) = with_row_scope(&outer, || {
325 let a = KeepAliveHandle::acquire().expect("in a row");
326 let b = with_row_scope(&inner, || KeepAliveHandle::acquire().expect("nested row"));
327 (a, b)
328 });
329 assert!(KeepAliveHandle::acquire().is_none(), "scopes closed");
330 a.set(true);
331 a.set(true);
332 b.set(true);
333 assert_eq!((outer.get(), inner.get()), (1, 1));
334 a.set(false);
335 assert_eq!(outer.get(), 0);
336 drop(b);
337 assert_eq!(inner.get(), 0, "dropping a handle withdraws its request");
338 assert!(!a.is_held());
339 }
340}