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::RequestRedraw { doc_id } => {
53 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 }
69 accesskit_xplat::WindowEvent::ActionRequested(_req) => {
70 }
72 }
73 }
74 }
75 BlitzShellEvent::Embedder(_) => {
76 }
78 BlitzShellEvent::Navigate(_opts) => {
79 }
81 BlitzShellEvent::NavigationLoad { .. } => {
82 }
84 }
85 }
86}
87
88impl<Rend: WindowRenderer> ApplicationHandler for BlitzApplication<Rend> {
89 fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
90 for (_, view) in self.windows.iter_mut() {
92 view.resume();
93 }
94
95 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 }
115
116 fn suspended(&mut self, _event_loop: &dyn ActiveEventLoop) {
117 }
119
120 fn window_event(
121 &mut self,
122 event_loop: &dyn ActiveEventLoop,
123 window_id: WindowId,
124 event: WindowEvent,
125 ) {
126 if matches!(event, WindowEvent::CloseRequested) {
128 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}