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 apply_limit_buckets: false,
99 })
100 .await
101 .context("Failed to find suitable GPU adapter")?;
102
103 let (device, queue) = adapter
105 .request_device(&wgpu::DeviceDescriptor {
106 required_limits: par_term_render::texture_limits(
107 adapter.limits().max_texture_dimension_2d,
108 ),
109 ..Default::default()
110 })
111 .await?;
112 par_term_render::install_nonfatal_error_handler(&device);
113
114 let device = Arc::new(device);
115 let queue = Arc::new(queue);
116
117 let surface_caps = surface.get_capabilities(&adapter);
119 let surface_format = surface_caps
120 .formats
121 .iter()
122 .find(|f| f.is_srgb())
123 .copied()
124 .or_else(|| surface_caps.formats.first().copied())
125 .context("Surface reports no supported texture formats")?;
126
127 let alpha_mode = if surface_caps
129 .alpha_modes
130 .contains(&wgpu::CompositeAlphaMode::PreMultiplied)
131 {
132 wgpu::CompositeAlphaMode::PreMultiplied
133 } else if surface_caps
134 .alpha_modes
135 .contains(&wgpu::CompositeAlphaMode::PostMultiplied)
136 {
137 wgpu::CompositeAlphaMode::PostMultiplied
138 } else if surface_caps
139 .alpha_modes
140 .contains(&wgpu::CompositeAlphaMode::Auto)
141 {
142 wgpu::CompositeAlphaMode::Auto
143 } else {
144 surface_caps
145 .alpha_modes
146 .first()
147 .copied()
148 .context("Surface reports no supported alpha modes")?
149 };
150
151 let (surface_width, surface_height) = par_term_render::clamp_surface_extent(
152 size.width,
153 size.height,
154 device.limits().max_texture_dimension_2d,
155 );
156 let surface_config = wgpu::SurfaceConfiguration {
157 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
158 format: surface_format,
159 color_space: wgpu::SurfaceColorSpace::Auto,
160 width: surface_width,
161 height: surface_height,
162 present_mode: wgpu::PresentMode::AutoVsync,
163 alpha_mode,
164 view_formats: vec![],
165 desired_maximum_frame_latency: 2,
166 };
167 surface.configure(&device, &surface_config);
168
169 let scale_factor = window.scale_factor() as f32;
171 let egui_ctx = egui::Context::default();
172 crate::settings_ui::nerd_font::configure_nerd_font(&egui_ctx);
173 let egui_state = egui_winit::State::new(
174 egui_ctx.clone(),
175 egui::ViewportId::ROOT,
176 &window,
177 Some(scale_factor),
178 None,
179 None,
180 );
181
182 let egui_renderer = egui_wgpu::Renderer::new(
184 &device,
185 surface_format,
186 egui_wgpu::RendererOptions {
187 msaa_samples: 1,
188 depth_stencil_format: None,
189 dithering: false,
190 predictable_texture_filtering: false,
191 },
192 );
193
194 let mut settings_ui = SettingsUI::new(config);
196 settings_ui.visible = true; settings_ui.update_supported_vsync_modes(supported_vsync_modes);
198
199 Ok(Self {
200 window,
201 window_id,
202 surface,
203 device,
204 queue,
205 surface_config,
206 egui_ctx,
207 egui_state,
208 egui_renderer,
209 settings_ui,
210 ready: true,
211 should_close: false,
212 pending_paste: None,
213 pending_events: Vec::new(),
214 })
215 }
216
217 pub fn window_id(&self) -> WindowId {
219 self.window_id
220 }
221
222 pub fn should_close(&self) -> bool {
224 self.should_close
225 }
226
227 pub fn inject_paste(&mut self, text: String) {
232 self.pending_paste = Some(text);
233 self.window.request_redraw();
234 }
235
236 pub fn inject_event(&mut self, event: egui::Event) {
241 self.pending_events.push(event);
242 self.window.request_redraw();
243 }
244
245 pub fn update_config(&mut self, config: Config) {
247 self.settings_ui.update_config(config);
248 }
249
250 pub fn force_update_config(&mut self, config: Config) {
254 self.settings_ui.force_update_config(config);
255 }
256
257 pub fn set_shader_error(&mut self, error: Option<String>) {
259 self.settings_ui.set_shader_error(error);
260 }
261
262 pub fn set_cursor_shader_error(&mut self, error: Option<String>) {
264 self.settings_ui.set_cursor_shader_error(error);
265 }
266
267 pub fn clear_shader_error(&mut self) {
269 self.settings_ui.clear_shader_error();
270 }
271
272 pub fn clear_cursor_shader_error(&mut self) {
274 self.settings_ui.clear_cursor_shader_error();
275 }
276
277 pub fn sync_shader_states(&mut self, custom_shader_enabled: bool, cursor_shader_enabled: bool) {
280 self.settings_ui.config.shader.custom_shader_enabled = custom_shader_enabled;
281 self.settings_ui.config.shader.cursor_shader_enabled = cursor_shader_enabled;
282 }
283
284 pub fn handle_window_event(&mut self, event: WindowEvent) -> SettingsWindowAction {
286 let event_response = self.egui_state.on_window_event(&self.window, &event);
288
289 match event {
290 WindowEvent::CloseRequested => {
291 self.should_close = true;
292 return SettingsWindowAction::Close;
293 }
294
295 WindowEvent::Resized(new_size)
296 if new_size.width > 0 && new_size.height > 0 => {
297 let (width, height) = par_term_render::clamp_surface_extent(
298 new_size.width,
299 new_size.height,
300 self.device.limits().max_texture_dimension_2d,
301 );
302 self.surface_config.width = width;
303 self.surface_config.height = height;
304 self.surface.configure(&self.device, &self.surface_config);
305 self.window.request_redraw();
306 }
307
308 WindowEvent::KeyboardInput { event, .. }
309 if !event_response.consumed
311 && event.state.is_pressed()
312 && matches!(event.logical_key, Key::Named(NamedKey::Escape))
313 && !self.settings_ui.shader_editor_visible
315 && !self.settings_ui.cursor_shader_editor_visible
316 => {
317 self.should_close = true;
318 return SettingsWindowAction::Close;
319 }
320
321 WindowEvent::RedrawRequested => {
322 return self.render();
323 }
324
325 _ => {}
326 }
327
328 if event_response.repaint {
330 self.window.request_redraw();
331 }
332
333 SettingsWindowAction::None
334 }
335
336 pub fn request_redraw(&self) {
338 self.window.request_redraw();
339 }
340
341 pub fn focus(&self) {
343 self.window.focus_window();
344 self.window.request_redraw();
345 }
346
347 pub fn is_focused(&self) -> bool {
349 self.window.has_focus()
350 }
351}