ZIm/crates/onboarding/src/theme_preview.rs
Danilo Leal faa45c53d7
onboarding: Add design adjustments (#35480)
Release Notes:

- N/A

---------

Co-authored-by: Anthony <anthony@zed.dev>
2025-08-01 15:08:15 -03:00

241 lines
8 KiB
Rust

#![allow(unused, dead_code)]
use gpui::{Hsla, Length};
use std::sync::Arc;
use theme::{Theme, ThemeRegistry};
use ui::{
IntoElement, RenderOnce, component_prelude::Documented, prelude::*, utils::inner_corner_radius,
};
/// 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,
}
impl ThemePreviewTile {
pub const CORNER_RADIUS: Pixels = px(8.0);
pub fn new(theme: Arc<Theme>, seed: f32) -> Self {
Self { theme, seed }
}
}
impl RenderOnce for ThemePreviewTile {
fn render(self, _window: &mut ui::Window, _cx: &mut ui::App) -> impl IntoElement {
let color = self.theme.colors();
let root_radius = Self::CORNER_RADIUS;
let root_border = px(2.0);
let root_padding = px(2.0);
let child_border = px(1.0);
let inner_radius =
inner_corner_radius(root_radius, root_border, root_padding, child_border);
let item_skeleton = |w: Length, h: Pixels, bg: Hsla| div().w(w).h(h).rounded_full().bg(bg);
let skeleton_height = px(2.);
let sidebar_seeded_width = |seed: f32, index: usize| {
let value = (seed * 1000.0 + index as f32 * 10.0).sin() * 0.5 + 0.5;
0.5 + value * 0.45
};
let sidebar_skeleton_items = 8;
let sidebar_skeleton = (0..sidebar_skeleton_items)
.map(|i| {
let width = sidebar_seeded_width(self.seed, i);
item_skeleton(
relative(width).into(),
skeleton_height,
color.text.alpha(0.45),
)
})
.collect::<Vec<_>>();
let sidebar = div()
.h_full()
.w(relative(0.25))
.border_r(px(1.))
.border_color(color.border_transparent)
.bg(color.panel_background)
.child(
v_flex()
.p_2()
.size_full()
.gap_1()
.children(sidebar_skeleton),
);
let pseudo_code_skeleton = |theme: Arc<Theme>, seed: f32| -> AnyElement {
let colors = theme.colors();
let syntax = theme.syntax();
let keyword_color = syntax.get("keyword").color;
let function_color = syntax.get("function").color;
let string_color = syntax.get("string").color;
let comment_color = syntax.get("comment").color;
let variable_color = syntax.get("variable").color;
let type_color = syntax.get("type").color;
let punctuation_color = syntax.get("punctuation").color;
let syntax_colors = [
keyword_color,
function_color,
string_color,
variable_color,
type_color,
punctuation_color,
comment_color,
];
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 = 13;
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);
item_skeleton(relative(width).into(), skeleton_height, color)
})
.collect::<Vec<_>>();
h_flex().gap(px(2.)).ml(relative(indent)).children(blocks)
})
.collect::<Vec<_>>();
v_flex()
.size_full()
.p_1()
.gap_1p5()
.children(lines)
.into_any_element()
};
let pane = v_flex().h_full().flex_grow().child(
div()
.size_full()
.overflow_hidden()
.bg(color.editor_background)
.p_2()
.child(pseudo_code_skeleton(self.theme.clone(), self.seed)),
);
let content = div().size_full().flex().child(sidebar).child(pane);
div()
.size_full()
.rounded(root_radius)
.p(root_padding)
.child(
div()
.size_full()
.rounded(inner_radius)
.border(child_border)
.border_color(color.border)
.bg(color.background)
.child(content),
)
}
}
impl Component for ThemePreviewTile {
fn description() -> Option<&'static str> {
Some(Self::DOCS)
}
fn preview(_window: &mut Window, cx: &mut App) -> Option<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.clone().ok(),
gruvbox_dark.clone().ok(),
gruvbox_light.clone().ok(),
]
.into_iter()
.flatten()
.collect::<Vec<_>>();
Some(
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.clone(), 0.42))
.into_any_element(),
)])]
} else {
vec![]
}
})
.child(
example_group(vec![single_example(
"Default Themes",
h_flex()
.gap_4()
.children(
themes_to_preview
.iter()
.enumerate()
.map(|(_, theme)| {
div()
.w(px(200.))
.h(px(140.))
.child(ThemePreviewTile::new(theme.clone(), 0.42))
})
.collect::<Vec<_>>(),
)
.into_any_element(),
)])
.grow(),
)
.into_any_element(),
)
}
}