use std::ffi::{CString, c_void}; use raw_window_handle::RawWindowHandle; use crate::{Browser, BrowserHandler, FuncRegistry, ffi::*}; /// A builder for creating a browser instance. pub struct BrowserBuilder { parent: Option, width: u32, height: u32, device_scale_factor: f32, frame_rate: u32, url: String, handler: T, func_registry: FuncRegistry, } impl BrowserBuilder<()> { pub(crate) fn new() -> BrowserBuilder<()> { BrowserBuilder { parent: None, width: 100, height: 100, device_scale_factor: 1.0, frame_rate: 60, url: "about:blank".to_string(), handler: (), func_registry: Default::default(), } } } pub(crate) struct BrowserState { pub(crate) handler: T, pub(crate) func_registry: FuncRegistry, } impl BrowserBuilder where T: BrowserHandler, { /// Sets the parent window handle. /// /// Default is `None`. pub fn parent(self, parent: impl Into>) -> Self { Self { parent: parent.into(), ..self } } /// Sets the size of the render target. pub fn size(self, width: u32, height: u32) -> Self { Self { width, height, ..self } } /// Sets the device scale factor. /// /// Default is `1.0`. #[inline] pub fn device_scale_factor(self, device_scale_factor: f32) -> Self { Self { device_scale_factor, ..self } } /// Sets the frame rate. /// /// Default is `60`. pub fn frame_rate(self, frame_rate: u32) -> Self { Self { frame_rate, ..self } } /// Sets the URL to load. /// /// Default is `about:blank`. pub fn url(self, url: impl Into) -> Self { Self { url: url.into(), ..self } } /// Sets the event handler. #[inline] pub fn handler(self, handler: Q) -> BrowserBuilder where Q: BrowserHandler, { BrowserBuilder { parent: self.parent, width: self.width, height: self.height, device_scale_factor: self.device_scale_factor, frame_rate: self.frame_rate, url: self.url, handler, func_registry: self.func_registry, } } /// Sets the function registry. /// /// See also [`FuncRegistry`] for more details. pub fn func_registry(self, func_registry: FuncRegistry) -> Self { Self { func_registry, ..self } } /// Cosumes the builder and creates a [`Browser`] instance. /// /// The creation of the browser is asynchronous, and the /// [`BrowserHandler::on_created`] method will be called upon completion. pub fn build(self) -> Browser { let callbacks = CBrowserCallbacks { on_created: crate::browser_handler::on_created::, on_closed: crate::browser_handler::on_closed::, on_popup_show: crate::browser_handler::on_popup_show::, on_popup_position: crate::browser_handler::on_popup_position::, on_paint: crate::browser_handler::on_paint::, on_address_changed: crate::browser_handler::on_address_changed::, on_title_changed: crate::browser_handler::on_title_changed::, on_favicon_url_changed: crate::browser_handler::on_favicon_url_changed::, on_tooltip: crate::browser_handler::on_tooltip::, on_status_message: crate::browser_handler::on_status_message::, on_console_message: crate::browser_handler::on_console_message::, on_cursor_changed: crate::browser_handler::on_cursor_changed::, on_before_popup: crate::browser_handler::on_before_popup::, on_loading_progress_changed: crate::browser_handler::on_loading_progress_changed::, on_loading_state_changed: crate::browser_handler::on_loading_state_changed::, on_load_start: crate::browser_handler::on_load_start::, on_load_end: crate::browser_handler::on_load_end::, on_load_error: crate::browser_handler::on_load_error::, on_ime_composition_range_changed: crate::browser_handler::on_ime_composition_range_changed::, on_file_dialog: crate::browser_handler::on_file_dialog::, on_context_menu: crate::browser_handler::on_context_menu::, on_find_result: crate::browser_handler::on_find_result::, on_js_dialog: crate::browser_handler::on_js_dialog::, on_query: crate::browser_handler::on_query::, }; let handler = Box::into_raw(Box::new(BrowserState { handler: self.handler, func_registry: self.func_registry.clone(), })); let parent_window_handle: *const c_void = match self.parent { Some(RawWindowHandle::Win32(handle)) => handle.hwnd.get() as *const c_void, Some(RawWindowHandle::AppKit(handle)) => handle.ns_view.as_ptr(), Some(RawWindowHandle::Xcb(handle)) => handle.window.get() as *const c_void, _ => std::ptr::null(), }; extern "C" fn destroy_handler(user_data: *mut c_void) { unsafe { _ = Box::from_raw(user_data as *mut T) } } let url_cstr = CString::new(self.url).unwrap(); let inject_javascript = CString::new(self.func_registry.javascript()).unwrap(); let settings = CBrowserSettings { parent: parent_window_handle, device_scale_factor: self.device_scale_factor, width: self.width as i32, height: self.height as i32, frame_rate: self.frame_rate as i32, url: url_cstr.as_ptr(), inject_javascript: inject_javascript.as_ptr(), callbacks, userdata: handler as *mut c_void, destroy_userdata: destroy_handler::, }; unsafe { Browser { wef_browser: wef_browser_create(&settings), } } } }