379 lines · 11.8 KB · Rust
RawDownload
#![allow(unused, dead_code)]use gpui::{Hsla, Length};use std::{    cell::LazyCell,    sync::{Arc, LazyLock, OnceLock},};use theme::{Theme, ThemeColors, ThemeRegistry};use ui::{    IntoElement, RenderOnce, component_prelude::Documented, prelude::*, utils::inner_corner_radius,}; #[derive(Clone, PartialEq)]pub enum ThemePreviewStyle {    Bordered,    Borderless,    SideBySide(Arc<Theme>),} /// Shows a preview of a theme as an abstract illustration/// of a thumbnail-sized editor.#[derive(IntoElement, RegisterComponent, Documented)]pub struct ThemePreviewTile {    theme: Arc<Theme>,    seed: f32,    style: ThemePreviewStyle,} static CHILD_RADIUS: LazyLock<Pixels> = LazyLock::new(|| {    inner_corner_radius(        ThemePreviewTile::ROOT_RADIUS,        ThemePreviewTile::ROOT_BORDER,        ThemePreviewTile::ROOT_PADDING,        ThemePreviewTile::CHILD_BORDER,    )}); impl ThemePreviewTile {    pub const SKELETON_HEIGHT_DEFAULT: Pixels = px(2.);    pub const SIDEBAR_SKELETON_ITEM_COUNT: usize = 8;    pub const SIDEBAR_WIDTH_DEFAULT: DefiniteLength = relative(0.25);    pub const ROOT_RADIUS: Pixels = px(8.0);    pub const ROOT_BORDER: Pixels = px(2.0);    pub const ROOT_PADDING: Pixels = px(2.0);    pub const CHILD_BORDER: Pixels = px(1.0);     pub fn new(theme: Arc<Theme>, seed: f32) -> Self {        Self {            theme,            seed,            style: ThemePreviewStyle::Bordered,        }    }     pub fn style(mut self, style: ThemePreviewStyle) -> Self {        self.style = style;        self    }     pub fn item_skeleton(w: Length, h: Length, bg: Hsla) -> impl IntoElement {        div().w(w).h(h).rounded_full().bg(bg)    }     pub fn render_sidebar_skeleton_items(        seed: f32,        colors: &ThemeColors,        skeleton_height: impl Into<Length> + Clone,    ) -> [impl IntoElement; Self::SIDEBAR_SKELETON_ITEM_COUNT] {        let skeleton_height = skeleton_height.into();        std::array::from_fn(|index| {            let width = {                let value = (seed * 1000.0 + index as f32 * 10.0).sin() * 0.5 + 0.5;                0.5 + value * 0.45            };            Self::item_skeleton(                relative(width).into(),                skeleton_height,                colors.text.alpha(0.45),            )        })    }     pub fn render_pseudo_code_skeleton(        seed: f32,        theme: Arc<Theme>,        skeleton_height: impl Into<Length>,    ) -> impl IntoElement {        let colors = theme.colors();        let syntax = theme.syntax();         let keyword_color = syntax.style_for_name("keyword").and_then(|s| s.color);        let function_color = syntax.style_for_name("function").and_then(|s| s.color);        let string_color = syntax.style_for_name("string").and_then(|s| s.color);        let comment_color = syntax.style_for_name("comment").and_then(|s| s.color);        let variable_color = syntax.style_for_name("variable").and_then(|s| s.color);        let type_color = syntax.style_for_name("type").and_then(|s| s.color);        let punctuation_color = syntax.style_for_name("punctuation").and_then(|s| s.color);         let syntax_colors = [            keyword_color,            function_color,            string_color,            variable_color,            type_color,            punctuation_color,            comment_color,        ];         let skeleton_height = skeleton_height.into();         let line_width = |line_idx: usize, block_idx: usize| -> f32 {            let val =                (seed * 100.0 + line_idx as f32 * 20.0 + block_idx as f32 * 5.0).sin() * 0.5 + 0.5;            0.05 + val * 0.2        };         let indentation = |line_idx: usize| -> f32 {            let step = line_idx % 6;            if step < 3 {                step as f32 * 0.1            } else {                (5 - step) as f32 * 0.1            }        };         let pick_color = |line_idx: usize, block_idx: usize| -> Hsla {            let idx = ((seed * 10.0 + line_idx as f32 * 7.0 + block_idx as f32 * 3.0).sin() * 3.5)                .abs() as usize                % syntax_colors.len();            syntax_colors[idx].unwrap_or(colors.text)        };         let line_count = 10;         let lines = (0..line_count)            .map(|line_idx| {                let block_count = (((seed * 30.0 + line_idx as f32 * 12.0).sin() * 0.5 + 0.5) * 3.0)                    .round() as usize                    + 2;                 let indent = indentation(line_idx);                 let blocks = (0..block_count)                    .map(|block_idx| {                        let width = line_width(line_idx, block_idx);                        let color = pick_color(line_idx, block_idx);                        Self::item_skeleton(relative(width).into(), skeleton_height, color)                    })                    .collect::<Vec<_>>();                 h_flex().gap_0p5().ml(relative(indent)).children(blocks)            })            .collect::<Vec<_>>();         v_flex().size_full().p_1().gap_1p5().children(lines)    }     pub fn render_sidebar(        seed: f32,        colors: &ThemeColors,        width: impl Into<Length> + Clone,        skeleton_height: impl Into<Length>,    ) -> impl IntoElement {        v_flex()            .h_full()            .w(width)            .p_2()            .gap_1()            .bg(colors.panel_background)            .children(Self::render_sidebar_skeleton_items(                seed,                colors,                skeleton_height.into(),            ))    }     pub fn render_pane(        seed: f32,        theme: Arc<Theme>,        skeleton_height: impl Into<Length>,    ) -> impl IntoElement {        div()            .p_2()            .size_full()            .overflow_hidden()            .bg(theme.colors().editor_background)            .child(Self::render_pseudo_code_skeleton(                seed,                theme,                skeleton_height.into(),            ))    }     pub fn render_editor(        seed: f32,        theme: Arc<Theme>,        sidebar_width: impl Into<Length> + Clone,        skeleton_height: impl Into<Length> + Clone,    ) -> impl IntoElement {        div()            .flex()            .size_full()            .bg(theme.colors().background.alpha(1.00))            .child(Self::render_sidebar(                seed,                theme.colors(),                sidebar_width,                skeleton_height.clone(),            ))            .child(Self::render_pane(seed, theme, skeleton_height))    }     fn render_borderless(seed: f32, theme: Arc<Theme>) -> impl IntoElement {        Self::render_editor(            seed,            theme,            Self::SIDEBAR_WIDTH_DEFAULT,            Self::SKELETON_HEIGHT_DEFAULT,        )    }     fn render_border(seed: f32, theme: Arc<Theme>) -> impl IntoElement {        div()            .size_full()            .p(Self::ROOT_PADDING)            .rounded(Self::ROOT_RADIUS)            .child(                div()                    .size_full()                    .rounded(*CHILD_RADIUS)                    .border(Self::CHILD_BORDER)                    .border_color(theme.colors().border)                    .child(Self::render_editor(                        seed,                        theme.clone(),                        Self::SIDEBAR_WIDTH_DEFAULT,                        Self::SKELETON_HEIGHT_DEFAULT,                    )),            )    }     fn render_side_by_side(        seed: f32,        theme: Arc<Theme>,        other_theme: Arc<Theme>,        border_color: Hsla,    ) -> impl IntoElement {        let sidebar_width = relative(0.20);         div()            .size_full()            .p(Self::ROOT_PADDING)            .rounded(Self::ROOT_RADIUS)            .child(                h_flex()                    .size_full()                    .relative()                    .rounded(*CHILD_RADIUS)                    .border(Self::CHILD_BORDER)                    .border_color(border_color)                    .overflow_hidden()                    .child(div().size_full().child(Self::render_editor(                        seed,                        theme,                        sidebar_width,                        Self::SKELETON_HEIGHT_DEFAULT,                    )))                    .child(                        div()                            .size_full()                            .absolute()                            .left_1_2()                            .bg(other_theme.colors().editor_background)                            .child(Self::render_editor(                                seed,                                other_theme,                                sidebar_width,                                Self::SKELETON_HEIGHT_DEFAULT,                            )),                    ),            )            .into_any_element()    }} impl RenderOnce for ThemePreviewTile {    fn render(self, _window: &mut ui::Window, _cx: &mut ui::App) -> impl IntoElement {        match self.style {            ThemePreviewStyle::Bordered => {                Self::render_border(self.seed, self.theme).into_any_element()            }            ThemePreviewStyle::Borderless => {                Self::render_borderless(self.seed, self.theme).into_any_element()            }            ThemePreviewStyle::SideBySide(other_theme) => Self::render_side_by_side(                self.seed,                self.theme,                other_theme,                _cx.theme().colors().border,            )            .into_any_element(),        }    }} impl Component for ThemePreviewTile {    fn scope() -> ComponentScope {        ComponentScope::Onboarding    }     fn name() -> &'static str {        "Theme Preview Tile"    }     fn sort_name() -> &'static str {        "Theme Preview Tile"    }     fn description() -> &'static str {        Self::DOCS    }     fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {        let theme_registry = ThemeRegistry::global(cx);         let one_dark = theme_registry.get("One Dark");        let one_light = theme_registry.get("One Light");        let gruvbox_dark = theme_registry.get("Gruvbox Dark");        let gruvbox_light = theme_registry.get("Gruvbox Light");         let themes_to_preview = vec![            one_dark.clone().ok(),            one_light.ok(),            gruvbox_dark.ok(),            gruvbox_light.ok(),        ]        .into_iter()        .flatten()        .collect::<Vec<_>>();         v_flex()            .gap_6()            .p_4()            .children({                if let Some(one_dark) = one_dark.ok() {                    vec![example_group(vec![single_example(                        "Default",                        div()                            .w(px(240.))                            .h(px(180.))                            .child(ThemePreviewTile::new(one_dark, 0.42))                            .into_any_element(),                    )])]                } else {                    vec![]                }            })            .child(                example_group(vec![single_example(                    "Default Themes",                    h_flex()                        .gap_4()                        .children(                            themes_to_preview                                .into_iter()                                .map(|theme| {                                    div()                                        .w(px(200.))                                        .h(px(140.))                                        .child(ThemePreviewTile::new(theme, 0.42))                                })                                .collect::<Vec<_>>(),                        )                        .into_any_element(),                )])                .grow(),            )            .into_any_element()    }}