use futures_channel::oneshot;
use std::{
borrow::Cow,
future::Future,
marker::PhantomData,
ops::Deref,
rc::Rc
};
use crate::application::*;
#[cfg(feature = "threadsafe")]
use crate::delegate::*;
use crate::window::*;
use browser_window_core::browser_window::{BrowserWindowExt, BrowserWindowImpl, JsEvaluationError};
use browser_window_core::window::WindowExt;
#[cfg(feature = "threadsafe")]
use unsafe_send_sync::UnsafeSend;
mod builder;
pub use builder::{BrowserWindowBuilder, Source};
#[cfg(feature = "threadsafe")]
pub type BrowserDelegateFuture<'a,R> = DelegateFuture<'a, BrowserWindowHandle, R>;
pub struct BrowserWindow {
pub(in super) handle: BrowserWindowHandle,
_not_send: PhantomData<Rc<()>>
}
#[cfg(feature = "threadsafe")]
pub struct BrowserWindowThreaded {
pub(in super) handle: BrowserWindowHandle
}
#[cfg(feature = "threadsafe")]
unsafe impl Sync for BrowserWindowThreaded {}
#[derive(Clone, Copy)]
pub struct BrowserWindowHandle {
pub(in super) inner: BrowserWindowImpl,
window: WindowHandle
}
pub trait OwnedBrowserWindow: OwnedWindow {
fn browser_handle( &self ) -> BrowserWindowHandle;
}
impl BrowserWindow {
fn new( handle: BrowserWindowHandle ) -> Self {
Self {
handle: 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 async fn eval_js( &self, js: &str ) -> Result<String, JsEvaluationError> {
let (tx, rx) = oneshot::channel::<Result<String, JsEvaluationError>>();
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<String, 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 );
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 );
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 {
inner: inner_handle,
window: WindowHandle::new( inner_handle.window() )
}
}
}
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()
)
}
}
unsafe fn eval_js_callback<H>( _handle: BrowserWindowImpl, cb_data: *mut (), result: Result<String, JsEvaluationError> ) where
H: FnOnce(BrowserWindowHandle, Result<String, JsEvaluationError>)
{
let data_ptr = cb_data as *mut H;
let data = Box::from_raw( data_ptr );
let handle = BrowserWindowHandle::new( _handle );
(*data)( handle, result );
}