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