theme_preview.rs

  1#![allow(unused, dead_code)]
  2use gpui::{actions, AppContext, EventEmitter, FocusHandle, FocusableView, Hsla};
  3use theme::all_theme_colors;
  4use ui::{
  5    prelude::*, utils::calculate_contrast_ratio, AudioStatus, Availability, Avatar,
  6    AvatarAudioStatusIndicator, AvatarAvailabilityIndicator, ButtonLike, ElevationIndex, Facepile,
  7    TintColor, Tooltip,
  8};
  9
 10use crate::{Item, Workspace};
 11
 12actions!(debug, [OpenThemePreview]);
 13
 14pub fn init(cx: &mut AppContext) {
 15    cx.observe_new_views(|workspace: &mut Workspace, _| {
 16        workspace.register_action(|workspace, _: &OpenThemePreview, cx| {
 17            let theme_preview = cx.new_view(ThemePreview::new);
 18            workspace.add_item_to_active_pane(Box::new(theme_preview), None, true, cx)
 19        });
 20    })
 21    .detach();
 22}
 23
 24struct ThemePreview {
 25    focus_handle: FocusHandle,
 26}
 27
 28impl ThemePreview {
 29    pub fn new(cx: &mut ViewContext<Self>) -> Self {
 30        Self {
 31            focus_handle: cx.focus_handle(),
 32        }
 33    }
 34}
 35
 36impl EventEmitter<()> for ThemePreview {}
 37
 38impl FocusableView for ThemePreview {
 39    fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle {
 40        self.focus_handle.clone()
 41    }
 42}
 43impl ThemePreview {}
 44
 45impl Item for ThemePreview {
 46    type Event = ();
 47
 48    fn to_item_events(_: &Self::Event, _: impl FnMut(crate::item::ItemEvent)) {}
 49
 50    fn tab_content_text(&self, cx: &WindowContext) -> Option<SharedString> {
 51        let name = cx.theme().name.clone();
 52        Some(format!("{} Preview", name).into())
 53    }
 54
 55    fn telemetry_event_text(&self) -> Option<&'static str> {
 56        None
 57    }
 58
 59    fn clone_on_split(
 60        &self,
 61        _workspace_id: Option<crate::WorkspaceId>,
 62        cx: &mut ViewContext<Self>,
 63    ) -> Option<gpui::View<Self>>
 64    where
 65        Self: Sized,
 66    {
 67        Some(cx.new_view(Self::new))
 68    }
 69}
 70
 71const AVATAR_URL: &str = "https://avatars.githubusercontent.com/u/1714999?v=4";
 72
 73impl ThemePreview {
 74    fn preview_bg(cx: &WindowContext) -> Hsla {
 75        cx.theme().colors().editor_background
 76    }
 77
 78    fn render_avatars(&self, cx: &ViewContext<Self>) -> impl IntoElement {
 79        v_flex()
 80            .gap_1()
 81            .child(
 82                Headline::new("Avatars")
 83                    .size(HeadlineSize::Small)
 84                    .color(Color::Muted),
 85            )
 86            .child(
 87                h_flex()
 88                    .items_start()
 89                    .gap_4()
 90                    .child(Avatar::new(AVATAR_URL).size(px(24.)))
 91                    .child(Avatar::new(AVATAR_URL).size(px(24.)).grayscale(true))
 92                    .child(
 93                        Avatar::new(AVATAR_URL)
 94                            .size(px(24.))
 95                            .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Muted)),
 96                    )
 97                    .child(
 98                        Avatar::new(AVATAR_URL)
 99                            .size(px(24.))
100                            .indicator(AvatarAudioStatusIndicator::new(AudioStatus::Deafened)),
101                    )
102                    .child(
103                        Avatar::new(AVATAR_URL)
104                            .size(px(24.))
105                            .indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
106                    )
107                    .child(
108                        Avatar::new(AVATAR_URL)
109                            .size(px(24.))
110                            .indicator(AvatarAvailabilityIndicator::new(Availability::Free)),
111                    )
112                    .child(
113                        Facepile::empty()
114                            .child(
115                                Avatar::new(AVATAR_URL)
116                                    .border_color(Self::preview_bg(cx))
117                                    .size(px(22.))
118                                    .into_any_element(),
119                            )
120                            .child(
121                                Avatar::new(AVATAR_URL)
122                                    .border_color(Self::preview_bg(cx))
123                                    .size(px(22.))
124                                    .into_any_element(),
125                            )
126                            .child(
127                                Avatar::new(AVATAR_URL)
128                                    .border_color(Self::preview_bg(cx))
129                                    .size(px(22.))
130                                    .into_any_element(),
131                            )
132                            .child(
133                                Avatar::new(AVATAR_URL)
134                                    .border_color(Self::preview_bg(cx))
135                                    .size(px(22.))
136                                    .into_any_element(),
137                            ),
138                    ),
139            )
140    }
141
142    fn render_buttons(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
143        v_flex()
144            .gap_1()
145            .child(
146                Headline::new("Buttons")
147                    .size(HeadlineSize::Small)
148                    .color(Color::Muted),
149            )
150            .child(
151                h_flex()
152                    .items_start()
153                    .gap_px()
154                    .child(
155                        IconButton::new("icon_button_transparent", IconName::Check)
156                            .style(ButtonStyle::Transparent),
157                    )
158                    .child(
159                        IconButton::new("icon_button_subtle", IconName::Check)
160                            .style(ButtonStyle::Subtle),
161                    )
162                    .child(
163                        IconButton::new("icon_button_filled", IconName::Check)
164                            .style(ButtonStyle::Filled),
165                    )
166                    .child(
167                        IconButton::new("icon_button_selected_accent", IconName::Check)
168                            .selected_style(ButtonStyle::Tinted(TintColor::Accent))
169                            .selected(true),
170                    )
171                    .child(IconButton::new("icon_button_selected", IconName::Check).selected(true))
172                    .child(
173                        IconButton::new("icon_button_positive", IconName::Check)
174                            .style(ButtonStyle::Tinted(TintColor::Positive)),
175                    )
176                    .child(
177                        IconButton::new("icon_button_warning", IconName::Check)
178                            .style(ButtonStyle::Tinted(TintColor::Warning)),
179                    )
180                    .child(
181                        IconButton::new("icon_button_negative", IconName::Check)
182                            .style(ButtonStyle::Tinted(TintColor::Negative)),
183                    ),
184            )
185            .child(
186                h_flex()
187                    .gap_px()
188                    .child(
189                        Button::new("button_transparent", "Transparent")
190                            .style(ButtonStyle::Transparent),
191                    )
192                    .child(Button::new("button_subtle", "Subtle").style(ButtonStyle::Subtle))
193                    .child(Button::new("button_filled", "Filled").style(ButtonStyle::Filled))
194                    .child(
195                        Button::new("button_selected", "Selected")
196                            .selected_style(ButtonStyle::Tinted(TintColor::Accent))
197                            .selected(true),
198                    )
199                    .child(
200                        Button::new("button_selected_tinted", "Selected (Tinted)")
201                            .selected_style(ButtonStyle::Tinted(TintColor::Accent))
202                            .selected(true),
203                    )
204                    .child(
205                        Button::new("button_positive", "Tint::Positive")
206                            .style(ButtonStyle::Tinted(TintColor::Positive)),
207                    )
208                    .child(
209                        Button::new("button_warning", "Tint::Warning")
210                            .style(ButtonStyle::Tinted(TintColor::Warning)),
211                    )
212                    .child(
213                        Button::new("button_negative", "Tint::Negative")
214                            .style(ButtonStyle::Tinted(TintColor::Negative)),
215                    ),
216            )
217    }
218
219    fn render_text(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
220        let bg = layer.bg(cx);
221
222        let label_with_contrast = |label: &str, fg: Hsla| {
223            let contrast = calculate_contrast_ratio(fg, bg);
224            format!("{} ({:.2})", label, contrast)
225        };
226
227        v_flex()
228            .gap_1()
229            .child(Headline::new("Text").size(HeadlineSize::Small).color(Color::Muted))
230            .child(
231                h_flex()
232                    .items_start()
233                    .gap_4()
234                    .child(
235                        v_flex()
236                            .gap_1()
237                            .child(Headline::new("Headline Sizes").size(HeadlineSize::Small).color(Color::Muted))
238                            .child(Headline::new("XLarge Headline").size(HeadlineSize::XLarge))
239                            .child(Headline::new("Large Headline").size(HeadlineSize::Large))
240                            .child(Headline::new("Medium Headline").size(HeadlineSize::Medium))
241                            .child(Headline::new("Small Headline").size(HeadlineSize::Small))
242                            .child(Headline::new("XSmall Headline").size(HeadlineSize::XSmall)),
243                    )
244                    .child(
245                        v_flex()
246                            .gap_1()
247                            .child(Headline::new("Text Colors").size(HeadlineSize::Small).color(Color::Muted))
248                            .child(
249                                Label::new(label_with_contrast(
250                                    "Default Text",
251                                    Color::Default.color(cx),
252                                ))
253                                .color(Color::Default),
254                            )
255                            .child(
256                                Label::new(label_with_contrast(
257                                    "Accent Text",
258                                    Color::Accent.color(cx),
259                                ))
260                                .color(Color::Accent),
261                            )
262                            .child(
263                                Label::new(label_with_contrast(
264                                    "Conflict Text",
265                                    Color::Conflict.color(cx),
266                                ))
267                                .color(Color::Conflict),
268                            )
269                            .child(
270                                Label::new(label_with_contrast(
271                                    "Created Text",
272                                    Color::Created.color(cx),
273                                ))
274                                .color(Color::Created),
275                            )
276                            .child(
277                                Label::new(label_with_contrast(
278                                    "Deleted Text",
279                                    Color::Deleted.color(cx),
280                                ))
281                                .color(Color::Deleted),
282                            )
283                            .child(
284                                Label::new(label_with_contrast(
285                                    "Disabled Text",
286                                    Color::Disabled.color(cx),
287                                ))
288                                .color(Color::Disabled),
289                            )
290                            .child(
291                                Label::new(label_with_contrast(
292                                    "Error Text",
293                                    Color::Error.color(cx),
294                                ))
295                                .color(Color::Error),
296                            )
297                            .child(
298                                Label::new(label_with_contrast(
299                                    "Hidden Text",
300                                    Color::Hidden.color(cx),
301                                ))
302                                .color(Color::Hidden),
303                            )
304                            .child(
305                                Label::new(label_with_contrast(
306                                    "Hint Text",
307                                    Color::Hint.color(cx),
308                                ))
309                                .color(Color::Hint),
310                            )
311                            .child(
312                                Label::new(label_with_contrast(
313                                    "Ignored Text",
314                                    Color::Ignored.color(cx),
315                                ))
316                                .color(Color::Ignored),
317                            )
318                            .child(
319                                Label::new(label_with_contrast(
320                                    "Info Text",
321                                    Color::Info.color(cx),
322                                ))
323                                .color(Color::Info),
324                            )
325                            .child(
326                                Label::new(label_with_contrast(
327                                    "Modified Text",
328                                    Color::Modified.color(cx),
329                                ))
330                                .color(Color::Modified),
331                            )
332                            .child(
333                                Label::new(label_with_contrast(
334                                    "Muted Text",
335                                    Color::Muted.color(cx),
336                                ))
337                                .color(Color::Muted),
338                            )
339                            .child(
340                                Label::new(label_with_contrast(
341                                    "Placeholder Text",
342                                    Color::Placeholder.color(cx),
343                                ))
344                                .color(Color::Placeholder),
345                            )
346                            .child(
347                                Label::new(label_with_contrast(
348                                    "Selected Text",
349                                    Color::Selected.color(cx),
350                                ))
351                                .color(Color::Selected),
352                            )
353                            .child(
354                                Label::new(label_with_contrast(
355                                    "Success Text",
356                                    Color::Success.color(cx),
357                                ))
358                                .color(Color::Success),
359                            )
360                            .child(
361                                Label::new(label_with_contrast(
362                                    "Warning Text",
363                                    Color::Warning.color(cx),
364                                ))
365                                .color(Color::Warning),
366                            )
367                    )
368                    .child(
369                        v_flex()
370                            .gap_1()
371                            .child(Headline::new("Wrapping Text").size(HeadlineSize::Small).color(Color::Muted))
372                            .child(
373                                div().max_w(px(200.)).child(
374                                "This is a longer piece of text that should wrap to multiple lines. It demonstrates how text behaves when it exceeds the width of its container."
375                            ))
376                    )
377            )
378    }
379
380    fn render_colors(&self, layer: ElevationIndex, cx: &ViewContext<Self>) -> impl IntoElement {
381        let bg = layer.bg(cx);
382        let all_colors = all_theme_colors(cx);
383
384        v_flex()
385            .gap_1()
386            .child(
387                Headline::new("Colors")
388                    .size(HeadlineSize::Small)
389                    .color(Color::Muted),
390            )
391            .child(
392                h_flex()
393                    .flex_wrap()
394                    .gap_1()
395                    .children(all_colors.into_iter().map(|(color, name)| {
396                        let id = ElementId::Name(format!("{:?}-preview", color).into());
397                        let name = name.clone();
398                        div().size_8().flex_none().child(
399                            ButtonLike::new(id)
400                                .child(
401                                    div()
402                                        .size_8()
403                                        .bg(color)
404                                        .border_1()
405                                        .border_color(cx.theme().colors().border)
406                                        .overflow_hidden(),
407                                )
408                                .size(ButtonSize::None)
409                                .style(ButtonStyle::Transparent)
410                                .tooltip(move |cx| {
411                                    let name = name.clone();
412                                    Tooltip::with_meta(name, None, format!("{:?}", color), cx)
413                                }),
414                        )
415                    })),
416            )
417    }
418
419    fn render_theme_layer(
420        &self,
421        layer: ElevationIndex,
422        cx: &ViewContext<Self>,
423    ) -> impl IntoElement {
424        v_flex()
425            .p_4()
426            .bg(layer.bg(cx))
427            .text_color(cx.theme().colors().text)
428            .gap_2()
429            .child(Headline::new(layer.clone().to_string()).size(HeadlineSize::Medium))
430            .child(self.render_avatars(cx))
431            .child(self.render_buttons(layer, cx))
432            .child(self.render_text(layer, cx))
433            .child(self.render_colors(layer, cx))
434    }
435}
436
437impl Render for ThemePreview {
438    fn render(&mut self, cx: &mut ViewContext<Self>) -> impl ui::IntoElement {
439        v_flex()
440            .id("theme-preview")
441            .key_context("ThemePreview")
442            .overflow_scroll()
443            .size_full()
444            .max_h_full()
445            .p_4()
446            .track_focus(&self.focus_handle)
447            .bg(Self::preview_bg(cx))
448            .gap_4()
449            .child(self.render_theme_layer(ElevationIndex::Background, cx))
450            .child(self.render_theme_layer(ElevationIndex::Surface, cx))
451            .child(self.render_theme_layer(ElevationIndex::EditorSurface, cx))
452            .child(self.render_theme_layer(ElevationIndex::ElevatedSurface, cx))
453    }
454}