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}