par_term/
shader_install_ui.rs1use crate::shader_installer;
7use egui::{Align2, Color32, Context, Frame, RichText, Window, epaint::Shadow};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ShaderInstallResponse {
12 Install,
14 Never,
16 Later,
18 None,
20}
21
22pub struct ShaderInstallUI {
24 pub visible: bool,
26 pub installing: bool,
28 pub progress_message: Option<String>,
30 pub error_message: Option<String>,
32 pub success_message: Option<String>,
34}
35
36impl ShaderInstallUI {
37 pub fn new() -> Self {
39 Self {
40 visible: false,
41 installing: false,
42 progress_message: None,
43 error_message: None,
44 success_message: None,
45 }
46 }
47
48 pub fn show_dialog(&mut self) {
50 self.visible = true;
51 self.installing = false;
52 self.progress_message = None;
53 self.error_message = None;
54 self.success_message = None;
55 }
56
57 pub fn hide(&mut self) {
59 self.visible = false;
60 }
61
62 pub fn show(&mut self, ctx: &Context) -> ShaderInstallResponse {
65 if !self.visible {
66 return ShaderInstallResponse::None;
67 }
68
69 let mut response = ShaderInstallResponse::None;
70
71 let mut style = (*ctx.style()).clone();
73 let solid_bg = Color32::from_rgba_unmultiplied(32, 32, 32, 255);
74 style.visuals.window_fill = solid_bg;
75 style.visuals.panel_fill = solid_bg;
76 style.visuals.widgets.noninteractive.bg_fill = solid_bg;
77 ctx.set_style(style);
78
79 let viewport = ctx.input(|i| i.viewport_rect());
80
81 Window::new("Shader Pack Available")
82 .resizable(false)
83 .collapsible(false)
84 .default_width(450.0)
85 .default_pos(viewport.center())
86 .pivot(Align2::CENTER_CENTER)
87 .frame(
88 Frame::window(&ctx.style())
89 .fill(solid_bg)
90 .inner_margin(20.0)
91 .stroke(egui::Stroke::new(1.0, Color32::from_gray(80)))
92 .shadow(Shadow {
93 offset: [4, 4],
94 blur: 16,
95 spread: 4,
96 color: Color32::from_black_alpha(180),
97 }),
98 )
99 .show(ctx, |ui| {
100 ui.vertical_centered(|ui| {
101 ui.add_space(8.0);
103 ui.label(
104 RichText::new("Custom Shaders Available")
105 .size(20.0)
106 .strong(),
107 );
108 ui.add_space(16.0);
109 });
110
111 ui.label(
113 "par-term includes 49+ custom background shaders and 12 cursor \
114 effect shaders that can transform your terminal experience.",
115 );
116 ui.add_space(8.0);
117
118 ui.label("Effects include:");
119 ui.indent("effects_list", |ui| {
120 ui.label("- CRT monitors, scanlines, and retro effects");
121 ui.label("- Matrix rain, starfields, and particle systems");
122 ui.label("- Plasma, fire, and abstract visualizations");
123 ui.label("- Cursor trails, glows, and ripple effects");
124 });
125
126 ui.add_space(16.0);
127
128 if self.installing {
130 ui.horizontal(|ui| {
131 ui.spinner();
132 ui.label(
133 self.progress_message
134 .as_deref()
135 .unwrap_or("Installing shaders..."),
136 );
137 });
138 } else if let Some(error) = &self.error_message {
139 ui.colored_label(Color32::from_rgb(255, 100, 100), error);
140 ui.add_space(8.0);
141 ui.label("You can try again later using: par-term install-shaders");
142 } else if let Some(success) = &self.success_message {
143 ui.colored_label(Color32::from_rgb(100, 255, 100), success);
144 ui.add_space(8.0);
145 ui.label("Configure shaders in Settings (F12) under 'Background & Effects'.");
146 }
147
148 ui.add_space(16.0);
149
150 ui.vertical_centered(|ui| {
152 if !self.installing && self.success_message.is_none() {
154 ui.horizontal(|ui| {
155 let button_width = 120.0;
157
158 if ui
159 .add_sized([button_width, 32.0], egui::Button::new("Yes, Install"))
160 .clicked()
161 {
162 response = ShaderInstallResponse::Install;
163 }
164
165 ui.add_space(8.0);
166
167 if ui
168 .add_sized([button_width, 32.0], egui::Button::new("Never"))
169 .on_hover_text("Don't ask again")
170 .clicked()
171 {
172 response = ShaderInstallResponse::Never;
173 }
174
175 ui.add_space(8.0);
176
177 if ui
178 .add_sized([button_width, 32.0], egui::Button::new("Later"))
179 .on_hover_text("Ask again next time")
180 .clicked()
181 {
182 response = ShaderInstallResponse::Later;
183 }
184 });
185 } else if self.success_message.is_some() {
186 if ui
188 .add_sized([120.0, 32.0], egui::Button::new("OK"))
189 .clicked()
190 {
191 self.visible = false;
192 }
193 }
194 });
195
196 ui.add_space(8.0);
197
198 if !self.installing
200 && self.success_message.is_none()
201 && self.error_message.is_none()
202 {
203 ui.vertical_centered(|ui| {
204 ui.label(
205 RichText::new(
206 "You can always install later with: par-term install-shaders",
207 )
208 .weak()
209 .small(),
210 );
211 });
212 }
213 });
214
215 response
216 }
217
218 pub fn set_installing(&mut self, message: &str) {
220 self.installing = true;
221 self.progress_message = Some(message.to_string());
222 self.error_message = None;
223 }
224
225 pub fn set_error(&mut self, error: &str) {
227 self.installing = false;
228 self.progress_message = None;
229 self.error_message = Some(error.to_string());
230 }
231
232 pub fn set_success(&mut self, message: &str) {
234 self.installing = false;
235 self.progress_message = None;
236 self.error_message = None;
237 self.success_message = Some(message.to_string());
238 }
239}
240
241impl Default for ShaderInstallUI {
242 fn default() -> Self {
243 Self::new()
244 }
245}
246
247pub fn install_shaders_headless() -> Result<usize, String> {
250 shader_installer::install_shaders()
251}