lib.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. use std::borrow::BorrowMut;
  2. use std::ops::{Deref, DerefMut};
  3. use std::sync::mpsc::channel;
  4. use std::sync::{Arc, RwLock};
  5. use dioxus_core::*;
  6. use wry::{
  7. application::window::{Window, WindowBuilder},
  8. webview::{RpcRequest, RpcResponse},
  9. };
  10. mod dom;
  11. mod escape;
  12. static HTML_CONTENT: &'static str = include_str!("./index.html");
  13. pub fn launch(
  14. root: FC<()>,
  15. builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
  16. ) -> anyhow::Result<()> {
  17. launch_with_props(root, (), builder)
  18. }
  19. pub fn launch_with_props<P: Properties + 'static>(
  20. root: FC<P>,
  21. props: P,
  22. builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
  23. ) -> anyhow::Result<()> {
  24. WebviewRenderer::run(root, props, builder)
  25. }
  26. /// The `WebviewRenderer` provides a way of rendering a Dioxus Virtual DOM through a bridge to a Webview instance.
  27. /// Components used in WebviewRenderer instances can directly use system libraries, access the filesystem, and multithread with ease.
  28. pub struct WebviewRenderer<T> {
  29. /// The root component used to render the Webview
  30. root: FC<T>,
  31. }
  32. enum RpcEvent<'a> {
  33. Initialize {
  34. //
  35. edits: Vec<DomEdit<'a>>,
  36. },
  37. }
  38. impl<T: Properties + 'static> WebviewRenderer<T> {
  39. pub fn run(
  40. root: FC<T>,
  41. props: T,
  42. user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
  43. ) -> anyhow::Result<()> {
  44. Self::run_with_edits(root, props, user_builder, None)
  45. }
  46. pub fn run_with_edits(
  47. root: FC<T>,
  48. props: T,
  49. user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
  50. redits: Option<Vec<DomEdit<'static>>>,
  51. ) -> anyhow::Result<()> {
  52. use wry::{
  53. application::{
  54. event::{Event, StartCause, WindowEvent},
  55. event_loop::{ControlFlow, EventLoop},
  56. window::WindowBuilder,
  57. },
  58. webview::WebViewBuilder,
  59. };
  60. let event_loop = EventLoop::new();
  61. let window = user_builder(WindowBuilder::new()).build(&event_loop)?;
  62. let vir = VirtualDom::new_with_props(root, props);
  63. // todo: combine these or something
  64. let vdom = Arc::new(RwLock::new(vir));
  65. let registry = Arc::new(RwLock::new(Some(WebviewRegistry::new())));
  66. let webview = WebViewBuilder::new(window)?
  67. .with_url(&format!("data:text/html,{}", HTML_CONTENT))?
  68. .with_rpc_handler(move |window: &Window, mut req: RpcRequest| {
  69. match req.method.as_str() {
  70. "initiate" => {
  71. let edits = if let Some(edits) = &redits {
  72. serde_json::to_value(edits).unwrap()
  73. } else {
  74. let mut lock = vdom.write().unwrap();
  75. let mut reg_lock = registry.write().unwrap();
  76. // Create the thin wrapper around the registry to collect the edits into
  77. let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
  78. // Serialize the edit stream
  79. let edits = {
  80. lock.rebuild(&mut real).unwrap();
  81. serde_json::to_value(&real.edits).unwrap()
  82. };
  83. // Give back the registry into its slot
  84. *reg_lock = Some(real.consume());
  85. edits
  86. };
  87. // Return the edits into the webview runtime
  88. Some(RpcResponse::new_result(req.id.take(), Some(edits)))
  89. }
  90. "user_event" => {
  91. let mut lock = vdom.write().unwrap();
  92. let mut reg_lock = registry.write().unwrap();
  93. // Create the thin wrapper around the registry to collect the edits into
  94. let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
  95. // Serialize the edit stream
  96. let edits = {
  97. lock.rebuild(&mut real).unwrap();
  98. serde_json::to_value(&real.edits).unwrap()
  99. };
  100. // Give back the registry into its slot
  101. *reg_lock = Some(real.consume());
  102. // Return the edits into the webview runtime
  103. Some(RpcResponse::new_result(req.id.take(), Some(edits)))
  104. }
  105. _ => todo!("this message failed"),
  106. }
  107. })
  108. .build()?;
  109. event_loop.run(move |event, _, control_flow| {
  110. *control_flow = ControlFlow::Wait;
  111. match event {
  112. Event::WindowEvent { event, .. } => {
  113. //
  114. match event {
  115. WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
  116. _ => {}
  117. }
  118. }
  119. _ => {
  120. // let _ = webview.resize();
  121. }
  122. }
  123. });
  124. // let mut view = web_view::builder()
  125. // .invoke_handler(|view, arg| {
  126. // let handle = view.handle();
  127. // sender
  128. // .send(InnerEvent::Initiate(handle))
  129. // .expect("should not fail");
  130. // Ok(())
  131. // })
  132. // .content(web_view::Content::Html(HTML_CONTENT))
  133. // .user_data(())
  134. // .title(title)
  135. // .size(width, height)
  136. // .resizable(resizable)
  137. // .debug(debug)
  138. // .frameless(frameless)
  139. // .visible(visible)
  140. // .min_size(min_width, min_height)
  141. // .build()
  142. // .unwrap();
  143. // loop {
  144. // view.step()
  145. // .expect("should not fail")
  146. // .expect("should not fail");
  147. // std::thread::sleep(std::time::Duration::from_millis(15));
  148. // if let Ok(event) = receiver.try_recv() {
  149. // if let InnerEvent::Initiate(handle) = event {
  150. // let editlist = ref_edits.clone();
  151. // handle
  152. // .dispatch(move |view| {
  153. // let escaped = escape(&editlist);
  154. // view.eval(&format!("EditListReceived({});", escaped))
  155. // })
  156. // .expect("Dispatch failed");
  157. // }
  158. // }
  159. // }
  160. }
  161. /// Create a new text-renderer instance from a functional component root.
  162. /// Automatically progresses the creation of the VNode tree to completion.
  163. ///
  164. /// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
  165. // pub fn new(root: FC<T>, builder: impl FnOnce() -> WVResult<WebView<'static, ()>>) -> Self {
  166. // Self { root }
  167. // }
  168. /// Create a new text renderer from an existing Virtual DOM.
  169. /// This will progress the existing VDom's events to completion.
  170. pub fn from_vdom() -> Self {
  171. todo!()
  172. }
  173. /// Pass new args to the root function
  174. pub fn update(&mut self, new_val: T) {
  175. todo!()
  176. }
  177. /// Modify the root function in place, forcing a re-render regardless if the props changed
  178. pub fn update_mut(&mut self, modifier: impl Fn(&mut T)) {
  179. todo!()
  180. }
  181. }
  182. use serde::{Deserialize, Serialize};
  183. use serde_json::Value;
  184. use crate::dom::WebviewRegistry;
  185. #[derive(Debug, Serialize, Deserialize)]
  186. struct MessageParameters {
  187. message: String,
  188. }
  189. fn HANDLER(window: &Window, mut req: RpcRequest) -> Option<RpcResponse> {
  190. use wry::{
  191. application::{
  192. event::{Event, WindowEvent},
  193. event_loop::{ControlFlow, EventLoop},
  194. window::{Fullscreen, Window, WindowBuilder},
  195. },
  196. webview::{RpcRequest, RpcResponse, WebViewBuilder},
  197. };
  198. let mut response = None;
  199. if &req.method == "fullscreen" {
  200. if let Some(params) = req.params.take() {
  201. if let Ok(mut args) = serde_json::from_value::<Vec<bool>>(params) {
  202. if !args.is_empty() {
  203. if args.swap_remove(0) {
  204. window.set_fullscreen(Some(Fullscreen::Borderless(None)));
  205. } else {
  206. window.set_fullscreen(None);
  207. }
  208. };
  209. response = Some(RpcResponse::new_result(req.id.take(), None));
  210. }
  211. }
  212. } else if &req.method == "send-parameters" {
  213. if let Some(params) = req.params.take() {
  214. if let Ok(mut args) = serde_json::from_value::<Vec<MessageParameters>>(params) {
  215. let result = if !args.is_empty() {
  216. let msg = args.swap_remove(0);
  217. Some(Value::String(format!("Hello, {}!", msg.message)))
  218. } else {
  219. // NOTE: in the real-world we should send an error response here!
  220. None
  221. };
  222. // Must always send a response as this is a `call()`
  223. response = Some(RpcResponse::new_result(req.id.take(), result));
  224. }
  225. }
  226. }
  227. response
  228. }
  229. pub struct DioxusWebviewBuilder<'a> {
  230. pub(crate) title: &'a str,
  231. pub(crate) width: i32,
  232. pub(crate) height: i32,
  233. pub(crate) resizable: bool,
  234. pub(crate) debug: bool,
  235. pub(crate) frameless: bool,
  236. pub(crate) visible: bool,
  237. pub(crate) min_width: i32,
  238. pub(crate) min_height: i32,
  239. }
  240. impl<'a> DioxusWebviewBuilder<'a> {
  241. fn new() -> Self {
  242. #[cfg(debug_assertions)]
  243. let debug = true;
  244. #[cfg(not(debug_assertions))]
  245. let debug = false;
  246. DioxusWebviewBuilder {
  247. title: "Application",
  248. width: 800,
  249. height: 600,
  250. resizable: true,
  251. debug,
  252. frameless: false,
  253. visible: true,
  254. min_width: 300,
  255. min_height: 300,
  256. }
  257. }
  258. /// Sets the title of the WebView window.
  259. ///
  260. /// Defaults to `"Application"`.
  261. pub fn title(mut self, title: &'a str) -> Self {
  262. self.title = title;
  263. self
  264. }
  265. /// Sets the size of the WebView window.
  266. ///
  267. /// Defaults to 800 x 600.
  268. pub fn size(mut self, width: i32, height: i32) -> Self {
  269. self.width = width;
  270. self.height = height;
  271. self
  272. }
  273. /// Sets the resizability of the WebView window. If set to false, the window cannot be resized.
  274. ///
  275. /// Defaults to `true`.
  276. pub fn resizable(mut self, resizable: bool) -> Self {
  277. self.resizable = resizable;
  278. self
  279. }
  280. /// Enables or disables debug mode.
  281. ///
  282. /// Defaults to `true` for debug builds, `false` for release builds.
  283. pub fn debug(mut self, debug: bool) -> Self {
  284. self.debug = debug;
  285. self
  286. }
  287. /// The window crated will be frameless
  288. ///
  289. /// defaults to `false`
  290. pub fn frameless(mut self, frameless: bool) -> Self {
  291. self.frameless = frameless;
  292. self
  293. }
  294. /// Set the visibility of the WebView window.
  295. ///
  296. /// defaults to `true`
  297. pub fn visible(mut self, visible: bool) -> Self {
  298. self.visible = visible;
  299. self
  300. }
  301. /// Sets the minimum size of the WebView window.
  302. ///
  303. /// Defaults to 300 x 300.
  304. pub fn min_size(mut self, width: i32, height: i32) -> Self {
  305. self.min_width = width;
  306. self.min_height = height;
  307. self
  308. }
  309. }