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