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(2.);
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 v_flex()
66 .p_2()
67 .size_full()
68 .gap_1()
69 .children(sidebar_skeleton),
70 );
71
72 let pseudo_code_skeleton = |theme: Arc<Theme>, seed: f32| -> AnyElement {
73 let colors = theme.colors();
74 let syntax = theme.syntax();
75
76 let keyword_color = syntax.get("keyword").color;
77 let function_color = syntax.get("function").color;
78 let string_color = syntax.get("string").color;
79 let comment_color = syntax.get("comment").color;
80 let variable_color = syntax.get("variable").color;
81 let type_color = syntax.get("type").color;
82 let punctuation_color = syntax.get("punctuation").color;
83
84 let syntax_colors = [
85 keyword_color,
86 function_color,
87 string_color,
88 variable_color,
89 type_color,
90 punctuation_color,
91 comment_color,
92 ];
93
94 let line_width = |line_idx: usize, block_idx: usize| -> f32 {
95 let val = (seed * 100.0 + line_idx as f32 * 20.0 + block_idx as f32 * 5.0).sin()
96 * 0.5
97 + 0.5;
98 0.05 + val * 0.2
99 };
100
101 let indentation = |line_idx: usize| -> f32 {
102 let step = line_idx % 6;
103 if step < 3 {
104 step as f32 * 0.1
105 } else {
106 (5 - step) as f32 * 0.1
107 }
108 };
109
110 let pick_color = |line_idx: usize, block_idx: usize| -> Hsla {
111 let idx = ((seed * 10.0 + line_idx as f32 * 7.0 + block_idx as f32 * 3.0).sin()
112 * 3.5)
113 .abs() as usize
114 % syntax_colors.len();
115 syntax_colors[idx].unwrap_or(colors.text)
116 };
117
118 let line_count = 13;
119
120 let lines = (0..line_count)
121 .map(|line_idx| {
122 let block_count = (((seed * 30.0 + line_idx as f32 * 12.0).sin() * 0.5 + 0.5)
123 * 3.0)
124 .round() as usize
125 + 2;
126
127 let indent = indentation(line_idx);
128
129 let blocks = (0..block_count)
130 .map(|block_idx| {
131 let width = line_width(line_idx, block_idx);
132 let color = pick_color(line_idx, block_idx);
133 item_skeleton(relative(width).into(), skeleton_height, color)
134 })
135 .collect::<Vec<_>>();
136
137 h_flex().gap(px(2.)).ml(relative(indent)).children(blocks)
138 })
139 .collect::<Vec<_>>();
140
141 v_flex()
142 .size_full()
143 .p_1()
144 .gap_1p5()
145 .children(lines)
146 .into_any_element()
147 };
148
149 let pane = v_flex().h_full().flex_grow().child(
150 div()
151 .size_full()
152 .overflow_hidden()
153 .bg(color.editor_background)
154 .p_2()
155 .child(pseudo_code_skeleton(self.theme.clone(), self.seed)),
156 );
157
158 let content = div().size_full().flex().child(sidebar).child(pane);
159
160 div()
161 .size_full()
162 .rounded(root_radius)
163 .p(root_padding)
164 .child(
165 div()
166 .size_full()
167 .rounded(inner_radius)
168 .border(child_border)
169 .border_color(color.border)
170 .bg(color.background)
171 .child(content),
172 )
173 }
174}
175
176impl Component for ThemePreviewTile {
177 fn description() -> Option<&'static str> {
178 Some(Self::DOCS)
179 }
180
181 fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
182 let theme_registry = ThemeRegistry::global(cx);
183
184 let one_dark = theme_registry.get("One Dark");
185 let one_light = theme_registry.get("One Light");
186 let gruvbox_dark = theme_registry.get("Gruvbox Dark");
187 let gruvbox_light = theme_registry.get("Gruvbox Light");
188
189 let themes_to_preview = vec![
190 one_dark.clone().ok(),
191 one_light.clone().ok(),
192 gruvbox_dark.clone().ok(),
193 gruvbox_light.clone().ok(),
194 ]
195 .into_iter()
196 .flatten()
197 .collect::<Vec<_>>();
198
199 Some(
200 v_flex()
201 .gap_6()
202 .p_4()
203 .children({
204 if let Some(one_dark) = one_dark.ok() {
205 vec![example_group(vec![single_example(
206 "Default",
207 div()
208 .w(px(240.))
209 .h(px(180.))
210 .child(ThemePreviewTile::new(one_dark.clone(), 0.42))
211 .into_any_element(),
212 )])]
213 } else {
214 vec![]
215 }
216 })
217 .child(
218 example_group(vec![single_example(
219 "Default Themes",
220 h_flex()
221 .gap_4()
222 .children(
223 themes_to_preview
224 .iter()
225 .enumerate()
226 .map(|(_, theme)| {
227 div()
228 .w(px(200.))
229 .h(px(140.))
230 .child(ThemePreviewTile::new(theme.clone(), 0.42))
231 })
232 .collect::<Vec<_>>(),
233 )
234 .into_any_element(),
235 )])
236 .grow(),
237 )
238 .into_any_element(),
239 )
240 }
241}