mod render;
use crate::settings_ui::SettingsUI;
use anyhow::{Context, Result};
use par_term_config::Config;
pub use crate::settings_ui::SettingsWindowAction;
use std::sync::Arc;
use winit::event::WindowEvent;
use winit::event_loop::ActiveEventLoop;
use winit::keyboard::{Key, NamedKey};
use winit::window::{Window, WindowId};
pub struct SettingsWindow {
pub(super) window: Arc<Window>,
window_id: WindowId,
pub(super) surface: wgpu::Surface<'static>,
pub(super) device: Arc<wgpu::Device>,
pub(super) queue: Arc<wgpu::Queue>,
pub(super) surface_config: wgpu::SurfaceConfiguration,
pub(super) egui_ctx: egui::Context,
pub(super) egui_state: egui_winit::State,
pub(super) egui_renderer: egui_wgpu::Renderer,
pub settings_ui: SettingsUI,
pub(super) ready: bool,
should_close: bool,
pub(super) pending_paste: Option<String>,
pub(super) pending_events: Vec<egui::Event>,
}
impl SettingsWindow {
pub async fn new(
event_loop: &ActiveEventLoop,
config: Config,
supported_vsync_modes: Vec<crate::config::VsyncMode>,
) -> Result<Self> {
let window_attrs = Window::default_attributes()
.with_title("Settings")
.with_inner_size(winit::dpi::LogicalSize::new(770, 800))
.with_min_inner_size(winit::dpi::LogicalSize::new(550, 400))
.with_resizable(true);
let window = Arc::new(event_loop.create_window(window_attrs)?);
let window_id = window.id();
let size = window.inner_size();
#[cfg(target_os = "windows")]
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
backends: wgpu::Backends::DX12,
..Default::default()
});
#[cfg(target_os = "macos")]
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
..Default::default()
});
#[cfg(target_os = "linux")]
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
backends: wgpu::Backends::VULKAN | wgpu::Backends::GL,
..Default::default()
});
let surface = instance.create_surface(window.clone())?;
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::LowPower,
compatible_surface: Some(&surface),
force_fallback_adapter: false,
})
.await
.context("Failed to find suitable GPU adapter")?;
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor::default())
.await?;
let device = Arc::new(device);
let queue = Arc::new(queue);
let surface_caps = surface.get_capabilities(&adapter);
let surface_format = surface_caps
.formats
.iter()
.find(|f| f.is_srgb())
.copied()
.unwrap_or(surface_caps.formats[0]);
let alpha_mode = if surface_caps
.alpha_modes
.contains(&wgpu::CompositeAlphaMode::PreMultiplied)
{
wgpu::CompositeAlphaMode::PreMultiplied
} else if surface_caps
.alpha_modes
.contains(&wgpu::CompositeAlphaMode::PostMultiplied)
{
wgpu::CompositeAlphaMode::PostMultiplied
} else if surface_caps
.alpha_modes
.contains(&wgpu::CompositeAlphaMode::Auto)
{
wgpu::CompositeAlphaMode::Auto
} else {
surface_caps.alpha_modes[0]
};
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface_format,
width: size.width.max(1),
height: size.height.max(1),
present_mode: wgpu::PresentMode::AutoVsync,
alpha_mode,
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &surface_config);
let scale_factor = window.scale_factor() as f32;
let egui_ctx = egui::Context::default();
crate::settings_ui::nerd_font::configure_nerd_font(&egui_ctx);
let egui_state = egui_winit::State::new(
egui_ctx.clone(),
egui::ViewportId::ROOT,
&window,
Some(scale_factor),
None,
None,
);
let egui_renderer = egui_wgpu::Renderer::new(
&device,
surface_format,
egui_wgpu::RendererOptions {
msaa_samples: 1,
depth_stencil_format: None,
dithering: false,
predictable_texture_filtering: false,
},
);
let mut settings_ui = SettingsUI::new(config);
settings_ui.visible = true; settings_ui.update_supported_vsync_modes(supported_vsync_modes);
Ok(Self {
window,
window_id,
surface,
device,
queue,
surface_config,
egui_ctx,
egui_state,
egui_renderer,
settings_ui,
ready: true,
should_close: false,
pending_paste: None,
pending_events: Vec::new(),
})
}
pub fn window_id(&self) -> WindowId {
self.window_id
}
pub fn should_close(&self) -> bool {
self.should_close
}
pub fn inject_paste(&mut self, text: String) {
self.pending_paste = Some(text);
self.window.request_redraw();
}
pub fn inject_event(&mut self, event: egui::Event) {
self.pending_events.push(event);
self.window.request_redraw();
}
pub fn update_config(&mut self, config: Config) {
self.settings_ui.update_config(config);
}
pub fn force_update_config(&mut self, config: Config) {
self.settings_ui.force_update_config(config);
}
pub fn set_shader_error(&mut self, error: Option<String>) {
self.settings_ui.set_shader_error(error);
}
pub fn set_cursor_shader_error(&mut self, error: Option<String>) {
self.settings_ui.set_cursor_shader_error(error);
}
pub fn clear_shader_error(&mut self) {
self.settings_ui.clear_shader_error();
}
pub fn clear_cursor_shader_error(&mut self) {
self.settings_ui.clear_cursor_shader_error();
}
pub fn sync_shader_states(&mut self, custom_shader_enabled: bool, cursor_shader_enabled: bool) {
self.settings_ui.config.shader.custom_shader_enabled = custom_shader_enabled;
self.settings_ui.config.shader.cursor_shader_enabled = cursor_shader_enabled;
}
pub fn handle_window_event(&mut self, event: WindowEvent) -> SettingsWindowAction {
let event_response = self.egui_state.on_window_event(&self.window, &event);
match event {
WindowEvent::CloseRequested => {
self.should_close = true;
return SettingsWindowAction::Close;
}
WindowEvent::Resized(new_size) => {
if new_size.width > 0 && new_size.height > 0 {
self.surface_config.width = new_size.width;
self.surface_config.height = new_size.height;
self.surface.configure(&self.device, &self.surface_config);
self.window.request_redraw();
}
}
WindowEvent::KeyboardInput { event, .. } => {
if !event_response.consumed
&& event.state.is_pressed()
&& matches!(event.logical_key, Key::Named(NamedKey::Escape))
{
if !self.settings_ui.shader_editor_visible
&& !self.settings_ui.cursor_shader_editor_visible
{
self.should_close = true;
return SettingsWindowAction::Close;
}
}
}
WindowEvent::RedrawRequested => {
return self.render();
}
_ => {}
}
if event_response.repaint {
self.window.request_redraw();
}
SettingsWindowAction::None
}
pub fn request_redraw(&self) {
self.window.request_redraw();
}
pub fn focus(&self) {
self.window.focus_window();
self.window.request_redraw();
}
pub fn is_focused(&self) -> bool {
self.window.has_focus()
}
}