1use gpui_ce_motion::{Motion, MotionValue, Transition, ValueFrame};
20use gpui_kit::{AnyElement, App, Empty, IntoElement, Styled, Window, px};
21
22use std::{
23 ops::Deref,
24 time::{Duration, Instant},
25};
26
27pub use gpui_ce_motion as core;
29
30mod layout;
31mod vector;
32
33pub use vector::{VectorElement, VectorState};
34
35pub use layout::{LayoutGroup, LayoutPositionElement};
36
37#[derive(Debug, Default)]
39pub struct MotionState {
40 inner: gpui_ce_motion::MotionState,
41 epoch: Option<Instant>,
42}
43
44impl MotionState {
45 pub fn frame(&mut self, motion: Motion, window: &mut Window, cx: &App) -> Frame {
48 let now = cx.background_executor().now();
49 let epoch = *self.epoch.get_or_insert(now);
50
51 let frame = self.inner.sample(
52 motion,
53 now.saturating_duration_since(epoch),
54 cx.reduce_motion(),
55 );
56
57 if frame.is_animating() {
58 window.request_animation_frame();
59 }
60
61 Frame(frame)
62 }
63
64 pub fn sample(&mut self, motion: Motion, now: Duration, reduced_motion: bool) -> Frame {
67 Frame(self.inner.sample(motion, now, reduced_motion))
68 }
69}
70
71#[derive(Clone, Copy, Debug)]
73pub struct Frame(gpui_ce_motion::Frame);
74
75impl Deref for Frame {
76 type Target = gpui_ce_motion::Frame;
77
78 fn deref(&self) -> &Self::Target {
79 &self.0
80 }
81}
82
83impl Frame {
84 pub fn apply<E: Styled>(self, mut element: E) -> E {
88 let target = self.target();
89
90 if let Some(width) = target.width_px() {
91 element = element.w(px(width));
92 }
93
94 if let Some(height) = target.height_px() {
95 element = element.h(px(height));
96 }
97
98 if let Some(radius) = target.border_radius_px() {
99 element = element.rounded(px(radius));
100 }
101
102 element
103 .relative()
104 .left(px(target.x_px()))
105 .top(px(target.y_px()))
106 .opacity(target.alpha())
107 }
108
109 pub fn render<E: IntoElement>(self, build: impl FnOnce(Self) -> E) -> AnyElement {
112 if self.is_mounted() {
113 build(self).into_any_element()
114 } else {
115 Empty.into_any_element()
116 }
117 }
118}
119
120#[derive(Debug)]
122pub struct ValueState {
123 inner: MotionValue,
124 epoch: Option<Instant>,
125}
126
127impl ValueState {
128 pub fn new(value: f64) -> Self {
130 Self {
131 inner: MotionValue::new(value),
132 epoch: None,
133 }
134 }
135
136 pub fn frame(
138 &mut self,
139 target: f64,
140 transition: Transition,
141 window: &mut Window,
142 cx: &App,
143 ) -> ValueFrame {
144 let clock = cx.background_executor().now();
145 let epoch = *self.epoch.get_or_insert(clock);
146 let now = clock.saturating_duration_since(epoch);
147
148 self.inner.animate(target, transition, now);
149 let frame = self.inner.sample(now, cx.reduce_motion());
150
151 if frame.is_animating() {
152 window.request_animation_frame();
153 }
154
155 frame
156 }
157}
158
159mod text {
160 gpui_ce_motion::__motion_text_adapter!(gpui_kit);
161}
162
163pub use gpui_ce_motion::{
164 NumberEffect, Scramble, TextClip, TextError, TextMatch, TextPreset, TextTarget, TextUnit,
165 TextUnitInfo,
166};
167
168pub use text::{NumberElement, NumberMotionState, TextElement, TextMotionState};