sequoia_view/
layout_live.rs1#![forbid(unsafe_code)]
2
3use sequoia_core::Length;
4use sequoia_layout::LayoutStyle;
5
6use crate::any::AnyView;
7use crate::live::{AnimSpec, Live, Prop, SpringSpec};
8use crate::runtime::{ElementId, Runtime};
9use crate::view::View;
10
11#[derive(Clone)]
12pub struct AnimatedSize {
13 child: AnyView,
14 width: Option<Live<f32>>,
15 height: Option<Live<f32>>,
16}
17
18impl AnimatedSize {
19 pub fn new<V: View + 'static>(
20 child: V,
21 width: Option<Live<f32>>,
22 height: Option<Live<f32>>,
23 ) -> Self {
24 Self {
25 child: AnyView::new(child),
26 width,
27 height,
28 }
29 }
30
31 pub fn width<V: View + 'static>(child: V, width: impl Into<Live<f32>>) -> Self {
32 Self::new(child, Some(width.into()), None)
33 }
34
35 pub fn height<V: View + 'static>(child: V, height: impl Into<Live<f32>>) -> Self {
36 Self::new(child, None, Some(height.into()))
37 }
38
39 pub fn size<V: View + 'static>(
40 child: V,
41 width: impl Into<Live<f32>>,
42 height: impl Into<Live<f32>>,
43 ) -> Self {
44 Self::new(child, Some(width.into()), Some(height.into()))
45 }
46
47 pub fn animate(mut self, spec: impl Into<AnimSpec>) -> Self {
48 let spec = spec.into();
49 if let Some(w) = &mut self.width {
50 w.animate_mut(spec);
51 }
52 if let Some(h) = &mut self.height {
53 h.animate_mut(spec);
54 }
55 self
56 }
57
58 pub fn spring(mut self, spec: SpringSpec) -> Self {
59 if let Some(w) = &mut self.width {
60 w.spring_mut(spec);
61 }
62 if let Some(h) = &mut self.height {
63 h.spring_mut(spec);
64 }
65 self
66 }
67
68 fn px_or_fit(live: &Option<Live<f32>>) -> Length {
69 match live {
70 Some(l) => Length::Px(l.target_untracked().max(0.0)),
71 None => Length::Fit,
72 }
73 }
74}
75
76impl View for AnimatedSize {
77 fn body(&self) -> impl View + 'static {
78 crate::views::Unit
79 }
80
81 fn children_override(&self) -> Option<Vec<AnyView>> {
82 if let Some(w) = &self.width {
83 let _ = w.target();
84 }
85 if let Some(h) = &self.height {
86 let _ = h.target();
87 }
88 Some(vec![self.child.clone()])
89 }
90
91 fn layout_style(&self) -> LayoutStyle {
92 LayoutStyle {
93 width: Self::px_or_fit(&self.width),
94 height: Self::px_or_fit(&self.height),
95 ..LayoutStyle::default()
96 }
97 }
98
99 fn layout_style_ctx(&self, rt: &Runtime, el: ElementId) -> LayoutStyle {
100 let width = match &self.width {
101 Some(live) => Length::Px(rt.resolve_live_f32(el, Prop::WIDTH, live).max(0.0)),
102 None => Length::Fit,
103 };
104 let height = match &self.height {
105 Some(live) => Length::Px(rt.resolve_live_f32(el, Prop::HEIGHT, live).max(0.0)),
106 None => Length::Fit,
107 };
108 LayoutStyle {
109 width,
110 height,
111 ..LayoutStyle::default()
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use crate::live::SignalLiveExt;
120 use sequoia_reactive::Signal;
121 use std::time::Duration;
122
123 #[test]
124 fn static_snapshot_without_runtime() {
125 let w = AnimatedSize::width(crate::views::Unit, 120.0f32);
126 let s = w.layout_style();
127 assert_eq!(s.width, Length::Px(120.0));
128 assert_eq!(s.height, Length::Fit);
129 }
130
131 #[test]
132 fn tracks_signal_target() {
133 let sig = Signal::new(80.0f32);
134 let w = AnimatedSize::width(crate::views::Unit, sig.clone().live());
135 let kids = w.children_override().expect("dziecko");
136 assert_eq!(kids.len(), 1);
137 sig.set(200.0);
138 let s = w.layout_style();
139 assert_eq!(s.width, Length::Px(200.0));
140 }
141
142 #[test]
143 fn runtime_anchors_then_animates() {
144 let rt = Runtime::new();
145 let el = ElementId {
146 index: 91,
147 generation: 0,
148 };
149 let spec = AnimSpec::from_duration(
150 Duration::from_secs_f32(10.0),
151 sequoia_animation::Easing::Linear,
152 );
153 let live = Live::fixed(80.0f32).animate(spec);
154 let w = AnimatedSize::width(crate::views::Unit, live);
155
156 let s1 = w.layout_style_ctx(&rt, el);
157 assert_eq!(s1.width, Length::Px(80.0));
158
159 let live2 = Live::fixed(200.0f32).animate(spec);
160 let w2 = AnimatedSize::width(crate::views::Unit, live2);
161 let s2 = w2.layout_style_ctx(&rt, el);
162 assert_eq!(s2.width, Length::Px(80.0));
163 assert_eq!(rt.live_anim_count(), 1);
164 }
165
166 #[test]
167 fn size_sets_both_axes() {
168 let w = AnimatedSize::size(crate::views::Unit, 100.0f32, 40.0f32);
169 let s = w.layout_style();
170 assert_eq!(s.width, Length::Px(100.0));
171 assert_eq!(s.height, Length::Px(40.0));
172 let w = w.animate(AnimSpec::FAST).spring(SpringSpec::BOUNCY);
173 let s = w.layout_style();
174 assert_eq!(s.width, Length::Px(100.0));
175 }
176}