use async_channel::Receiver;
use bevy::ecs::event::EntityTrigger;
use bevy::prelude::*;
#[cfg(not(target_os = "windows"))]
use bevy_cef_core::prelude::Browsers;
#[cfg(target_os = "windows")]
use bevy_cef_core::prelude::BrowsersProxy;
use bevy_cef_core::prelude::{
AddressChangedMessage, AddressChangedSenderInner, LoadHandlerMessage, LoadHandlerSenderInner,
};
use serde::{Deserialize, Serialize};
pub(super) struct NavigationPlugin;
impl Plugin for NavigationPlugin {
fn build(&self, app: &mut App) {
let (tx, rx) = async_channel::unbounded();
let (addr_tx, addr_rx) = async_channel::unbounded();
app.insert_resource(LoadHandlerSender(tx))
.insert_resource(LoadHandlerReceiver(rx))
.insert_resource(AddressChangedSender(addr_tx))
.insert_resource(AddressChangedReceiver(addr_rx))
.register_type::<RequestGoBack>()
.register_type::<RequestGoForward>()
.register_type::<RequestNavigate>()
.register_type::<RequestReload>()
.register_type::<LoadingStateChanged>()
.register_type::<LoadStarted>()
.register_type::<LoadFinished>()
.register_type::<LoadError>()
.register_type::<AddressChanged>()
.add_systems(PreUpdate, (drain_load_events, drain_address_changed));
#[cfg(not(target_os = "windows"))]
app.add_observer(apply_request_go_back)
.add_observer(apply_request_go_forward)
.add_observer(apply_request_navigate)
.add_observer(apply_request_reload);
#[cfg(target_os = "windows")]
app.add_observer(apply_request_go_back_win)
.add_observer(apply_request_go_forward_win)
.add_observer(apply_request_navigate_win)
.add_observer(apply_request_reload_win);
}
}
#[derive(Debug, EntityEvent, Copy, Clone, Reflect, Serialize, Deserialize)]
pub struct RequestGoBack {
#[event_target]
pub webview: Entity,
}
#[derive(Debug, EntityEvent, Copy, Clone, Reflect, Serialize, Deserialize)]
pub struct RequestGoForward {
#[event_target]
pub webview: Entity,
}
#[derive(Debug, EntityEvent, Clone, Reflect, Serialize, Deserialize)]
pub struct RequestNavigate {
#[event_target]
pub webview: Entity,
pub url: String,
}
#[derive(Debug, EntityEvent, Copy, Clone, Reflect, Serialize, Deserialize)]
pub struct RequestReload {
#[event_target]
pub webview: Entity,
}
#[derive(Debug, EntityEvent, Clone, Reflect, Serialize, Deserialize)]
pub struct LoadingStateChanged {
#[event_target]
pub webview: Entity,
pub is_loading: bool,
pub can_go_back: bool,
pub can_go_forward: bool,
}
#[derive(Debug, EntityEvent, Copy, Clone, Reflect, Serialize, Deserialize)]
pub struct LoadStarted {
#[event_target]
pub webview: Entity,
}
#[derive(Debug, EntityEvent, Copy, Clone, Reflect, Serialize, Deserialize)]
pub struct LoadFinished {
#[event_target]
pub webview: Entity,
pub http_status_code: i32,
}
#[derive(Debug, EntityEvent, Clone, Reflect, Serialize, Deserialize)]
pub struct LoadError {
#[event_target]
pub webview: Entity,
pub error_code: i32,
pub url: String,
}
#[derive(Debug, EntityEvent, Clone, Reflect, Serialize, Deserialize)]
pub struct AddressChanged {
#[event_target]
pub webview: Entity,
pub url: String,
pub can_go_back: bool,
pub can_go_forward: bool,
}
#[derive(Resource, Debug, Deref)]
pub(crate) struct LoadHandlerSender(pub(crate) LoadHandlerSenderInner);
#[derive(Resource, Debug, Deref)]
pub(crate) struct AddressChangedSender(pub(crate) AddressChangedSenderInner);
#[derive(Resource, Debug)]
struct AddressChangedReceiver(Receiver<AddressChangedMessage>);
#[derive(Resource, Debug)]
struct LoadHandlerReceiver(Receiver<LoadHandlerMessage>);
fn drain_load_events(mut commands: Commands, receiver: Res<LoadHandlerReceiver>) {
while let Ok(msg) = receiver.0.try_recv() {
match msg {
LoadHandlerMessage::LoadingStateChanged {
webview,
is_loading,
can_go_back,
can_go_forward,
} => {
commands.trigger_with(
LoadingStateChanged {
webview,
is_loading,
can_go_back,
can_go_forward,
},
EntityTrigger,
);
if is_loading {
commands.trigger_with(LoadStarted { webview }, EntityTrigger);
}
}
LoadHandlerMessage::Finished {
webview,
http_status_code,
} => {
commands.trigger_with(
LoadFinished {
webview,
http_status_code,
},
EntityTrigger,
);
}
LoadHandlerMessage::Error {
webview,
error_code,
url,
} => {
commands.trigger_with(
LoadError {
webview,
error_code,
url,
},
EntityTrigger,
);
}
}
}
}
fn drain_address_changed(mut commands: Commands, receiver: Res<AddressChangedReceiver>) {
while let Ok(msg) = receiver.0.try_recv() {
commands.trigger_with(
AddressChanged {
webview: msg.webview,
url: msg.url,
can_go_back: msg.can_go_back,
can_go_forward: msg.can_go_forward,
},
EntityTrigger,
);
}
}
#[cfg(not(target_os = "windows"))]
fn apply_request_go_back(trigger: On<RequestGoBack>, browsers: NonSend<Browsers>) {
browsers.go_back(&trigger.webview);
}
#[cfg(not(target_os = "windows"))]
fn apply_request_go_forward(trigger: On<RequestGoForward>, browsers: NonSend<Browsers>) {
browsers.go_forward(&trigger.webview);
}
#[cfg(not(target_os = "windows"))]
fn apply_request_navigate(trigger: On<RequestNavigate>, browsers: NonSend<Browsers>) {
browsers.navigate(&trigger.webview, &trigger.url);
}
#[cfg(not(target_os = "windows"))]
fn apply_request_reload(trigger: On<RequestReload>, browsers: NonSend<Browsers>) {
browsers.reload_webview(&trigger.webview);
}
#[cfg(target_os = "windows")]
fn apply_request_go_back_win(trigger: On<RequestGoBack>, proxy: Res<BrowsersProxy>) {
proxy.go_back(&trigger.webview);
}
#[cfg(target_os = "windows")]
fn apply_request_go_forward_win(trigger: On<RequestGoForward>, proxy: Res<BrowsersProxy>) {
proxy.go_forward(&trigger.webview);
}
#[cfg(target_os = "windows")]
fn apply_request_navigate_win(trigger: On<RequestNavigate>, proxy: Res<BrowsersProxy>) {
proxy.navigate(&trigger.webview, &trigger.url);
}
#[cfg(target_os = "windows")]
fn apply_request_reload_win(trigger: On<RequestReload>, proxy: Res<BrowsersProxy>) {
proxy.reload_webview(&trigger.webview);
}