bevy_webview/
events.rs

1use std::{any::TypeId, collections::HashMap, sync::atomic::AtomicBool};
2
3use bevy::{
4    ecs::system::{Resource, SystemParam},
5    prelude::*,
6};
7use headless_webview::webview::RpcRequest;
8use serde::{Deserialize, Serialize};
9
10use crate::{systems, PostUpdateLabel, PreUpdateLabel};
11
12/// Mapping of RPC Input Event methods
13#[derive(Default)]
14pub(crate) struct InputEventMapping {
15    pub events: HashMap<TypeId, &'static str>,
16}
17
18/// Mapping of RPC Output Event methods
19#[derive(Default)]
20pub(crate) struct OutputEventMapping {
21    pub events: HashMap<TypeId, &'static str>,
22}
23
24// RPC events from a webview
25pub(crate) struct InputEvent {
26    // Sending entity
27    pub entity: Entity,
28
29    // Barebones request to be converted into Bevy-world
30    pub request: RpcRequest,
31
32    // Whether the request was converted to native event, or it fell through
33    // Fell through = was not handled by any converter
34    pub matched: AtomicBool,
35}
36
37impl InputEvent {
38    pub fn new(entity: Entity, request: RpcRequest) -> Self {
39        Self {
40            matched: AtomicBool::new(false),
41            entity,
42            request,
43        }
44    }
45}
46
47/// Wraps an event of type `T` into a webview structure
48#[derive(Deserialize, Serialize, Debug)]
49pub struct WebviewEvent<T> {
50    pub(crate) entity: Option<Entity>,
51    pub(crate) val: T,
52}
53
54impl<T> WebviewEvent<T> {
55    pub fn new(entity: Option<Entity>, val: T) -> Self {
56        Self { entity, val }
57    }
58}
59
60/// Read Events sent from Javascript
61#[derive(SystemParam)]
62pub struct WebviewEventReader<'w, 's, T: Resource> {
63    pub events: EventReader<'w, 's, WebviewEvent<T>>,
64}
65
66impl<'w, 's, T: Resource> WebviewEventReader<'w, 's, T> {
67    pub fn iter(&mut self) -> impl DoubleEndedIterator<Item = &T> {
68        self.events
69            .iter_with_id()
70            .map(|(event, _id)| event)
71            .map(|event| &event.val)
72    }
73
74    pub fn iter_with_entity(&mut self) -> impl DoubleEndedIterator<Item = (&T, Entity)> {
75        self.events
76            .iter_with_id()
77            .map(|(event, _id)| event)
78            .map(|event| (&event.val, event.entity.unwrap()))
79    }
80
81    // #[inline]
82    // pub fn len(&self) -> usize {
83    //     self.events.len()
84    // }
85
86    // #[inline]
87    // pub fn is_empty(&self) -> bool {
88    //     self.events.is_empty()
89    // }
90}
91
92/// Send Events to webview Javascript
93#[derive(SystemParam)]
94pub struct WebviewEventWriter<'w, 's, T: Resource> {
95    pub events: EventWriter<'w, 's, WebviewEvent<T>>,
96}
97
98impl<'w, 's, T: Resource> WebviewEventWriter<'w, 's, T> {
99    /// Will send an event to **all** webviews
100    pub fn send(&mut self, event: T) {
101        self.events.send(WebviewEvent::new(None, event));
102    }
103
104    /// Will send an event to a specific webview, identified by `Entity`
105    pub fn send_to_entity(&mut self, entity: Entity, event: T) {
106        self.events.send(WebviewEvent::new(Some(entity), event));
107    }
108}
109
110/// Trait that extends a Bevy [`App`] for registring webview events
111/// # Example
112///
113/// ```rust
114/// # use bevy_webview::serde::{Serialize, Deserialize};
115/// use bevy::prelude::*;
116/// use bevy_webview::prelude::*;
117///
118/// #[derive(Deserialize, Debug)]
119/// pub struct LoginRequest {
120///     username: String,
121/// }
122///
123/// #[derive(Serialize, Debug)]
124/// pub struct AppTime {
125///     seconds_since_startup: f64,
126/// }
127///
128/// fn main() {
129///     App::new()
130///         .add_plugins(DefaultPlugins)
131///         .add_plugin(WebviewPlugin::new().register_engine(webview_engine::headless))
132///         .add_webview_input_event::<LoginRequest>("login")
133///         .add_webview_output_event::<AppTime>("app_time")
134///         .add_system(login_handler)
135///         .add_system(send_time_system);
136///         // .run();
137/// }
138///
139/// fn login_handler(mut login_request_events: WebviewEventReader<LoginRequest>) {
140///     for event in login_request_events.iter() {
141///         println!("Received a login request, username={:?}", event.username);
142///     }
143/// }
144///
145/// fn send_time_system(mut app_time: WebviewEventWriter<AppTime>, time: Res<Time>) {
146///     app_time.send(AppTime {
147///         seconds_since_startup: time.seconds_since_startup(),
148///     });
149/// }
150/// ```
151pub trait WebviewApp {
152    /// Register an input webview event. `method` is an identifier key for sending messages from JS
153    fn add_webview_input_event<T>(&mut self, method: &'static str) -> &mut Self
154    where
155        T: Resource + for<'de> serde::Deserialize<'de>;
156
157    /// Register an output webview event. `method` is an identifier key for hooking into events from JS
158    fn add_webview_output_event<T>(&mut self, method: &'static str) -> &mut Self
159    where
160        T: Resource + for<'de> serde::Serialize;
161}
162
163impl WebviewApp for App {
164    fn add_webview_input_event<T>(&mut self, method: &'static str) -> &mut Self
165    where
166        T: Resource + for<'de> serde::Deserialize<'de>,
167    {
168        let mut rpc_input_events = self
169            .world
170            .get_resource_mut::<InputEventMapping>()
171            .expect("Add `WebviewPlugin` before calling `.add_webview_input_event`");
172
173        rpc_input_events.events.insert(TypeId::of::<T>(), method);
174
175        self.add_event::<WebviewEvent<T>>();
176
177        self.add_system_to_stage(
178            CoreStage::PreUpdate,
179            systems::rpc_event_receiver::<T>.after(PreUpdateLabel::Pre),
180        );
181        self
182    }
183
184    fn add_webview_output_event<T>(&mut self, method: &'static str) -> &mut Self
185    where
186        T: Resource + for<'de> serde::Serialize,
187    {
188        let mut rpc_output_events = self
189            .world
190            .get_resource_mut::<OutputEventMapping>()
191            .expect("Add `WebviewPlugin` before calling `.add_webview_input_event`");
192
193        rpc_output_events.events.insert(TypeId::of::<T>(), method);
194
195        self.add_event::<WebviewEvent<T>>();
196
197        self.add_system_to_stage(
198            CoreStage::PostUpdate,
199            systems::rpc_event_sender::<T>.label(PostUpdateLabel::Pre),
200        );
201
202        self
203    }
204}
205
206/// Enum of builtin event methods
207#[derive(Deserialize, Debug)]
208#[serde(rename_all(deserialize = "lowercase"))]
209pub(crate) enum BuiltinWebviewEvent {
210    Despawn,
211    Initialize,
212}