Skip to main content

freya_winit/
plugins.rs

1use std::{
2    cell::RefCell,
3    collections::HashMap,
4    rc::Rc,
5};
6
7use freya_core::integration::*;
8use freya_engine::prelude::{
9    Canvas,
10    FontCollection,
11};
12pub use keyboard_types::{
13    Code,
14    Key,
15    Modifiers,
16};
17use winit::{
18    event_loop::EventLoopProxy,
19    window::{
20        Window,
21        WindowId,
22    },
23};
24
25use crate::renderer::{
26    NativeEvent,
27    NativeWindowEvent,
28    NativeWindowEventAction,
29};
30
31#[derive(Clone)]
32pub struct PluginHandle {
33    pub proxy: EventLoopProxy<NativeEvent>,
34}
35
36impl PluginHandle {
37    pub fn new(proxy: &EventLoopProxy<NativeEvent>) -> Self {
38        Self {
39            proxy: proxy.clone(),
40        }
41    }
42
43    /// Emit a [PlatformEvent]. Useful to simulate certain events.
44    pub fn send_platform_event(&self, event: PlatformEvent, window_id: WindowId) {
45        self.proxy
46            .send_event(NativeEvent::Window(NativeWindowEvent {
47                window_id,
48                action: NativeWindowEventAction::PlatformEvent(event),
49            }))
50            .ok();
51    }
52
53    /// Emit a [NativeEvent].
54    pub fn send_event_loop_event(&self, event: NativeEvent) {
55        self.proxy.send_event(event).ok();
56    }
57}
58
59/// Manages all loaded plugins.
60#[derive(Default, Clone)]
61pub struct PluginsManager {
62    plugins: Rc<RefCell<HashMap<&'static str, Box<dyn FreyaPlugin>>>>,
63}
64
65impl PluginsManager {
66    /// Add a plugin by its ID. First insert wins.
67    pub fn add_plugin(&mut self, plugin: impl FreyaPlugin + 'static) {
68        self.plugins
69            .borrow_mut()
70            .entry(plugin.plugin_id())
71            .or_insert(Box::new(plugin));
72    }
73
74    pub fn send(&mut self, mut event: PluginEvent, handle: PluginHandle) {
75        for plugin in self.plugins.borrow_mut().values_mut() {
76            plugin.on_event(&mut event, handle.clone())
77        }
78    }
79
80    /// Observer reporting every tasks polling batch of a [Runner] as plugin events.
81    pub fn tasks_poll_observer<'a>(
82        &'a mut self,
83        window: &'a Window,
84        handle: PluginHandle,
85    ) -> impl FnMut(TasksPollStage) + 'a {
86        move |stage| {
87            let event = match stage {
88                TasksPollStage::Started => PluginEvent::StartedPollingTasks { window },
89                TasksPollStage::Finished => PluginEvent::FinishedPollingTasks { window },
90            };
91            self.send(event, handle.clone())
92        }
93    }
94
95    /// Compose the root element through all plugins' root components.
96    pub fn wrap_root(&self, mut root: Element) -> Element {
97        for plugin in self.plugins.borrow().values() {
98            root = plugin.root_component(root);
99        }
100        root
101    }
102}
103
104/// Event emitted to Plugins.
105pub enum PluginEvent<'a> {
106    /// A runner just got created.
107    RunnerCreated {
108        runner: &'a mut Runner,
109    },
110    /// A Window just got created.
111    WindowCreated {
112        window: &'a Window,
113        font_collection: &'a FontCollection,
114        tree: &'a Tree,
115        animation_clock: &'a AnimationClock,
116        runner: &'a mut Runner,
117        graphics_driver: &'static str,
118        gpu_name: Option<&'a str>,
119    },
120
121    /// The graphics driver was rebuilt at runtime.
122    GraphicsDriverChanged {
123        window: &'a Window,
124        graphics_driver: &'static str,
125        gpu_name: Option<&'a str>,
126    },
127
128    /// A Window just got closed.
129    WindowClosed {
130        window: &'a Window,
131        tree: &'a Tree,
132    },
133
134    /// After having rendered, presented and everything else.
135    AfterRedraw {
136        window: &'a Window,
137        font_collection: &'a FontCollection,
138        tree: &'a Tree,
139    },
140
141    /// Before presenting the canvas to the window.
142    BeforePresenting {
143        window: &'a Window,
144        font_collection: &'a FontCollection,
145        tree: &'a Tree,
146    },
147
148    /// After presenting the canvas to the window.
149    AfterPresenting {
150        window: &'a Window,
151        font_collection: &'a FontCollection,
152        tree: &'a Tree,
153    },
154
155    /// Before starting to render the app to the Canvas.
156    BeforeRender {
157        window: &'a Window,
158        canvas: &'a Canvas,
159        font_collection: &'a FontCollection,
160        tree: &'a Tree,
161    },
162
163    /// After rendering the app to the Canvas.
164    AfterRender {
165        window: &'a Window,
166        canvas: &'a Canvas,
167        font_collection: &'a FontCollection,
168        tree: &'a Tree,
169        animation_clock: &'a AnimationClock,
170    },
171
172    /// Before starting to measure the layout.
173    StartedMeasuringLayout {
174        window: &'a Window,
175        tree: &'a Tree,
176    },
177
178    /// After measuringg the layout.
179    FinishedMeasuringLayout {
180        window: &'a Window,
181        tree: &'a Tree,
182    },
183
184    /// Before starting to process the queued events.
185    StartedMeasuringEvents {
186        window: &'a Window,
187        tree: &'a Tree,
188    },
189
190    /// After processing the queued events.
191    FinishedMeasuringEvents {
192        window: &'a Window,
193        tree: &'a Tree,
194    },
195
196    StartedUpdatingTree {
197        window: &'a Window,
198        tree: &'a Tree,
199    },
200
201    FinishedUpdatingTree {
202        window: &'a Window,
203        tree: &'a Tree,
204    },
205
206    /// Before starting to poll a batch of async tasks.
207    StartedPollingTasks {
208        window: &'a Window,
209    },
210
211    /// After polling a batch of async tasks.
212    FinishedPollingTasks {
213        window: &'a Window,
214    },
215
216    BeforeAccessibility {
217        window: &'a Window,
218        font_collection: &'a FontCollection,
219        tree: &'a Tree,
220    },
221
222    AfterAccessibility {
223        window: &'a Window,
224        font_collection: &'a FontCollection,
225        tree: &'a Tree,
226    },
227
228    /// A keyboard input was received.
229    KeyboardInput {
230        window: &'a Window,
231        key: Key,
232        code: Code,
233        modifiers: Modifiers,
234        is_pressed: bool,
235    },
236}
237
238/// Skeleton for Freya plugins.
239pub trait FreyaPlugin {
240    /// Unique identifier for this plugin. Used for deduplication.
241    fn plugin_id(&self) -> &'static str;
242
243    /// React on events emitted by Freya.
244    fn on_event(&mut self, event: &mut PluginEvent, handle: PluginHandle);
245
246    /// Wrap the root element with a custom component.
247    /// Called during window creation. The default implementation returns the element unchanged.
248    fn root_component(&self, root: Element) -> Element {
249        root
250    }
251}