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}