diff --git a/crates/agent/src/active_thread.rs b/crates/agent/src/active_thread.rs index 2b54fe962f..92dc8ab0e0 100644 --- a/crates/agent/src/active_thread.rs +++ b/crates/agent/src/active_thread.rs @@ -1222,17 +1222,30 @@ impl ActiveThread { Label::new("Generating") .color(Color::Muted) .size(LabelSize::Small) - .with_animation( + .with_animations( "generating-label", - Animation::new(Duration::from_secs(1)).repeat(), - |mut label, delta| { - let text = match delta { - d if d < 0.25 => "Generating", - d if d < 0.5 => "Generating.", - d if d < 0.75 => "Generating..", - _ => "Generating...", - }; - label.set_text(text); + vec![ + Animation::new(Duration::from_secs(1)), + Animation::new(Duration::from_secs(1)).repeat(), + ], + |mut label, animation_ix, delta| { + match animation_ix { + 0 => { + let chars_to_show = (delta * 10.).ceil() as usize; + let text = &"Generating"[0..chars_to_show]; + label.set_text(text); + } + 1 => { + let text = match delta { + d if d < 0.25 => "Generating", + d if d < 0.5 => "Generating.", + d if d < 0.75 => "Generating..", + _ => "Generating...", + }; + label.set_text(text); + } + _ => {} + } label }, ) diff --git a/crates/gpui/src/elements/animation.rs b/crates/gpui/src/elements/animation.rs index 2db3d3c049..fc2baaaf17 100644 --- a/crates/gpui/src/elements/animation.rs +++ b/crates/gpui/src/elements/animation.rs @@ -3,6 +3,7 @@ use std::time::{Duration, Instant}; use crate::{AnyElement, App, Element, ElementId, GlobalElementId, IntoElement, Window}; pub use easing::*; +use smallvec::SmallVec; /// An animation that can be applied to an element. pub struct Animation { @@ -50,6 +51,24 @@ pub trait AnimationExt { animation: Animation, animator: impl Fn(Self, f32) -> Self + 'static, ) -> AnimationElement + where + Self: Sized, + { + AnimationElement { + id: id.into(), + element: Some(self), + animator: Box::new(move |this, _, value| animator(this, value)), + animations: smallvec::smallvec![animation], + } + } + + /// Render this component or element with a chain of animations + fn with_animations( + self, + id: impl Into, + animations: Vec, + animator: impl Fn(Self, usize, f32) -> Self + 'static, + ) -> AnimationElement where Self: Sized, { @@ -57,7 +76,7 @@ pub trait AnimationExt { id: id.into(), element: Some(self), animator: Box::new(animator), - animation, + animations: animations.into(), } } } @@ -68,8 +87,8 @@ impl AnimationExt for E {} pub struct AnimationElement { id: ElementId, element: Option, - animation: Animation, - animator: Box E + 'static>, + animations: SmallVec<[Animation; 1]>, + animator: Box E + 'static>, } impl AnimationElement { @@ -91,6 +110,7 @@ impl IntoElement for AnimationElement { struct AnimationState { start: Instant, + animation_ix: usize, } impl Element for AnimationElement { @@ -108,22 +128,30 @@ impl Element for AnimationElement { cx: &mut App, ) -> (crate::LayoutId, Self::RequestLayoutState) { window.with_element_state(global_id.unwrap(), |state, window| { - let state = state.unwrap_or_else(|| AnimationState { + let mut state = state.unwrap_or_else(|| AnimationState { start: Instant::now(), + animation_ix: 0, }); - let mut delta = - state.start.elapsed().as_secs_f32() / self.animation.duration.as_secs_f32(); + let animation_ix = state.animation_ix; + + let mut delta = state.start.elapsed().as_secs_f32() + / self.animations[animation_ix].duration.as_secs_f32(); let mut done = false; if delta > 1.0 { - if self.animation.oneshot { - done = true; + if self.animations[animation_ix].oneshot { + if animation_ix >= self.animations.len() - 1 { + done = true; + } else { + state.start = Instant::now(); + state.animation_ix += 1; + } delta = 1.0; } else { delta %= 1.0; } } - let delta = (self.animation.easing)(delta); + let delta = (self.animations[animation_ix].easing)(delta); debug_assert!( (0.0..=1.0).contains(&delta), @@ -131,7 +159,7 @@ impl Element for AnimationElement { ); let element = self.element.take().expect("should only be called once"); - let mut element = (self.animator)(element, delta).into_any_element(); + let mut element = (self.animator)(element, animation_ix, delta).into_any_element(); if !done { window.request_animation_frame();