1use crate::core::{Color, ColorProperty, StyleMotion, StyleProperty};
3use gpui_kit::{App, Rgba, Window};
4use std::{ops::Deref, time::Duration};
5
6pub fn color_to_rgba(color: Color) -> Rgba {
8 let [r, g, b, a] = color.channels();
9
10 Rgba { r, g, b, a }
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct UnsupportedStyle(StyleProperty);
16
17impl UnsupportedStyle {
18 pub fn property(self) -> StyleProperty {
20 self.0
21 }
22}
23
24impl std::fmt::Display for UnsupportedStyle {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 write!(
27 f,
28 "stock GPUI Kit does not support the {:?} style track",
29 self.0
30 )
31 }
32}
33
34impl std::error::Error for UnsupportedStyle {}
35
36#[derive(Debug, Default)]
38pub struct StyleMotionState {
39 inner: crate::core::StyleMotionState,
40 clock: crate::Clock,
41}
42
43impl StyleMotionState {
44 pub fn frame(&mut self, motion: StyleMotion, window: &mut Window, cx: &App) -> StyleFrame {
47 let now = self.clock.tick(cx);
48
49 let frame = self.sample(motion, now, cx.reduce_motion());
50
51 if frame.is_animating() {
52 window.request_animation_frame();
53 }
54
55 frame
56 }
57
58 pub fn sample(
60 &mut self,
61 motion: StyleMotion,
62 now: Duration,
63 reduced_motion: bool,
64 ) -> StyleFrame {
65 StyleFrame(self.inner.sample(motion, now, reduced_motion))
66 }
67}
68
69#[derive(Clone, Copy, Debug)]
71pub struct StyleFrame(crate::core::StyleFrame);
72
73impl From<crate::core::StyleFrame> for StyleFrame {
74 fn from(frame: crate::core::StyleFrame) -> Self {
75 Self(frame)
76 }
77}
78
79impl Deref for StyleFrame {
80 type Target = crate::core::StyleFrame;
81
82 fn deref(&self) -> &Self::Target {
83 &self.0
84 }
85}
86
87impl StyleFrame {
88 pub fn apply<E: gpui_kit::Styled>(self, element: E) -> E {
95 self.try_apply(element).expect("unsupported Kit style")
96 }
97
98 pub fn try_apply<E: gpui_kit::Styled>(self, mut element: E) -> Result<E, UnsupportedStyle> {
105 for property in [StyleProperty::Blur, StyleProperty::BackdropBlur] {
106 if self.target().get(property).is_some() {
107 return Err(UnsupportedStyle(property));
108 }
109 }
110
111 use StyleProperty::*;
112 use gpui_kit::px;
113
114 if let Some(v) = self.target().get(RadiusTopLeft) {
115 element.style().corner_radii.top_left = Some(px(v).into());
116 }
117
118 if let Some(v) = self.target().get(RadiusTopRight) {
119 element.style().corner_radii.top_right = Some(px(v).into());
120 }
121
122 if let Some(v) = self.target().get(RadiusBottomRight) {
123 element.style().corner_radii.bottom_right = Some(px(v).into());
124 }
125
126 if let Some(v) = self.target().get(RadiusBottomLeft) {
127 element.style().corner_radii.bottom_left = Some(px(v).into());
128 }
129
130 if let Some(v) = self.target().get(BorderTop) {
131 element.style().border_widths.top = Some(px(v).into());
132 }
133
134 if let Some(v) = self.target().get(BorderRight) {
135 element.style().border_widths.right = Some(px(v).into());
136 }
137
138 if let Some(v) = self.target().get(BorderBottom) {
139 element.style().border_widths.bottom = Some(px(v).into());
140 }
141
142 if let Some(v) = self.target().get(BorderLeft) {
143 element.style().border_widths.left = Some(px(v).into());
144 }
145
146 if let Some(v) = self.target().get(PaddingTop) {
147 element.style().padding.top = Some(px(v).into());
148 }
149
150 if let Some(v) = self.target().get(PaddingRight) {
151 element.style().padding.right = Some(px(v).into());
152 }
153
154 if let Some(v) = self.target().get(PaddingBottom) {
155 element.style().padding.bottom = Some(px(v).into());
156 }
157
158 if let Some(v) = self.target().get(PaddingLeft) {
159 element.style().padding.left = Some(px(v).into());
160 }
161
162 if let Some(v) = self.target().get(MarginTop) {
163 element.style().margin.top = Some(px(v).into());
164 }
165
166 if let Some(v) = self.target().get(MarginRight) {
167 element.style().margin.right = Some(px(v).into());
168 }
169
170 if let Some(v) = self.target().get(MarginBottom) {
171 element.style().margin.bottom = Some(px(v).into());
172 }
173
174 if let Some(v) = self.target().get(MarginLeft) {
175 element.style().margin.left = Some(px(v).into());
176 }
177
178 if let Some(v) = self.target().get(GapX) {
179 element.style().gap.width = Some(px(v).into());
180 }
181
182 if let Some(v) = self.target().get(GapY) {
183 element.style().gap.height = Some(px(v).into());
184 }
185
186 if let Some(color) = self.target().get_color(ColorProperty::Background) {
187 element = element.bg(color_to_rgba(color));
188 }
189
190 if let Some(color) = self.target().get_color(ColorProperty::Text) {
191 element = element.text_color(color_to_rgba(color));
192 }
193
194 if let Some(color) = self.target().get_color(ColorProperty::Border) {
195 element = element.border_color(color_to_rgba(color));
196 }
197
198 if [ShadowX, ShadowY, ShadowBlur, ShadowSpread]
199 .iter()
200 .any(|property| self.target().get(*property).is_some())
201 || self.target().get_color(ColorProperty::Shadow).is_some()
202 {
203 let color = self
204 .target()
205 .get_color(ColorProperty::Shadow)
206 .unwrap_or(Color::rgba(0., 0., 0., 0.25));
207
208 let color = color_to_rgba(color);
209
210 element.style().box_shadow = Some(vec![
211 gpui_kit::BoxShadow::new(
212 px(self.target().get(ShadowX).unwrap_or(0.)),
213 px(self.target().get(ShadowY).unwrap_or(0.)),
214 color.into(),
215 )
216 .blur_radius(px(self.target().get(ShadowBlur).unwrap_or(0.)))
217 .spread_radius(px(self.target().get(ShadowSpread).unwrap_or(0.))),
218 ]);
219 }
220
221 Ok(element)
222 }
223}