use crate::common::{CefWebviewUri, HostWindow, IpcEventRawSender, WebviewSize}; use crate::cursor_icon::SystemCursorIconSender; use crate::webview::mesh::MeshWebviewPlugin; use bevy::ecs::component::HookContext; use bevy::ecs::world::DeferredWorld; use bevy::prelude::*; use bevy::window::PrimaryWindow; use bevy::winit::WinitWindows; use bevy_cef_core::prelude::*; use bevy_remote::BrpSender; #[allow(deprecated)] use raw_window_handle::HasRawWindowHandle; use serde::{Deserialize, Serialize}; mod mesh; mod webview_sprite; pub mod prelude { pub use crate::webview::{RequestCloseDevtool, RequestShowDevTool, WebviewPlugin, mesh::*}; } /// A Trigger event to request showing the developer tools in a webview. /// /// When you want to close the developer tools, use [`RequestCloseDevtool`]. /// /// ```rust /// use bevy::prelude::*; /// use bevy_cef::prelude::*; /// /// #[derive(Component)] /// struct DebugWebview; /// /// fn show_devtool_system(mut commands: Commands, webviews: Query>) { /// commands.entity(webviews.single().unwrap()).trigger(RequestShowDevTool); /// } /// ``` #[derive(Reflect, Debug, Default, Copy, Clone, Serialize, Deserialize, Event)] #[reflect(Default, Serialize, Deserialize)] pub struct RequestShowDevTool; /// A Trigger event to request closing the developer tools in a webview. /// /// When showing the devtool, use [`RequestShowDevTool`] instead. /// /// ```rust /// use bevy::prelude::*; /// use bevy_cef::prelude::*; /// /// #[derive(Component)] /// struct DebugWebview; /// /// fn close_devtool_system(mut commands: Commands, webviews: Query>) { /// commands.entity(webviews.single().unwrap()).trigger(RequestCloseDevtool); /// } /// ``` #[derive(Reflect, Debug, Default, Copy, Clone, Serialize, Deserialize, Event)] #[reflect(Default, Serialize, Deserialize)] pub struct RequestCloseDevtool; pub struct WebviewPlugin; impl Plugin for WebviewPlugin { fn build(&self, app: &mut App) { app.register_type::() .init_non_send_resource::() .add_plugins((MeshWebviewPlugin,)) .add_systems(Main, send_external_begin_frame) .add_systems( Update, ( resize.run_if(any_resized), create_webview.run_if(added_webview), ), ) .add_observer(apply_request_show_devtool) .add_observer(apply_request_close_devtool); app.world_mut() .register_component_hooks::() .on_despawn(|mut world: DeferredWorld, ctx: HookContext| { world.non_send_resource_mut::().close(&ctx.entity); }); } } fn any_resized(webviews: Query>) -> bool { !webviews.is_empty() } fn added_webview(webviews: Query>) -> bool { !webviews.is_empty() } fn send_external_begin_frame(mut hosts: NonSendMut) { hosts.send_external_begin_frame(); } #[allow(clippy::too_many_arguments)] fn create_webview( mut browsers: NonSendMut, requester: Res, ipc_event_sender: Res, brp_sender: Res, cursor_icon_sender: Res, winit_windows: NonSend, webviews: Query< (Entity, &CefWebviewUri, &WebviewSize, Option<&HostWindow>), Added, >, primary_window: Query>, ) { for (entity, uri, size, parent) in webviews.iter() { let host_window = parent .and_then(|w| winit_windows.get_window(w.0)) .or_else(|| winit_windows.get_window(primary_window.single().ok()?)) .and_then(|w| { #[allow(deprecated)] w.raw_window_handle().ok() }); browsers.create_browser( entity, &uri.0, size.0, requester.clone(), ipc_event_sender.0.clone(), brp_sender.clone(), cursor_icon_sender.clone(), host_window, ); } } fn resize( browsers: NonSend, webviews: Query<(Entity, &WebviewSize), Changed>, ) { for (webview, size) in webviews.iter() { browsers.resize(&webview, size.0); } } fn apply_request_show_devtool(trigger: Trigger, browsers: NonSend) { browsers.show_devtool(&trigger.target()); } fn apply_request_close_devtool(trigger: Trigger, browsers: NonSend) { browsers.close_devtools(&trigger.target()); }