use boxfnonce::SendBoxFnOnce;
use browser_window_ffi::*;
use std::{
error::Error,
ffi::CStr,
ops::Deref,
os::raw::*,
rc::Rc,
sync::Arc
};
use tokio::sync::oneshot;
use crate::application::{
ApplicationHandle
};
use crate::common::*;
#[derive(Clone)]
pub struct BrowserWindow {
pub inner: Rc<BrowserWindowInner>
}
#[derive(Clone)]
pub struct BrowserWindowAsync {
pub inner: Arc<BrowserWindowInner>
}
#[derive(Clone)]
pub struct BrowserWindowHandle {
pub _ffi_handle: *mut bw_BrowserWindow
}
unsafe impl Send for BrowserWindowHandle {}
unsafe impl Sync for BrowserWindowHandle {}
pub struct BrowserWindowInner {
pub app: ApplicationHandle,
pub handle: BrowserWindowHandle }
impl AppHandle for BrowserWindow {
fn app_handle( &self ) -> ApplicationHandle {
self.inner.app.clone()
}
}
impl BrowserWindow {
pub fn into_async( self ) -> BrowserWindowAsync {
let inner = unsafe { Arc::from_raw( Rc::into_raw( self.inner ) ) };
BrowserWindowAsync {
inner: inner
}
}
}
impl Deref for BrowserWindow {
type Target = BrowserWindowHandle;
fn deref( &self ) -> &Self::Target {
&self.inner.handle
}
}
impl BrowserWindowAsync {
pub async fn close( self ) {
self.dispatch(|bw| {
bw.close()
}).await;
}
pub fn dispatch<'a,F,R>( &self, func: F ) -> BrowserWindowDispatchFuture<'a,R> where
F: FnOnce( BrowserWindowHandle ) -> R + Send + 'a,
R: Send
{
BrowserWindowDispatchFuture::new( self.inner.handle.clone(), func )
}
pub async fn eval_js( &self, js: &str ) -> Result<String, Box<dyn Error + Send>> {
let (tx, rx) = oneshot::channel::<Result<String, Box<dyn Error + Send>>>();
self.dispatch(move |bw| {
bw.eval_js( js, move |_, result| {
tx.send( result ).unwrap();
} );
}).await;
rx.await.unwrap()
}
pub async fn navigate( &self, url: &str ) -> Result<(), Box<dyn Error + Send>> {
*self.dispatch(|bw| {
bw.navigate( url )
}).await
}
}
type BrowserWindowCallbackData<'a> = SendBoxFnOnce<'a,(BrowserWindowHandle, Result<String, Box<dyn Error + Send>>),()>;
pub type BrowserWindowDispatchFuture<'a,R> = DispatchFuture<'a, BrowserWindowHandle, R>;
impl BrowserWindowHandle {
pub fn close( self ) {
unsafe { bw_BrowserWindow_close( self._ffi_handle ); }
}
pub fn eval_js<'a,H>( &self, js: &str, on_complete: H ) where
H: FnOnce( BrowserWindowHandle, Result<String, Box<dyn Error + Send>> ) + Send + 'a
{
let data_ptr = Box::into_raw( Box::new(
BrowserWindowCallbackData::<'a>::new( on_complete )
) );
unsafe { bw_BrowserWindow_evalJs(
self._ffi_handle,
js.into(),
ffi_eval_js_callback,
data_ptr as _
) };
}
pub fn exec_js( &self, js: &str ) {
self.eval_js( js, |_,_|{} );
}
pub unsafe fn from_ptr( ptr: *mut bw_BrowserWindow ) -> Self {
Self {
_ffi_handle: ptr
}
}
pub fn navigate( &self, url: &str ) -> Result<(), Box<dyn Error + Send>> {
let err = unsafe { bw_BrowserWindow_navigate( self._ffi_handle, url.into() ) };
if err.code == 0 {
return Ok(());
}
Err( Box::new( err ) )
}
}
impl AppHandle for BrowserWindowHandle {
fn app_handle( &self ) -> ApplicationHandle {
ApplicationHandle {
_ffi_handle: unsafe { bw_BrowserWindow_getApp( self._ffi_handle ) as _ }
}
}
}
impl Deref for BrowserWindowInner {
type Target = BrowserWindowHandle;
fn deref( &self ) -> &Self::Target {
&self.handle
}
}
impl Drop for BrowserWindowInner {
fn drop( &mut self ) {
unsafe { bw_BrowserWindow_drop( self.handle._ffi_handle ) }
}
}
extern "C" fn ffi_eval_js_callback( bw: *mut bw_BrowserWindow, cb_data: *mut c_void, result: *const c_char, error: *const bw_Err ) {
let data_ptr = cb_data as *mut BrowserWindowCallbackData;
let data = unsafe { Box::from_raw( data_ptr ) };
let result_val: Result<String, Box<dyn Error + Send>> = if error.is_null() {
let result_str = unsafe { CStr::from_ptr( result ).to_string_lossy().to_owned().to_string() };
Ok( result_str )
}
else {
Err( Box::new( unsafe { (*error).clone() } ) )
};
let handle = unsafe { BrowserWindowHandle::from_ptr( bw ) };
data.call( handle, result_val );
}