1use crate::window::Window;
2use crate::event::PlatformEvent;
3use winit::{
4 application::ApplicationHandler,
5 event::WindowEvent,
6 event_loop::{ActiveEventLoop, EventLoop},
7 window::WindowId,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum WindowAction {
12 Drag,
13 Minimize,
14 Maximize,
15 Close,
16}
17
18pub struct Frame<'a> {
19 pub window: &'a mut Window,
20 pub events: &'a [PlatformEvent],
21 pub request_redraw_at: Option<std::time::Instant>,
22 pub window_actions: Vec<WindowAction>,
23}
24
25impl<'a> Frame<'a> {
26 pub fn width(&self) -> u32 {
27 self.window.width()
28 }
29 pub fn height(&self) -> u32 {
30 self.window.height()
31 }
32 pub fn scale_factor(&self) -> f64 {
33 self.window.scale_factor()
34 }
35}
36
37pub struct App {
38 title: String,
39 width: u32,
40 height: u32,
41 decorations: bool,
42 draw_fn: Option<Box<dyn FnMut(&mut Frame) -> bool + 'static>>,
43}
44
45impl App {
46 pub fn new() -> Self {
47 Self {
48 title: "Zenthra".to_string(),
49 width: 800,
50 height: 600,
51 decorations: true,
52 draw_fn: None,
53 }
54 }
55
56 pub fn title(mut self, t: &str) -> Self {
57 self.title = t.to_string();
58 self
59 }
60
61 pub fn size(mut self, w: u32, h: u32) -> Self {
62 self.width = w;
63 self.height = h;
64 self
65 }
66
67 pub fn decorations(mut self, dec: bool) -> Self {
68 self.decorations = dec;
69 self
70 }
71
72 pub fn with_ui<F>(mut self, f: F) -> Self
73 where
74 F: FnMut(&mut Frame) -> bool + 'static,
75 {
76 self.draw_fn = Some(Box::new(f));
77 self
78 }
79
80 pub fn run(self) {
81 let event_loop = EventLoop::new().unwrap();
82 self.run_with_event_loop(event_loop);
83 }
84
85 pub fn run_with_event_loop(self, event_loop: EventLoop<()>) {
86 let mut runner = AppRunner {
87 title: self.title,
88 width: self.width,
89 height: self.height,
90 decorations: self.decorations,
91 draw_fn: self.draw_fn,
92 window: None,
93 pending_events: Vec::new(),
94 next_wakeup: None,
95 };
96 event_loop.run_app(&mut runner).unwrap();
97 }
98}
99
100struct AppRunner {
101 title: String,
102 width: u32,
103 height: u32,
104 decorations: bool,
105 draw_fn: Option<Box<dyn FnMut(&mut Frame) -> bool + 'static>>,
106 window: Option<Window>,
107 pending_events: Vec<PlatformEvent>,
108 next_wakeup: Option<std::time::Instant>,
109}
110
111impl AppRunner {
112 fn render(&mut self, event_loop: &ActiveEventLoop) {
113 let Some(window) = &mut self.window else { return };
114
115 let mut needs_redraw = false;
116 let mut request_redraw_at = None;
117 let mut actions = Vec::new();
118 if let Some(draw_fn) = &mut self.draw_fn {
119 let mut frame = Frame {
120 window,
121 events: &self.pending_events,
122 request_redraw_at: None,
123 window_actions: Vec::new(),
124 };
125 needs_redraw = draw_fn(&mut frame);
126 request_redraw_at = frame.request_redraw_at;
127 actions = frame.window_actions;
128 }
129 self.pending_events.clear();
130
131 self.next_wakeup = request_redraw_at;
132
133 for action in actions {
134 match action {
135 WindowAction::Drag => {
136 let _ = window.winit_window.drag_window();
137 }
138 WindowAction::Minimize => {
139 window.winit_window.set_minimized(true);
140 }
141 WindowAction::Maximize => {
142 let is_max = window.winit_window.is_maximized();
143 window.winit_window.set_maximized(!is_max);
144 }
145 WindowAction::Close => {
146 event_loop.exit();
147 }
148 }
149 }
150
151 if needs_redraw {
152 window.request_redraw();
153 }
154 }
155}
156
157impl ApplicationHandler for AppRunner {
158 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
159 if self.window.is_none() {
160 let window = pollster::block_on(Window::new(
161 event_loop,
162 &self.title,
163 self.width,
164 self.height,
165 self.decorations,
166 ));
167 window.request_redraw();
168 self.window = Some(window);
169 }
170 }
171
172 fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
173 match event {
174 WindowEvent::CloseRequested => {
175 event_loop.exit();
176 }
177 WindowEvent::Resized(size) => {
178 if let Some(w) = &mut self.window {
179 w.resize(size);
180 w.request_redraw();
181 }
182 }
183 WindowEvent::CursorMoved { position, .. } => {
184 self.pending_events.push(PlatformEvent::MouseMoved {
185 x: position.x,
186 y: position.y,
187 });
188 if let Some(w) = &mut self.window { w.request_redraw(); }
189 }
190 WindowEvent::MouseInput { button, state, .. } => {
191 self.pending_events.push(PlatformEvent::MouseButton { button, state });
192 if let Some(w) = &mut self.window { w.request_redraw(); }
193 }
194 WindowEvent::MouseWheel { delta, .. } => {
195 let (x, y) = match delta {
196 winit::event::MouseScrollDelta::LineDelta(x, y) => (x, y),
197 winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32),
198 };
199 self.pending_events.push(PlatformEvent::MouseWheel { delta_x: x, delta_y: y });
200 if let Some(w) = &mut self.window { w.request_redraw(); }
201 }
202 WindowEvent::Touch(touch) => {
203 self.pending_events.push(PlatformEvent::Touch {
204 id: touch.id,
205 phase: touch.phase,
206 x: touch.location.x,
207 y: touch.location.y,
208 });
209
210 if touch.id == 0 {
212 match touch.phase {
213 winit::event::TouchPhase::Started => {
214 self.pending_events.push(PlatformEvent::MouseMoved { x: touch.location.x, y: touch.location.y });
215 self.pending_events.push(PlatformEvent::MouseButton {
216 button: winit::event::MouseButton::Left,
217 state: winit::event::ElementState::Pressed
218 });
219 }
220 winit::event::TouchPhase::Moved => {
221 self.pending_events.push(PlatformEvent::MouseMoved { x: touch.location.x, y: touch.location.y });
222 }
223 winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => {
224 self.pending_events.push(PlatformEvent::MouseButton {
225 button: winit::event::MouseButton::Left,
226 state: winit::event::ElementState::Released
227 });
228 }
229 }
230 }
231 if let Some(w) = &mut self.window { w.request_redraw(); }
232 }
233 WindowEvent::KeyboardInput { event, .. } => {
234 if let winit::keyboard::PhysicalKey::Code(key) = event.physical_key {
235 if event.state == winit::event::ElementState::Pressed {
236 self.pending_events.push(PlatformEvent::KeyDown { key });
237 } else {
238 self.pending_events.push(PlatformEvent::KeyUp { key });
239 }
240 }
241
242 if event.state == winit::event::ElementState::Pressed {
243 if let Some(text) = event.text {
244 for c in text.chars() {
245 if !c.is_control() {
246 self.pending_events.push(PlatformEvent::CharTyped(c));
247 }
248 }
249 }
250 }
251 if let Some(w) = &mut self.window { w.request_redraw(); }
252 }
253 WindowEvent::RedrawRequested => {
254 self.render(event_loop);
255 }
256 _ => {}
257 }
258 }
259
260 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
261 if let Some(wakeup) = self.next_wakeup {
262 event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(wakeup));
263 } else {
264 event_loop.set_control_flow(winit::event_loop::ControlFlow::Wait);
265 }
266 }
267}