From 7e3a6fb4a484fdda77fd5903b0f8131208c088b5 Mon Sep 17 00:00:00 2001 From: David Craven Date: Mon, 24 Oct 2022 04:40:41 +0200 Subject: [PATCH 1/6] Some fixes for wayland and ios. (#602) --- packages/desktop/Cargo.toml | 2 +- packages/desktop/src/desktop_context.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 2e807283e..232b38ffb 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -28,7 +28,7 @@ tokio = { version = "1.16.1", features = [ "rt", "time", ], optional = true, default-features = false } -webbrowser = "0.7.1" +webbrowser = "0.8.0" infer = "0.9.0" dunce = "1.0.2" diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 89d0e2fe4..bf3a253c5 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -173,7 +173,13 @@ pub(super) fn handler( ) { // currently dioxus-desktop supports a single window only, // so we can grab the only webview from the map; - let webview = desktop.webviews.values().next().unwrap(); + // on wayland it is possible that a user event is emitted + // before the webview is initialized. ignore the event. + let webview = if let Some(webview) = desktop.webviews.values().next() { + webview + } else { + return; + }; let window = webview.window(); match user_event { From f89cd204557f09febebf1afc48f418ad6ebc5254 Mon Sep 17 00:00:00 2001 From: Demonthos Date: Fri, 28 Oct 2022 20:06:39 -0500 Subject: [PATCH 2/6] fix tui example links (#607) --- examples/README.md | 28 ++++---- .../examples/{tui_frame.rs => tui_flex.rs} | 0 packages/tui/examples/tui_keys.rs | 66 ------------------- 3 files changed, 15 insertions(+), 79 deletions(-) rename packages/tui/examples/{tui_frame.rs => tui_flex.rs} (100%) delete mode 100644 packages/tui/examples/tui_keys.rs diff --git a/examples/README.md b/examples/README.md index 732b19ba8..2533722ed 100644 --- a/examples/README.md +++ b/examples/README.md @@ -134,31 +134,33 @@ cargo run --example hello_world ## Terminal UI -[tui_border](./tui_border.rs) +[tui_all_events](../packages/tui/examples/tui_all_events.rs) - All of the terminal events -[tui_buttons](./tui_buttons.rs) +[tui_border](../packages/tui/examples/tui_border.rs) - Different styles of borders and corners -[tui_color_test](./tui_color_test.rs) +[tui_buttons](../packages/tui/examples/tui_buttons.rs) - A grid of buttons that work demonstrate the focus system -[tui_components](./tui_components.rs) +[tui_color_test](../packages/tui/examples/tui_color_test.rs) - Grid of colors to demonstrate compatablility with different terminal color rendering modes -[tui_frame](./tui_frame.rs) +[tui_colorpicker](../packages/tui/examples/tui_colorpicker.rs) - A colorpicker -[tui_hover](./tui_hover.rs) +[tui_components](../packages/tui/examples/tui_components.rs) - Simple component example -[tui_keys](./tui_keys.rs) +[tui_flex](../packages/tui/examples/tui_flex.rs) - Flexbox support -[tui_list](./tui_list.rs) +[tui_hover](../packages/tui/examples/tui_hover.rs) - Detect hover and mouse events -[tui_margin](./tui_margin.rs) +[tui_list](../packages/tui/examples/tui_list.rs) - Renders a list of items -[tui_quadrants](./tui_quadrants.rs) +[tui_margin](../packages/tui/examples/tui_margin.rs) - Margins around flexboxes -[tui_readme](./tui_readme.rs) +[tui_quadrants](../packages/tui/examples/tui_quadrants.rs) - Four quadrants -[tui_task](./tui_task.rs) +[tui_readme](../packages/tui/examples/tui_readme.rs) - The readme example -[tui_text](./tui_text.rs) +[tui_task](../packages/tui/examples/tui_task.rs) - Background tasks + +[tui_text](../packages/tui/examples/tui_text.rs) - Simple text example # TODO Missing Features diff --git a/packages/tui/examples/tui_frame.rs b/packages/tui/examples/tui_flex.rs similarity index 100% rename from packages/tui/examples/tui_frame.rs rename to packages/tui/examples/tui_flex.rs diff --git a/packages/tui/examples/tui_keys.rs b/packages/tui/examples/tui_keys.rs deleted file mode 100644 index 3c3d04cf2..000000000 --- a/packages/tui/examples/tui_keys.rs +++ /dev/null @@ -1,66 +0,0 @@ -use dioxus::events::WheelEvent; -use dioxus::prelude::*; -use dioxus_html::geometry::ScreenPoint; -use dioxus_html::input_data::keyboard_types::Code; -use dioxus_html::input_data::MouseButtonSet; -use dioxus_html::on::{KeyboardEvent, MouseEvent}; - -fn main() { - dioxus_tui::launch(app); -} - -fn app(cx: Scope) -> Element { - let key = use_state(&cx, || "".to_string()); - let mouse = use_state(&cx, ScreenPoint::zero); - let count = use_state(&cx, || 0); - let buttons = use_state(&cx, MouseButtonSet::empty); - let mouse_clicked = use_state(&cx, || false); - - let key_down_handler = move |evt: KeyboardEvent| { - match evt.data.code() { - Code::ArrowLeft => count.set(count + 1), - Code::ArrowRight => count.set(count - 1), - Code::ArrowUp => count.set(count + 10), - Code::ArrowDown => count.set(count - 10), - _ => {} - } - key.set(format!( - "{:?} repeating: {:?}", - evt.key(), - evt.is_auto_repeating() - )); - }; - - cx.render(rsx! { - div { - width: "100%", - height: "10px", - background_color: "red", - justify_content: "center", - align_items: "center", - flex_direction: "column", - onkeydown: key_down_handler, - onwheel: move |evt: WheelEvent| { - count.set(count + evt.data.delta().strip_units().y as i64); - }, - ondrag: move |evt: MouseEvent| { - mouse.set(evt.data.screen_coordinates()); - }, - onmousedown: move |evt: MouseEvent| { - mouse.set(evt.data.screen_coordinates()); - buttons.set(evt.data.held_buttons()); - mouse_clicked.set(true); - }, - onmouseup: move |evt: MouseEvent| { - buttons.set(evt.data.held_buttons()); - mouse_clicked.set(false); - }, - - "count: {count:?}", - "key: {key}", - "mouse buttons: {buttons:?}", - "mouse pos: {mouse:?}", - "mouse button pressed: {mouse_clicked}" - } - }) -} From 5c996f21fbe148b96e335a395d222502ae908f70 Mon Sep 17 00:00:00 2001 From: David Craven Date: Sun, 6 Nov 2022 09:49:25 +0100 Subject: [PATCH 3/6] Update wry to 0.22.0 (#604) --- packages/desktop/Cargo.toml | 2 +- packages/desktop/src/cfg.rs | 4 ++-- packages/desktop/src/protocol.rs | 38 +++++++++++++++++++------------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 232b38ffb..8639875a6 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -20,7 +20,7 @@ serde = "1.0.136" serde_json = "1.0.79" thiserror = "1.0.30" log = "0.4.14" -wry = { version = "0.20.2" } +wry = { version = "0.22.0", git = "https://github.com/tauri-apps/wry", branch = "release" } futures-channel = "0.3.21" tokio = { version = "1.16.1", features = [ "sync", diff --git a/packages/desktop/src/cfg.rs b/packages/desktop/src/cfg.rs index 85be2727f..060e95ce2 100644 --- a/packages/desktop/src/cfg.rs +++ b/packages/desktop/src/cfg.rs @@ -27,7 +27,7 @@ type DropHandler = Box bool>; pub(crate) type WryProtocol = ( String, - Box WryResult + 'static>, + Box>) -> WryResult>> + 'static>, ); impl Config { @@ -96,7 +96,7 @@ impl Config { /// Set a custom protocol pub fn with_custom_protocol(mut self, name: String, handler: F) -> Self where - F: Fn(&HttpRequest) -> WryResult + 'static, + F: Fn(&HttpRequest>) -> WryResult>> + 'static, { self.protocols.push((name, Box::new(handler))); self diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 1c8aaa54f..b8e5cc341 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; use wry::{ - http::{status::StatusCode, Request, Response, ResponseBuilder}, + http::{status::StatusCode, Request, Response}, Result, }; @@ -13,14 +13,14 @@ const MODULE_LOADER: &str = r#" "#; pub(super) fn desktop_handler( - request: &Request, + request: &Request>, asset_root: Option, custom_head: Option, custom_index: Option, -) -> Result { +) -> 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://", ""); + let path = request.uri().to_string().replace("dioxus://", ""); // all assets should be called from index.html let trimmed = path.trim_start_matches("index.html/"); @@ -32,7 +32,10 @@ pub(super) fn desktop_handler( let rendered = custom_index .replace("", &format!("{}", MODULE_LOADER)) .into_bytes(); - ResponseBuilder::new().mimetype("text/html").body(rendered) + Response::builder() + .header("Content-Type", "text/html") + .body(rendered) + .map_err(From::from) } 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(); @@ -41,14 +44,16 @@ pub(super) fn desktop_handler( } template = template.replace("", MODULE_LOADER); - ResponseBuilder::new() - .mimetype("text/html") + Response::builder() + .header("Content-Type", "text/html") .body(template.into_bytes()) + .map_err(From::from) } } else if trimmed == "index.js" { - ResponseBuilder::new() - .mimetype("text/javascript") + Response::builder() + .header("Content-Type", "text/javascript") .body(dioxus_interpreter_js::INTERPRETER_JS.as_bytes().to_vec()) + .map_err(From::from) } else { let asset_root = asset_root .unwrap_or_else(|| get_asset_root().unwrap_or_else(|| Path::new(".").to_path_buf())) @@ -57,20 +62,23 @@ pub(super) fn desktop_handler( let asset = asset_root.join(trimmed).canonicalize()?; if !asset.starts_with(asset_root) { - return ResponseBuilder::new() + return Response::builder() .status(StatusCode::FORBIDDEN) - .body(String::from("Forbidden").into_bytes()); + .body(String::from("Forbidden").into_bytes()) + .map_err(From::from); } if !asset.exists() { - return ResponseBuilder::new() + return Response::builder() .status(StatusCode::NOT_FOUND) - .body(String::from("Not Found").into_bytes()); + .body(String::from("Not Found").into_bytes()) + .map_err(From::from); } - ResponseBuilder::new() - .mimetype(get_mime_from_path(trimmed)?) + Response::builder() + .header("Content-Type", get_mime_from_path(trimmed)?) .body(std::fs::read(asset)?) + .map_err(From::from) } } From 79ea1994dbdc10813060663fed65bf639977f533 Mon Sep 17 00:00:00 2001 From: Adrian Wannenmacher Date: Thu, 10 Nov 2022 19:03:26 +0100 Subject: [PATCH 4/6] fix desktop dependency wry (#618) --- packages/desktop/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 8639875a6..857568b3d 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -20,7 +20,7 @@ serde = "1.0.136" serde_json = "1.0.79" thiserror = "1.0.30" log = "0.4.14" -wry = { version = "0.22.0", git = "https://github.com/tauri-apps/wry", branch = "release" } +wry = { version = "0.22.0" } futures-channel = "0.3.21" tokio = { version = "1.16.1", features = [ "sync", From 0085b9cb1de4fce6db0d6c394f87b82eff538161 Mon Sep 17 00:00:00 2001 From: Miles Murgaw Date: Thu, 10 Nov 2022 15:41:25 -0500 Subject: [PATCH 5/6] Fix the Android support disagreement in the docs (#614) --- docs/guide/src/en/getting_started/mobile.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/guide/src/en/getting_started/mobile.md b/docs/guide/src/en/getting_started/mobile.md index ad49817ec..56fb891e1 100644 --- a/docs/guide/src/en/getting_started/mobile.md +++ b/docs/guide/src/en/getting_started/mobile.md @@ -5,12 +5,13 @@ Build a mobile app with Dioxus! Example: [Todo App](https://github.com/DioxusLabs/example-projects/blob/master/ios_demo) ## Support -Mobile is currently the least-supported renderer target for Dioxus. Mobile apps are rendered with the platform's WebView, meaning that animations, transparency, and native widgets are not currently achievable. +Mobile is currently the least-supported renderer target for Dioxus. Mobile apps are rendered with either the platform's WebView or experimentally through WGPU. WebView doesn't support animations, transparency, and native widgets. -In addition, iOS is the only supported Mobile Platform. It is possible to get Dioxus running on Android and rendered with WebView, but the Rust windowing library that Dioxus uses – tao – does not currently support Android. Mobile support is currently best suited for CRUD-style apps, ideally for internal teams who need to develop quickly but don't care much about animations or native widgets. +This guide is primarily targetted for iOS apps, however, you can follow it while using the `android` guide in `cargo-mobile`. + ## Getting Set up Getting set up with mobile can be quite challenging. The tooling here isn't great (yet) and might take some hacking around to get things working. macOS M1 is broadly unexplored and might not work for you. From 8ea61e1b3e26652f827bf126c4d17c6ddeaf178d Mon Sep 17 00:00:00 2001 From: Jon Kelley Date: Wed, 16 Nov 2022 22:10:50 -0800 Subject: [PATCH 6/6] feat: simple iterators and conditionals in rsx (#564) * feat: simple iterators * fix: into_iter * feat: add support for unterminated conditionasl * fix: add tempalte mapping for helpers --- examples/simple_list.rs | 12 ++++ packages/rsx/src/node.rs | 127 ++++++++++++++++++++++++++++++++++- packages/rsx/src/template.rs | 16 +++++ 3 files changed, 153 insertions(+), 2 deletions(-) diff --git a/examples/simple_list.rs b/examples/simple_list.rs index d615e2371..d4616c19b 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -8,12 +8,24 @@ fn app(cx: Scope) -> Element { cx.render(rsx!( // Use Map directly to lazily pull elements (0..10).map(|f| rsx! { "{f}" }), + // Collect into an intermediate collection if necessary ["a", "b", "c"] .into_iter() .map(|f| rsx! { "{f}" }) .collect::>(), + // Use optionals Some(rsx! { "Some" }), + + div { + for name in 0..10 { + rsx! { "{name}" } + } + + if true { + rsx!{ "hello world!" } + } + } )) } diff --git a/packages/rsx/src/node.rs b/packages/rsx/src/node.rs index 406a24ddc..5572030b6 100644 --- a/packages/rsx/src/node.rs +++ b/packages/rsx/src/node.rs @@ -5,7 +5,7 @@ use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned, - token, Expr, LitStr, Result, + token, Block, Expr, ExprIf, LitStr, Pat, Result, }; /* @@ -20,6 +20,8 @@ Parse pub enum BodyNode { Element(Element), Component(Component), + ForLoop(ForLoop), + IfChain(ExprIf), Text(IfmtInput), RawExpr(Expr), } @@ -35,6 +37,8 @@ impl BodyNode { BodyNode::Component(component) => component.name.span(), BodyNode::Text(text) => text.source.span(), BodyNode::RawExpr(exp) => exp.span(), + BodyNode::ForLoop(fl) => fl.for_token.span(), + BodyNode::IfChain(f) => f.if_token.span(), } } } @@ -83,11 +87,32 @@ impl Parse for BodyNode { // crate::Input:: {} if body_stream.peek(token::Brace) { Component::validate_component_path(&path)?; - return Ok(BodyNode::Component(stream.parse()?)); } } + // Transform for loops into into_iter calls + if stream.peek(Token![for]) { + let _f = stream.parse::()?; + let pat = stream.parse::()?; + let _i = stream.parse::()?; + let expr = stream.parse::>()?; + let body = stream.parse::()?; + + return Ok(BodyNode::ForLoop(ForLoop { + for_token: _f, + pat, + in_token: _i, + expr, + body, + })); + } + + // Transform unterminated if statements into terminated optional if statements + if stream.peek(Token![if]) { + return Ok(BodyNode::IfChain(stream.parse()?)); + } + Ok(BodyNode::RawExpr(stream.parse::()?)) } } @@ -103,6 +128,104 @@ impl ToTokens for BodyNode { BodyNode::RawExpr(exp) => tokens.append_all(quote! { __cx.fragment_from_iter(#exp) }), + BodyNode::ForLoop(exp) => { + let ForLoop { + pat, expr, body, .. + } = exp; + + tokens.append_all(quote! { + __cx.fragment_from_iter( + (#expr).into_iter().map(|#pat| { + #body + }) + ) + }) + } + BodyNode::IfChain(chain) => { + if is_if_chain_terminated(chain) { + tokens.append_all(quote! { + __cx.fragment_from_iter(#chain) + }); + } else { + let ExprIf { + cond, + then_branch, + else_branch, + .. + } = chain; + + let mut body = TokenStream2::new(); + + body.append_all(quote! { + if #cond { + Some(#then_branch) + } + }); + + let mut elif = else_branch; + + while let Some((_, ref branch)) = elif { + match branch.as_ref() { + Expr::If(ref eelif) => { + let ExprIf { + cond, + then_branch, + else_branch, + .. + } = eelif; + + body.append_all(quote! { + else if #cond { + Some(#then_branch) + } + }); + + elif = else_branch; + } + _ => { + body.append_all(quote! { + else { + #branch + } + }); + break; + } + } + } + + body.append_all(quote! { + else { None } + }); + + tokens.append_all(quote! { + __cx.fragment_from_iter(#body) + }); + } + } + } + } +} + +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub struct ForLoop { + pub for_token: Token![for], + pub pat: Pat, + pub in_token: Token![in], + pub expr: Box, + pub body: Block, +} + +fn is_if_chain_terminated(chain: &ExprIf) -> bool { + let mut current = chain; + loop { + if let Some((_, else_block)) = ¤t.else_branch { + if let Expr::If(else_if) = else_block.as_ref() { + current = else_if; + } else { + return true; + } + } else { + return false; } } } diff --git a/packages/rsx/src/template.rs b/packages/rsx/src/template.rs index 693f29b05..8e1544105 100644 --- a/packages/rsx/src/template.rs +++ b/packages/rsx/src/template.rs @@ -557,6 +557,22 @@ impl TemplateBuilder { fully_static: false, }); } + BodyNode::ForLoop(expr) => { + self.nodes.push(TemplateNodeBuilder { + id, + node_type: TemplateNodeTypeBuilder::DynamicNode( + self.dynamic_context.add_node(BodyNode::ForLoop(expr)), + ), + }); + } + BodyNode::IfChain(expr) => { + self.nodes.push(TemplateNodeBuilder { + id, + node_type: TemplateNodeTypeBuilder::DynamicNode( + self.dynamic_context.add_node(BodyNode::IfChain(expr)), + ), + }); + } } id }