1use std::rc::Rc;
4use std::time::Duration;
5
6use inset_animation::{
7 Animatable, Animation, AnimationBehavior, AnimationController, AnimationStatus,
8 AnimationStatusListener, Curve, CurvedAnimation, SizeTween,
9};
10use inset_embedder::{Clip, Offset, Size, TextBaseline, TextDirection};
11use inset_foundation::{App, Handle, Listener};
12use inset_painting::AlignmentGeometry;
13
14use crate::box_::{AnyRenderBox, BoxConstraints, BoxHitTestResult, RenderBox, RenderBoxData};
15use crate::layer::{ClipRectLayer, LayerHandle};
16use crate::object::{
17 AnyRenderObject, Constraints, RenderHandle, RenderObject, RenderObjectBase, RenderObjectData,
18 RenderObjectWithChildData, RenderObjectWithChildMixin,
19};
20use crate::painting_context::PaintingContext;
21use crate::pipeline_owner::PipelineOwner;
22use crate::shifted_box::{
23 RenderAligningShiftedBox, RenderAligningShiftedBoxData, RenderShiftedBox,
24};
25use crate::sliver_persistent_header::TickerProviderRef;
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum RenderAnimatedSizeState {
30 Start,
35 Stable,
41 Changed,
44 Unstable,
47}
48
49pub struct RenderAnimatedSize {
52 render_object: RenderObjectData,
53 render_box: RenderBoxData,
54 child: RenderObjectWithChildData<AnyRenderBox>,
55 aligning: RenderAligningShiftedBoxData,
56 vsync: TickerProviderRef,
57 clip_behavior: Clip,
58 controller: Handle<AnimationController>,
59 animation: Handle<CurvedAnimation>,
60 size_tween: Handle<SizeTween>,
61 has_visual_overflow: bool,
62 last_value: Option<f64>,
63 state: RenderAnimatedSizeState,
64 current_size: Size,
65 on_end: Option<Listener>,
66 clip_rect_layer: LayerHandle<Handle<ClipRectLayer>>,
67}
68
69impl RenderAnimatedSize {
70 #[allow(clippy::too_many_arguments)]
72 pub fn new(
73 app: &mut App,
74 vsync: TickerProviderRef,
75 duration: Duration,
76 reverse_duration: Option<Duration>,
77 curve: Rc<dyn Curve>,
78 alignment: AlignmentGeometry,
79 text_direction: Option<TextDirection>,
80 clip_behavior: Clip,
81 on_end: Option<Listener>,
82 child: Option<AnyRenderBox>,
83 ) -> RenderHandle<Self> {
84 let controller = AnimationController::create(
85 app,
86 None,
87 Some(duration),
88 reverse_duration,
89 0.0,
90 1.0,
91 AnimationBehavior::Normal,
92 vsync.clone(),
93 );
94 let animation = CurvedAnimation::create(app, controller.view(), curve, None);
95 let size_tween = SizeTween::new(app, None, None);
96 let this = RenderHandle::new_box(
97 app,
98 RenderAnimatedSize {
99 render_object: RenderObjectData::new(),
100 render_box: RenderBoxData::new(),
101 child: RenderObjectWithChildData::new(),
102 aligning: RenderAligningShiftedBoxData::new(alignment, text_direction),
103 vsync,
104 clip_behavior,
105 controller,
106 animation,
107 size_tween,
108 has_visual_overflow: false,
109 last_value: None,
110 state: RenderAnimatedSizeState::Start,
111 current_size: Size::ZERO,
112 on_end,
113 clip_rect_layer: LayerHandle::new(),
114 },
115 );
116 controller.add_listener(
117 app,
118 Listener::handle_method(this.handle(), on_controller_tick),
119 );
120 this.set_child(app, child);
121 this
122 }
123
124 pub fn duration(self: RenderHandle<Self>, app: &App) -> Duration {
126 app.get(self.get(app).controller)
127 .duration
128 .expect("duration")
129 }
130
131 pub fn set_duration(self: RenderHandle<Self>, app: &mut App, value: Duration) {
133 let controller = self.get(app).controller;
134 if app.get(controller).duration == Some(value) {
135 return;
136 }
137 app.get_mut(controller).duration = Some(value);
138 }
139
140 pub fn reverse_duration(self: RenderHandle<Self>, app: &App) -> Option<Duration> {
142 app.get(self.get(app).controller).reverse_duration
143 }
144
145 pub fn set_reverse_duration(self: RenderHandle<Self>, app: &mut App, value: Option<Duration>) {
147 let controller = self.get(app).controller;
148 if app.get(controller).reverse_duration == value {
149 return;
150 }
151 app.get_mut(controller).reverse_duration = value;
152 }
153
154 pub fn curve(self: RenderHandle<Self>, app: &App) -> Rc<dyn Curve> {
156 app.get(self.get(app).animation).curve.clone()
157 }
158
159 pub fn set_curve(self: RenderHandle<Self>, app: &mut App, value: Rc<dyn Curve>) {
161 let animation = self.get(app).animation;
162 if Rc::ptr_eq(&app.get(animation).curve, &value) {
163 return;
164 }
165 app.get_mut(animation).curve = value;
166 }
167
168 pub fn clip_behavior(self: RenderHandle<Self>, app: &App) -> Clip {
170 self.get(app).clip_behavior
171 }
172
173 pub fn set_clip_behavior(self: RenderHandle<Self>, app: &mut App, value: Clip) {
175 if self.get(app).clip_behavior == value {
176 return;
177 }
178 self.get_mut(app).clip_behavior = value;
179 RenderBox::mark_needs_paint(self, app);
180 }
181
182 pub fn is_animating(self: RenderHandle<Self>, app: &App) -> bool {
184 self.get(app).controller.as_animation().is_animating(app)
185 }
186
187 pub fn vsync(self: RenderHandle<Self>, app: &App) -> TickerProviderRef {
189 self.get(app).vsync.clone()
190 }
191
192 pub fn set_vsync(self: RenderHandle<Self>, app: &mut App, value: TickerProviderRef) {
194 if self.get(app).vsync == value {
195 return;
196 }
197 self.get_mut(app).vsync = value.clone();
198 let controller = self.get(app).controller;
199 controller.resync(app, value);
200 }
201
202 pub fn on_end(self: RenderHandle<Self>, app: &App) -> Option<Listener> {
204 self.get(app).on_end.clone()
205 }
206
207 pub fn set_on_end(self: RenderHandle<Self>, app: &mut App, value: Option<Listener>) {
209 self.get_mut(app).on_end = value;
210 }
211
212 pub fn state(self: RenderHandle<Self>, app: &App) -> RenderAnimatedSizeState {
214 self.get(app).state
215 }
216
217 fn animated_size(self: RenderHandle<Self>, app: &App) -> Option<Size> {
218 let size_tween = self.get(app).size_tween;
219 let animation = self.get(app).animation;
220 Animatable::evaluate(&size_tween, app, animation.as_animation())
221 }
222
223 fn restart_animation(self: RenderHandle<Self>, app: &mut App) {
224 self.get_mut(app).last_value = Some(0.0);
225 let controller = self.get(app).controller;
226 controller.forward(app, Some(0.0));
227 }
228
229 fn layout_start(self: RenderHandle<Self>, app: &mut App) {
230 let child_size = self.child(app).expect("child").size(app);
231 let size_tween = self.get(app).size_tween;
232 app.get_mut(size_tween).begin = Some(child_size);
233 app.get_mut(size_tween).end = Some(child_size);
234 self.get_mut(app).state = RenderAnimatedSizeState::Stable;
235 }
236
237 fn layout_stable(self: RenderHandle<Self>, app: &mut App) {
238 let child_size = self.child(app).expect("child").size(app);
239 let size_tween = self.get(app).size_tween;
240 let end = app.get(size_tween).end;
241 if end != Some(child_size) {
242 let size = self.size(app);
243 app.get_mut(size_tween).begin = Some(size);
244 app.get_mut(size_tween).end = Some(child_size);
245 self.restart_animation(app);
246 self.get_mut(app).state = RenderAnimatedSizeState::Changed;
247 } else {
248 let controller = self.get(app).controller;
249 let value = controller.value(app);
250 let upper = app.get(controller).upper_bound;
251 if value == upper {
252 app.get_mut(size_tween).begin = Some(child_size);
253 app.get_mut(size_tween).end = Some(child_size);
254 } else if !controller.is_animating(app) {
255 controller.forward(app, None);
256 }
257 }
258 }
259
260 fn layout_changed(self: RenderHandle<Self>, app: &mut App) {
261 let child_size = self.child(app).expect("child").size(app);
262 let size_tween = self.get(app).size_tween;
263 if app.get(size_tween).end != Some(child_size) {
264 app.get_mut(size_tween).begin = Some(child_size);
265 app.get_mut(size_tween).end = Some(child_size);
266 self.restart_animation(app);
267 self.get_mut(app).state = RenderAnimatedSizeState::Unstable;
268 } else {
269 self.get_mut(app).state = RenderAnimatedSizeState::Stable;
270 let controller = self.get(app).controller;
271 if !controller.is_animating(app) {
272 controller.forward(app, None);
273 }
274 }
275 }
276
277 fn layout_unstable(self: RenderHandle<Self>, app: &mut App) {
278 let child_size = self.child(app).expect("child").size(app);
279 let size_tween = self.get(app).size_tween;
280 if app.get(size_tween).end != Some(child_size) {
281 app.get_mut(size_tween).begin = Some(child_size);
282 app.get_mut(size_tween).end = Some(child_size);
283 self.restart_animation(app);
284 } else {
285 let controller = self.get(app).controller;
286 controller.stop(app, true);
287 self.get_mut(app).state = RenderAnimatedSizeState::Stable;
288 }
289 }
290}
291
292impl RenderObjectWithChildMixin for RenderAnimatedSize {
293 type ChildType = AnyRenderBox;
294
295 fn child_data(self: RenderHandle<Self>, app: &App) -> &RenderObjectWithChildData<AnyRenderBox> {
296 &self.get(app).child
297 }
298
299 fn child_data_mut(
300 self: RenderHandle<Self>,
301 app: &mut App,
302 ) -> &mut RenderObjectWithChildData<AnyRenderBox> {
303 &mut self.get_mut(app).child
304 }
305}
306
307impl RenderShiftedBox for RenderAnimatedSize {}
308
309impl RenderAligningShiftedBox for RenderAnimatedSize {
310 fn aligning_data(self: RenderHandle<Self>, app: &App) -> &RenderAligningShiftedBoxData {
311 &self.get(app).aligning
312 }
313
314 fn aligning_data_mut(
315 self: RenderHandle<Self>,
316 app: &mut App,
317 ) -> &mut RenderAligningShiftedBoxData {
318 &mut self.get_mut(app).aligning
319 }
320}
321
322impl RenderObject for RenderAnimatedSize {
323 crate::render_object_accessors!();
324
325 fn did_attach(self: RenderHandle<Self>, app: &mut App, owner: Handle<PipelineOwner>) {
326 if let Some(child) = self.child(app) {
327 child.as_object().attach(app, owner);
328 }
329 match self.get(app).state {
330 RenderAnimatedSizeState::Start | RenderAnimatedSizeState::Stable => {}
331 RenderAnimatedSizeState::Changed | RenderAnimatedSizeState::Unstable => {
332 RenderBox::mark_needs_layout(self, app);
333 }
334 }
335 let controller = self.get(app).controller;
336 controller.add_status_listener(
337 app,
338 AnimationStatusListener::handle_method(self.handle(), animation_status_listener),
339 );
340 }
341
342 fn did_detach(self: RenderHandle<Self>, app: &mut App) {
343 let controller = self.get(app).controller;
344 controller.stop(app, true);
345 controller.remove_status_listener(
346 app,
347 &AnimationStatusListener::handle_method(self.handle(), animation_status_listener),
348 );
349 if let Some(child) = self.child(app) {
350 child.as_object().detach(app);
351 }
352 }
353
354 fn perform_layout(self: RenderHandle<Self>, app: &mut App) {
355 let controller = self.get(app).controller;
356 let value = controller.value(app);
357 self.get_mut(app).last_value = Some(value);
358 self.get_mut(app).has_visual_overflow = false;
359 let constraints = self.constraints(app);
360 if self.child(app).is_none() || constraints.is_tight() {
361 controller.stop(app, true);
362 let smallest = constraints.smallest();
363 let size_tween = self.get(app).size_tween;
364 app.get_mut(size_tween).begin = Some(smallest);
365 app.get_mut(size_tween).end = Some(smallest);
366 self.set_size(app, smallest);
367 self.get_mut(app).current_size = smallest;
368 self.get_mut(app).state = RenderAnimatedSizeState::Start;
369 if let Some(child) = self.child(app) {
370 child.layout(app, constraints, false);
371 }
372 return;
373 }
374
375 let child = self.child(app).expect("child");
376 child.layout(app, constraints, true);
377
378 match self.get(app).state {
379 RenderAnimatedSizeState::Start => self.layout_start(app),
380 RenderAnimatedSizeState::Stable => self.layout_stable(app),
381 RenderAnimatedSizeState::Changed => self.layout_changed(app),
382 RenderAnimatedSizeState::Unstable => self.layout_unstable(app),
383 }
384
385 let animated = self.animated_size(app).expect("animated size");
386 let size = constraints.constrain(animated);
387 self.set_size(app, size);
388 self.get_mut(app).current_size = size;
389 self.align_child(app);
390
391 let end = app.get(self.get(app).size_tween).end.expect("end");
392 if size.width() < end.width() || size.height() < end.height() {
393 self.get_mut(app).has_visual_overflow = true;
394 }
395 }
396
397 fn paint(
398 self: RenderHandle<Self>,
399 app: &mut App,
400 context: &mut PaintingContext,
401 offset: Offset,
402 ) {
403 if self.child(app).is_some()
404 && self.get(app).has_visual_overflow
405 && self.get(app).clip_behavior != Clip::None
406 {
407 let clip_rect = Offset::ZERO & self.size(app);
408 let clip_behavior = self.get(app).clip_behavior;
409 let old = self.get(app).clip_rect_layer.layer();
410 let layer = context.push_clip_rect(
411 app,
412 self.as_object().needs_compositing(app),
413 offset,
414 clip_rect,
415 |app, context, offset| RenderShiftedBox::paint(self, app, context, offset),
416 clip_behavior,
417 old,
418 );
419 LayerHandle::set_layer(app, |app| &mut self.get_mut(app).clip_rect_layer, layer);
420 } else {
421 LayerHandle::set_layer(app, |app| &mut self.get_mut(app).clip_rect_layer, None);
422 RenderShiftedBox::paint(self, app, context, offset);
423 }
424 }
425
426 fn visit_children(
427 self: RenderHandle<Self>,
428 app: &App,
429 visitor: &mut dyn FnMut(AnyRenderObject),
430 ) {
431 if let Some(child) = self.child(app) {
432 visitor(child.as_object());
433 }
434 }
435
436 fn dispose(self: RenderHandle<Self>, app: &mut App) {
437 LayerHandle::set_layer(app, |app| &mut self.get_mut(app).clip_rect_layer, None);
438 let controller = self.get(app).controller;
439 let animation = self.get(app).animation;
440 controller.dispose(app);
441 animation.dispose(app);
442 RenderObjectBase::dispose(self, app);
443 }
444}
445
446impl RenderBox for RenderAnimatedSize {
447 crate::render_box_accessors!();
448
449 fn hit_test_children(
450 self: RenderHandle<Self>,
451 app: &mut App,
452 result: &mut BoxHitTestResult<'_>,
453 position: Offset,
454 ) -> bool {
455 RenderShiftedBox::hit_test_children(self, app, result, position)
456 }
457
458 fn compute_min_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
459 RenderShiftedBox::compute_min_intrinsic_width(self, app, height)
460 }
461
462 fn compute_max_intrinsic_width(self: RenderHandle<Self>, app: &mut App, height: f64) -> f64 {
463 RenderShiftedBox::compute_max_intrinsic_width(self, app, height)
464 }
465
466 fn compute_min_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
467 RenderShiftedBox::compute_min_intrinsic_height(self, app, width)
468 }
469
470 fn compute_max_intrinsic_height(self: RenderHandle<Self>, app: &mut App, width: f64) -> f64 {
471 RenderShiftedBox::compute_max_intrinsic_height(self, app, width)
472 }
473
474 fn compute_distance_to_actual_baseline(
475 self: RenderHandle<Self>,
476 app: &mut App,
477 baseline: TextBaseline,
478 ) -> Option<f64> {
479 RenderShiftedBox::compute_distance_to_actual_baseline(self, app, baseline)
480 }
481
482 fn compute_dry_layout(
483 self: RenderHandle<Self>,
484 app: &mut App,
485 constraints: BoxConstraints,
486 ) -> Size {
487 let Some(child) = self.child(app) else {
488 return constraints.smallest();
489 };
490 if constraints.is_tight() {
491 return constraints.smallest();
492 }
493 let child_size = child.get_dry_layout(app, constraints);
494 match self.get(app).state {
495 RenderAnimatedSizeState::Start => return constraints.constrain(child_size),
496 RenderAnimatedSizeState::Stable => {
497 if app.get(self.get(app).size_tween).end != Some(child_size) {
498 return constraints.constrain(self.get(app).current_size);
499 }
500 let controller = self.get(app).controller;
501 if controller.value(app) == app.get(controller).upper_bound {
502 return constraints.constrain(child_size);
503 }
504 }
505 RenderAnimatedSizeState::Unstable | RenderAnimatedSizeState::Changed => {
506 if app.get(self.get(app).size_tween).end != Some(child_size) {
507 return constraints.constrain(child_size);
508 }
509 }
510 }
511 constraints.constrain(self.animated_size(app).expect("animated size"))
512 }
513
514 fn compute_dry_baseline(
515 self: RenderHandle<Self>,
516 app: &mut App,
517 constraints: BoxConstraints,
518 baseline: TextBaseline,
519 ) -> Option<f64> {
520 let child = self.child(app)?;
521 let result = child.get_dry_baseline(app, constraints, baseline)?;
522 let child_size = child.get_dry_layout(app, constraints);
523 let my_size = self.get_dry_layout(app, constraints);
524 let offset = self
525 .resolved_alignment(app)
526 .along_offset(my_size - child_size);
527 Some(result + offset.dy())
528 }
529}
530
531impl std::fmt::Debug for RenderAnimatedSize {
532 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
533 f.debug_struct("RenderAnimatedSize").finish_non_exhaustive()
534 }
535}
536
537fn on_controller_tick(this: Handle<RenderAnimatedSize>, app: &mut App) {
538 let this = RenderHandle::from_handle(this);
539 let value = this.get(app).controller.value(app);
540 if this.get(app).last_value != Some(value) {
541 RenderBox::mark_needs_layout(this, app);
542 }
543}
544
545fn animation_status_listener(
546 this: Handle<RenderAnimatedSize>,
547 app: &mut App,
548 status: AnimationStatus,
549) {
550 let this = RenderHandle::from_handle(this);
551 if status.is_completed()
552 && let Some(on_end) = this.get(app).on_end.clone()
553 {
554 on_end.call(app);
555 }
556}