theme_testbench.rs

  1use gpui::{
  2    actions,
  3    color::Color,
  4    elements::{
  5        AnyElement, Canvas, Container, ContainerStyle, Flex, Label, Margin, MouseEventHandler,
  6        Padding, ParentElement,
  7    },
  8    fonts::TextStyle,
  9    AppContext, Border, Element, Entity, ModelHandle, Quad, Task, View, ViewContext, ViewHandle,
 10    WeakViewHandle,
 11};
 12use project::Project;
 13use settings::Settings;
 14use theme::{ColorScheme, Layer, Style, StyleSet};
 15use workspace::{item::Item, register_deserializable_item, Pane, Workspace};
 16
 17actions!(theme, [DeployThemeTestbench]);
 18
 19pub fn init(cx: &mut AppContext) {
 20    cx.add_action(ThemeTestbench::deploy);
 21
 22    register_deserializable_item::<ThemeTestbench>(cx)
 23}
 24
 25pub struct ThemeTestbench {}
 26
 27impl ThemeTestbench {
 28    pub fn deploy(
 29        workspace: &mut Workspace,
 30        _: &DeployThemeTestbench,
 31        cx: &mut ViewContext<Workspace>,
 32    ) {
 33        let view = cx.add_view(|_| ThemeTestbench {});
 34        workspace.add_item(Box::new(view), cx);
 35    }
 36
 37    fn render_ramps(color_scheme: &ColorScheme) -> Flex<Self> {
 38        fn display_ramp(ramp: &Vec<Color>) -> AnyElement<ThemeTestbench> {
 39            Flex::row()
 40                .with_children(ramp.iter().cloned().map(|color| {
 41                    Canvas::new(move |scene, bounds, _, _, _| {
 42                        scene.push_quad(Quad {
 43                            bounds,
 44                            background: Some(color),
 45                            ..Default::default()
 46                        });
 47                    })
 48                    .flex(1.0, false)
 49                }))
 50                .flex(1.0, false)
 51                .into_any()
 52        }
 53
 54        Flex::column()
 55            .with_child(display_ramp(&color_scheme.ramps.neutral))
 56            .with_child(display_ramp(&color_scheme.ramps.red))
 57            .with_child(display_ramp(&color_scheme.ramps.orange))
 58            .with_child(display_ramp(&color_scheme.ramps.yellow))
 59            .with_child(display_ramp(&color_scheme.ramps.green))
 60            .with_child(display_ramp(&color_scheme.ramps.cyan))
 61            .with_child(display_ramp(&color_scheme.ramps.blue))
 62            .with_child(display_ramp(&color_scheme.ramps.violet))
 63            .with_child(display_ramp(&color_scheme.ramps.magenta))
 64    }
 65
 66    fn render_layer(
 67        layer_index: usize,
 68        layer: &Layer,
 69        cx: &mut ViewContext<Self>,
 70    ) -> Container<Self> {
 71        Flex::column()
 72            .with_child(
 73                Self::render_button_set(0, layer_index, "base", &layer.base, cx).flex(1., false),
 74            )
 75            .with_child(
 76                Self::render_button_set(1, layer_index, "variant", &layer.variant, cx)
 77                    .flex(1., false),
 78            )
 79            .with_child(
 80                Self::render_button_set(2, layer_index, "on", &layer.on, cx).flex(1., false),
 81            )
 82            .with_child(
 83                Self::render_button_set(3, layer_index, "accent", &layer.accent, cx)
 84                    .flex(1., false),
 85            )
 86            .with_child(
 87                Self::render_button_set(4, layer_index, "positive", &layer.positive, cx)
 88                    .flex(1., false),
 89            )
 90            .with_child(
 91                Self::render_button_set(5, layer_index, "warning", &layer.warning, cx)
 92                    .flex(1., false),
 93            )
 94            .with_child(
 95                Self::render_button_set(6, layer_index, "negative", &layer.negative, cx)
 96                    .flex(1., false),
 97            )
 98            .contained()
 99            .with_style(ContainerStyle {
100                margin: Margin {
101                    top: 10.,
102                    bottom: 10.,
103                    left: 10.,
104                    right: 10.,
105                },
106                background_color: Some(layer.base.default.background),
107                ..Default::default()
108            })
109    }
110
111    fn render_button_set(
112        set_index: usize,
113        layer_index: usize,
114        set_name: &'static str,
115        style_set: &StyleSet,
116        cx: &mut ViewContext<Self>,
117    ) -> Flex<Self> {
118        Flex::row()
119            .with_child(Self::render_button(
120                set_index * 6,
121                layer_index,
122                set_name,
123                &style_set,
124                None,
125                cx,
126            ))
127            .with_child(Self::render_button(
128                set_index * 6 + 1,
129                layer_index,
130                "hovered",
131                &style_set,
132                Some(|style_set| &style_set.hovered),
133                cx,
134            ))
135            .with_child(Self::render_button(
136                set_index * 6 + 2,
137                layer_index,
138                "pressed",
139                &style_set,
140                Some(|style_set| &style_set.pressed),
141                cx,
142            ))
143            .with_child(Self::render_button(
144                set_index * 6 + 3,
145                layer_index,
146                "active",
147                &style_set,
148                Some(|style_set| &style_set.active),
149                cx,
150            ))
151            .with_child(Self::render_button(
152                set_index * 6 + 4,
153                layer_index,
154                "disabled",
155                &style_set,
156                Some(|style_set| &style_set.disabled),
157                cx,
158            ))
159            .with_child(Self::render_button(
160                set_index * 6 + 5,
161                layer_index,
162                "inverted",
163                &style_set,
164                Some(|style_set| &style_set.inverted),
165                cx,
166            ))
167    }
168
169    fn render_button(
170        button_index: usize,
171        layer_index: usize,
172        text: &'static str,
173        style_set: &StyleSet,
174        style_override: Option<fn(&StyleSet) -> &Style>,
175        cx: &mut ViewContext<Self>,
176    ) -> AnyElement<Self> {
177        enum TestBenchButton {}
178        MouseEventHandler::<TestBenchButton, _>::new(layer_index + button_index, cx, |state, cx| {
179            let style = if let Some(style_override) = style_override {
180                style_override(&style_set)
181            } else if state.clicked().is_some() {
182                &style_set.pressed
183            } else if state.hovered() {
184                &style_set.hovered
185            } else {
186                &style_set.default
187            };
188
189            Self::render_label(text.to_string(), style, cx)
190                .contained()
191                .with_style(ContainerStyle {
192                    margin: Margin {
193                        top: 4.,
194                        bottom: 4.,
195                        left: 4.,
196                        right: 4.,
197                    },
198                    padding: Padding {
199                        top: 4.,
200                        bottom: 4.,
201                        left: 4.,
202                        right: 4.,
203                    },
204                    background_color: Some(style.background),
205                    border: Border {
206                        width: 1.,
207                        color: style.border,
208                        overlay: false,
209                        top: true,
210                        bottom: true,
211                        left: true,
212                        right: true,
213                    },
214                    corner_radius: 2.,
215                    ..Default::default()
216                })
217        })
218        .flex(1., true)
219        .into_any()
220    }
221
222    fn render_label(text: String, style: &Style, cx: &mut ViewContext<Self>) -> Label {
223        let settings = cx.global::<Settings>();
224        let font_cache = cx.font_cache();
225        let family_id = settings.buffer_font_family;
226        let font_size = settings.buffer_font_size;
227        let font_id = font_cache
228            .select_font(family_id, &Default::default())
229            .unwrap();
230
231        let text_style = TextStyle {
232            color: style.foreground,
233            font_family_id: family_id,
234            font_family_name: font_cache.family_name(family_id).unwrap(),
235            font_id,
236            font_size,
237            font_properties: Default::default(),
238            underline: Default::default(),
239        };
240
241        Label::new(text, text_style)
242    }
243}
244
245impl Entity for ThemeTestbench {
246    type Event = ();
247}
248
249impl View for ThemeTestbench {
250    fn ui_name() -> &'static str {
251        "ThemeTestbench"
252    }
253
254    fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> AnyElement<Self> {
255        let color_scheme = &cx.global::<Settings>().theme.clone().color_scheme;
256
257        Flex::row()
258            .with_child(
259                Self::render_ramps(color_scheme)
260                    .contained()
261                    .with_margin_right(10.)
262                    .flex(0.1, false),
263            )
264            .with_child(
265                Flex::column()
266                    .with_child(Self::render_layer(100, &color_scheme.lowest, cx).flex(1., true))
267                    .with_child(Self::render_layer(200, &color_scheme.middle, cx).flex(1., true))
268                    .with_child(Self::render_layer(300, &color_scheme.highest, cx).flex(1., true))
269                    .flex(1., false),
270            )
271            .into_any()
272    }
273}
274
275impl Item for ThemeTestbench {
276    fn tab_content<T: View>(
277        &self,
278        _: Option<usize>,
279        style: &theme::Tab,
280        _: &AppContext,
281    ) -> AnyElement<T> {
282        Label::new("Theme Testbench", style.label.clone())
283            .aligned()
284            .contained()
285            .into_any()
286    }
287
288    fn serialized_item_kind() -> Option<&'static str> {
289        Some("ThemeTestBench")
290    }
291
292    fn deserialize(
293        _project: ModelHandle<Project>,
294        _workspace: WeakViewHandle<Workspace>,
295        _workspace_id: workspace::WorkspaceId,
296        _item_id: workspace::ItemId,
297        cx: &mut ViewContext<Pane>,
298    ) -> Task<gpui::anyhow::Result<ViewHandle<Self>>> {
299        Task::ready(Ok(cx.add_view(|_| Self {})))
300    }
301}