Skip to main content

winit_ui/
app.rs

1pub struct App {
2    window: Option<std::sync::Arc<winit::window::Window>>,
3    pixels: Option<pixels::Pixels<'static>>,
4    title: String,
5    width: u32,
6    height: u32,
7    aspect_ratio: f64,
8    keep_aspect_ratio: bool,
9    resizable: bool,
10    fullscreen: bool,
11    draw_fn: Option<Box<dyn FnMut(&mut crate::canvas::Canvas)>>,
12}
13
14impl App {
15    pub fn new(title: &str, width: u32, height: u32) -> Self {
16        Self {
17            window: None,
18            pixels: None,
19            title: title.to_string(),
20            width,
21            height,
22            aspect_ratio: width as f64 / height as f64,
23            keep_aspect_ratio: false,
24            resizable: false,
25            fullscreen: false,
26            draw_fn: None,
27        }
28    }
29
30    pub fn set_fullscreen(mut self, fullscreen: bool) -> Self {
31        self.fullscreen = fullscreen;
32        self
33    }
34
35    pub fn set_resizable(mut self, resizable: bool) -> Self {
36        self.resizable = resizable;
37        self
38    }
39
40    pub fn set_title(mut self, title: &str) -> Self {
41        self.title = title.to_string();
42        self
43    }
44
45    pub fn keep_aspect_ratio(mut self, keep: bool) -> Self {
46        self.keep_aspect_ratio = keep;
47        self
48    }
49
50    pub fn on_draw(mut self, f: impl FnMut(&mut crate::canvas::Canvas) + 'static) -> Self {
51        self.draw_fn = Some(Box::new(f));
52        self
53    }
54
55    pub fn run(mut self) {
56        let event_loop = winit::event_loop::EventLoop::new().unwrap();
57        event_loop.run_app(&mut self).unwrap();
58    }
59}
60
61impl winit::application::ApplicationHandler for App {
62    fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
63        let attributes = winit::window::Window::default_attributes()
64            .with_title(self.title.clone())
65            .with_resizable(self.resizable)
66            .with_inner_size(winit::dpi::PhysicalSize::new(self.width, self.height))
67            .with_fullscreen(
68                if self.fullscreen {
69                    Some(winit::window::Fullscreen::Borderless(None))
70                }
71                else {
72                    None
73                }
74            );
75        
76        let window = std::sync::Arc::new(event_loop.create_window(attributes).expect("Failed to create window"));
77
78        let surface_texture = pixels::SurfaceTexture::new(self.width, self.height, window.clone());
79        let mut pixels = pixels::Pixels::new(self.width, self.height, surface_texture).unwrap();
80        pixels.set_scaling_mode(pixels::ScalingMode::Fill);
81
82        self.pixels = Some(pixels);
83        self.window = Some(window);
84        self.window.as_ref().unwrap().request_redraw();
85    }
86
87    fn window_event(
88        &mut self,
89        event_loop: &winit::event_loop::ActiveEventLoop,
90        window_id: winit::window::WindowId,
91        event: winit::event::WindowEvent,
92    )
93    {
94        if let Some(window) = &self.window {
95            if window.id() == window_id {
96                match event {
97                    winit::event::WindowEvent::CloseRequested => {
98                        event_loop.exit();
99                    },
100                    winit::event::WindowEvent::RedrawRequested => {
101                        if let (Some(pixels), Some(draw_fn)) = (self.pixels.as_mut(), self.draw_fn.as_mut()) {
102                            let mut canvas = crate::canvas::Canvas::new(pixels.frame_mut(), self.width, self.height);
103                            draw_fn(&mut canvas);
104                            pixels.render().unwrap();
105                        }
106                        self.window.as_ref().unwrap().request_redraw();
107                    }
108                    winit::event::WindowEvent::Resized(new_size) => {
109                        if new_size.width > 0 && new_size.height > 0 {
110                            if self.keep_aspect_ratio {
111                                let corrected_height = (new_size.width as f64 / self.aspect_ratio).round() as u32;
112                                if corrected_height != new_size.height {
113                                    let window = self.window.as_ref().unwrap();
114                                    let _ = window.request_inner_size(
115                                        winit::dpi::PhysicalSize::new(new_size.width, corrected_height.max(1)),
116                                    );
117                                }
118                            }
119
120                            if let Some(pixels) = self.pixels.as_mut() {
121                                pixels.resize_surface(new_size.width, new_size.height).unwrap();
122                            }
123                        }
124                    }
125                    _ => {}
126                }
127            }
128        }
129    }
130}