blitz_shell/
application.rs1use 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::ResumeReady { window_id } => {
53 if let Some(window) = self.windows.get_mut(&window_id) {
57 let ok = window.complete_resume();
58 debug_assert!(ok, "ResumeReady received but renderer not ready");
59 }
60 }
61 BlitzShellEvent::RequestRedraw { doc_id } => {
62 if let Some(window) = self.window_mut_by_doc_id(doc_id) {
64 window.request_redraw();
65 }
66 }
67
68 #[cfg(feature = "accessibility")]
69 BlitzShellEvent::Accessibility { window_id, data } => {
70 if let Some(window) = self.windows.get_mut(&window_id) {
71 match &*data {
72 accesskit_xplat::WindowEvent::InitialTreeRequested => {
73 window.build_accessibility_tree();
74 }
75 accesskit_xplat::WindowEvent::AccessibilityDeactivated => {
76 }
78 accesskit_xplat::WindowEvent::ActionRequested(_req) => {
79 }
81 }
82 }
83 }
84 BlitzShellEvent::Embedder(_) => {
85 }
87 BlitzShellEvent::Navigate(_opts) => {
88 }
90 BlitzShellEvent::NavigationLoad { .. } => {
91 }
93 #[cfg(target_arch = "wasm32")]
94 BlitzShellEvent::ResizeSettleCheck { window_id } => {
95 if let Some(window) = self.windows.get_mut(&window_id) {
96 window.apply_pending_resize_if_settled();
97 }
98 }
99 }
100 }
101}
102
103impl<Rend: WindowRenderer> ApplicationHandler for BlitzApplication<Rend> {
104 fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
105 for (_, view) in self.windows.iter_mut() {
107 view.resume();
108 }
109
110 for window_config in self.pending_windows.drain(..) {
115 let mut view = View::init(window_config, event_loop, &self.proxy);
116 view.resume();
117 self.windows.insert(view.window_id(), view);
118 }
119 }
120
121 fn destroy_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {
122 for (_, view) in self.windows.iter_mut() {
123 view.suspend();
124 }
125 }
126
127 fn resumed(&mut self, _event_loop: &dyn ActiveEventLoop) {
128 }
130
131 fn suspended(&mut self, _event_loop: &dyn ActiveEventLoop) {
132 }
134
135 fn window_event(
136 &mut self,
137 event_loop: &dyn ActiveEventLoop,
138 window_id: WindowId,
139 event: WindowEvent,
140 ) {
141 if matches!(event, WindowEvent::CloseRequested) {
143 let window = self.windows.remove(&window_id);
146 drop(window);
147 if self.windows.is_empty() {
148 event_loop.exit();
149 }
150 return;
151 }
152
153 if let Some(window) = self.windows.get_mut(&window_id) {
154 window.handle_winit_event(event);
155 }
156 self.proxy.send_event(BlitzShellEvent::Poll { window_id });
157 }
158
159 fn proxy_wake_up(&mut self, event_loop: &dyn ActiveEventLoop) {
160 while let Ok(event) = self.event_queue.try_recv() {
161 self.handle_blitz_shell_event(event_loop, event);
162 }
163 }
164
165 #[cfg(target_os = "macos")]
166 fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> {
167 Some(self)
168 }
169
170 #[cfg(target_os = "ios")]
171 fn about_to_wait(&mut self, _event_loop: &dyn ActiveEventLoop) {
172 for view in self.windows.values_mut() {
173 if view.ios_request_redraw.get() {
174 view.window.request_redraw();
175 }
176 }
177 }
178}
179
180#[cfg(target_os = "macos")]
181impl<Rend: WindowRenderer> ApplicationHandlerExtMacOS for BlitzApplication<Rend> {
182 fn standard_key_binding(
183 &mut self,
184 _event_loop: &dyn ActiveEventLoop,
185 window_id: WindowId,
186 action: &str,
187 ) {
188 if let Some(window) = self.windows.get_mut(&window_id) {
189 window.handle_apple_standard_keybinding(action);
190 self.proxy.send_event(BlitzShellEvent::Poll { window_id });
191 }
192 }
193}