1use gpui::{App, ElementId, Window};
2
3use super::{Instant, Interpolate, MotionStatus, Transition, TransitionId};
4
5#[derive(Clone)]
7pub struct SequenceStep<T> {
8 target: T,
9 transition: Transition,
10}
11
12impl<T> SequenceStep<T> {
13 pub fn new(target: T, transition: Transition) -> Self {
14 Self { target, transition }
15 }
16
17 pub fn target(&self) -> &T {
18 &self.target
19 }
20
21 pub fn transition(&self) -> &Transition {
22 &self.transition
23 }
24}
25
26#[derive(Clone, Copy, Debug, PartialEq)]
28pub struct SequenceSample<T> {
29 value: T,
30 step: usize,
31 status: MotionStatus,
32}
33
34impl<T> SequenceSample<T> {
35 pub fn value(&self) -> &T {
37 &self.value
38 }
39
40 pub fn into_value(self) -> T {
41 self.value
42 }
43
44 pub fn step(&self) -> usize {
47 self.step
48 }
49
50 pub fn status(&self) -> MotionStatus {
54 self.status
55 }
56
57 pub fn is_finished(&self) -> bool {
58 self.status == MotionStatus::Finished
59 }
60
61 fn is_active(&self) -> bool {
62 matches!(self.status, MotionStatus::Delayed | MotionStatus::Running)
63 }
64}
65
66#[derive(Clone)]
92pub struct Sequence<T> {
93 id: TransitionId,
94 from: T,
95 steps: Vec<SequenceStep<T>>,
96}
97
98impl<T> Sequence<T>
99where
100 T: Interpolate + PartialEq + 'static,
101{
102 pub fn new(id: impl Into<TransitionId>, from: T) -> Self {
104 Self {
105 id: id.into(),
106 from,
107 steps: Vec::new(),
108 }
109 }
110
111 pub fn with_step(mut self, target: T, transition: Transition) -> Self {
113 self.steps.push(SequenceStep::new(target, transition));
114 self
115 }
116
117 pub fn with_steps(mut self, steps: impl IntoIterator<Item = SequenceStep<T>>) -> Self {
119 self.steps.extend(steps);
120 self
121 }
122
123 pub fn from(&self) -> &T {
124 &self.from
125 }
126
127 pub fn steps(&self) -> &[SequenceStep<T>] {
128 &self.steps
129 }
130
131 pub fn sample(self, window: &mut Window, cx: &mut App) -> SequenceSample<T> {
133 let Some(last) = self.steps.last() else {
134 return SequenceSample {
135 value: self.from,
136 step: 0,
137 status: MotionStatus::Idle,
138 };
139 };
140 let last_step = self.steps.len() - 1;
141
142 let id = ElementId::NamedChild(self.id.0.into(), "__sequence".into());
143 let now = cx.background_executor().now();
144 let state = window.use_keyed_state(id, cx, |_, _| {
145 SequenceState::start(0, self.from.clone(), &self.steps, now)
146 });
147
148 if cx.reduce_motion() {
149 let settled = state.read(cx);
150 if settled.step != last_step || settled.from != last.target {
151 state.update(cx, |state, _| {
152 *state = SequenceState::start(last_step, last.target.clone(), &self.steps, now);
153 });
154 }
155 return SequenceSample {
156 value: last.target.clone(),
157 step: last_step,
158 status: MotionStatus::Finished,
159 };
160 }
161
162 let snapshot = state.read(cx);
163 let (progress, status) = snapshot.progress(now);
164 let retargeted =
165 snapshot.step > last_step || self.steps[snapshot.step].target != snapshot.target;
166 let handing_over = status == MotionStatus::Finished && snapshot.step < last_step;
167
168 if !retargeted && !handing_over {
169 let sample = SequenceSample {
170 value: snapshot.value(progress),
171 step: snapshot.step,
172 status,
173 };
174 if sample.is_active() {
175 window.request_animation_frame();
176 }
177 return sample;
178 }
179
180 let mut next = if retargeted {
186 SequenceState::start(0, snapshot.value(progress), &self.steps, now)
187 } else {
188 snapshot.hand_over(&self.steps)
189 };
190 let (value, status) = loop {
191 let (progress, status) = next.progress(now);
192 if status == MotionStatus::Finished && next.step < last_step {
193 next = next.hand_over(&self.steps);
194 continue;
195 }
196 break (next.value(progress), status);
197 };
198 let sample = SequenceSample {
199 value,
200 step: next.step,
201 status,
202 };
203 state.update(cx, |state, _| *state = next);
204 if sample.is_active() {
205 window.request_animation_frame();
206 }
207 sample
208 }
209}
210
211#[derive(Clone)]
214struct SequenceState<T> {
215 step: usize,
216 from: T,
217 target: T,
218 transition: Transition,
219 started_at: Instant,
220}
221
222impl<T: Interpolate> SequenceState<T> {
223 fn start(step: usize, from: T, steps: &[SequenceStep<T>], started_at: Instant) -> Self {
224 Self {
225 step,
226 from,
227 target: steps[step].target.clone(),
228 transition: steps[step].transition.clone(),
229 started_at,
230 }
231 }
232
233 fn progress(&self, now: Instant) -> (f32, MotionStatus) {
234 self.transition.progress(
235 now.saturating_duration_since(self.started_at),
236 self.transition.duration,
237 )
238 }
239
240 fn value(&self, progress: f32) -> T {
241 self.from
242 .interpolate(&self.target, self.transition.sample(progress))
243 }
244
245 fn hand_over(&self, steps: &[SequenceStep<T>]) -> Self {
249 Self::start(
250 self.step + 1,
251 self.target.clone(),
252 steps,
253 self.started_at + self.transition.finishes_after(),
254 )
255 }
256}