browser_window/browser/
builder.rs

1use std::{ops::DerefMut, path::PathBuf};
2
3#[cfg(feature = "threadsafe")]
4use unsafe_send_sync::UnsafeSend;
5
6use crate::{
7	application::ApplicationHandle,
8	browser::*,
9	core::{browser_window::*, window::*},
10	rc::Rc,
11	window::WindowBuilder,
12};
13
14/// The type of content to display in a browser window
15#[derive(Clone)]
16pub enum Source {
17	/// Displays the given HTML code in the browser.
18	Html(String),
19	/// Displays the local file for the given path.
20	File(PathBuf),
21	/// Displays the given URL in the browser.
22	Url(String),
23}
24
25/// Used to create a [`BrowserWindow`] or [`BrowserWindowThreaded`] instance,
26/// depending on whether or not you have feature `threadsafe` enabled.
27///
28/// ```ignore
29/// let mut bwb = BrowserWindowBuilder::new(Source::Url("https://www.duckduckgo.com".into()))
30/// bwb.dev_tools(true);
31/// bwb.title("DuckDuckGo");
32/// let bw = bwb.build( app );
33/// ```
34pub struct BrowserWindowBuilder {
35	dev_tools: bool,
36	source: Source,
37	window: WindowBuilder,
38}
39
40impl BrowserWindowBuilder {
41	/// Sets whether or not an extra window with developer tools will be opened
42	/// together with this browser. When in debug mode the default is `true`.
43	/// When in release mode the default is `false`.
44	pub fn dev_tools(&mut self, enabled: bool) -> &mut Self {
45		self.dev_tools = enabled;
46		self
47	}
48
49	/// Creates an instance of a browser window builder.
50	///
51	/// # Arguments
52	/// * `source` - The content that will be displayed in the browser window.
53	pub fn new(source: Source) -> Self {
54		Self {
55			dev_tools: false,
56			source,
57			window: WindowBuilder::new(),
58		}
59	}
60
61	#[deprecated(since = "0.12.1", note = "please use `build_async` instead")]
62	pub async fn build(self, app: &ApplicationHandle) -> BrowserWindow {
63		self.build_async(app).await
64	}
65
66	/// Creates the browser window.
67	///
68	/// # Arguments
69	/// * `app` - An application handle that this browser window can spawn into
70	pub async fn build_async(self, app: &ApplicationHandle) -> BrowserWindow {
71		let (tx, rx) = oneshot::channel::<BrowserWindowHandle>();
72
73		self._build(app, move |handle| {
74			if let Err(_) = tx.send(handle) {
75				panic!("Unable to send browser handle back")
76			}
77		});
78
79		Self::prepare_handle(rx.await.unwrap())
80	}
81
82	/// Creates the browser window.
83	///
84	/// Keep in mind that the description of this function is for when feature
85	/// `threadsafe` is enabled. When it is not enabled, it looks like this:
86	/// ```ignore
87	/// pub async fn build( self, app: ApplicationHandle ) -> Result<BrowserWindowThreaded, DelegateError> { /* ... */ }
88	/// ```
89	///
90	/// # Arguments
91	/// * `app` - An (thread-safe) application handle.
92	#[cfg(feature = "threadsafe")]
93	pub async fn build_threaded(
94		self, app: &ApplicationHandleThreaded,
95	) -> Result<BrowserWindowThreaded, DelegateError> {
96		let (tx, rx) = oneshot::channel::<UnsafeSend<BrowserWindowHandle>>();
97
98		// We need to dispatch the spawning of the browser to the GUI thread
99		app.delegate(|app_handle| {
100			self._build(&*app_handle, |inner_handle| {
101				if let Err(_) = tx.send(UnsafeSend::new(inner_handle)) {
102					panic!("Unable to send browser handle back")
103				}
104			});
105		})
106		.await?;
107
108		Ok(BrowserWindowThreaded(Self::prepare_handle(
109			rx.await.unwrap().unwrap(),
110		)))
111	}
112
113	fn prepare_handle(handle: BrowserWindowHandle) -> BrowserWindow {
114		// Put a reference counted handle in the user data of the window, so that there
115		// exists 'ownership' for as long as the window actually lives.
116		let owner = BrowserWindowOwner(handle);
117		let rc_handle = Rc::new(owner);
118		let user_data = Box::into_raw(Box::new(BrowserUserData {
119			_handle: rc_handle.clone(),
120		}));
121		rc_handle.0.window().0.set_user_data(user_data as _);
122
123		BrowserWindow(rc_handle)
124	}
125
126	pub fn build_sync(self, app: &ApplicationHandle, on_created: impl FnOnce(BrowserWindow)) {
127		self._build(app, move |inner| {
128			let handle = Self::prepare_handle(inner);
129			on_created(handle);
130		})
131	}
132
133	fn _build(self, app: &ApplicationHandle, on_created: impl FnOnce(BrowserWindowHandle)) {
134		match self {
135			Self {
136				source,
137				dev_tools,
138				window,
139			} => {
140				// Parent
141				let parent_handle = match window.parent {
142					None => WindowImpl::default(),
143					Some(p) => p.i,
144				};
145
146				// Title
147				let title = match window.title.as_ref() {
148					None => "Browser Window".into(),
149					Some(t) => t.as_str().into(),
150				};
151
152				let callback_data: *mut Box<dyn FnOnce(BrowserWindowHandle)> =
153					Box::into_raw(Box::new(Box::new(on_created)));
154
155				// Convert options to FFI structs
156				let window_options = WindowOptions {
157					borders: window.borders,
158					minimizable: window.minimizable,
159					resizable: window.resizable,
160				};
161				let other_options = BrowserWindowOptions {
162					dev_tools: if dev_tools { 1 } else { 0 },
163					resource_path: "".into(),
164				};
165
166				BrowserWindowImpl::new(
167					app.inner.clone(),
168					parent_handle,
169					source,
170					title,
171					window.width,
172					window.height,
173					&window_options,
174					&other_options,
175					browser_window_created_callback,
176					callback_data as _,
177				);
178			}
179		}
180	}
181}
182
183impl Deref for BrowserWindowBuilder {
184	type Target = WindowBuilder;
185
186	fn deref(&self) -> &Self::Target { &self.window }
187}
188
189impl DerefMut for BrowserWindowBuilder {
190	fn deref_mut(&mut self) -> &mut Self::Target { &mut self.window }
191}
192
193fn browser_window_created_callback(inner_handle: BrowserWindowImpl, data: *mut ()) {
194	let data_ptr = data as *mut Box<dyn FnOnce(&BrowserWindowHandle)>;
195	let func = unsafe { Box::from_raw(data_ptr) };
196
197	let rust_handle = BrowserWindowHandle::new(inner_handle);
198
199	func(&rust_handle);
200}