use std::{borrow::Cow, future::Future, marker::PhantomData, ops::Deref, rc::Rc};
use futures_channel::oneshot;
#[cfg(feature = "threadsafe")]
use unsafe_send_sync::UnsafeSend;
#[cfg(feature = "threadsafe")]
use crate::delegate::*;
use crate::{
application::*,
core::{
browser_window::{BrowserWindowExt, BrowserWindowImpl, JsEvaluationError},
window::WindowExt,
},
prelude::*,
window::*,
};
mod builder;
pub use builder::{BrowserWindowBuilder, Source};
#[cfg(feature = "threadsafe")]
pub type BrowserDelegateFuture<'a, R> = DelegateFuture<'a, BrowserWindowHandle, R>;
pub struct BrowserWindow {
pub(super) handle: BrowserWindowHandle,
_not_send: PhantomData<Rc<()>>,
}
#[cfg(feature = "threadsafe")]
pub struct BrowserWindowThreaded {
pub(super) handle: BrowserWindowHandle,
}
#[cfg(feature = "threadsafe")]
unsafe impl Sync for BrowserWindowThreaded {}
#[derive(Clone)]
pub struct BrowserWindowHandle {
pub(super) inner: BrowserWindowImpl,
window: WindowHandle,
}
pub trait OwnedBrowserWindow: OwnedWindow {
fn browser_handle(&self) -> BrowserWindowHandle;
}
impl BrowserWindow {
fn new(handle: BrowserWindowHandle) -> Self {
Self {
handle,
_not_send: PhantomData,
}
}
}
impl Deref for BrowserWindow {
type Target = BrowserWindowHandle;
fn deref(&self) -> &Self::Target { &self.handle }
}
impl Drop for BrowserWindow {
fn drop(&mut self) { self.handle.inner.window().drop(); }
}
impl HasAppHandle for BrowserWindow {
fn app_handle(&self) -> ApplicationHandle { self.handle.app_handle() }
}
impl OwnedWindow for BrowserWindow {
fn window_handle(&self) -> WindowHandle { self.handle.window() }
}
impl OwnedBrowserWindow for BrowserWindow {
fn browser_handle(&self) -> BrowserWindowHandle { self.handle.clone() }
}
impl BrowserWindowHandle {
pub fn app(&self) -> ApplicationHandle { ApplicationHandle::new(self.inner.window().app()) }
pub fn close(self) { self.inner.window().destroy() }
pub async fn eval_js(&self, js: &str) -> Result<JsValue, JsEvaluationError> {
let (tx, rx) = oneshot::channel();
self._eval_js(js, |_, result| {
if let Err(_) = tx.send(result) {
panic!("Unable to send JavaScript result back")
}
});
rx.await.unwrap()
}
fn _eval_js<'a, H>(&self, js: &str, on_complete: H)
where
H: FnOnce(BrowserWindowHandle, Result<JsValue, JsEvaluationError>) + 'a,
{
let data_ptr: *mut H = Box::into_raw(Box::new(on_complete));
self.inner
.eval_js(js.into(), eval_js_callback::<H>, data_ptr as _);
}
pub fn exec_js(&self, js: &str) { self._eval_js(js, |_, _| {}); }
pub fn navigate(&self, url: &str) { self.inner.navigate(url) }
pub fn url<'a>(&'a self) -> Cow<'a, str> { self.inner.url() }
pub fn window(&self) -> WindowHandle { WindowHandle::new(self.inner.window()) }
}
#[cfg(feature = "threadsafe")]
impl BrowserWindowThreaded {
pub fn app(&self) -> ApplicationHandleThreaded {
ApplicationHandleThreaded::from_core_handle(self.handle.inner.window().app())
}
pub fn close(self) -> bool { self.dispatch(|bw| bw.close()) }
pub fn delegate<'a, F, R>(&self, func: F) -> BrowserDelegateFuture<'a, R>
where
F: FnOnce(BrowserWindowHandle) -> R + Send + 'a,
R: Send,
{
BrowserDelegateFuture::new(self.handle.clone(), func)
}
pub fn delegate_async<'a, C, F, R>(&self, func: C) -> DelegateFutureFuture<'a, R>
where
C: FnOnce(BrowserWindowHandle) -> F + Send + 'a,
F: Future<Output = R>,
R: Send + 'static,
{
let handle = self.handle.clone();
DelegateFutureFuture::new(self.app().handle.clone(), async move {
func(handle.into()).await
})
}
pub fn delegate_future<'a, F, R>(&self, fut: F) -> DelegateFutureFuture<'a, R>
where
F: Future<Output = R> + Send + 'a,
R: Send + 'static,
{
let handle = self.handle.clone();
DelegateFutureFuture::new(self.app().handle.clone(), fut)
}
pub fn dispatch<'a, F>(&self, func: F) -> bool
where
F: FnOnce(BrowserWindowHandle) + Send + 'a,
{
let handle = UnsafeSend::new(self.handle.clone());
self.app().dispatch(move |_| {
func(handle.i);
})
}
pub fn dispatch_async<'a, C, F>(&self, func: C) -> bool
where
C: FnOnce(BrowserWindowHandle) -> F + Send + 'a,
F: Future<Output = ()> + 'static,
{
let handle = UnsafeSend::new(self.handle.clone());
self.app().dispatch(move |a| {
a.spawn(func(handle.i));
})
}
fn new(handle: BrowserWindowHandle) -> Self { Self { handle } }
}
#[cfg(feature = "threadsafe")]
impl HasAppHandle for BrowserWindowThreaded {
fn app_handle(&self) -> ApplicationHandle { self.handle.app_handle() }
}
#[cfg(feature = "threadsafe")]
impl OwnedWindow for BrowserWindowThreaded {
fn window_handle(&self) -> WindowHandle { self.handle.window() }
}
#[cfg(feature = "threadsafe")]
impl OwnedBrowserWindow for BrowserWindowThreaded {
fn browser_handle(&self) -> BrowserWindowHandle { self.handle.clone() }
}
impl BrowserWindowHandle {
fn new(inner_handle: BrowserWindowImpl) -> Self {
Self {
window: WindowHandle::new(inner_handle.window()),
inner: inner_handle,
}
}
}
impl Deref for BrowserWindowHandle {
type Target = WindowHandle;
fn deref(&self) -> &Self::Target { &self.window }
}
impl HasAppHandle for BrowserWindowHandle {
fn app_handle(&self) -> ApplicationHandle { ApplicationHandle::new(self.inner.window().app()) }
}
fn eval_js_callback<H>(
_handle: BrowserWindowImpl, cb_data: *mut (), result: Result<JsValue, JsEvaluationError>,
) where
H: FnOnce(BrowserWindowHandle, Result<JsValue, JsEvaluationError>),
{
let data_ptr = cb_data as *mut H;
let data = unsafe { Box::from_raw(data_ptr) };
let handle = BrowserWindowHandle::new(_handle);
(*data)(handle, result);
}