#[cfg(feature = "threadsafe")]
use std::future::Future;
use std::{borrow::Cow, ops::Deref};
use futures_channel::oneshot;
#[cfg(feature = "threadsafe")]
use unsafe_send_sync::UnsafeSend;
#[cfg(feature = "threadsafe")]
use crate::delegate::*;
use crate::{
application::*,
core::{
browser_window::{
BrowserWindowEventExt, BrowserWindowExt, BrowserWindowImpl, JsEvaluationError,
},
window::WindowExt,
},
decl_browser_event, decl_event,
event::EventHandler,
prelude::*,
rc::Rc,
window::*,
HasHandle,
};
mod builder;
pub use builder::{BrowserWindowBuilder, Source};
#[cfg(feature = "threadsafe")]
pub type BrowserDelegateFuture<'a, R> = DelegateFuture<'a, BrowserWindow, BrowserWindowHandle, R>;
pub struct BrowserWindowOwner(pub(super) BrowserWindowHandle);
#[derive(Clone)]
pub struct BrowserWindow(pub(super) Rc<BrowserWindowOwner>);
#[cfg(feature = "threadsafe")]
#[derive(Clone)]
pub struct BrowserWindowThreaded(BrowserWindow);
#[cfg(feature = "threadsafe")]
unsafe impl Sync for BrowserWindowThreaded {}
pub type BrowserWindowEventHandler<A> = EventHandler<BrowserWindowHandle, BrowserWindow, A>;
pub struct BrowserWindowHandle {
pub(super) inner: BrowserWindowImpl,
app: ApplicationHandle,
window: WindowHandle,
}
pub struct MessageEventArgs {
pub cmd: String,
pub args: Vec<JsValue>,
}
decl_browser_event!(AddressChangedEvent);
decl_browser_event!(AuthCredentialsEvent);
decl_browser_event!(CertificateErrorEvent);
decl_browser_event!(ConsoleMessageEvent);
decl_browser_event!(DownloadProgressEvent);
decl_browser_event!(DownloadStartedEvent);
decl_browser_event!(FaviconChangedEvent);
decl_browser_event!(FileDialogEvent);
decl_browser_event!(FullscreenModeChangedEvent);
decl_browser_event!(KeyPressEvent);
decl_browser_event!(KeyPressedEvent);
decl_browser_event!(LoadingProgressChangedEvent);
decl_browser_event!(MessageEvent);
decl_browser_event!(NavigationEndEvent);
decl_browser_event!(NavigationStartEvent);
decl_browser_event!(PageTitleChangedEvent);
decl_browser_event!(ScrollOffsetChangedEvent);
decl_browser_event!(SelectClientCertificateEvent);
decl_browser_event!(StartDraggingEvent);
decl_browser_event!(StatusMessageEvent);
decl_browser_event!(TooltipEvent);
decl_browser_event!(TextSelectionChangedEvent);
impl BrowserWindow {
pub fn on_address_changed(&self) -> AddressChangedEvent {
self.0.0.inner.on_address_changed(Rc::downgrade(&self.0))
}
pub fn on_console_message(&self) -> ConsoleMessageEvent {
self.0.0.inner.on_console_message(Rc::downgrade(&self.0))
}
pub fn on_fullscreen_mode_changed(&self) -> FullscreenModeChangedEvent {
self.0
.0
.inner
.on_fullscreen_mode_changed(Rc::downgrade(&self.0))
}
pub fn on_loading_progress_changed(&self) -> LoadingProgressChangedEvent {
self.0
.0
.inner
.on_loading_progress_changed(Rc::downgrade(&self.0))
}
pub fn on_message(&self) -> MessageEvent { self.0.0.inner.on_message(Rc::downgrade(&self.0)) }
pub fn on_navigation_end(&self) -> NavigationEndEvent {
self.0.0.inner.on_navigation_end(Rc::downgrade(&self.0))
}
pub fn on_navigation_start(&self) -> NavigationStartEvent {
self.0.0.inner.on_navigation_start(Rc::downgrade(&self.0))
}
pub fn on_page_title_changed(&self) -> PageTitleChangedEvent {
self.0.0.inner.on_page_title_changed(Rc::downgrade(&self.0))
}
pub fn on_status_message(&self) -> StatusMessageEvent {
self.0.0.inner.on_status_message(Rc::downgrade(&self.0))
}
pub fn on_tooltip(&self) -> TooltipEvent { self.0.0.inner.on_tooltip(Rc::downgrade(&self.0)) }
pub fn on_auth_credentials(&self) -> AuthCredentialsEvent {
unimplemented!();
}
pub fn on_certificate_error(&self) -> CertificateErrorEvent {
unimplemented!();
}
pub fn on_download_progress(&self) -> DownloadProgressEvent {
unimplemented!();
}
pub fn on_download_started(&self) -> DownloadStartedEvent {
unimplemented!();
}
pub fn on_favicon_changed(&self) -> FaviconChangedEvent {
unimplemented!();
}
pub fn on_file_dialog(&self) -> FileDialogEvent {
unimplemented!();
}
pub fn on_key_press(&self) -> KeyPressEvent {
unimplemented!();
}
pub fn on_key_pressed(&self) -> KeyPressedEvent {
unimplemented!();
}
pub fn on_scroll_offset_changed(&self) -> ScrollOffsetChangedEvent {
unimplemented!();
}
pub fn on_select_client_certificate(&self) -> SelectClientCertificateEvent {
unimplemented!();
}
pub fn on_start_dragging(&self) -> StartDraggingEvent {
unimplemented!();
}
pub fn on_text_selection_changed(&self) -> TextSelectionChangedEvent {
unimplemented!();
}
}
impl Deref for BrowserWindow {
type Target = BrowserWindowHandle;
fn deref(&self) -> &Self::Target { &self.0.0 }
}
impl HasHandle<ApplicationHandle> for BrowserWindow {
fn handle(&self) -> &ApplicationHandle { &self.app }
}
impl HasHandle<WindowHandle> for BrowserWindow {
fn handle(&self) -> &WindowHandle { &self.window }
}
impl BrowserWindowHandle {
pub fn app(&self) -> ApplicationHandle { ApplicationHandle::new(self.inner.window().app()) }
pub fn close(self) {
self.inner.window().hide();
}
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 { &self.window }
}
impl HasHandle<ApplicationHandle> for BrowserWindowHandle {
fn handle(&self) -> &ApplicationHandle { &self.app }
}
impl HasHandle<WindowHandle> for BrowserWindowHandle {
fn handle(&self) -> &WindowHandle { &self.window }
}
#[cfg(feature = "threadsafe")]
impl BrowserWindowThreaded {
pub fn app(&self) -> ApplicationHandleThreaded {
ApplicationHandleThreaded::from_core_handle(self.0.inner.window().app())
}
pub fn close(self) -> bool { self.dispatch(|bw| unsafe { bw.clone().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.0.clone(), func)
}
pub fn delegate_async<'a, C, F, R>(&self, func: C) -> DelegateFutureFuture<'a, R>
where
C: FnOnce(BrowserWindow) -> F + Send + 'a,
F: Future<Output = R>,
R: Send + 'static,
{
let handle = self.0.clone();
DelegateFutureFuture::new(unsafe { 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,
{
DelegateFutureFuture::new(unsafe { 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.0.clone());
self.app().dispatch(move |_| {
func(&handle.unwrap());
})
}
pub fn dispatch_async<'a, C, F>(&self, func: C) -> bool
where
C: FnOnce(BrowserWindow) -> F + Send + 'a,
F: Future<Output = ()> + 'static,
{
let handle = UnsafeSend::new(self.0.clone());
self.app().dispatch(move |a| {
a.spawn(func(handle.unwrap()));
})
}
}
impl BrowserWindowHandle {
pub(crate) fn new(inner_handle: BrowserWindowImpl) -> Self {
Self {
app: ApplicationHandle::new(inner_handle.window().app()),
window: WindowHandle::new(inner_handle.window()),
inner: inner_handle,
}
}
#[cfg(feature = "threadsafe")]
unsafe fn clone(&self) -> Self {
Self {
app: self.app.clone(),
window: self.window.clone(),
inner: self.inner.clone(),
}
}
}
impl Deref for BrowserWindowHandle {
type Target = WindowHandle;
fn deref(&self) -> &Self::Target { &self.window }
}
impl HasAppHandle for BrowserWindowHandle {
fn app_handle(&self) -> &ApplicationHandle { &self.app }
}
impl BrowserWindowOwner {
fn cleanup(handle: &BrowserWindowHandle) {
handle.inner.free();
handle.inner.window().free();
}
}
impl Deref for BrowserWindowOwner {
type Target = BrowserWindowHandle;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl Drop for BrowserWindowOwner {
fn drop(&mut self) {
#[cfg(not(feature = "threadsafe"))]
Self::cleanup(&self.0);
#[cfg(feature = "threadsafe")]
{
let bw = unsafe { UnsafeSend::new(self.0.clone()) };
self.app().into_threaded().dispatch(|_| {
Self::cleanup(&bw.unwrap());
});
}
}
}
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);
}