theme_preview.rs

  1#![allow(unused, dead_code)]
  2use gpui::{Hsla, Length};
  3use std::sync::Arc;
  4use theme::{Theme, ThemeRegistry};
  5use ui::{
  6    IntoElement, RenderOnce, component_prelude::Documented, prelude::*, utils::inner_corner_radius,
  7};
  8
  9/// Shows a preview of a theme as an abstract illustration
 10/// of a thumbnail-sized editor.
 11#[derive(IntoElement, RegisterComponent, Documented)]
 12pub struct ThemePreviewTile {
 13    theme: Arc<Theme>,
 14    seed: f32,
 15}
 16
 17impl ThemePreviewTile {
 18    pub const CORNER_RADIUS: Pixels = px(8.0);
 19
 20    pub fn new(theme: Arc<Theme>, seed: f32) -> Self {
 21        Self { theme, seed }
 22    }
 23}
 24
 25impl RenderOnce for ThemePreviewTile {
 26    fn render(self, _window: &mut ui::Window, _cx: &mut ui::App) -> impl IntoElement {
 27        let color = self.theme.colors();
 28
 29        let root_radius = Self::CORNER_RADIUS;
 30        let root_border = px(2.0);
 31        let root_padding = px(2.0);
 32        let child_border = px(1.0);
 33        let inner_radius =
 34            inner_corner_radius(root_radius, root_border, root_padding, child_border);
 35
 36        let item_skeleton = |w: Length, h: Pixels, bg: Hsla| div().w(w).h(h).rounded_full().bg(bg);
 37
 38        let skeleton_height = px(4.);
 39
 40        let sidebar_seeded_width = |seed: f32, index: usize| {
 41            let value = (seed * 1000.0 + index as f32 * 10.0).sin() * 0.5 + 0.5;
 42            0.5 + value * 0.45
 43        };
 44
 45        let sidebar_skeleton_items = 8;
 46
 47        let sidebar_skeleton = (0..sidebar_skeleton_items)
 48            .map(|i| {
 49                let width = sidebar_seeded_width(self.seed, i);
 50                item_skeleton(
 51                    relative(width).into(),
 52                    skeleton_height,
 53                    color.text.alpha(0.45),
 54                )
 55            })
 56            .collect::<Vec<_>>();
 57
 58        let sidebar = div()
 59            .h_full()
 60            .w(relative(0.25))
 61            .border_r(px(1.))
 62            .border_color(color.border_transparent)
 63            .bg(color.panel_background)
 64            .child(
 65                div()
 66                    .p_2()
 67                    .flex()
 68                    .flex_col()
 69                    .size_full()
 70                    .gap(px(4.))
 71                    .children(sidebar_skeleton),
 72            );
 73
 74        let pseudo_code_skeleton = |theme: Arc<Theme>, seed: f32| -> AnyElement {
 75            let colors = theme.colors();
 76            let syntax = theme.syntax();
 77
 78            let keyword_color = syntax.get("keyword").color;
 79            let function_color = syntax.get("function").color;
 80            let string_color = syntax.get("string").color;
 81            let comment_color = syntax.get("comment").color;
 82            let variable_color = syntax.get("variable").color;
 83            let type_color = syntax.get("type").color;
 84            let punctuation_color = syntax.get("punctuation").color;
 85
 86            let syntax_colors = [
 87                keyword_color,
 88                function_color,
 89                string_color,
 90                variable_color,
 91                type_color,
 92                punctuation_color,
 93                comment_color,
 94            ];
 95
 96            let line_width = |line_idx: usize, block_idx: usize| -> f32 {
 97                let val = (seed * 100.0 + line_idx as f32 * 20.0 + block_idx as f32 * 5.0).sin()
 98                    * 0.5
 99                    + 0.5;
100                0.05 + val * 0.2
101            };
102
103            let indentation = |line_idx: usize| -> f32 {
104                let step = line_idx % 6;
105                if step < 3 {
106                    step as f32 * 0.1
107                } else {
108                    (5 - step) as f32 * 0.1
109                }
110            };
111
112            let pick_color = |line_idx: usize, block_idx: usize| -> Hsla {
113                let idx = ((seed * 10.0 + line_idx as f32 * 7.0 + block_idx as f32 * 3.0).sin()
114                    * 3.5)
115                    .abs() as usize
116                    % syntax_colors.len();
117                syntax_colors[idx].unwrap_or(colors.text)
118            };
119
120            let line_count = 13;
121
122            let lines = (0..line_count)
123                .map(|line_idx| {
124                    let block_count = (((seed * 30.0 + line_idx as f32 * 12.0).sin() * 0.5 + 0.5)
125                        * 3.0)
126                        .round() as usize
127                        + 2;
128
129                    let indent = indentation(line_idx);
130
131                    let blocks = (0..block_count)
132                        .map(|block_idx| {
133                            let width = line_width(line_idx, block_idx);
134                            let color = pick_color(line_idx, block_idx);
135                            item_skeleton(relative(width).into(), skeleton_height, color)
136                        })
137                        .collect::<Vec<_>>();
138
139                    h_flex().gap(px(2.)).ml(relative(indent)).children(blocks)
140                })
141                .collect::<Vec<_>>();
142
143            v_flex()
144                .size_full()
145                .p_1()
146                .gap(px(6.))
147                .children(lines)
148                .into_any_element()
149        };
150
151        let pane = div()
152            .h_full()
153            .flex_grow()
154            .flex()
155            .flex_col()
156            // .child(
157            //     div()
158            //         .w_full()
159            //         .border_color(color.border)
160            //         .border_b(px(1.))
161            //         .h(relative(0.1))
162            //         .bg(color.tab_bar_background),
163            // )
164            .child(
165                div()
166                    .size_full()
167                    .overflow_hidden()
168                    .bg(color.editor_background)
169                    .p_2()
170                    .child(pseudo_code_skeleton(self.theme.clone(), self.seed)),
171            );
172
173        let content = div().size_full().flex().child(sidebar).child(pane);
174
175        div()
176            .size_full()
177            .rounded(root_radius)
178            .p(root_padding)
179            .child(
180                div()
181                    .size_full()
182                    .rounded(inner_radius)
183                    .border(child_border)
184                    .border_color(color.border)
185                    .bg(color.background)
186                    .child(content),
187            )
188    }
189}
190
191impl Component for ThemePreviewTile {
192    fn description() -> Option<&'static str> {
193        Some(Self::DOCS)
194    }
195
196    fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
197        let theme_registry = ThemeRegistry::global(cx);
198
199        let one_dark = theme_registry.get("One Dark");
200        let one_light = theme_registry.get("One Light");
201        let gruvbox_dark = theme_registry.get("Gruvbox Dark");
202        let gruvbox_light = theme_registry.get("Gruvbox Light");
203
204        let themes_to_preview = vec![
205            one_dark.clone().ok(),
206            one_light.clone().ok(),
207            gruvbox_dark.clone().ok(),
208            gruvbox_light.clone().ok(),
209        ]
210        .into_iter()
211        .flatten()
212        .collect::<Vec<_>>();
213
214        Some(
215            v_flex()
216                .gap_6()
217                .p_4()
218                .children({
219                    if let Some(one_dark) = one_dark.ok() {
220                        vec![example_group(vec![single_example(
221                            "Default",
222                            div()
223                                .w(px(240.))
224                                .h(px(180.))
225                                .child(ThemePreviewTile::new(one_dark.clone(), 0.42))
226                                .into_any_element(),
227                        )])]
228                    } else {
229                        vec![]
230                    }
231                })
232                .child(
233                    example_group(vec![single_example(
234                        "Default Themes",
235                        h_flex()
236                            .gap_4()
237                            .children(
238                                themes_to_preview
239                                    .iter()
240                                    .enumerate()
241                                    .map(|(_, theme)| {
242                                        div()
243                                            .w(px(200.))
244                                            .h(px(140.))
245                                            .child(ThemePreviewTile::new(theme.clone(), 0.42))
246                                    })
247                                    .collect::<Vec<_>>(),
248                            )
249                            .into_any_element(),
250                    )])
251                    .grow(),
252                )
253                .into_any_element(),
254        )
255    }
256}