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