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