From 049976d23ab5404bb53e52db6545651ed31cb8c1 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 16 Apr 2022 16:53:47 -0400 Subject: [PATCH] feat: allow customizing the index and head --- examples/custom_html.rs | 37 +++++++++++++++++++++++++ packages/desktop/src/cfg.rs | 26 +++++++++++++++++ packages/desktop/src/desktop_context.rs | 2 +- packages/desktop/src/index.html | 7 ++--- packages/desktop/src/lib.rs | 10 +++++-- packages/desktop/src/protocol.rs | 37 ++++++++++++++++++++++--- 6 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 examples/custom_html.rs diff --git a/examples/custom_html.rs b/examples/custom_html.rs new file mode 100644 index 000000000..02f603710 --- /dev/null +++ b/examples/custom_html.rs @@ -0,0 +1,37 @@ +//! This example shows how to use a custom index.html and custom extensions +//! to add things like stylesheets, scripts, and third-party JS libraries. + +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch_cfg(app, |c| { + c.with_custom_head("".into()) + }); + + dioxus::desktop::launch_cfg(app, |c| { + c.with_custom_index( + r#" + + + + Dioxus app + + + + +
+ + + "# + .into(), + ) + }); +} + +fn app(cx: Scope) -> Element { + cx.render(rsx! { + div { + h1 {"hello world!"} + } + }) +} diff --git a/packages/desktop/src/cfg.rs b/packages/desktop/src/cfg.rs index 6028706bc..cc154942b 100644 --- a/packages/desktop/src/cfg.rs +++ b/packages/desktop/src/cfg.rs @@ -21,6 +21,8 @@ pub struct DesktopConfig { pub(crate) event_handler: Option>, pub(crate) disable_context_menu: bool, pub(crate) resource_dir: Option, + pub(crate) custom_head: Option, + pub(crate) custom_index: Option, } pub(crate) type WryProtocol = ( @@ -42,6 +44,8 @@ impl DesktopConfig { pre_rendered: None, disable_context_menu: !cfg!(debug_assertions), resource_dir: None, + custom_head: None, + custom_index: None, } } @@ -100,10 +104,32 @@ impl DesktopConfig { self } + + /// Add a custom icon for this application pub fn with_icon(&mut self, icon: Icon) -> &mut Self { self.window.window.window_icon = Some(icon); self } + + + /// Inject additional content into the document's HEAD. + /// + /// This is useful for loading CSS libraries, JS libraries, etc. + pub fn with_custom_head(&mut self, head: String) -> &mut Self { + self.custom_head = Some(head); + self + } + + /// Use a custom index.html instead of the default Dioxus one. + /// + /// Make sure your index.html is valid HTML. + /// + /// Dioxus injects some loader code into the closing body tag. Your document + /// must include a body element! + pub fn with_custom_index(&mut self, index: String) -> &mut Self { + self.custom_index = Some(index); + self + } } impl DesktopConfig { diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 4ad2ff3a7..155f7af1b 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -201,7 +201,7 @@ pub(super) fn handler( /// Get a closure that executes any JavaScript in the WebView context. pub fn use_eval(cx: &ScopeState) -> &dyn Fn(S) { - let desktop = use_window(&cx).clone(); + let desktop = use_window(cx).clone(); cx.use_hook(|_| move |script| desktop.eval(script)) } diff --git a/packages/desktop/src/index.html b/packages/desktop/src/index.html index 292a1ae6a..0704fd0f0 100644 --- a/packages/desktop/src/index.html +++ b/packages/desktop/src/index.html @@ -3,13 +3,10 @@ Dioxus app +
- + diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 596956c53..89222f271 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -125,8 +125,9 @@ pub fn launch_with_props( let proxy = proxy.clone(); let file_handler = cfg.file_drop_handler.take(); - + let custom_head = cfg.custom_head.clone(); let resource_dir = cfg.resource_dir.clone(); + let index_file = cfg.custom_index.clone(); let mut webview = WebViewBuilder::new(window) .unwrap() @@ -164,7 +165,12 @@ pub fn launch_with_props( }); }) .with_custom_protocol(String::from("dioxus"), move |r| { - protocol::desktop_handler(r, resource_dir.clone()) + protocol::desktop_handler( + r, + resource_dir.clone(), + custom_head.clone(), + index_file.clone(), + ) }) .with_file_drop_handler(move |window, evet| { file_handler diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index bd8bb3d84..085c3c32d 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -4,7 +4,20 @@ use wry::{ Result, }; -pub(super) fn desktop_handler(request: &Request, asset_root: Option) -> Result { +const MODULE_LOADER: &str = r#" + +"#; + +pub(super) fn desktop_handler( + request: &Request, + asset_root: Option, + custom_head: Option, + custom_index: Option, +) -> Result { // Any content that uses the `dioxus://` scheme will be shuttled through this handler as a "special case". // For now, we only serve two pieces of content which get included as bytes into the final binary. let path = request.uri().replace("dioxus://", ""); @@ -13,9 +26,25 @@ pub(super) fn desktop_handler(request: &Request, asset_root: Option) -> let trimmed = path.trim_start_matches("index.html/"); if trimmed.is_empty() { - ResponseBuilder::new() - .mimetype("text/html") - .body(include_bytes!("./index.html").to_vec()) + // If a custom index is provided, just defer to that, expecting the user to know what they're doing. + // we'll look for the closing tag and insert our little module loader there. + if let Some(custom_index) = custom_index { + let rendered = custom_index + .replace("", &format!("{}", MODULE_LOADER)) + .into_bytes(); + ResponseBuilder::new().mimetype("text/html").body(rendered) + } else { + // Otherwise, we'll serve the default index.html and apply a custom head if that's specified. + let mut template = include_str!("./index.html").to_string(); + if let Some(custom_head) = custom_head { + template = template.replace("", &custom_head); + } + template = template.replace("", MODULE_LOADER); + + ResponseBuilder::new() + .mimetype("text/html") + .body(template.into_bytes()) + } } else if trimmed == "index.js" { ResponseBuilder::new() .mimetype("text/javascript")