Less clumsy configuration for desktop and mobile (#553)

* chore: dont use prebuilt builder pattern for configuring desktop

* chore: use regular config pattern for web

* Chore: update docs too

* chore: clean up some warnings
This commit is contained in:
Jon Kelley authored and GitHub committed 2022-09-13 16:22:27 -07:00
1 parent 41b450eedf
commit 540e785d8b
28 files changed
+172 -156

No files matched your search

+19 -32
View File
@@ -11,9 +11,9 @@ use wry::{
// pub(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView);
/// The configuration for the desktop application.
pub struct DesktopConfig {
pub struct Config {
pub(crate) window: WindowBuilder,
pub(crate) file_drop_handler: Option<Box<dyn Fn(&Window, FileDropEvent) -> bool>>,
pub(crate) file_drop_handler: Option<DropHandler>,
pub(crate) protocols: Vec<WryProtocol>,
pub(crate) pre_rendered: Option<String>,
// pub(crate) event_handler: Option<Box<DynEventHandlerFn>>,
@@ -23,12 +23,14 @@ pub struct DesktopConfig {
pub(crate) custom_index: Option<String>,
}
type DropHandler = Box<dyn Fn(&Window, FileDropEvent) -> bool>;
pub(crate) type WryProtocol = (
String,
Box<dyn Fn(&HttpRequest) -> WryResult<HttpResponse> + 'static>,
);
impl DesktopConfig {
impl Config {
/// Initializes a new `WindowBuilder` with default values.
#[inline]
pub fn new() -> Self {
@@ -48,57 +50,51 @@ impl DesktopConfig {
}
/// set the directory from which assets will be searched in release mode
pub fn with_resource_directory(&mut self, path: impl Into<PathBuf>) -> &mut Self {
pub fn with_resource_directory(mut self, path: impl Into<PathBuf>) -> Self {
self.resource_dir = Some(path.into());
self
}
/// Set whether or not the right-click context menu should be disabled.
pub fn with_disable_context_menu(&mut self, disable: bool) -> &mut Self {
pub fn with_disable_context_menu(mut self, disable: bool) -> Self {
self.disable_context_menu = disable;
self
}
/// Set the pre-rendered HTML content
pub fn with_prerendered(&mut self, content: String) -> &mut Self {
pub fn with_prerendered(mut self, content: String) -> Self {
self.pre_rendered = Some(content);
self
}
/// Set the configuration for the window.
pub fn with_window(
&mut self,
configure: impl FnOnce(WindowBuilder) -> WindowBuilder,
) -> &mut Self {
pub fn with_window(mut self, window: WindowBuilder) -> Self {
// gots to do a swap because the window builder only takes itself as muy self
// I wish more people knew about returning &mut Self
let mut builder = WindowBuilder::default().with_title("Dioxus App");
std::mem::swap(&mut self.window, &mut builder);
builder = configure(builder);
std::mem::swap(&mut self.window, &mut builder);
self.window = window;
self
}
// /// Set a custom event handler
// pub fn with_event_handler(
// &mut self,
// mut self,
// handler: impl Fn(&mut EventLoop<()>, &mut WebView) + 'static,
// ) -> &mut Self {
// ) -> Self {
// self.event_handler = Some(Box::new(handler));
// self
// }
/// Set a file drop handler
pub fn with_file_drop_handler(
&mut self,
mut self,
handler: impl Fn(&Window, FileDropEvent) -> bool + 'static,
) -> &mut Self {
) -> Self {
self.file_drop_handler = Some(Box::new(handler));
self
}
/// Set a custom protocol
pub fn with_custom_protocol<F>(&mut self, name: String, handler: F) -> &mut Self
pub fn with_custom_protocol<F>(mut self, name: String, handler: F) -> Self
where
F: Fn(&HttpRequest) -> WryResult<HttpResponse> + 'static,
{
@@ -107,7 +103,7 @@ impl DesktopConfig {
}
/// Set a custom icon for this application
pub fn with_icon(&mut self, icon: Icon) -> &mut Self {
pub fn with_icon(mut self, icon: Icon) -> Self {
self.window.window.window_icon = Some(icon);
self
}
@@ -115,7 +111,7 @@ impl DesktopConfig {
/// 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 {
pub fn with_custom_head(mut self, head: String) -> Self {
self.custom_head = Some(head);
self
}
@@ -126,22 +122,13 @@ impl DesktopConfig {
///
/// 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 {
pub fn with_custom_index(mut self, index: String) -> Self {
self.custom_index = Some(index);
self
}
}
impl DesktopConfig {
pub(crate) fn with_default_icon(mut self) -> Self {
let bin: &[u8] = include_bytes!("./assets/default_icon.bin");
let rgba = Icon::from_rgba(bin.to_owned(), 460, 460).expect("image parse failed");
self.window.window.window_icon = Some(rgba);
self
}
}
impl Default for DesktopConfig {
impl Default for Config {
fn default() -> Self {
Self::new()
}
+17 -14
View File
@@ -18,10 +18,11 @@ pub use wry;
pub use wry::application as tao;
use crate::events::trigger_from_serialized;
pub use cfg::DesktopConfig;
pub use cfg::Config;
use controller::DesktopController;
use dioxus_core::*;
use events::parse_ipc_message;
pub use tao::dpi::{LogicalSize, PhysicalSize};
pub use tao::window::WindowBuilder;
use tao::{
event::{Event, StartCause, WindowEvent},
@@ -48,7 +49,7 @@ use wry::webview::WebViewBuilder;
/// }
/// ```
pub fn launch(root: Component) {
launch_with_props(root, (), |c| c)
launch_with_props(root, (), Config::default())
}
/// Launch the WebView and run the event loop, with configuration.
@@ -70,10 +71,7 @@ pub fn launch(root: Component) {
/// })
/// }
/// ```
pub fn launch_cfg(
root: Component,
config_builder: impl FnOnce(&mut DesktopConfig) -> &mut DesktopConfig,
) {
pub fn launch_cfg(root: Component, config_builder: Config) {
launch_with_props(root, (), config_builder)
}
@@ -100,19 +98,24 @@ pub fn launch_cfg(
/// })
/// }
/// ```
pub fn launch_with_props<P: 'static + Send>(
root: Component<P>,
props: P,
builder: impl FnOnce(&mut DesktopConfig) -> &mut DesktopConfig,
) {
let mut cfg = DesktopConfig::default().with_default_icon();
builder(&mut cfg);
pub fn launch_with_props<P: 'static + Send>(root: Component<P>, props: P, mut cfg: Config) {
let event_loop = EventLoop::with_user_event();
let mut desktop = DesktopController::new_on_tokio(root, props, event_loop.create_proxy());
let proxy = event_loop.create_proxy();
// We assume that if the icon is None, then the user just didnt set it
if cfg.window.window.window_icon.is_none() {
cfg.window.window.window_icon = Some(
tao::window::Icon::from_rgba(
include_bytes!("./assets/default_icon.bin").to_vec(),
460,
460,
)
.expect("image parse failed"),
);
}
event_loop.run(move |window_event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
+3 -3
View File
@@ -87,11 +87,11 @@ impl RouterCore {
let route = match &cfg.initial_url {
Some(url) => Arc::new(ParsedRoute {
url: Url::from_str(&url).expect(
format!(
url: Url::from_str(url).unwrap_or_else(|_|
panic!(
"RouterCfg expects a valid initial_url, but got '{}'. Example: '{{scheme}}://{{?authority}}/{{?path}}'",
&url
).as_str()
)
),
title: None,
serialized_state: None,
+1 -1
View File
@@ -143,7 +143,7 @@ impl Default for BorderEdge {
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BorderStyle {
Dotted,
Dashed,
+2 -1
View File
@@ -1,4 +1,5 @@
use dioxus::prelude::*;
use dioxus_web::Config;
use web_sys::window;
fn app(cx: Scope) -> Element {
@@ -59,5 +60,5 @@ fn main() {
.set_inner_html(&pre);
// now rehydtrate
dioxus_web::launch_with_props(app, (), |c| c.hydrate(true));
dioxus_web::launch_with_props(app, (), Config::new().hydrate(true));
}
+24 -7
View File
@@ -5,32 +5,41 @@
/// # Example
///
/// ```rust, ignore
/// dioxus_web::launch(App, |cfg| cfg.hydrate(true).root_name("myroot"))
/// dioxus_web::launch(App, Config::new().hydrate(true).root_name("myroot"))
/// ```
pub struct WebConfig {
pub struct Config {
pub(crate) hydrate: bool,
pub(crate) rootname: String,
pub(crate) cached_strings: Vec<String>,
pub(crate) default_panic_hook: bool,
}
impl Default for WebConfig {
impl Default for Config {
fn default() -> Self {
Self {
hydrate: false,
rootname: "main".to_string(),
cached_strings: Vec::new(),
default_panic_hook: true,
}
}
}
impl WebConfig {
impl Config {
/// Create a new Default instance of the Config.
///
/// This is no different than calling `Config::default()`
pub fn new() -> Self {
Self::default()
}
/// Enable SSR hydration
///
/// This enables Dioxus to pick up work from a pre-renderd HTML file. Hydration will completely skip over any async
/// work and suspended nodes.
///
/// Dioxus will load up all the elements with the `dio_el` data attribute into memory when the page is loaded.
pub fn hydrate(&mut self, f: bool) -> &mut Self {
pub fn hydrate(mut self, f: bool) -> Self {
self.hydrate = f;
self
}
@@ -38,7 +47,7 @@ impl WebConfig {
/// Set the name of the element that Dioxus will use as the root.
///
/// This is akint to calling React.render() on the element with the specified name.
pub fn rootname(&mut self, name: impl Into<String>) -> &mut Self {
pub fn rootname(mut self, name: impl Into<String>) -> Self {
self.rootname = name.into();
self
}
@@ -46,8 +55,16 @@ impl WebConfig {
/// Set the name of the element that Dioxus will use as the root.
///
/// This is akint to calling React.render() on the element with the specified name.
pub fn with_string_cache(&mut self, cache: Vec<String>) -> &mut Self {
pub fn with_string_cache(mut self, cache: Vec<String>) -> Self {
self.cached_strings = cache;
self
}
/// Set whether or not Dioxus should use the built-in panic hook or defer to your own.
///
/// The panic hook is set to true normally so even the simplest apps have helpful error messages.
pub fn with_default_panic_hook(mut self, f: bool) -> Self {
self.default_panic_hook = f;
self
}
}
+2 -2
View File
@@ -15,7 +15,7 @@ use std::{any::Any, rc::Rc, sync::Arc};
use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{Document, Element, Event, HtmlElement};
use crate::WebConfig;
use crate::Config;
pub struct WebsysDom {
pub interpreter: Interpreter,
@@ -26,7 +26,7 @@ pub struct WebsysDom {
}
impl WebsysDom {
pub fn new(cfg: WebConfig, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
pub fn new(cfg: Config, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
// eventually, we just want to let the interpreter do all the work of decoding events into our event type
let callback: Box<dyn FnMut(&Event)> = Box::new(move |event: &web_sys::Event| {
let mut target = event
+13 -18
View File
@@ -56,7 +56,7 @@
use std::rc::Rc;
pub use crate::cfg::WebConfig;
pub use crate::cfg::Config;
pub use crate::util::use_eval;
use dioxus_core::prelude::Component;
use dioxus_core::SchedulerMsg;
@@ -93,7 +93,7 @@ mod util;
/// }
/// ```
pub fn launch(root_component: Component) {
launch_with_props(root_component, (), |c| c);
launch_with_props(root_component, (), Config::default());
}
/// Launch your app and run the event loop, with configuration.
@@ -106,7 +106,7 @@ pub fn launch(root_component: Component) {
/// use dioxus::prelude::*;
///
/// fn main() {
/// dioxus_web::launch_with_props(App, |config| config.pre_render(true));
/// dioxus_web::launch_with_props(App, Config::new().pre_render(true));
/// }
///
/// fn app(cx: Scope) -> Element {
@@ -115,8 +115,8 @@ pub fn launch(root_component: Component) {
/// })
/// }
/// ```
pub fn launch_cfg(root: Component, config_builder: impl FnOnce(&mut WebConfig) -> &mut WebConfig) {
launch_with_props(root, (), config_builder)
pub fn launch_cfg(root: Component, config: Config) {
launch_with_props(root, (), config)
}
/// Launches the VirtualDOM from the specified component function and props.
@@ -130,7 +130,7 @@ pub fn launch_cfg(root: Component, config_builder: impl FnOnce(&mut WebConfig) -
/// dioxus_web::launch_with_props(
/// App,
/// RootProps { name: String::from("joe") },
/// |config| config
/// Config::new()
/// );
/// }
///
@@ -143,19 +143,10 @@ pub fn launch_cfg(root: Component, config_builder: impl FnOnce(&mut WebConfig) -
/// rsx!(cx, div {"hello {cx.props.name}"})
/// }
/// ```
pub fn launch_with_props<T>(
root_component: Component<T>,
root_properties: T,
configuration_builder: impl FnOnce(&mut WebConfig) -> &mut WebConfig,
) where
pub fn launch_with_props<T>(root_component: Component<T>, root_properties: T, config: Config)
where
T: Send + 'static,
{
if cfg!(feature = "panic_hook") {
console_error_panic_hook::set_once();
}
let mut config = WebConfig::default();
configuration_builder(&mut config);
wasm_bindgen_futures::spawn_local(run_with_props(root_component, root_properties, config));
}
@@ -171,9 +162,13 @@ pub fn launch_with_props<T>(
/// wasm_bindgen_futures::spawn_local(app_fut);
/// }
/// ```
pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T, cfg: WebConfig) {
pub async fn run_with_props<T: 'static + Send>(root: Component<T>, root_props: T, cfg: Config) {
let mut dom = VirtualDom::new_with_props(root, root_props);
if cfg!(feature = "panic_hook") && cfg.default_panic_hook {
console_error_panic_hook::set_once();
}
#[cfg(feature = "hot-reload")]
hot_reload::init(&dom);
+2 -2
View File
@@ -16,7 +16,7 @@ use web_sys::{
HtmlOptionElement, HtmlTextAreaElement, Node,
};
use crate::{nodeslab::NodeSlab, WebConfig};
use crate::{nodeslab::NodeSlab, Config};
pub struct WebsysDom {
stack: Stack,
@@ -39,7 +39,7 @@ pub struct WebsysDom {
type ListenerEntry = (usize, Closure<dyn FnMut(&Event)>);
impl WebsysDom {
pub fn new(cfg: WebConfig, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
pub fn new(cfg: Config, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
let document = load_document();
let nodes = NodeSlab::new(2000);
+2 -1
View File
@@ -1,4 +1,5 @@
use dioxus::prelude::*;
use dioxus_web::Config;
use wasm_bindgen_test::wasm_bindgen_test;
use web_sys::window;
@@ -59,5 +60,5 @@ fn rehydrates() {
.unwrap()
.set_inner_html(&format!("<div id='main'>{}</div>", out));
dioxus_web::launch_cfg(app, |c| c.hydrate(true));
dioxus_web::launch_cfg(app, Config::new().hydrate(true));
}