par_term/settings_window/
mod.rs1mod render;
7
8use crate::settings_ui::SettingsUI;
9use anyhow::{Context, Result};
10use par_term_config::Config;
11
12pub use crate::settings_ui::SettingsWindowAction;
15use std::sync::Arc;
16use winit::event::WindowEvent;
17use winit::event_loop::ActiveEventLoop;
18use winit::keyboard::{Key, NamedKey};
19use winit::window::{Window, WindowId};
20
21pub struct SettingsWindow {
23 pub(super) window: Arc<Window>,
25 window_id: WindowId,
27 pub(super) surface: wgpu::Surface<'static>,
29 pub(super) device: Arc<wgpu::Device>,
31 pub(super) queue: Arc<wgpu::Queue>,
33 pub(super) surface_config: wgpu::SurfaceConfiguration,
35 pub(super) egui_ctx: egui::Context,
37 pub(super) egui_state: egui_winit::State,
39 pub(super) egui_renderer: egui_wgpu::Renderer,
41 pub settings_ui: SettingsUI,
43 pub(super) ready: bool,
45 should_close: bool,
47 pub(super) pending_paste: Option<String>,
49 pub(super) pending_events: Vec<egui::Event>,
51}
52
53impl SettingsWindow {
54 pub async fn new(
56 event_loop: &ActiveEventLoop,
57 config: Config,
58 supported_vsync_modes: Vec<crate::config::VsyncMode>,
59 ) -> Result<Self> {
60 let window_attrs = Window::default_attributes()
62 .with_title("Settings")
63 .with_inner_size(winit::dpi::LogicalSize::new(770, 800))
64 .with_min_inner_size(winit::dpi::LogicalSize::new(550, 400))
65 .with_resizable(true);
66
67 let window = Arc::new(event_loop.create_window(window_attrs)?);
68 let window_id = window.id();
69 let size = window.inner_size();
70
71 #[cfg(target_os = "windows")]
74 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
75 backends: wgpu::Backends::DX12,
76 ..wgpu::InstanceDescriptor::new_without_display_handle()
77 });
78 #[cfg(target_os = "macos")]
79 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
80 backends: wgpu::Backends::all(),
81 ..wgpu::InstanceDescriptor::new_without_display_handle()
82 });
83 #[cfg(target_os = "linux")]
84 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
85 backends: wgpu::Backends::VULKAN | wgpu::Backends::GL,
86 ..wgpu::InstanceDescriptor::new_without_display_handle()
87 });
88
89 let surface = instance.create_surface(window.clone())?;
91
92 let adapter = instance
94 .request_adapter(&wgpu::RequestAdapterOptions {
95 power_preference: wgpu::PowerPreference::LowPower,
96 compatible_surface: Some(&surface),
97 force_fallback_adapter: false,
98 })
99 .await
100 .context("Failed to find suitable GPU adapter")?;
101
102 let (device, queue) = adapter
104 .request_device(&wgpu::DeviceDescriptor::default())
105 .await?;
106
107 let device = Arc::new(device);
108 let queue = Arc::new(queue);
109
110 let surface_caps = surface.get_capabilities(&adapter);
112 let surface_format = surface_caps
113 .formats
114 .iter()
115 .find(|f| f.is_srgb())
116 .copied()
117 .or_else(|| surface_caps.formats.first().copied())
118 .context("Surface reports no supported texture formats")?;
119
120 let alpha_mode = if surface_caps
122 .alpha_modes
123 .contains(&wgpu::CompositeAlphaMode::PreMultiplied)
124 {
125 wgpu::CompositeAlphaMode::PreMultiplied
126 } else if surface_caps
127 .alpha_modes
128 .contains(&wgpu::CompositeAlphaMode::PostMultiplied)
129 {
130 wgpu::CompositeAlphaMode::PostMultiplied
131 } else if surface_caps
132 .alpha_modes
133 .contains(&wgpu::CompositeAlphaMode::Auto)
134 {
135 wgpu::CompositeAlphaMode::Auto
136 } else {
137 surface_caps
138 .alpha_modes
139 .first()
140 .copied()
141 .context("Surface reports no supported alpha modes")?
142 };
143
144 let surface_config = wgpu::SurfaceConfiguration {
145 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
146 format: surface_format,
147 width: size.width.max(1),
148 height: size.height.max(1),
149 present_mode: wgpu::PresentMode::AutoVsync,
150 alpha_mode,
151 view_formats: vec![],
152 desired_maximum_frame_latency: 2,
153 };
154 surface.configure(&device, &surface_config);
155
156 let scale_factor = window.scale_factor() as f32;
158 let egui_ctx = egui::Context::default();
159 crate::settings_ui::nerd_font::configure_nerd_font(&egui_ctx);
160 let egui_state = egui_winit::State::new(
161 egui_ctx.clone(),
162 egui::ViewportId::ROOT,
163 &window,
164 Some(scale_factor),
165 None,
166 None,
167 );
168
169 let egui_renderer = egui_wgpu::Renderer::new(
171 &device,
172 surface_format,
173 egui_wgpu::RendererOptions {
174 msaa_samples: 1,
175 depth_stencil_format: None,
176 dithering: false,
177 predictable_texture_filtering: false,
178 },
179 );
180
181 let mut settings_ui = SettingsUI::new(config);
183 settings_ui.visible = true; settings_ui.update_supported_vsync_modes(supported_vsync_modes);
185
186 Ok(Self {
187 window,
188 window_id,
189 surface,
190 device,
191 queue,
192 surface_config,
193 egui_ctx,
194 egui_state,
195 egui_renderer,
196 settings_ui,
197 ready: true,
198 should_close: false,
199 pending_paste: None,
200 pending_events: Vec::new(),
201 })
202 }
203
204 pub fn window_id(&self) -> WindowId {
206 self.window_id
207 }
208
209 pub fn should_close(&self) -> bool {
211 self.should_close
212 }
213
214 pub fn inject_paste(&mut self, text: String) {
219 self.pending_paste = Some(text);
220 self.window.request_redraw();
221 }
222
223 pub fn inject_event(&mut self, event: egui::Event) {
228 self.pending_events.push(event);
229 self.window.request_redraw();
230 }
231
232 pub fn update_config(&mut self, config: Config) {
234 self.settings_ui.update_config(config);
235 }
236
237 pub fn force_update_config(&mut self, config: Config) {
241 self.settings_ui.force_update_config(config);
242 }
243
244 pub fn set_shader_error(&mut self, error: Option<String>) {
246 self.settings_ui.set_shader_error(error);
247 }
248
249 pub fn set_cursor_shader_error(&mut self, error: Option<String>) {
251 self.settings_ui.set_cursor_shader_error(error);
252 }
253
254 pub fn clear_shader_error(&mut self) {
256 self.settings_ui.clear_shader_error();
257 }
258
259 pub fn clear_cursor_shader_error(&mut self) {
261 self.settings_ui.clear_cursor_shader_error();
262 }
263
264 pub fn sync_shader_states(&mut self, custom_shader_enabled: bool, cursor_shader_enabled: bool) {
267 self.settings_ui.config.shader.custom_shader_enabled = custom_shader_enabled;
268 self.settings_ui.config.shader.cursor_shader_enabled = cursor_shader_enabled;
269 }
270
271 pub fn handle_window_event(&mut self, event: WindowEvent) -> SettingsWindowAction {
273 let event_response = self.egui_state.on_window_event(&self.window, &event);
275
276 match event {
277 WindowEvent::CloseRequested => {
278 self.should_close = true;
279 return SettingsWindowAction::Close;
280 }
281
282 WindowEvent::Resized(new_size)
283 if new_size.width > 0 && new_size.height > 0 => {
284 self.surface_config.width = new_size.width;
285 self.surface_config.height = new_size.height;
286 self.surface.configure(&self.device, &self.surface_config);
287 self.window.request_redraw();
288 }
289
290 WindowEvent::KeyboardInput { event, .. }
291 if !event_response.consumed
293 && event.state.is_pressed()
294 && matches!(event.logical_key, Key::Named(NamedKey::Escape))
295 && !self.settings_ui.shader_editor_visible
297 && !self.settings_ui.cursor_shader_editor_visible
298 => {
299 self.should_close = true;
300 return SettingsWindowAction::Close;
301 }
302
303 WindowEvent::RedrawRequested => {
304 return self.render();
305 }
306
307 _ => {}
308 }
309
310 if event_response.repaint {
312 self.window.request_redraw();
313 }
314
315 SettingsWindowAction::None
316 }
317
318 pub fn request_redraw(&self) {
320 self.window.request_redraw();
321 }
322
323 pub fn focus(&self) {
325 self.window.focus_window();
326 self.window.request_redraw();
327 }
328
329 pub fn is_focused(&self) -> bool {
331 self.window.has_focus()
332 }
333}