From 1df329bda40baf5b3233300c856f5c06de499b80 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 17 Jan 2024 18:41:31 -0800 Subject: [PATCH 1/7] Fix router examples --- Cargo.lock | 1 - Cargo.toml | 1 - examples/flat_router.rs | 22 +++++++++++++--------- examples/router.rs | 6 +++++- examples/simple_desktop.rs | 6 +++++- examples/simple_router.rs | 14 ++++++++++++-- 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c96c8ad61..43962c635 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2571,7 +2571,6 @@ dependencies = [ "env_logger", "futures-util", "http-range", - "im-rc", "log", "manganis", "num-format", diff --git a/Cargo.toml b/Cargo.toml index e512f9b10..c8590ea47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,7 +136,6 @@ log = "0.4.14" num-format = "0.4.0" separator = "0.4.1" serde = { version = "1.0.136", features = ["derive"] } -im-rc = "15.0.0" anyhow = "1.0.53" serde_json = "1.0.79" rand = { version = "0.8.4", features = ["small_rng"] } diff --git a/examples/flat_router.rs b/examples/flat_router.rs index 3db802bfe..dcebe2298 100644 --- a/examples/flat_router.rs +++ b/examples/flat_router.rs @@ -5,15 +5,19 @@ use dioxus_router::prelude::*; fn main() { env_logger::init(); - LaunchBuilder::new(Route::Home {}) - .cfg( - Config::new().with_window( - WindowBuilder::new() - .with_inner_size(LogicalSize::new(600, 1000)) - .with_resizable(false), - ), - ) - .launch_desktop() + LaunchBuilder::new(|| { + rsx! { + Router:: {} + } + }) + .cfg( + Config::new().with_window( + WindowBuilder::new() + .with_inner_size(LogicalSize::new(600, 1000)) + .with_resizable(false), + ), + ) + .launch_desktop() } #[derive(Routable, Clone)] diff --git a/examples/router.rs b/examples/router.rs index 72844dc11..49a992748 100644 --- a/examples/router.rs +++ b/examples/router.rs @@ -2,7 +2,11 @@ use dioxus::prelude::*; use dioxus_router::prelude::*; fn main() { - launch_desktop(Route::Home {}); + launch_desktop(|| { + rsx! { + Router:: {} + } + }); } #[derive(Routable, Clone, Debug, PartialEq)] diff --git a/examples/simple_desktop.rs b/examples/simple_desktop.rs index bc758388a..aee7763cc 100644 --- a/examples/simple_desktop.rs +++ b/examples/simple_desktop.rs @@ -9,7 +9,11 @@ fn main() { .with_module_level("dioxus", log::LevelFilter::Trace) .init() .unwrap(); - launch_desktop(Route::Home {}); + launch_desktop(|| { + rsx! { + Router:: {} + } + }); } #[derive(Routable, Clone)] diff --git a/examples/simple_router.rs b/examples/simple_router.rs index 811aa32a3..8f8ec5b3e 100644 --- a/examples/simple_router.rs +++ b/examples/simple_router.rs @@ -54,7 +54,17 @@ fn Nav() -> Element { } fn main() { - launch_desktop(Route::Blog { - id: "hello".to_string(), + launch_desktop(|| { + let cfg = || { + RouterConfig::default().initial_route(Route::Blog { + id: "hello".to_string(), + }) + }; + + rsx! { + Router:: { + config: cfg + } + } }); } From 8918bf946306f5eeafa110ef4e1f67e87ac9ec32 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 17 Jan 2024 19:01:28 -0800 Subject: [PATCH 2/7] Make the calculator more beautiful --- examples/assets/calculator.css | 39 ++++++++++++++++++++++------------ examples/calculator.rs | 4 ++-- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/examples/assets/calculator.css b/examples/assets/calculator.css index 047b618ad..d2a622dc9 100644 --- a/examples/assets/calculator.css +++ b/examples/assets/calculator.css @@ -1,13 +1,17 @@ html { box-sizing: border-box; } -*, *:before, *:after { + +*, +*:before, +*:after { box-sizing: inherit; } body { margin: 0; font: 100 14px 'Roboto'; + font-family: Arial; overflow: hidden; } @@ -20,18 +24,18 @@ button { user-select: none; cursor: pointer; outline: none; - - -webkit-tap-highlight-color: rgba(0,0,0,0); + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } button:active { - box-shadow: inset 0px 0px 80px 0px rgba(0,0,0,0.25); + box-shadow: inset 0px 0px 80px 0px rgba(0, 0, 0, 0.25); } #wrapper { /* height: 100vh; */ height: max-content; - + display: flex; align-items: center; justify-content: center; @@ -47,7 +51,7 @@ button:active { width: 100%; height: 100%; background: black; - + display: flex; flex-direction: column; } @@ -61,14 +65,14 @@ button:active { background: #1c191c; line-height: 130px; /* font-size: 6em; */ - font-size: 16px; - font-size: 4vw; + font-size: 16px; + font-size: 4vw; max-height: 160px; padding: 0 30px; /* height: 80px; */ - + flex: 1; } @@ -85,7 +89,7 @@ button:active { .calculator-keypad { height: 400px; - + display: flex; } @@ -99,7 +103,7 @@ button:active { .calculator .digit-keys { background: #e0e0e7; - + display: flex; flex-direction: row; flex-wrap: wrap-reverse; @@ -109,28 +113,34 @@ button:active { width: 80px; height: 80px; border-top: 1px solid #777; - border-right: 1px solid #666; + border-right: 1px solid #666; text-align: center; line-height: 80px; } + .calculator .function-keys .calculator-key { font-size: 2em; } + .calculator .function-keys .key-multiply { line-height: 50px; } + .calculator .digit-keys .calculator-key { font-size: 2.25em; } + .calculator .digit-keys .key-0 { width: 160px; text-align: left; padding-left: 32px; } + .calculator .digit-keys .key-dot { padding-top: 1em; font-size: 0.75em; } + .calculator .operator-keys .calculator-key { color: white; border-right: 0; @@ -138,8 +148,9 @@ button:active { } .calculator .function-keys { - background: linear-gradient(to bottom, rgba(202,202,204,1) 0%, rgba(196,194,204,1) 100%); + background: linear-gradient(to bottom, rgba(202, 202, 204, 1) 0%, rgba(196, 194, 204, 1) 100%); } + .calculator .operator-keys { - background: linear-gradient(to bottom, rgba(252,156,23,1) 0%, rgba(247,126,27,1) 100%); + background: linear-gradient(to bottom, rgba(252, 156, 23, 1) 0%, rgba(247, 126, 27, 1) 100%); } diff --git a/examples/calculator.rs b/examples/calculator.rs index 8aa35ab11..aa3385fa8 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -12,10 +12,10 @@ fn main() { let config = Config::new().with_window( WindowBuilder::default() .with_title("Calculator") - .with_inner_size(LogicalSize::new(300.0, 500.0)), + .with_inner_size(LogicalSize::new(300.0, 525.0)), ); - LaunchBuilder::new(app).cfg(config); + LaunchBuilder::new(app).cfg(config).launch(); } fn app() -> Element { From d4d7cb11f5628023d27b8c89c65dda66538b29d6 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 17 Jan 2024 19:03:28 -0800 Subject: [PATCH 3/7] Remove all_css example --- examples/all_css.rs | 413 -------------------------------------------- 1 file changed, 413 deletions(-) delete mode 100644 examples/all_css.rs diff --git a/examples/all_css.rs b/examples/all_css.rs deleted file mode 100644 index a2529aeba..000000000 --- a/examples/all_css.rs +++ /dev/null @@ -1,413 +0,0 @@ -use dioxus::prelude::*; - -fn main() { - launch_desktop(app); -} - -fn app() -> Element { - rsx! { - div { - align_content: "a", - align_items: "a", - align_self: "a", - alignment_adjust: "a", - alignment_baseline: "a", - all: "a", - alt: "a", - animation: "a", - animation_delay: "a", - animation_direction: "a", - animation_duration: "a", - animation_fill_mode: "a", - animation_iteration_count: "a", - animation_name: "a", - animation_play_state: "a", - animation_timing_function: "a", - azimuth: "a", - backface_visibility: "a", - background: "a", - background_attachment: "a", - background_clip: "a", - background_color: "a", - background_image: "a", - background_origin: "a", - background_position: "a", - background_repeat: "a", - background_size: "a", - background_blend_mode: "a", - baseline_shift: "a", - bleed: "a", - bookmark_label: "a", - bookmark_level: "a", - bookmark_state: "a", - border: "a", - border_color: "a", - border_style: "a", - border_width: "a", - border_bottom: "a", - border_bottom_color: "a", - border_bottom_style: "a", - border_bottom_width: "a", - border_left: "a", - border_left_color: "a", - border_left_style: "a", - border_left_width: "a", - border_right: "a", - border_right_color: "a", - border_right_style: "a", - border_right_width: "a", - border_top: "a", - border_top_color: "a", - border_top_style: "a", - border_top_width: "a", - border_collapse: "a", - border_image: "a", - border_image_outset: "a", - border_image_repeat: "a", - border_image_slice: "a", - border_image_source: "a", - border_image_width: "a", - border_radius: "a", - border_bottom_left_radius: "a", - border_bottom_right_radius: "a", - border_top_left_radius: "a", - border_top_right_radius: "a", - border_spacing: "a", - bottom: "a", - box_decoration_break: "a", - box_shadow: "a", - box_sizing: "a", - box_snap: "a", - break_after: "a", - break_before: "a", - break_inside: "a", - buffered_rendering: "a", - caption_side: "a", - clear: "a", - clear_side: "a", - clip: "a", - clip_path: "a", - clip_rule: "a", - color: "a", - color_adjust: "a", - color_correction: "a", - color_interpolation: "a", - color_interpolation_filters: "a", - color_profile: "a", - color_rendering: "a", - column_fill: "a", - column_gap: "a", - column_rule: "a", - column_rule_color: "a", - column_rule_style: "a", - column_rule_width: "a", - column_span: "a", - columns: "a", - column_count: "a", - column_width: "a", - contain: "a", - content: "a", - counter_increment: "a", - counter_reset: "a", - counter_set: "a", - cue: "a", - cue_after: "a", - cue_before: "a", - cursor: "a", - direction: "a", - display: "a", - display_inside: "a", - display_outside: "a", - display_extras: "a", - display_box: "a", - dominant_baseline: "a", - elevation: "a", - empty_cells: "a", - enable_background: "a", - fill: "a", - fill_opacity: "a", - fill_rule: "a", - filter: "a", - float: "a", - float_defer_column: "a", - float_defer_page: "a", - float_offset: "a", - float_wrap: "a", - flow_into: "a", - flow_from: "a", - flex: "a", - flex_basis: "a", - flex_grow: "a", - flex_shrink: "a", - flex_flow: "a", - flex_direction: "a", - flex_wrap: "a", - flood_color: "a", - flood_opacity: "a", - font: "a", - font_family: "a", - font_size: "a", - font_stretch: "a", - font_style: "a", - font_weight: "a", - font_feature_settings: "a", - font_kerning: "a", - font_language_override: "a", - font_size_adjust: "a", - font_synthesis: "a", - font_variant: "a", - font_variant_alternates: "a", - font_variant_caps: "a", - font_variant_east_asian: "a", - font_variant_ligatures: "a", - font_variant_numeric: "a", - font_variant_position: "a", - footnote_policy: "a", - glyph_orientation_horizontal: "a", - glyph_orientation_vertical: "a", - grid: "a", - grid_auto_flow: "a", - grid_auto_columns: "a", - grid_auto_rows: "a", - grid_template: "a", - grid_template_areas: "a", - grid_template_columns: "a", - grid_template_rows: "a", - grid_area: "a", - grid_column: "a", - grid_column_start: "a", - grid_column_end: "a", - grid_row: "a", - grid_row_start: "a", - grid_row_end: "a", - hanging_punctuation: "a", - height: "a", - hyphenate_character: "a", - hyphenate_limit_chars: "a", - hyphenate_limit_last: "a", - hyphenate_limit_lines: "a", - hyphenate_limit_zone: "a", - hyphens: "a", - icon: "a", - image_orientation: "a", - image_resolution: "a", - image_rendering: "a", - ime: "a", - ime_align: "a", - ime_mode: "a", - ime_offset: "a", - ime_width: "a", - initial_letters: "a", - inline_box_align: "a", - isolation: "a", - justify_content: "a", - justify_items: "a", - justify_self: "a", - kerning: "a", - left: "a", - letter_spacing: "a", - lighting_color: "a", - line_box_contain: "a", - line_break: "a", - line_grid: "a", - line_height: "a", - line_slack: "a", - line_snap: "a", - list_style: "a", - list_style_image: "a", - list_style_position: "a", - list_style_type: "a", - margin: "a", - margin_bottom: "a", - margin_left: "a", - margin_right: "a", - margin_top: "a", - marker: "a", - marker_end: "a", - marker_mid: "a", - marker_pattern: "a", - marker_segment: "a", - marker_start: "a", - marker_knockout_left: "a", - marker_knockout_right: "a", - marker_side: "a", - marks: "a", - marquee_direction: "a", - marquee_play_count: "a", - marquee_speed: "a", - marquee_style: "a", - mask: "a", - mask_image: "a", - mask_repeat: "a", - mask_position: "a", - mask_clip: "a", - mask_origin: "a", - mask_size: "a", - mask_box: "a", - mask_box_outset: "a", - mask_box_repeat: "a", - mask_box_slice: "a", - mask_box_source: "a", - mask_box_width: "a", - mask_type: "a", - max_height: "a", - max_lines: "a", - max_width: "a", - min_height: "a", - min_width: "a", - mix_blend_mode: "a", - nav_down: "a", - nav_index: "a", - nav_left: "a", - nav_right: "a", - nav_up: "a", - object_fit: "a", - object_position: "a", - offset_after: "a", - offset_before: "a", - offset_end: "a", - offset_start: "a", - opacity: "a", - order: "a", - orphans: "a", - outline: "a", - outline_color: "a", - outline_style: "a", - outline_width: "a", - outline_offset: "a", - overflow: "a", - overflow_x: "a", - overflow_y: "a", - overflow_style: "a", - overflow_wrap: "a", - padding: "a", - padding_bottom: "a", - padding_left: "a", - padding_right: "a", - padding_top: "a", - page: "a", - page_break_after: "a", - page_break_before: "a", - page_break_inside: "a", - paint_order: "a", - pause: "a", - pause_after: "a", - pause_before: "a", - perspective: "a", - perspective_origin: "a", - pitch: "a", - pitch_range: "a", - play_during: "a", - pointer_events: "a", - position: "a", - quotes: "a", - region_fragment: "a", - resize: "a", - rest: "a", - rest_after: "a", - rest_before: "a", - richness: "a", - right: "a", - ruby_align: "a", - ruby_merge: "a", - ruby_position: "a", - scroll_behavior: "a", - scroll_snap_coordinate: "a", - scroll_snap_destination: "a", - scroll_snap_points_x: "a", - scroll_snap_points_y: "a", - scroll_snap_type: "a", - shape_image_threshold: "a", - shape_inside: "a", - shape_margin: "a", - shape_outside: "a", - shape_padding: "a", - shape_rendering: "a", - size: "a", - speak: "a", - speak_as: "a", - speak_header: "a", - speak_numeral: "a", - speak_punctuation: "a", - speech_rate: "a", - stop_color: "a", - stop_opacity: "a", - stress: "a", - string_set: "a", - stroke: "a", - stroke_dasharray: "a", - stroke_dashoffset: "a", - stroke_linecap: "a", - stroke_linejoin: "a", - stroke_miterlimit: "a", - stroke_opacity: "a", - stroke_width: "a", - tab_size: "a", - table_layout: "a", - text_align: "a", - text_align_all: "a", - text_align_last: "a", - text_anchor: "a", - text_combine_upright: "a", - text_decoration: "a", - text_decoration_color: "a", - text_decoration_line: "a", - text_decoration_style: "a", - text_decoration_skip: "a", - text_emphasis: "a", - text_emphasis_color: "a", - text_emphasis_style: "a", - text_emphasis_position: "a", - text_emphasis_skip: "a", - text_height: "a", - text_indent: "a", - text_justify: "a", - text_orientation: "a", - text_overflow: "a", - text_rendering: "a", - text_shadow: "a", - text_size_adjust: "a", - text_space_collapse: "a", - text_spacing: "a", - text_transform: "a", - text_underline_position: "a", - text_wrap: "a", - top: "a", - touch_action: "a", - transform: "a", - transform_box: "a", - transform_origin: "a", - transform_style: "a", - transition: "a", - transition_delay: "a", - transition_duration: "a", - transition_property: "a", - unicode_bidi: "a", - vector_effect: "a", - vertical_align: "a", - visibility: "a", - voice_balance: "a", - voice_duration: "a", - voice_family: "a", - voice_pitch: "a", - voice_range: "a", - voice_rate: "a", - voice_stress: "a", - voice_volumn: "a", - volume: "a", - white_space: "a", - widows: "a", - width: "a", - will_change: "a", - word_break: "a", - word_spacing: "a", - word_wrap: "a", - wrap_flow: "a", - wrap_through: "a", - writing_mode: "a", - z_index: "a", - - "This example isn't quite useful yet" - } - } -} From 0a612657e69c7d9cfc16a864b4450a7e13c7c3e9 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 17 Jan 2024 19:53:34 -0800 Subject: [PATCH 4/7] Provide context on desktop again --- packages/desktop/src/escape.rs | 82 --------------------------------- packages/desktop/src/lib.rs | 1 - packages/desktop/src/webview.rs | 26 ++--------- 3 files changed, 5 insertions(+), 104 deletions(-) delete mode 100644 packages/desktop/src/escape.rs diff --git a/packages/desktop/src/escape.rs b/packages/desktop/src/escape.rs deleted file mode 100644 index 8ee564720..000000000 --- a/packages/desktop/src/escape.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::fmt::{self, Write}; - -/// Escape a string to pass it into JavaScript. -/// -/// # Example -/// -/// ```rust,ignore -/// # use web_view::WebView; -/// # use std::mem; -/// # -/// # let mut view: WebView<()> = unsafe { mem::uninitialized() }; -/// # -/// let string = "Hello, world!"; -/// -/// // Calls the function callback with "Hello, world!" as its parameter. -/// -/// view.eval(&format!("callback({});", web_view::escape(string))); -/// ``` -#[allow(unused)] -pub fn escape_js_string(string: &str) -> Escaper { - Escaper(string) -} - -// "All code points may appear literally in a string literal except for the -// closing quote code points, U+005C (REVERSE SOLIDUS), U+000D (CARRIAGE -// RETURN), U+2028 (LINE SEPARATOR), U+2029 (PARAGRAPH SEPARATOR), and U+000A -// (LINE FEED)." - ES6 Specification - -pub struct Escaper<'a>(&'a str); - -const SPECIAL: &[char] = &[ - '\n', // U+000A (LINE FEED) - '\r', // U+000D (CARRIAGE RETURN) - '\'', // U+0027 (APOSTROPHE) - '\\', // U+005C (REVERSE SOLIDUS) - '\u{2028}', // U+2028 (LINE SEPARATOR) - '\u{2029}', // U+2029 (PARAGRAPH SEPARATOR) -]; - -impl<'a> fmt::Display for Escaper<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let &Escaper(mut string) = self; - - f.write_char('\'')?; - - while !string.is_empty() { - if let Some(i) = string.find(SPECIAL) { - if i > 0 { - f.write_str(&string[..i])?; - } - - let mut chars = string[i..].chars(); - - f.write_str(match chars.next().unwrap() { - '\n' => "\\n", - '\r' => "\\r", - '\'' => "\\'", - '\\' => "\\\\", - '\u{2028}' => "\\u2028", - '\u{2029}' => "\\u2029", - _ => unreachable!(), - })?; - - string = chars.as_str(); - } else { - f.write_str(string)?; - break; - } - } - - f.write_char('\'')?; - - Ok(()) - } -} - -#[test] -fn test() { - let plain = "ABC \n\r' abc \\ \u{2028} \u{2029}123"; - let escaped = escape_js_string(plain).to_string(); - assert!(escaped == "'ABC \\n\\r\\' abc \\\\ \\u2028 \\u2029123'"); -} diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index 43e7e006a..d2968dc35 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -9,7 +9,6 @@ mod config; mod desktop_context; mod edits; mod element; -mod escape; mod eval; mod events; mod file_upload; diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index c79a1f6e9..22da1af55 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -8,7 +8,7 @@ use crate::{ waker::tao_waker, Config, DesktopContext, DesktopService, }; -use dioxus_core::VirtualDom; +use dioxus_core::{ScopeId, VirtualDom}; use dioxus_html::prelude::EvalProvider; use futures_util::{pin_mut, FutureExt}; use std::{any::Any, rc::Rc, task::Waker}; @@ -148,27 +148,13 @@ impl WebviewInstance { asset_handlers, )); - // Provide the desktop context to the virtualdom - // dom.base_scope().provide_context(desktop_context.clone()); - - // let query = dom.in_runtime(|| { - // let query = ScopeId::ROOT.provide_context(desktop_context.clone()); - // // Init eval - // init_eval(); - // query - // }); - - // let desktop_ctx = ScopeId::ROOT.consume_context::().unwrap(); - // let provider: Rc = Rc::new(DesktopEvalProvider { desktop_ctx }); - // ScopeId::ROOT.provide_context(provider); - - // Also set up its eval provider - // It's important that we provide as dyn EvalProvider - using the concrete type has - // a different TypeId and can not be downcasted as dyn EvalProvider let provider: Rc = Rc::new(DesktopEvalProvider::new(desktop_context.clone())); - // dom.base_scope().provide_context(provider); + dom.in_runtime(|| { + ScopeId::ROOT.provide_context(desktop_context.clone()); + ScopeId::ROOT.provide_context(provider); + }); WebviewInstance { waker: tao_waker(shared.proxy.clone(), desktop_context.window.id()), @@ -196,8 +182,6 @@ impl WebviewInstance { } } - // self.desktop_context.send_edits(self.dom.render_immediate()); - self.dom .render_immediate(&mut *self.desktop_context.mutation_state.borrow_mut()); self.desktop_context.send_edits(); From 1b65ee8501bc3a2b48dc01efa97c7172aeaa891d Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 18 Jan 2024 03:03:17 -0800 Subject: [PATCH 5/7] Tweak launch cfg --- examples/counter.rs | 6 +++--- examples/optional_props.rs | 4 ++-- packages/dioxus/src/launch.rs | 32 +++++++++++++------------------- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/examples/counter.rs b/examples/counter.rs index 33c611b48..633636bde 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -9,7 +9,7 @@ fn main() { fn app() -> Element { let mut counters = use_signal(|| vec![0, 0, 0]); - let mut sum = use_selector(move || counters.read().iter().copied().sum::()); + let sum = use_selector(move || counters.read().iter().copied().sum::()); rsx! { div { @@ -29,14 +29,14 @@ fn app() -> Element { } #[component] -fn Child(i: usize, counters: Signal>) -> Element { +fn Child(i: usize, counters: Signal>) -> Element { rsx! { li { button { onclick: move |_| counters.write()[i] -= 1, "-1" } input { value: "{counters.read()[i]}", oninput: move |e| { - if let Ok(value) = e.value().parse::() { + if let Ok(value) = e.value().parse::() { counters.write()[i] = value; } } diff --git a/examples/optional_props.rs b/examples/optional_props.rs index ad7edfdb3..391570e25 100644 --- a/examples/optional_props.rs +++ b/examples/optional_props.rs @@ -33,8 +33,6 @@ fn app() -> Element { } } -type SthElse = Option; - #[derive(Props, PartialEq, Clone)] struct ButtonProps { a: String, @@ -51,6 +49,8 @@ struct ButtonProps { e: SthElse, } +type SthElse = Option; + fn Button(props: ButtonProps) -> Element { rsx! { button { diff --git a/packages/dioxus/src/launch.rs b/packages/dioxus/src/launch.rs index 212a1f0fb..5003117e8 100644 --- a/packages/dioxus/src/launch.rs +++ b/packages/dioxus/src/launch.rs @@ -67,19 +67,7 @@ impl LaunchBuilder { self } - #[allow(clippy::unit_arg)] - /// Launch the app. - pub fn launch(self) { - current_platform::launch( - self.cross_platform_config, - Default::default(), - self.platform_config.unwrap_or_default(), - ); - } -} - -#[cfg(feature = "web")] -impl LaunchBuilder { + #[cfg(feature = "web")] /// Launch your web application. pub fn launch_web(self) { dioxus_web::launch::launch( @@ -88,11 +76,9 @@ impl LaunchBuilder { Default::default(), ); } -} -#[cfg(feature = "desktop")] -impl LaunchBuilder { /// Launch your desktop application. + #[cfg(feature = "desktop")] pub fn launch_desktop(self) { dioxus_desktop::launch::launch( self.cross_platform_config, @@ -100,11 +86,9 @@ impl LaunchBuilder { Default::default(), ); } -} -#[cfg(feature = "fullstack")] -impl LaunchBuilder { /// Launch your fullstack application. + #[cfg(feature = "fullstack")] pub fn launch_fullstack(self) { dioxus_fullstack::launch::launch( self.cross_platform_config, @@ -112,6 +96,16 @@ impl LaunchBuilder { Default::default(), ); } + + #[allow(clippy::unit_arg)] + /// Launch the app. + pub fn launch(self) { + current_platform::launch( + self.cross_platform_config, + Default::default(), + self.platform_config.unwrap_or_default(), + ); + } } mod current_platform { From 94b17cc8ca99d680e176c87402a6aa3fcb7d06b0 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 18 Jan 2024 04:07:28 -0800 Subject: [PATCH 6/7] Fix non tokio builds for desktop --- Cargo.lock | 99 +-------- packages/desktop/Cargo.toml | 4 +- packages/desktop/headless_tests/rendering.rs | 46 ++-- packages/desktop/src/app.rs | 2 +- packages/desktop/src/desktop_context.rs | 124 +---------- packages/desktop/src/event_handlers.rs | 65 ++++++ packages/desktop/src/hooks.rs | 61 +++-- packages/desktop/src/launch.rs | 2 +- packages/desktop/src/lib.rs | 10 +- packages/desktop/src/query.rs | 35 ++- packages/desktop/src/shortcut.rs | 220 ++++++++----------- packages/hooks/src/lib.rs | 6 - packages/hooks/src/use_on_destroy.rs | 10 + packages/interpreter/Cargo.toml | 19 +- packages/signals/Cargo.toml | 8 +- 15 files changed, 274 insertions(+), 437 deletions(-) create mode 100644 packages/desktop/src/event_handlers.rs diff --git a/Cargo.lock b/Cargo.lock index 43962c635..ea347b119 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2033,23 +2033,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cssparser" -version = "0.29.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 1.0.10", - "matches", - "phf 0.10.1", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - [[package]] name = "cssparser" version = "0.33.0" @@ -2533,6 +2516,7 @@ dependencies = [ "dioxus-hot-reload", "dioxus-html", "dioxus-interpreter-js", + "dioxus-signals", "dunce", "exitcode", "futures-channel", @@ -2545,7 +2529,6 @@ dependencies = [ "objc_id", "rfd", "rustc-hash", - "scraper", "serde", "serde_json", "slab", @@ -3115,12 +3098,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ego-tree" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" - [[package]] name = "either" version = "1.9.0" @@ -3910,15 +3887,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - [[package]] name = "getrandom" version = "0.1.16" @@ -5615,7 +5583,7 @@ dependencies = [ "html5ever", "indexmap 1.9.3", "matches", - "selectors 0.22.0", + "selectors", ] [[package]] @@ -7249,9 +7217,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_macros 0.10.0", "phf_shared 0.10.0", - "proc-macro-hack", ] [[package]] @@ -7328,20 +7294,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "phf_macros" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "phf_macros" version = "0.11.2" @@ -8843,23 +8795,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scraper" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e25654b5e9fd557a67dbaab5a5d36b8c448d0561beb4c041b6dbb902eddfa6" -dependencies = [ - "ahash 0.8.7", - "cssparser 0.29.6", - "ego-tree", - "getopts", - "html5ever", - "once_cell", - "selectors 0.24.0", - "smallvec", - "tendril", -] - [[package]] name = "sct" version = "0.7.1" @@ -8928,29 +8863,11 @@ dependencies = [ "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", - "servo_arc 0.1.1", + "servo_arc", "smallvec", "thin-slice", ] -[[package]] -name = "selectors" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" -dependencies = [ - "bitflags 1.3.2", - "cssparser 0.29.6", - "derive_more", - "fxhash", - "log", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc 0.2.0", - "smallvec", -] - [[package]] name = "semver" version = "1.0.21" @@ -9201,16 +9118,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "servo_arc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - [[package]] name = "sha-1" version = "0.10.1" diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 3cf7d3690..2acf5fc15 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -30,7 +30,7 @@ wry = { version = "0.35.0", default-features = false, features = [ "protocol", "file-drop", ] } -futures-channel = { workspace = true } +futures-channel.workspace = true tokio = { workspace = true, features = [ "sync", "rt-multi-thread", @@ -81,8 +81,8 @@ features = ["tokio_runtime", "hot-reload"] [dev-dependencies] dioxus-core-macro = { workspace = true } dioxus-hooks = { workspace = true } +dioxus-signals = { workspace = true } exitcode = "1.1.2" -scraper = "0.16.0" [build-dependencies] dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } diff --git a/packages/desktop/headless_tests/rendering.rs b/packages/desktop/headless_tests/rendering.rs index 041b0ed05..d8e5cfd8f 100644 --- a/packages/desktop/headless_tests/rendering.rs +++ b/packages/desktop/headless_tests/rendering.rs @@ -1,6 +1,11 @@ use dioxus::prelude::*; +use dioxus_core::Element; use dioxus_desktop::DesktopContext; +fn main() { + check_app_exits(check_html_renders); +} + pub(crate) fn check_app_exits(app: Component) { use dioxus_desktop::Config; use tao::window::WindowBuilder; @@ -22,28 +27,20 @@ pub(crate) fn check_app_exits(app: Component) { should_panic.store(false, std::sync::atomic::Ordering::SeqCst); } -fn main() { - check_app_exits(check_html_renders); -} - fn use_inner_html(d: &'static str) -> Option { - let eval_provider = use_eval(cx); - let value: Signal> = use_signal(|| None); - use_effect((), |_| { - to_owned![value, eval_provider]; - async move { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - let html = eval_provider(&format!( - r#"let element = document.getElementById('{}'); + use_effect(|| async move { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + window().eval(); + let html = eval_provider(&format!( + r#"let element = document.getElementById('{}'); return element.innerHTML"#, - id - )) - .unwrap(); - if let Ok(serde_json::Value::String(html)) = html.await { - println!("html: {}", html); - value.set(Some(html)); - } + id + )) + .unwrap(); + if let Ok(serde_json::Value::String(html)) = html.await { + println!("html: {}", html); + value.set(Some(html)); } }); value.read().clone() @@ -58,10 +55,13 @@ fn check_html_renders() -> Element { if let Some(raw_html) = inner_html { println!("{}", raw_html); - let fragment = scraper::Html::parse_fragment(&raw_html); - println!("fragment: {}", fragment.html()); - let expected = scraper::Html::parse_fragment(EXPECTED_HTML); - println!("expected: {}", expected.html()); + let fragment = &raw_html; + let expected = EXPECTED_HTML; + // let fragment = scraper::Html::parse_fragment(&raw_html); + // println!("fragment: {}", fragment.html()); + // let expected = scraper::Html::parse_fragment(EXPECTED_HTML); + // println!("expected: {}", expected.html()); + assert_eq!(raw_html, EXPECTED_HTML); if fragment == expected { println!("html matches"); desktop_context.close(); diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index cb43a97e3..15f989e3b 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -1,7 +1,7 @@ use crate::{ config::{Config, WindowCloseBehaviour}, - desktop_context::WindowEventHandlers, element::DesktopElement, + event_handlers::WindowEventHandlers, file_upload::FileDialogRequest, ipc::IpcMessage, ipc::{EventData, UserWindowEvent}, diff --git a/packages/desktop/src/desktop_context.rs b/packages/desktop/src/desktop_context.rs index 1c2cd84a1..867516a64 100644 --- a/packages/desktop/src/desktop_context.rs +++ b/packages/desktop/src/desktop_context.rs @@ -4,17 +4,16 @@ use crate::{ edits::EditQueue, ipc::{EventData, UserWindowEvent}, query::QueryEngine, - shortcut::{HotKey, ShortcutId, ShortcutRegistryError}, + shortcut::{HotKey, ShortcutHandle, ShortcutRegistryError}, webview::WebviewInstance, - AssetRequest, Config, + AssetRequest, Config, WryEventHandler, }; use dioxus_core::{ prelude::{current_scope_id, ScopeId}, - use_hook, VirtualDom, + VirtualDom, }; use dioxus_interpreter_js::MutationState; -use slab::Slab; -use std::{cell::RefCell, fmt::Debug, rc::Rc, rc::Weak}; +use std::{cell::RefCell, rc::Rc, rc::Weak}; use tao::{ event::Event, event_loop::EventLoopWindowTarget, @@ -208,12 +207,12 @@ impl DesktopService { pub fn create_wry_event_handler( &self, handler: impl FnMut(&Event, &EventLoopWindowTarget) + 'static, - ) -> WryEventHandlerId { + ) -> WryEventHandler { self.shared.event_handlers.add(self.window.id(), handler) } /// Remove a wry event handler created with [`DesktopContext::create_wry_event_handler`] - pub fn remove_wry_event_handler(&self, id: WryEventHandlerId) { + pub fn remove_wry_event_handler(&self, id: WryEventHandler) { self.shared.event_handlers.remove(id) } @@ -224,14 +223,14 @@ impl DesktopService { &self, hotkey: HotKey, callback: impl FnMut() + 'static, - ) -> Result { + ) -> Result { self.shared .shortcut_manager .add_shortcut(hotkey, Box::new(callback)) } /// Remove a global shortcut - pub fn remove_shortcut(&self, id: ShortcutId) { + pub fn remove_shortcut(&self, id: ShortcutHandle) { self.shared.shortcut_manager.remove_shortcut(id) } @@ -250,12 +249,12 @@ impl DesktopService { pub fn register_asset_handler( &self, name: String, - f: Box, + handler: Box, scope: Option, ) { self.asset_handlers.register_handler( name, - f, + handler, scope.unwrap_or(current_scope_id().unwrap_or(ScopeId(0))), ) } @@ -313,106 +312,3 @@ fn is_main_thread() -> bool { let result: BOOL = unsafe { msg_send![cls, isMainThread] }; result != NO } - -/// The unique identifier of a window event handler. This can be used to later remove the handler. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct WryEventHandlerId(usize); - -#[derive(Clone, Default)] -pub(crate) struct WindowEventHandlers { - handlers: Rc>>, -} - -impl WindowEventHandlers { - pub(crate) fn add( - &self, - window_id: WindowId, - handler: impl FnMut(&Event, &EventLoopWindowTarget) + 'static, - ) -> WryEventHandlerId { - WryEventHandlerId( - self.handlers - .borrow_mut() - .insert(WryWindowEventHandlerInner { - window_id, - handler: Box::new(handler), - }), - ) - } - - pub(crate) fn remove(&self, id: WryEventHandlerId) { - self.handlers.borrow_mut().try_remove(id.0); - } - - pub(crate) fn apply_event( - &self, - event: &Event, - target: &EventLoopWindowTarget, - ) { - for (_, handler) in self.handlers.borrow_mut().iter_mut() { - handler.apply_event(event, target); - } - } -} - -struct WryWindowEventHandlerInner { - window_id: WindowId, - handler: WryEventHandlerCallback, -} - -type WryEventHandlerCallback = - Box, &EventLoopWindowTarget) + 'static>; - -impl WryWindowEventHandlerInner { - fn apply_event( - &mut self, - event: &Event, - target: &EventLoopWindowTarget, - ) { - // if this event does not apply to the window this listener cares about, return - if let Event::WindowEvent { window_id, .. } = event { - if *window_id != self.window_id { - return; - } - } - (self.handler)(event, target) - } -} - -/// Get a closure that executes any JavaScript in the WebView context. -pub fn use_wry_event_handler( - handler: impl FnMut(&Event, &EventLoopWindowTarget) + 'static, -) -> WryEventHandler { - use_hook(move || { - let desktop = window(); - - let id = desktop.create_wry_event_handler(handler); - - WryEventHandler { - handlers: desktop.shared.event_handlers.clone(), - id, - } - }) -} - -/// A wry event handler that is scoped to the current component and window. The event handler will only receive events for the window it was created for and global events. -/// -/// This will automatically be removed when the component is unmounted. -#[derive(Clone)] -pub struct WryEventHandler { - pub(crate) handlers: WindowEventHandlers, - /// The unique identifier of the event handler. - pub id: WryEventHandlerId, -} - -impl WryEventHandler { - /// Remove the event handler. - pub fn remove(&self) { - self.handlers.remove(self.id); - } -} - -impl Drop for WryEventHandler { - fn drop(&mut self) { - self.handlers.remove(self.id); - } -} diff --git a/packages/desktop/src/event_handlers.rs b/packages/desktop/src/event_handlers.rs new file mode 100644 index 000000000..cd47f01d8 --- /dev/null +++ b/packages/desktop/src/event_handlers.rs @@ -0,0 +1,65 @@ +use crate::{ipc::UserWindowEvent, window}; +use slab::Slab; +use std::cell::RefCell; +use tao::{event::Event, event_loop::EventLoopWindowTarget, window::WindowId}; + +/// The unique identifier of a window event handler. This can be used to later remove the handler. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct WryEventHandler(pub(crate) usize); + +impl WryEventHandler { + /// Unregister this event handler from the window + pub fn remove(&self) { + window().shared.event_handlers.remove(*self) + } +} + +#[derive(Default)] +pub struct WindowEventHandlers { + handlers: RefCell>, +} + +struct WryWindowEventHandlerInner { + window_id: WindowId, + + #[allow(clippy::type_complexity)] + handler: + Box, &EventLoopWindowTarget) + 'static>, +} + +impl WindowEventHandlers { + pub(crate) fn add( + &self, + window_id: WindowId, + handler: impl FnMut(&Event, &EventLoopWindowTarget) + 'static, + ) -> WryEventHandler { + WryEventHandler( + self.handlers + .borrow_mut() + .insert(WryWindowEventHandlerInner { + window_id, + handler: Box::new(handler), + }), + ) + } + + pub(crate) fn remove(&self, id: WryEventHandler) { + self.handlers.borrow_mut().try_remove(id.0); + } + + pub fn apply_event( + &self, + event: &Event, + target: &EventLoopWindowTarget, + ) { + for (_, handler) in self.handlers.borrow_mut().iter_mut() { + // if this event does not apply to the window this listener cares about, return + if let Event::WindowEvent { window_id, .. } = event { + if *window_id != handler.window_id { + return; + } + } + (handler.handler)(event, target) + } + } +} diff --git a/packages/desktop/src/hooks.rs b/packages/desktop/src/hooks.rs index 60bf4c736..a6de4a203 100644 --- a/packages/desktop/src/hooks.rs +++ b/packages/desktop/src/hooks.rs @@ -8,29 +8,23 @@ use dioxus_core::{ prelude::{consume_context, current_scope_id}, use_hook, }; -use dioxus_hooks::use_on_drop; +use dioxus_hooks::use_hook_with_cleanup; use tao::{event::Event, event_loop::EventLoopWindowTarget}; use wry::RequestAsyncResponder; /// Get an imperative handle to the current window pub fn use_window() -> DesktopContext { - use_hook(|| consume_context::()) + use_hook(consume_context::) } /// Get a closure that executes any JavaScript in the WebView context. pub fn use_wry_event_handler( handler: impl FnMut(&Event, &EventLoopWindowTarget) + 'static, ) -> WryEventHandler { - use_hook(move || { - let desktop = window(); - - let id = desktop.create_wry_event_handler(handler); - - WryEventHandler { - handlers: desktop.shared.event_handlers.clone(), - id, - } - }) + use_hook_with_cleanup( + move || window().create_wry_event_handler(handler), + move |handler| handler.remove(), + ) } /// Provide a callback to handle asset loading yourself. @@ -41,19 +35,20 @@ pub fn use_asset_handler( name: &str, handler: impl Fn(AssetRequest, RequestAsyncResponder) + 'static, ) { - let name = use_hook(|| { - crate::window().asset_handlers.register_handler( - name.to_string(), - Box::new(handler), - current_scope_id().unwrap(), - ); + use_hook_with_cleanup( + || { + crate::window().asset_handlers.register_handler( + name.to_string(), + Box::new(handler), + current_scope_id().unwrap(), + ); - Rc::new(name.to_string()) - }); - - use_on_drop(move || { - _ = crate::window().asset_handlers.remove_handler(name.as_ref()); - }); + Rc::new(name.to_string()) + }, + move |name| { + _ = crate::window().asset_handlers.remove_handler(name.as_ref()); + }, + ); } /// Get a closure that executes any JavaScript in the WebView context. @@ -61,14 +56,12 @@ pub fn use_global_shortcut( accelerator: impl IntoAccelerator, handler: impl FnMut() + 'static, ) -> Result { - use_hook(move || { - let desktop = window(); - - let id = desktop.create_shortcut(accelerator.accelerator(), handler); - - Ok(ShortcutHandle { - desktop, - shortcut_id: id?, - }) - }) + use_hook_with_cleanup( + move || window().create_shortcut(accelerator.accelerator(), handler), + |handle| { + if let Ok(handle) = handle { + handle.remove(); + } + }, + ) } diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index d32070140..11cdd4c15 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -70,5 +70,5 @@ pub fn launch( })); #[cfg(not(feature = "tokio"))] - launch_with_props_blocking(config, platform_config) + launch_with_props_blocking(virtual_dom, platform_config) } diff --git a/packages/desktop/src/lib.rs b/packages/desktop/src/lib.rs index d2968dc35..6305b2729 100644 --- a/packages/desktop/src/lib.rs +++ b/packages/desktop/src/lib.rs @@ -10,6 +10,7 @@ mod desktop_context; mod edits; mod element; mod eval; +mod event_handlers; mod events; mod file_upload; mod hooks; @@ -38,11 +39,8 @@ pub use wry; // Public exports pub use assets::AssetRequest; pub use config::{Config, WindowCloseBehaviour}; -pub use desktop_context::{ - window, DesktopContext, DesktopService, WryEventHandler, WryEventHandlerId, -}; +pub use desktop_context::{window, DesktopContext, DesktopService}; +pub use event_handlers::WryEventHandler; pub use hooks::{use_asset_handler, use_global_shortcut, use_window, use_wry_event_handler}; -pub use shortcut::{ShortcutHandle, ShortcutId, ShortcutRegistryError}; +pub use shortcut::{ShortcutHandle, ShortcutRegistryError}; pub use wry::RequestAsyncResponder; - -pub use hooks::*; diff --git a/packages/desktop/src/query.rs b/packages/desktop/src/query.rs index d38478c61..9470c972a 100644 --- a/packages/desktop/src/query.rs +++ b/packages/desktop/src/query.rs @@ -1,11 +1,11 @@ use std::{cell::RefCell, rc::Rc}; use crate::DesktopContext; +use futures_util::StreamExt; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; use slab::Slab; use thiserror::Error; -use tokio::sync::broadcast::error::RecvError; const DIOXUS_CODE: &str = r#" let dioxus = { @@ -64,8 +64,8 @@ impl Default for SharedSlab { } struct QueryEntry { - channel_sender: tokio::sync::mpsc::UnboundedSender, - return_sender: Option>, + channel_sender: futures_channel::mpsc::UnboundedSender, + return_sender: Option>, } const QUEUE_NAME: &str = "__msg_queues"; @@ -83,8 +83,8 @@ impl QueryEngine { script: &str, context: DesktopContext, ) -> Query { - let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - let (return_tx, return_rx) = tokio::sync::oneshot::channel(); + let (tx, rx) = futures_channel::mpsc::unbounded(); + let (return_tx, return_rx) = futures_channel::oneshot::channel(); let request_id = self.active_requests.slab.borrow_mut().insert(QueryEntry { channel_sender: tx, return_sender: Some(return_tx), @@ -99,14 +99,14 @@ impl QueryEngine { if (!window.{QUEUE_NAME}) {{ window.{QUEUE_NAME} = []; }} - + let _request_id = {request_id}; - + if (!window.{QUEUE_NAME}[{request_id}]) {{ window.{QUEUE_NAME}[{request_id}] = []; }} let _message_queue = window.{QUEUE_NAME}[{request_id}]; - + {script} }})().then((result)=>{{ let returned_value = {{ @@ -150,7 +150,7 @@ impl QueryEngine { let _ = sender.send(data); } } else { - let _ = entry.channel_sender.send(data); + let _ = entry.channel_sender.unbounded_send(data); } } } @@ -159,8 +159,8 @@ impl QueryEngine { pub(crate) struct Query { desktop: DesktopContext, slab: SharedSlab, - receiver: tokio::sync::mpsc::UnboundedReceiver, - return_receiver: Option>, + receiver: futures_channel::mpsc::UnboundedReceiver, + return_receiver: Option>, id: usize, phantom: std::marker::PhantomData, } @@ -200,18 +200,13 @@ impl Query { /// Receive a message from the query pub async fn recv(&mut self) -> Result { - self.receiver - .recv() - .await - .ok_or(QueryError::Recv(RecvError::Closed)) + self.receiver.next().await.ok_or(QueryError::Recv) } /// Receive the result of the query pub async fn result(&mut self) -> Result { match self.return_receiver.take() { - Some(receiver) => receiver - .await - .map_err(|_| QueryError::Recv(RecvError::Closed)), + Some(receiver) => receiver.await.map_err(|_| QueryError::Recv), None => Err(QueryError::Finished), } } @@ -238,8 +233,8 @@ impl Drop for Query { #[derive(Error, Debug)] pub enum QueryError { - #[error("Error receiving query result: {0}")] - Recv(RecvError), + #[error("Error receiving query result.")] + Recv, #[error("Error sending message to query: {0}")] Send(String), #[error("Error deserializing query result: {0}")] diff --git a/packages/desktop/src/shortcut.rs b/packages/desktop/src/shortcut.rs index 1fee28e34..b647d1337 100644 --- a/packages/desktop/src/shortcut.rs +++ b/packages/desktop/src/shortcut.rs @@ -1,11 +1,3 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr}; - -use dioxus_html::input_data::keyboard_types::Modifiers; -use slab::Slab; -use tao::keyboard::ModifiersState; - -use crate::desktop_context::DesktopContext; - #[cfg(any( target_os = "windows", target_os = "macos", @@ -23,101 +15,23 @@ pub use global_hotkey::{ #[cfg(any(target_os = "ios", target_os = "android"))] pub use crate::mobile_shortcut::*; -pub(crate) struct ShortcutRegistry { - manager: GlobalHotKeyManager, - shortcuts: RefCell>, +use crate::window; +use dioxus_html::input_data::keyboard_types::Modifiers; +use slab::Slab; +use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr}; +use tao::keyboard::ModifiersState; + +/// An global id for a shortcut. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ShortcutHandle { + id: u32, + number: usize, } -struct Shortcut { - #[allow(unused)] - shortcut: HotKey, - callbacks: Slab>, -} - -impl Shortcut { - fn insert(&mut self, callback: Box) -> usize { - self.callbacks.insert(callback) - } - - fn remove(&mut self, id: usize) { - let _ = self.callbacks.remove(id); - } - - fn is_empty(&self) -> bool { - self.callbacks.is_empty() - } -} - -impl ShortcutRegistry { - pub fn new() -> Self { - Self { - manager: GlobalHotKeyManager::new().unwrap(), - shortcuts: RefCell::new(HashMap::new()), - } - } - - pub(crate) fn call_handlers(&self, id: GlobalHotKeyEvent) { - if let Some(Shortcut { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) { - for (_, callback) in callbacks.iter_mut() { - (callback)(); - } - } - } - - pub(crate) fn add_shortcut( - &self, - hotkey: HotKey, - callback: Box, - ) -> Result { - let accelerator_id = hotkey.clone().id(); - - let mut shortcuts = self.shortcuts.borrow_mut(); - - if let Some(callbacks) = shortcuts.get_mut(&accelerator_id) { - return Ok(ShortcutId { - id: accelerator_id, - number: callbacks.insert(callback), - }); - }; - - self.manager.register(hotkey).map_err(|e| match e { - HotkeyError::HotKeyParseError(shortcut) => { - ShortcutRegistryError::InvalidShortcut(shortcut) - } - err => ShortcutRegistryError::Other(Rc::new(err)), - })?; - - let mut shortcut = Shortcut { - shortcut: hotkey, - callbacks: Slab::new(), - }; - - let id = shortcut.callbacks.insert(callback); - - shortcuts.insert(accelerator_id, shortcut); - - Ok(ShortcutId { - id: accelerator_id, - number: id, - }) - } - - pub(crate) fn remove_shortcut(&self, id: ShortcutId) { - let mut shortcuts = self.shortcuts.borrow_mut(); - if let Some(callbacks) = shortcuts.get_mut(&id.id) { - callbacks.remove(id.number); - if callbacks.is_empty() { - if let Some(_shortcut) = shortcuts.remove(&id.id) { - let _ = self.manager.unregister(_shortcut.shortcut); - } - } - } - } - - pub(crate) fn remove_all(&self) { - let mut shortcuts = self.shortcuts.borrow_mut(); - let hotkeys: Vec<_> = shortcuts.drain().map(|(_, v)| v.shortcut).collect(); - let _ = self.manager.unregister_all(&hotkeys); +impl ShortcutHandle { + /// Remove the shortcut. + pub fn remove(&self) { + window().remove_shortcut(*self); } } @@ -131,19 +45,88 @@ pub enum ShortcutRegistryError { Other(Rc), } -/// An global id for a shortcut. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ShortcutId { - id: u32, - number: usize, +pub(crate) struct ShortcutRegistry { + manager: GlobalHotKeyManager, + shortcuts: RefCell>, } -/// A global shortcut. This will be automatically removed when it is dropped. -#[derive(Clone)] -pub struct ShortcutHandle { - pub(crate) desktop: DesktopContext, - /// The id of the shortcut - pub shortcut_id: ShortcutId, +struct ShortcutInner { + #[allow(unused)] + shortcut: HotKey, + callbacks: Slab>, +} + +impl ShortcutRegistry { + pub fn new() -> Self { + Self { + manager: GlobalHotKeyManager::new().unwrap(), + shortcuts: RefCell::new(HashMap::new()), + } + } + + pub(crate) fn call_handlers(&self, id: GlobalHotKeyEvent) { + if let Some(ShortcutInner { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) { + for (_, callback) in callbacks.iter_mut() { + (callback)(); + } + } + } + + pub(crate) fn add_shortcut( + &self, + hotkey: HotKey, + callback: Box, + ) -> Result { + let accelerator_id = hotkey.clone().id(); + + let mut shortcuts = self.shortcuts.borrow_mut(); + + if let Some(callbacks) = shortcuts.get_mut(&accelerator_id) { + return Ok(ShortcutHandle { + id: accelerator_id, + number: callbacks.callbacks.insert(callback), + }); + }; + + self.manager.register(hotkey).map_err(|e| match e { + HotkeyError::HotKeyParseError(shortcut) => { + ShortcutRegistryError::InvalidShortcut(shortcut) + } + err => ShortcutRegistryError::Other(Rc::new(err)), + })?; + + let mut shortcut = ShortcutInner { + shortcut: hotkey, + callbacks: Slab::new(), + }; + + let id = shortcut.callbacks.insert(callback); + + shortcuts.insert(accelerator_id, shortcut); + + Ok(ShortcutHandle { + id: accelerator_id, + number: id, + }) + } + + pub(crate) fn remove_shortcut(&self, id: ShortcutHandle) { + let mut shortcuts = self.shortcuts.borrow_mut(); + if let Some(callbacks) = shortcuts.get_mut(&id.id) { + let _ = callbacks.callbacks.remove(id.number); + if callbacks.callbacks.is_empty() { + if let Some(_shortcut) = shortcuts.remove(&id.id) { + let _ = self.manager.unregister(_shortcut.shortcut); + } + } + } + } + + pub(crate) fn remove_all(&self) { + let mut shortcuts = self.shortcuts.borrow_mut(); + let hotkeys: Vec<_> = shortcuts.drain().map(|(_, v)| v.shortcut).collect(); + let _ = self.manager.unregister_all(&hotkeys); + } } pub trait IntoAccelerator { @@ -174,19 +157,6 @@ impl IntoAccelerator for &str { } } -impl ShortcutHandle { - /// Remove the shortcut. - pub fn remove(&self) { - self.desktop.remove_shortcut(self.shortcut_id); - } -} - -impl Drop for ShortcutHandle { - fn drop(&mut self) { - self.remove() - } -} - pub trait IntoModifersState { fn into_modifiers_state(self) -> Modifiers; } diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index f8f36ce24..4f5a8c35b 100644 --- a/packages/hooks/src/lib.rs +++ b/packages/hooks/src/lib.rs @@ -67,12 +67,6 @@ pub use use_coroutine::*; mod use_future; pub use use_future::*; -// mod use_effect; -// pub use use_effect::*; - -// mod use_memo; -// pub use use_memo::*; - // mod use_on_create; // pub use use_on_create::*; diff --git a/packages/hooks/src/use_on_destroy.rs b/packages/hooks/src/use_on_destroy.rs index 4ce5d5a0e..26c85b823 100644 --- a/packages/hooks/src/use_on_destroy.rs +++ b/packages/hooks/src/use_on_destroy.rs @@ -100,3 +100,13 @@ pub fn use_on_destroy(destroy: D) { pub fn use_on_drop(ondrop: D) { use_on_destroy(ondrop); } + +pub fn use_hook_with_cleanup( + hook: impl FnOnce() -> T, + cleanup: impl FnOnce(T) + 'static, +) -> T { + let value = use_hook(|| hook()); + let _value = value.clone(); + use_on_destroy(move || cleanup(_value)); + value +} diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml index aa285d49c..b2520a96a 100644 --- a/packages/interpreter/Cargo.toml +++ b/packages/interpreter/Cargo.toml @@ -13,18 +13,27 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] [dependencies] wasm-bindgen = { workspace = true, optional = true } js-sys = { version = "0.3.56", optional = true } -web-sys = { version = "0.3.56", optional = true, features = ["Element", "Node"] } +web-sys = { version = "0.3.56", optional = true, features = [ + "Element", + "Node", +] } sledgehammer_bindgen = { version = "0.3.1", default-features = false, optional = true } sledgehammer_utils = { version = "0.2", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } -dioxus-core = { workspace = true , optional = true } -dioxus-html = { workspace = true , optional = true } +dioxus-core = { workspace = true, optional = true } +dioxus-html = { workspace = true, optional = true } [features] default = [] serialize = ["serde"] sledgehammer = ["sledgehammer_bindgen", "sledgehammer_utils"] -web = ["sledgehammer", "wasm-bindgen", "js-sys", "web-sys", "sledgehammer_bindgen/web"] -binary-protocol = ["sledgehammer", "wasm-bindgen", "dioxus-core", "dioxus-html"] +web = [ + "sledgehammer", + "wasm-bindgen", + "js-sys", + "web-sys", + "sledgehammer_bindgen/web", +] +binary-protocol = ["sledgehammer", "dioxus-core", "dioxus-html"] minimal_bindings = [] diff --git a/packages/signals/Cargo.toml b/packages/signals/Cargo.toml index f48120ca2..089e89bec 100644 --- a/packages/signals/Cargo.toml +++ b/packages/signals/Cargo.toml @@ -16,19 +16,19 @@ rust-version = "1.60.0" dioxus-core = { workspace = true } generational-box = { workspace = true } tracing = { workspace = true } -simple_logger = "4.2.0" serde = { version = "1", features = ["derive"], optional = true } parking_lot = "0.12.1" once_cell = "1.18.0" -rustc-hash.workspace = true -futures-channel.workspace = true -futures-util.workspace = true +rustc-hash = { workspace = true } +futures-channel = { workspace = true } +futures-util = { workspace = true } [dev-dependencies] dioxus = { workspace = true } dioxus-desktop = { workspace = true } tokio = { version = "1", features = ["full"] } tracing-subscriber = "0.3.17" +simple_logger = "4.2.0" [features] default = [] From 2ed763e6607e43faa3c76f042470b3d85a213063 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Thu, 18 Jan 2024 04:19:17 -0800 Subject: [PATCH 7/7] Use lockless in editqueue --- packages/desktop/src/edits.rs | 145 ++-------------------------------- 1 file changed, 8 insertions(+), 137 deletions(-) diff --git a/packages/desktop/src/edits.rs b/packages/desktop/src/edits.rs index ac4d7d7f7..8e5f5ec16 100644 --- a/packages/desktop/src/edits.rs +++ b/packages/desktop/src/edits.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, sync::Mutex}; +use std::{cell::RefCell, rc::Rc, sync::Arc, sync::Mutex}; use std::fmt::{Debug, Formatter}; @@ -6,8 +6,8 @@ use std::fmt::{Debug, Formatter}; /// It will hold onto the requests until the interpreter is ready to handle them and hold onto any pending edits until a new request is made. #[derive(Default, Clone)] pub(crate) struct EditQueue { - queue: Arc>>>, - responder: Arc>>, + queue: Rc>>>, + responder: Rc>>, } impl Debug for EditQueue { @@ -15,7 +15,7 @@ impl Debug for EditQueue { f.debug_struct("EditQueue") .field("queue", &self.queue) .field("responder", { - &self.responder.lock().unwrap().as_ref().map(|_| ()) + &self.responder.borrow().as_ref().map(|_| ()) }) .finish() } @@ -23,149 +23,20 @@ impl Debug for EditQueue { impl EditQueue { pub fn handle_request(&self, responder: wry::RequestAsyncResponder) { - let mut queue = self.queue.lock().unwrap(); + let mut queue = self.queue.borrow_mut(); if let Some(bytes) = queue.pop() { responder.respond(wry::http::Response::new(bytes)); } else { - *self.responder.lock().unwrap() = Some(responder); + *self.responder.borrow_mut() = Some(responder); } } pub fn add_edits(&self, edits: Vec) { - let mut responder = self.responder.lock().unwrap(); + let mut responder = self.responder.borrow_mut(); if let Some(responder) = responder.take() { responder.respond(wry::http::Response::new(edits)); } else { - self.queue.lock().unwrap().push(edits); + self.queue.borrow_mut().push(edits); } } } - -// pub(crate) fn apply_edits( -// mutations: Mutations, -// channel: &mut Channel, -// templates: &mut FxHashMap, -// max_template_count: &AtomicU16, -// ) -> Option> { -// if mutations.templates.is_empty() && mutations.edits.is_empty() { -// return None; -// } - -// for template in mutations.templates { -// add_template(&template, channel, templates, max_template_count); -// } - -// use dioxus_core::Mutation::*; -// for edit in mutations.edits { -// match edit { -// AppendChildren { id, m } => channel.append_children(id.0 as u32, m as u16), -// AssignId { path, id } => channel.assign_id(path, id.0 as u32), -// CreatePlaceholder { id } => channel.create_placeholder(id.0 as u32), -// CreateTextNode { value, id } => channel.create_text_node(value, id.0 as u32), -// HydrateText { path, value, id } => channel.hydrate_text(path, value, id.0 as u32), -// LoadTemplate { name, index, id } => { -// if let Some(tmpl_id) = templates.get(name) { -// channel.load_template(*tmpl_id, index as u16, id.0 as u32) -// } -// } -// ReplaceWith { id, m } => channel.replace_with(id.0 as u32, m as u16), -// ReplacePlaceholder { path, m } => channel.replace_placeholder(path, m as u16), -// InsertAfter { id, m } => channel.insert_after(id.0 as u32, m as u16), -// InsertBefore { id, m } => channel.insert_before(id.0 as u32, m as u16), -// SetAttribute { -// name, -// value, -// id, -// ns, -// } => match value { -// AttributeValue::Text(txt) => { -// channel.set_attribute(id.0 as u32, name, txt, ns.unwrap_or_default()) -// } -// AttributeValue::Float(f) => { -// channel.set_attribute(id.0 as u32, name, &f.to_string(), ns.unwrap_or_default()) -// } -// AttributeValue::Int(n) => { -// channel.set_attribute(id.0 as u32, name, &n.to_string(), ns.unwrap_or_default()) -// } -// AttributeValue::Bool(b) => channel.set_attribute( -// id.0 as u32, -// name, -// if b { "true" } else { "false" }, -// ns.unwrap_or_default(), -// ), -// AttributeValue::None => { -// channel.remove_attribute(id.0 as u32, name, ns.unwrap_or_default()) -// } -// _ => unreachable!(), -// }, -// SetText { value, id } => channel.set_text(id.0 as u32, value), -// NewEventListener { name, id, .. } => { -// channel.new_event_listener(name, id.0 as u32, event_bubbles(name) as u8) -// } -// RemoveEventListener { name, id } => { -// channel.remove_event_listener(name, id.0 as u32, event_bubbles(name) as u8) -// } -// Remove { id } => channel.remove(id.0 as u32), -// PushRoot { id } => channel.push_root(id.0 as u32), -// } -// } - -// let bytes: Vec<_> = channel.export_memory().collect(); -// channel.reset(); -// Some(bytes) -// } - -// pub fn add_template( -// template: &Template, -// channel: &mut Channel, -// templates: &mut FxHashMap, -// max_template_count: &AtomicU16, -// ) { -// let current_max_template_count = max_template_count.load(Ordering::Relaxed); -// for root in template.roots.iter() { -// create_template_node(channel, root); -// templates.insert(template.name.to_owned(), current_max_template_count); -// } -// channel.add_templates(current_max_template_count, template.roots.len() as u16); - -// max_template_count.fetch_add(1, Ordering::Relaxed); -// } - -// pub fn create_template_node(channel: &mut Channel, node: &'static TemplateNode) { -// use TemplateNode::*; -// match node { -// Element { -// tag, -// namespace, -// attrs, -// children, -// .. -// } => { -// // Push the current node onto the stack -// match namespace { -// Some(ns) => channel.create_element_ns(tag, ns), -// None => channel.create_element(tag), -// } -// // Set attributes on the current node -// for attr in *attrs { -// if let TemplateAttribute::Static { -// name, -// value, -// namespace, -// } = attr -// { -// channel.set_top_attribute(name, value, namespace.unwrap_or_default()) -// } -// } -// // Add each child to the stack -// for child in *children { -// create_template_node(channel, child); -// } -// // Add all children to the parent -// channel.append_children_to_top(children.len() as u16); -// } -// Text { text } => channel.create_raw_text(text), -// DynamicText { .. } => channel.create_raw_text("p"), -// Dynamic { .. } => channel.add_placeholder(), -// } -// }