Skip to main content

blitz_shell/
application.rs

1use crate::event::{BlitzShellEvent, BlitzShellProxy};
2
3use anyrender::WindowRenderer;
4use std::collections::HashMap;
5use std::sync::mpsc::Receiver;
6use winit::application::ApplicationHandler;
7use winit::event::WindowEvent;
8use winit::event_loop::ActiveEventLoop;
9use winit::window::WindowId;
10
11#[cfg(target_os = "macos")]
12use winit::platform::macos::ApplicationHandlerExtMacOS;
13
14use crate::{View, WindowConfig};
15
16pub struct BlitzApplication<Rend: WindowRenderer> {
17    pub windows: HashMap<WindowId, View<Rend>>,
18    pub pending_windows: Vec<WindowConfig<Rend>>,
19    pub proxy: BlitzShellProxy,
20    pub event_queue: Receiver<BlitzShellEvent>,
21}
22
23impl<Rend: WindowRenderer> BlitzApplication<Rend> {
24    pub fn new(proxy: BlitzShellProxy, event_queue: Receiver<BlitzShellEvent>) -> Self {
25        BlitzApplication {
26            windows: HashMap::new(),
27            pending_windows: Vec::new(),
28            proxy,
29            event_queue,
30        }
31    }
32
33    pub fn add_window(&mut self, window_config: WindowConfig<Rend>) {
34        self.pending_windows.push(window_config);
35    }
36
37    fn window_mut_by_doc_id(&mut self, doc_id: usize) -> Option<&mut View<Rend>> {
38        self.windows.values_mut().find(|w| w.doc.id() == doc_id)
39    }
40
41    pub fn handle_blitz_shell_event(
42        &mut self,
43        _event_loop: &dyn ActiveEventLoop,
44        event: BlitzShellEvent,
45    ) {
46        match event {
47            BlitzShellEvent::Poll { window_id } => {
48                if let Some(window) = self.windows.get_mut(&window_id) {
49                    window.poll();
50                };
51            }
52            BlitzShellEvent::RequestRedraw { doc_id } => {
53                // TODO: Handle multiple documents per window
54                if let Some(window) = self.window_mut_by_doc_id(doc_id) {
55                    window.request_redraw();
56                }
57            }
58
59            #[cfg(feature = "accessibility")]
60            BlitzShellEvent::Accessibility { window_id, data } => {
61                if let Some(window) = self.windows.get_mut(&window_id) {
62                    match &*data {
63                        accesskit_xplat::WindowEvent::InitialTreeRequested => {
64                            window.build_accessibility_tree();
65                        }
66                        accesskit_xplat::WindowEvent::AccessibilityDeactivated => {
67                            // TODO
68                        }
69                        accesskit_xplat::WindowEvent::ActionRequested(_req) => {
70                            // TODO
71                        }
72                    }
73                }
74            }
75            BlitzShellEvent::Embedder(_) => {
76                // Do nothing. Should be handled by embedders (if required).
77            }
78            BlitzShellEvent::Navigate(_opts) => {
79                // Do nothing. Should be handled by embedders (if required).
80            }
81            BlitzShellEvent::NavigationLoad { .. } => {
82                // Do nothing. Should be handled by embedders (if required).
83            }
84        }
85    }
86}
87
88impl<Rend: WindowRenderer> ApplicationHandler for BlitzApplication<Rend> {
89    fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
90        // Resume existing windows
91        for (_, view) in self.windows.iter_mut() {
92            view.resume();
93        }
94
95        // Initialise pending windows
96        for window_config in self.pending_windows.drain(..) {
97            let mut view = View::init(window_config, event_loop, &self.proxy);
98            view.resume();
99            if !view.renderer.is_active() {
100                continue;
101            }
102            self.windows.insert(view.window_id(), view);
103        }
104    }
105
106    fn destroy_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {
107        for (_, view) in self.windows.iter_mut() {
108            view.suspend();
109        }
110    }
111
112    fn resumed(&mut self, _event_loop: &dyn ActiveEventLoop) {
113        // TODO
114    }
115
116    fn suspended(&mut self, _event_loop: &dyn ActiveEventLoop) {
117        // TODO
118    }
119
120    fn window_event(
121        &mut self,
122        event_loop: &dyn ActiveEventLoop,
123        window_id: WindowId,
124        event: WindowEvent,
125    ) {
126        // Exit the app when window close is requested.
127        if matches!(event, WindowEvent::CloseRequested) {
128            // Drop window before exiting event loop
129            // See https://github.com/rust-windowing/winit/issues/4135
130            let window = self.windows.remove(&window_id);
131            drop(window);
132            if self.windows.is_empty() {
133                event_loop.exit();
134            }
135            return;
136        }
137
138        if let Some(window) = self.windows.get_mut(&window_id) {
139            window.handle_winit_event(event);
140        }
141        self.proxy.send_event(BlitzShellEvent::Poll { window_id });
142    }
143
144    fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
145        while let Ok(event) = self.event_queue.try_recv() {
146            self.handle_blitz_shell_event(event_loop, event);
147        }
148    }
149
150    #[cfg(target_os = "macos")]
151    fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
152        Some(self)
153    }
154
155    #[cfg(target_os = "ios")]
156    fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
157        for view in self.windows.values_mut() {
158            if view.ios_request_redraw.get() {
159                view.window.request_redraw();
160            }
161        }
162    }
163}
164
165#[cfg(target_os = "macos")]
166impl<Rend: WindowRenderer> ApplicationHandlerExtMacOS for BlitzApplication<Rend> {
167    fn standard_key_binding(
168        &mut self,
169        _event_loop: &dyn ActiveEventLoop,
170        window_id: WindowId,
171        action: &str,
172    ) {
173        if let Some(window) = self.windows.get_mut(&window_id) {
174            window.handle_apple_standard_keybinding(action);
175            self.proxy.send_event(BlitzShellEvent::Poll { window_id });
176        }
177    }
178}