use browser_window_ffi::*;
use std::ffi::*;
use crate::application::{ApplicationHandle, ApplicationHandleThreaded};
use crate::browser::*;
use std::{
mem,
path::PathBuf,
pin::Pin,
ptr,
vec::Vec
};
pub enum Source {
Html( String ),
File( PathBuf ),
Url( String )
}
pub struct BrowserWindowBuilder {
parent: Option<BrowserWindowHandle>,
source: Source,
title: Option<String>,
width: Option<u32>,
height: Option<u32>,
handler: Option<Box<dyn FnMut(BrowserWindowHandle, String, Vec<String>) -> Pin<Box<dyn Future<Output=()>>> + Send>>,
borders: bool,
dev_tools: bool,
minimizable: bool,
resizable: bool
}
impl BrowserWindowBuilder {
pub fn borders( mut self, value: bool ) -> Self {
self.borders = value; self
}
pub fn dev_tools( mut self, enabled: bool ) -> Self {
self.dev_tools = enabled; self
}
pub fn async_handler<H,F>( mut self, mut handler: H ) -> Self where
H: FnMut(BrowserWindowHandle, String, Vec<String>) -> F + Send + 'static,
F: Future<Output=()> + 'static
{
self.handler = Some( Box::new(
move |handle, cmd, args| Box::pin(handler( handle, cmd, args ) )
) );
self
}
pub fn minimizable( mut self, value: bool ) -> Self {
self.minimizable = value; self
}
pub fn parent<B>( mut self, bw: &B ) -> Self where
B: OwnedBrowserWindow
{
self.parent = Some( bw.handle() );
self
}
pub fn new( source: Source ) -> Self {
Self {
parent: None,
source,
handler: None,
title: None,
width: None,
height: None,
borders: true,
dev_tools: cfg!(debug_assertions),
minimizable: true,
resizable: true
}
}
pub fn title<S: Into<String>>( mut self, title: S ) -> Self {
self.title = Some( title.into() );
self
}
pub fn width( mut self, width: u32 ) -> Self {
self.width = Some( width );
self
}
pub fn height( mut self, height: u32 ) -> Self {
self.height = Some( height );
self
}
pub fn resizable( mut self, resizable: bool ) -> Self {
self.resizable = resizable; self
}
pub async fn build( self, app: ApplicationHandle ) -> BrowserWindow
{
let (tx, rx) = oneshot::channel::<BrowserWindowHandle>();
self._build( app, move |handle| {
if let Err(_) = tx.send( handle ) {
panic!("Unable to send browser handle back")
}
} );
BrowserWindow::new( rx.await.unwrap() )
}
pub async fn build_threaded( self, app: ApplicationHandleThreaded ) -> Result<BrowserWindowThreaded, DelegateError> {
let (tx, rx) = oneshot::channel::<BrowserWindowHandle>();
app.delegate(|app_handle| {
self._build(app_handle, |inner_handle| {
if let Err(_) = tx.send( inner_handle ) {
panic!("Unable to send browser handle back")
}
} );
}).await?;
Ok( BrowserWindowThreaded::new( rx.await.unwrap() ) )
}
pub fn size( mut self, width: u32, height: u32 ) -> Self {
self.width = Some( width );
self.height = Some( height );
self
}
fn _build<H>( self, app: ApplicationHandle, on_created: H ) where
H: FnOnce( BrowserWindowHandle )
{
match self {
Self { parent,
source,
title,
width,
height,
handler,
borders,
minimizable,
resizable,
dev_tools
} => {
let parent_handle = match parent {
None => ptr::null(),
Some( p ) => p.ffi_handle
};
let mut _url: PathBuf = "file:///".into();
let csource = match &source {
Source::File( path ) => {
_url.push( path );
bw_BrowserWindowSource {
data: _url.to_str().unwrap().into(),
is_html: false
}
},
Source::Html( html ) => { bw_BrowserWindowSource {
data: html.as_str().into(),
is_html: true
} },
Source::Url( url ) => { bw_BrowserWindowSource {
data: url.as_str().into(),
is_html: false
} }
};
let title_ptr = match title.as_ref() {
None => "Browser Window".into(),
Some( t ) => t.as_str().into()
};
let w: i32 = match width {
Some(w) => w as i32,
None => -1
};
let h: i32 = match height {
Some(h) => h as i32,
None => -1
};
let user_data = Box::into_raw( Box::new(
BrowserUserData {
handler: match handler {
Some(f) => f,
None => Box::new(|_,_,_| Box::pin(async {}))
}
}
) );
let callback_data: *mut Box<dyn FnOnce( BrowserWindowHandle )> = Box::into_raw( Box::new( Box::new(on_created ) ) );
let window_options = bw_WindowOptions {
minimizable: minimizable,
resizable: resizable,
closable: true,
borders: borders
};
let other_options = bw_BrowserWindowOptions {
dev_tools,
resource_path: "".into()
};
unsafe { bw_BrowserWindow_new(
app.ffi_handle,
parent_handle,
csource,
title_ptr,
w,
h,
&window_options,
&other_options,
ffi_window_invoke_handler,
user_data as _,
ffi_browser_window_created_callback,
callback_data as _
) };
}
}
}
}
struct BrowserUserData {
handler: Box<dyn FnMut( BrowserWindowHandle, String, Vec<String>) -> Pin<Box<dyn Future<Output=()>>>>
}
fn args_to_vec( args: *const bw_CStrSlice, args_count: usize ) -> Vec<String> {
let mut vec = Vec::with_capacity( args_count );
for i in 0..args_count {
let str_arg: String = unsafe { *args.offset(i as _) }.into();
vec.push( str_arg );
}
vec
}
extern "C" fn ffi_window_invoke_handler( inner_handle: *mut bw_BrowserWindow, _command: bw_CStrSlice, _args: *const bw_CStrSlice, args_count: usize ) {
unsafe {
let data_ptr: *mut BrowserUserData = mem::transmute( bw_BrowserWindow_getUserData( inner_handle ) );
let data: &mut BrowserUserData = &mut *data_ptr;
match data {
BrowserUserData{ handler } => {
let outer_handle = BrowserWindowHandle::new( inner_handle );
let args = args_to_vec( _args, args_count );
let future = handler( outer_handle, _command.into(), args );
outer_handle.app().spawn( future );
}
}
}
}
extern "C" fn ffi_browser_window_created_callback( inner_handle: *mut bw_BrowserWindow, data: *mut c_void ) {
unsafe {
let data_ptr: *mut Box<dyn FnOnce( BrowserWindowHandle )> = mem::transmute( data );
let data = Box::from_raw( data_ptr );
let outer_handle = BrowserWindowHandle::new( inner_handle );
data( outer_handle )
}
}