blitz_shell/
application.rs1use crate::event::BlitzShellEvent;
2
3use anyrender::WindowRenderer;
4use std::collections::HashMap;
5use winit::application::ApplicationHandler;
6use winit::event::WindowEvent;
7use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
8use winit::window::WindowId;
9
10use crate::{View, WindowConfig};
11
12pub struct BlitzApplication<Rend: WindowRenderer> {
13 pub windows: HashMap<WindowId, View<Rend>>,
14 pending_windows: Vec<WindowConfig<Rend>>,
15 pub proxy: EventLoopProxy<BlitzShellEvent>,
16 #[cfg(all(feature = "menu", not(any(target_os = "android", target_os = "ios"))))]
17 menu_channel: muda::MenuEventReceiver,
18}
19
20impl<Rend: WindowRenderer> BlitzApplication<Rend> {
21 pub fn new(proxy: EventLoopProxy<BlitzShellEvent>) -> Self {
22 BlitzApplication {
23 windows: HashMap::new(),
24 pending_windows: Vec::new(),
25 proxy,
26
27 #[cfg(all(feature = "menu", not(any(target_os = "android", target_os = "ios"))))]
28 menu_channel: muda::MenuEvent::receiver().clone(),
29 }
30 }
31
32 pub fn add_window(&mut self, window_config: WindowConfig<Rend>) {
33 self.pending_windows.push(window_config);
34 }
35
36 fn window_mut_by_doc_id(&mut self, doc_id: usize) -> Option<&mut View<Rend>> {
37 self.windows.values_mut().find(|w| w.doc.id() == doc_id)
38 }
39}
40
41impl<Rend: WindowRenderer> ApplicationHandler<BlitzShellEvent> for BlitzApplication<Rend> {
42 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
43 for (_, view) in self.windows.iter_mut() {
45 view.resume();
46 }
47
48 for window_config in self.pending_windows.drain(..) {
50 let mut view = View::init(window_config, event_loop, &self.proxy);
51 view.resume();
52 if !view.renderer.is_active() {
53 continue;
54 }
55 self.windows.insert(view.window_id(), view);
56 }
57 }
58
59 fn suspended(&mut self, _event_loop: &ActiveEventLoop) {
60 for (_, view) in self.windows.iter_mut() {
61 view.suspend();
62 }
63 }
64
65 fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
66 #[cfg(all(feature = "menu", not(any(target_os = "android", target_os = "ios"))))]
67 if let Ok(event) = self.menu_channel.try_recv() {
68 if event.id == muda::MenuId::new("dev.show_layout") {
69 for (_, view) in self.windows.iter_mut() {
70 view.doc.as_mut().devtools_mut().toggle_show_layout();
71 view.request_redraw();
72 }
73 }
74 }
75 }
76
77 fn window_event(
78 &mut self,
79 event_loop: &ActiveEventLoop,
80 window_id: WindowId,
81 event: WindowEvent,
82 ) {
83 if matches!(event, WindowEvent::CloseRequested) {
85 let window = self.windows.remove(&window_id);
88 drop(window);
89 if self.windows.is_empty() {
90 event_loop.exit();
91 }
92 return;
93 }
94
95 if let Some(window) = self.windows.get_mut(&window_id) {
96 window.handle_winit_event(event);
97 }
98
99 let _ = self.proxy.send_event(BlitzShellEvent::Poll { window_id });
100 }
101
102 fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: BlitzShellEvent) {
103 match event {
104 BlitzShellEvent::Poll { window_id } => {
105 if let Some(window) = self.windows.get_mut(&window_id) {
106 window.poll();
107 };
108 }
109 BlitzShellEvent::ResourceLoad { doc_id, data } => {
110 if let Some(window) = self.window_mut_by_doc_id(doc_id) {
112 window.doc.as_mut().load_resource(data);
113 window.request_redraw();
114 }
115 }
116
117 #[cfg(feature = "accessibility")]
118 BlitzShellEvent::Accessibility { window_id, data } => {
119 if let Some(window) = self.windows.get_mut(&window_id) {
120 match &*data {
121 accesskit_winit::WindowEvent::InitialTreeRequested => {
122 window.build_accessibility_tree();
123 }
124 accesskit_winit::WindowEvent::AccessibilityDeactivated => {
125 }
127 accesskit_winit::WindowEvent::ActionRequested(_req) => {
128 }
130 }
131 }
132 }
133
134 BlitzShellEvent::Embedder(_) => {
135 }
137 BlitzShellEvent::Navigate(_opts) => {
138 }
140 BlitzShellEvent::NavigationLoad { .. } => {
141 }
143 }
144 }
145}