blitz_shell/
application.rs

1use 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    pub pending_windows: Vec<WindowConfig<Rend>>,
15    pub proxy: EventLoopProxy<BlitzShellEvent>,
16}
17
18impl<Rend: WindowRenderer> BlitzApplication<Rend> {
19    pub fn new(proxy: EventLoopProxy<BlitzShellEvent>) -> Self {
20        BlitzApplication {
21            windows: HashMap::new(),
22            pending_windows: Vec::new(),
23            proxy,
24        }
25    }
26
27    pub fn add_window(&mut self, window_config: WindowConfig<Rend>) {
28        self.pending_windows.push(window_config);
29    }
30
31    fn window_mut_by_doc_id(&mut self, doc_id: usize) -> Option<&mut View<Rend>> {
32        self.windows.values_mut().find(|w| w.doc.id() == doc_id)
33    }
34}
35
36impl<Rend: WindowRenderer> ApplicationHandler<BlitzShellEvent> for BlitzApplication<Rend> {
37    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
38        // Resume existing windows
39        for (_, view) in self.windows.iter_mut() {
40            view.resume();
41        }
42
43        // Initialise pending windows
44        for window_config in self.pending_windows.drain(..) {
45            let mut view = View::init(window_config, event_loop, &self.proxy);
46            view.resume();
47            if !view.renderer.is_active() {
48                continue;
49            }
50            self.windows.insert(view.window_id(), view);
51        }
52    }
53
54    fn suspended(&mut self, _event_loop: &ActiveEventLoop) {
55        for (_, view) in self.windows.iter_mut() {
56            view.suspend();
57        }
58    }
59
60    fn window_event(
61        &mut self,
62        event_loop: &ActiveEventLoop,
63        window_id: WindowId,
64        event: WindowEvent,
65    ) {
66        // Exit the app when window close is requested.
67        if matches!(event, WindowEvent::CloseRequested) {
68            // Drop window before exiting event loop
69            // See https://github.com/rust-windowing/winit/issues/4135
70            let window = self.windows.remove(&window_id);
71            drop(window);
72            if self.windows.is_empty() {
73                event_loop.exit();
74            }
75            return;
76        }
77
78        if let Some(window) = self.windows.get_mut(&window_id) {
79            window.handle_winit_event(event);
80        }
81
82        let _ = self.proxy.send_event(BlitzShellEvent::Poll { window_id });
83    }
84
85    fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: BlitzShellEvent) {
86        match event {
87            BlitzShellEvent::Poll { window_id } => {
88                if let Some(window) = self.windows.get_mut(&window_id) {
89                    window.poll();
90                };
91            }
92            BlitzShellEvent::ResourceLoad { doc_id, data } => {
93                // TODO: Handle multiple documents per window
94                if let Some(window) = self.window_mut_by_doc_id(doc_id) {
95                    window.doc.as_mut().load_resource(data);
96                    window.request_redraw();
97                }
98            }
99
100            #[cfg(feature = "accessibility")]
101            BlitzShellEvent::Accessibility { window_id, data } => {
102                if let Some(window) = self.windows.get_mut(&window_id) {
103                    match &*data {
104                        accesskit_winit::WindowEvent::InitialTreeRequested => {
105                            window.build_accessibility_tree();
106                        }
107                        accesskit_winit::WindowEvent::AccessibilityDeactivated => {
108                            // TODO
109                        }
110                        accesskit_winit::WindowEvent::ActionRequested(_req) => {
111                            // TODO
112                        }
113                    }
114                }
115            }
116
117            BlitzShellEvent::Embedder(_) => {
118                // Do nothing. Should be handled by embedders (if required).
119            }
120            BlitzShellEvent::Navigate(_opts) => {
121                // Do nothing. Should be handled by embedders (if required).
122            }
123            BlitzShellEvent::NavigationLoad { .. } => {
124                // Do nothing. Should be handled by embedders (if required).
125            }
126        }
127    }
128}