1#![allow(unused, dead_code)]
2use gpui::{Hsla, Length};
3use std::sync::Arc;
4use theme::{Theme, ThemeRegistry};
5use ui::{IntoElement, RenderOnce, prelude::*, utils::CornerSolver};
6
7/// Shows a preview of a theme as an abstract illustration
8/// of a thumbnail-sized editor.
9#[derive(IntoElement)]
10pub struct ThemePreviewTile {
11 theme: Arc<Theme>,
12 selected: bool,
13 seed: f32,
14}
15
16impl ThemePreviewTile {
17 pub fn new(theme: Arc<Theme>, selected: bool, seed: f32) -> Self {
18 Self {
19 theme,
20 selected,
21 seed,
22 }
23 }
24
25 pub fn selected(mut self, selected: bool) -> Self {
26 self.selected = selected;
27 self
28 }
29}
30
31impl RenderOnce for ThemePreviewTile {
32 fn render(self, _window: &mut ui::Window, _cx: &mut ui::App) -> impl IntoElement {
33 let color = self.theme.colors();
34
35 let root_radius = px(8.0);
36 let root_border = px(2.0);
37 let root_padding = px(0.0);
38 let child_border = px(1.0);
39 let inner_radius =
40 CornerSolver::child_radius(root_radius, root_border, root_padding, child_border);
41
42 let item_skeleton = |w: Length, h: Pixels, bg: Hsla| div().w(w).h(h).rounded_full().bg(bg);
43
44 let skeleton_height = px(4.);
45
46 let sidebar_seeded_width = |seed: f32, index: usize| {
47 let value = (seed * 1000.0 + index as f32 * 10.0).sin() * 0.5 + 0.5;
48 0.5 + value * 0.45
49 };
50
51 let sidebar_skeleton_items = 8;
52
53 let sidebar_skeleton = (0..sidebar_skeleton_items)
54 .map(|i| {
55 let width = sidebar_seeded_width(self.seed, i);
56 item_skeleton(
57 relative(width).into(),
58 skeleton_height,
59 color.text.alpha(0.45),
60 )
61 })
62 .collect::<Vec<_>>();
63
64 let sidebar = div()
65 .h_full()
66 .w(relative(0.25))
67 .border_r(px(1.))
68 .border_color(color.border_transparent)
69 .bg(color.panel_background)
70 .child(
71 div()
72 .p_2()
73 .flex()
74 .flex_col()
75 .size_full()
76 .gap(px(4.))
77 .children(sidebar_skeleton),
78 );
79
80 let pseudo_code_skeleton = |theme: Arc<Theme>, seed: f32| -> AnyElement {
81 let colors = theme.colors();
82 let syntax = theme.syntax();
83
84 let keyword_color = syntax.get("keyword").color;
85 let function_color = syntax.get("function").color;
86 let string_color = syntax.get("string").color;
87 let comment_color = syntax.get("comment").color;
88 let variable_color = syntax.get("variable").color;
89 let type_color = syntax.get("type").color;
90 let punctuation_color = syntax.get("punctuation").color;
91
92 let syntax_colors = [
93 keyword_color,
94 function_color,
95 string_color,
96 variable_color,
97 type_color,
98 punctuation_color,
99 comment_color,
100 ];
101
102 let line_width = |line_idx: usize, block_idx: usize| -> f32 {
103 let val = (seed * 100.0 + line_idx as f32 * 20.0 + block_idx as f32 * 5.0).sin()
104 * 0.5
105 + 0.5;
106 0.05 + val * 0.2
107 };
108
109 let indentation = |line_idx: usize| -> f32 {
110 let step = line_idx % 6;
111 if step < 3 {
112 step as f32 * 0.1
113 } else {
114 (5 - step) as f32 * 0.1
115 }
116 };
117
118 let pick_color = |line_idx: usize, block_idx: usize| -> Hsla {
119 let idx = ((seed * 10.0 + line_idx as f32 * 7.0 + block_idx as f32 * 3.0).sin()
120 * 3.5)
121 .abs() as usize
122 % syntax_colors.len();
123 syntax_colors[idx].unwrap_or(colors.text)
124 };
125
126 let line_count = 13;
127
128 let lines = (0..line_count)
129 .map(|line_idx| {
130 let block_count = (((seed * 30.0 + line_idx as f32 * 12.0).sin() * 0.5 + 0.5)
131 * 3.0)
132 .round() as usize
133 + 2;
134
135 let indent = indentation(line_idx);
136
137 let blocks = (0..block_count)
138 .map(|block_idx| {
139 let width = line_width(line_idx, block_idx);
140 let color = pick_color(line_idx, block_idx);
141 item_skeleton(relative(width).into(), skeleton_height, color)
142 })
143 .collect::<Vec<_>>();
144
145 h_flex().gap(px(2.)).ml(relative(indent)).children(blocks)
146 })
147 .collect::<Vec<_>>();
148
149 v_flex()
150 .size_full()
151 .p_1()
152 .gap(px(6.))
153 .children(lines)
154 .into_any_element()
155 };
156
157 let pane = div()
158 .h_full()
159 .flex_grow()
160 .flex()
161 .flex_col()
162 // .child(
163 // div()
164 // .w_full()
165 // .border_color(color.border)
166 // .border_b(px(1.))
167 // .h(relative(0.1))
168 // .bg(color.tab_bar_background),
169 // )
170 .child(
171 div()
172 .size_full()
173 .overflow_hidden()
174 .bg(color.editor_background)
175 .p_2()
176 .child(pseudo_code_skeleton(self.theme.clone(), self.seed)),
177 );
178
179 let content = div().size_full().flex().child(sidebar).child(pane);
180
181 div()
182 .size_full()
183 .rounded(root_radius)
184 .p(root_padding)
185 .border(root_border)
186 .border_color(color.border_transparent)
187 .when(self.selected, |this| {
188 this.border_color(color.border_selected)
189 })
190 .child(
191 div()
192 .size_full()
193 .rounded(inner_radius)
194 .border(child_border)
195 .border_color(color.border)
196 .bg(color.background)
197 .child(content),
198 )
199 }
200}