theme_testbench.rs

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