use std::cell::RefCell;
use std::rc::Rc;
use std::rc::Weak;
use crate::create_new_window;
use crate::eval::EvalResult;
use crate::events::IpcMessage;
use crate::Config;
use crate::WebviewHandler;
use dioxus_core::ScopeState;
use dioxus_core::VirtualDom;
use serde_json::Value;
use wry::application::event_loop::EventLoopProxy;
use wry::application::event_loop::EventLoopWindowTarget;
#[cfg(target_os = "ios")]
use wry::application::platform::ios::WindowExtIOS;
use wry::application::window::Fullscreen as WryFullscreen;
use wry::application::window::Window;
use wry::application::window::WindowId;
use wry::webview::WebView;
pub type ProxyType = EventLoopProxy<UserWindowEvent>;
pub fn use_window(cx: &ScopeState) -> &DesktopContext {
cx.use_hook(|| cx.consume_context::<DesktopContext>())
.as_ref()
.unwrap()
}
pub(crate) type WebviewQueue = Rc<RefCell<Vec<WebviewHandler>>>;
#[derive(Clone)]
pub struct DesktopContext {
pub webview: Rc<WebView>,
pub proxy: ProxyType,
pub(super) eval: tokio::sync::broadcast::Sender<Value>,
pub(super) pending_windows: WebviewQueue,
pub(crate) event_loop: EventLoopWindowTarget<UserWindowEvent>,
#[cfg(target_os = "ios")]
pub(crate) views: Rc<RefCell<Vec<*mut objc::runtime::Object>>>,
}
impl std::ops::Deref for DesktopContext {
type Target = Window;
fn deref(&self) -> &Self::Target {
self.webview.window()
}
}
impl DesktopContext {
pub(crate) fn new(
webview: Rc<WebView>,
proxy: ProxyType,
event_loop: EventLoopWindowTarget<UserWindowEvent>,
webviews: WebviewQueue,
) -> Self {
Self {
webview,
proxy,
event_loop,
eval: tokio::sync::broadcast::channel(8).0,
pending_windows: webviews,
#[cfg(target_os = "ios")]
views: Default::default(),
}
}
pub fn new_window(&self, dom: VirtualDom, cfg: Config) -> Weak<WebView> {
let window = create_new_window(
cfg,
&self.event_loop,
&self.proxy,
dom,
&self.pending_windows,
);
let id = window.webview.window().id();
self.proxy
.send_event(UserWindowEvent(EventData::NewWindow, id))
.unwrap();
self.proxy
.send_event(UserWindowEvent(EventData::Poll, id))
.unwrap();
let webview = window.webview.clone();
self.pending_windows.borrow_mut().push(window);
Rc::downgrade(&webview)
}
pub fn drag(&self) {
let window = self.webview.window();
if window.fullscreen().is_none() {
window.drag_window().unwrap();
}
}
pub fn toggle_maximized(&self) {
let window = self.webview.window();
window.set_maximized(!window.is_maximized())
}
pub fn close(&self) {
let _ = self
.proxy
.send_event(UserWindowEvent(EventData::CloseWindow, self.id()));
}
pub fn close_window(&self, id: WindowId) {
let _ = self
.proxy
.send_event(UserWindowEvent(EventData::CloseWindow, id));
}
pub fn set_fullscreen(&self, fullscreen: bool) {
if let Some(handle) = self.webview.window().current_monitor() {
self.webview
.window()
.set_fullscreen(fullscreen.then_some(WryFullscreen::Borderless(Some(handle))));
}
}
pub fn print(&self) {
if let Err(e) = self.webview.print() {
log::warn!("Open print modal failed: {e}");
}
}
pub fn set_zoom_level(&self, level: f64) {
self.webview.zoom(level);
}
pub fn devtool(&self) {
#[cfg(debug_assertions)]
self.webview.open_devtools();
#[cfg(not(debug_assertions))]
log::warn!("Devtools are disabled in release builds");
}
pub fn eval(&self, code: &str) -> EvalResult {
let script = format!(
r#"
window.ipc.postMessage(
JSON.stringify({{
"method":"eval_result",
"params": (
function(){{
{}
}}
)()
}})
);
"#,
code
);
if let Err(e) = self.webview.evaluate_script(&script) {
log::warn!("Eval script error: {e}");
}
EvalResult::new(self.eval.clone())
}
#[cfg(target_os = "ios")]
pub fn push_view(&self, view: objc_id::ShareId<objc::runtime::Object>) {
let window = self.webview.window();
unsafe {
use objc::runtime::Object;
use objc::*;
assert!(is_main_thread());
let ui_view = window.ui_view() as *mut Object;
let ui_view_frame: *mut Object = msg_send![ui_view, frame];
let _: () = msg_send![view, setFrame: ui_view_frame];
let _: () = msg_send![view, setAutoresizingMask: 31];
let ui_view_controller = window.ui_view_controller() as *mut Object;
let _: () = msg_send![ui_view_controller, setView: view];
self.views.borrow_mut().push(ui_view);
}
}
#[cfg(target_os = "ios")]
pub fn pop_view(&self) {
let window = self.webview.window();
unsafe {
use objc::runtime::Object;
use objc::*;
assert!(is_main_thread());
if let Some(view) = self.views.borrow_mut().pop() {
let ui_view_controller = window.ui_view_controller() as *mut Object;
let _: () = msg_send![ui_view_controller, setView: view];
}
}
}
}
#[derive(Debug, Clone)]
pub struct UserWindowEvent(pub EventData, pub WindowId);
#[derive(Debug, Clone)]
pub enum EventData {
Poll,
Ipc(IpcMessage),
NewWindow,
CloseWindow,
}
#[cfg(target_os = "ios")]
fn is_main_thread() -> bool {
use objc::runtime::{Class, BOOL, NO};
use objc::*;
let cls = Class::get("NSThread").unwrap();
let result: BOOL = unsafe { msg_send![cls, isMainThread] };
result != NO
}