Skip to main content

zenthra_platform/
app.rs

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                // Map first touch to mouse for basic interaction support
239                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}