Procházet zdrojové kódy

Merge branch 'breaking' of https://github.com/Demonthos/dioxus into breaking

Evan Almloff před 1 rokem
rodič
revize
b165d707c4

+ 3 - 97
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",
@@ -2571,7 +2554,6 @@ dependencies = [
  "env_logger",
  "futures-util",
  "http-range",
- "im-rc",
  "log",
  "manganis",
  "num-format",
@@ -3116,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"
@@ -3911,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"
@@ -5616,7 +5583,7 @@ dependencies = [
  "html5ever",
  "indexmap 1.9.3",
  "matches",
- "selectors 0.22.0",
+ "selectors",
 ]
 
 [[package]]
@@ -7250,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]]
@@ -7329,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"
@@ -8844,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"
@@ -8929,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"
@@ -9202,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"

+ 0 - 1
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"] }

+ 0 - 413
examples/all_css.rs

@@ -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"
-        }
-    }
-}

+ 25 - 14
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%);
 }

+ 2 - 2
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 {

+ 3 - 3
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::<usize>());
+    let sum = use_selector(move || counters.read().iter().copied().sum::<i32>());
 
     rsx! {
         div {
@@ -29,14 +29,14 @@ fn app() -> Element {
 }
 
 #[component]
-fn Child(i: usize, counters: Signal<Vec<usize>>) -> Element {
+fn Child(i: usize, counters: Signal<Vec<i32>>) -> 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::<usize>() {
+                    if let Ok(value) = e.value().parse::<i32>() {
                         counters.write()[i] = value;
                     }
                 }

+ 13 - 9
examples/flat_router.rs

@@ -5,15 +5,19 @@ use dioxus_router::prelude::*;
 fn main() {
     env_logger::init();
 
-    LaunchBuilder::new(|| rsx! {Router::<Route> {}})
-        .cfg(
-            Config::new().with_window(
-                WindowBuilder::new()
-                    .with_inner_size(LogicalSize::new(600, 1000))
-                    .with_resizable(false),
-            ),
-        )
-        .launch_desktop()
+    LaunchBuilder::new(|| {
+        rsx! {
+            Router::<Route> {}
+        }
+    })
+    .cfg(
+        Config::new().with_window(
+            WindowBuilder::new()
+                .with_inner_size(LogicalSize::new(600, 1000))
+                .with_resizable(false),
+        ),
+    )
+    .launch_desktop()
 }
 
 #[derive(Routable, Clone)]

+ 2 - 2
examples/optional_props.rs

@@ -33,8 +33,6 @@ fn app() -> Element {
     }
 }
 
-type SthElse<T> = Option<T>;
-
 #[derive(Props, PartialEq, Clone)]
 struct ButtonProps {
     a: String,
@@ -51,6 +49,8 @@ struct ButtonProps {
     e: SthElse<String>,
 }
 
+type SthElse<T> = Option<T>;
+
 fn Button(props: ButtonProps) -> Element {
     rsx! {
         button {

+ 5 - 1
examples/router.rs

@@ -2,7 +2,11 @@ use dioxus::prelude::*;
 use dioxus_router::prelude::*;
 
 fn main() {
-    launch_desktop(|| rsx! {Router::<Route> {}});
+    launch_desktop(|| {
+        rsx! {
+            Router::<Route> {}
+        }
+    });
 }
 
 #[derive(Routable, Clone, Debug, PartialEq)]

+ 5 - 1
examples/simple_desktop.rs

@@ -9,7 +9,11 @@ fn main() {
         .with_module_level("dioxus", log::LevelFilter::Trace)
         .init()
         .unwrap();
-    launch_desktop(|| rsx! {Router::<Route> {}});
+    launch_desktop(|| {
+        rsx! {
+            Router::<Route> {}
+        }
+    });
 }
 
 #[derive(Routable, Clone)]

+ 1 - 1
examples/simple_router.rs

@@ -54,5 +54,5 @@ fn Nav() -> Element {
 }
 
 fn main() {
-    launch_desktop(|| rsx! {Router::<Route> {}});
+    launch_desktop(|| rsx! { Router::<Route> {} });
 }

+ 2 - 2
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"] }

+ 23 - 23
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<String> {
-    let eval_provider = use_eval(cx);
-
     let value: Signal<Option<String>> = 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();

+ 1 - 1
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},

+ 10 - 114
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<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + '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<ShortcutId, ShortcutRegistryError> {
+    ) -> Result<ShortcutHandle, ShortcutRegistryError> {
         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<dyn Fn(AssetRequest, RequestAsyncResponder) + 'static>,
+        handler: Box<dyn Fn(AssetRequest, RequestAsyncResponder) + 'static>,
         scope: Option<ScopeId>,
     ) {
         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<RefCell<Slab<WryWindowEventHandlerInner>>>,
-}
-
-impl WindowEventHandlers {
-    pub(crate) fn add(
-        &self,
-        window_id: WindowId,
-        handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + '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<UserWindowEvent>,
-        target: &EventLoopWindowTarget<UserWindowEvent>,
-    ) {
-        for (_, handler) in self.handlers.borrow_mut().iter_mut() {
-            handler.apply_event(event, target);
-        }
-    }
-}
-
-struct WryWindowEventHandlerInner {
-    window_id: WindowId,
-    handler: WryEventHandlerCallback,
-}
-
-type WryEventHandlerCallback =
-    Box<dyn FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static>;
-
-impl WryWindowEventHandlerInner {
-    fn apply_event(
-        &mut self,
-        event: &Event<UserWindowEvent>,
-        target: &EventLoopWindowTarget<UserWindowEvent>,
-    ) {
-        // 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<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + '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);
-    }
-}

+ 8 - 137
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<Mutex<Vec<Vec<u8>>>>,
-    responder: Arc<Mutex<Option<wry::RequestAsyncResponder>>>,
+    queue: Rc<RefCell<Vec<Vec<u8>>>>,
+    responder: Rc<RefCell<Option<wry::RequestAsyncResponder>>>,
 }
 
 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<u8>) {
-        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<String, u16>,
-//     max_template_count: &AtomicU16,
-// ) -> Option<Vec<u8>> {
-//     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<String, u16>,
-//     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(),
-//     }
-// }

+ 0 - 82
packages/desktop/src/escape.rs

@@ -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'");
-}

+ 65 - 0
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<Slab<WryWindowEventHandlerInner>>,
+}
+
+struct WryWindowEventHandlerInner {
+    window_id: WindowId,
+
+    #[allow(clippy::type_complexity)]
+    handler:
+        Box<dyn FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static>,
+}
+
+impl WindowEventHandlers {
+    pub(crate) fn add(
+        &self,
+        window_id: WindowId,
+        handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + '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<UserWindowEvent>,
+        target: &EventLoopWindowTarget<UserWindowEvent>,
+    ) {
+        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)
+        }
+    }
+}

+ 27 - 34
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::<DesktopContext>())
+    use_hook(consume_context::<DesktopContext>)
 }
 
 /// Get a closure that executes any JavaScript in the WebView context.
 pub fn use_wry_event_handler(
     handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + '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(),
-        );
-
-        Rc::new(name.to_string())
-    });
+    use_hook_with_cleanup(
+        || {
+            crate::window().asset_handlers.register_handler(
+                name.to_string(),
+                Box::new(handler),
+                current_scope_id().unwrap(),
+            );
 
-    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<ShortcutHandle, ShortcutRegistryError> {
-    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();
+            }
+        },
+    )
 }

+ 1 - 1
packages/desktop/src/launch.rs

@@ -85,5 +85,5 @@ pub fn launch(
         }));
 
     #[cfg(not(feature = "tokio"))]
-    launch_with_props_blocking(config, platform_config)
+    launch_with_props_blocking(virtual_dom, platform_config)
 }

+ 4 - 7
packages/desktop/src/lib.rs

@@ -9,8 +9,8 @@ mod config;
 mod desktop_context;
 mod edits;
 mod element;
-mod escape;
 mod eval;
+mod event_handlers;
 mod events;
 mod file_upload;
 mod hooks;
@@ -39,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::*;

+ 15 - 20
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<T> Default for SharedSlab<T> {
 }
 
 struct QueryEntry {
-    channel_sender: tokio::sync::mpsc::UnboundedSender<Value>,
-    return_sender: Option<tokio::sync::oneshot::Sender<Value>>,
+    channel_sender: futures_channel::mpsc::UnboundedSender<Value>,
+    return_sender: Option<futures_channel::oneshot::Sender<Value>>,
 }
 
 const QUEUE_NAME: &str = "__msg_queues";
@@ -83,8 +83,8 @@ impl QueryEngine {
         script: &str,
         context: DesktopContext,
     ) -> Query<V> {
-        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<V: DeserializeOwned> {
     desktop: DesktopContext,
     slab: SharedSlab<QueryEntry>,
-    receiver: tokio::sync::mpsc::UnboundedReceiver<Value>,
-    return_receiver: Option<tokio::sync::oneshot::Receiver<Value>>,
+    receiver: futures_channel::mpsc::UnboundedReceiver<Value>,
+    return_receiver: Option<futures_channel::oneshot::Receiver<Value>>,
     id: usize,
     phantom: std::marker::PhantomData<V>,
 }
@@ -200,18 +200,13 @@ impl<V: DeserializeOwned> Query<V> {
 
     /// Receive a message from the query
     pub async fn recv(&mut self) -> Result<Value, QueryError> {
-        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<Value, QueryError> {
         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<V: DeserializeOwned> Drop for Query<V> {
 
 #[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}")]

+ 41 - 71
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,31 +15,47 @@ pub use global_hotkey::{
 #[cfg(any(target_os = "ios", target_os = "android"))]
 pub use crate::mobile_shortcut::*;
 
+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,
+}
+
+impl ShortcutHandle {
+    /// Remove the shortcut.
+    pub fn remove(&self) {
+        window().remove_shortcut(*self);
+    }
+}
+
+/// An error that can occur when registering a shortcut.
+#[non_exhaustive]
+#[derive(Debug, Clone)]
+pub enum ShortcutRegistryError {
+    /// The shortcut is invalid.
+    InvalidShortcut(String),
+    /// An unknown error occurred.
+    Other(Rc<dyn std::error::Error>),
+}
+
 pub(crate) struct ShortcutRegistry {
     manager: GlobalHotKeyManager,
-    shortcuts: RefCell<HashMap<u32, Shortcut>>,
+    shortcuts: RefCell<HashMap<u32, ShortcutInner>>,
 }
 
-struct Shortcut {
+struct ShortcutInner {
     #[allow(unused)]
     shortcut: HotKey,
     callbacks: Slab<Box<dyn FnMut()>>,
 }
 
-impl Shortcut {
-    fn insert(&mut self, callback: Box<dyn FnMut()>) -> 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 {
@@ -57,7 +65,7 @@ impl ShortcutRegistry {
     }
 
     pub(crate) fn call_handlers(&self, id: GlobalHotKeyEvent) {
-        if let Some(Shortcut { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) {
+        if let Some(ShortcutInner { callbacks, .. }) = self.shortcuts.borrow_mut().get_mut(&id.id) {
             for (_, callback) in callbacks.iter_mut() {
                 (callback)();
             }
@@ -68,15 +76,15 @@ impl ShortcutRegistry {
         &self,
         hotkey: HotKey,
         callback: Box<dyn FnMut()>,
-    ) -> Result<ShortcutId, ShortcutRegistryError> {
+    ) -> Result<ShortcutHandle, ShortcutRegistryError> {
         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 {
+            return Ok(ShortcutHandle {
                 id: accelerator_id,
-                number: callbacks.insert(callback),
+                number: callbacks.callbacks.insert(callback),
             });
         };
 
@@ -87,7 +95,7 @@ impl ShortcutRegistry {
             err => ShortcutRegistryError::Other(Rc::new(err)),
         })?;
 
-        let mut shortcut = Shortcut {
+        let mut shortcut = ShortcutInner {
             shortcut: hotkey,
             callbacks: Slab::new(),
         };
@@ -96,17 +104,17 @@ impl ShortcutRegistry {
 
         shortcuts.insert(accelerator_id, shortcut);
 
-        Ok(ShortcutId {
+        Ok(ShortcutHandle {
             id: accelerator_id,
             number: id,
         })
     }
 
-    pub(crate) fn remove_shortcut(&self, id: ShortcutId) {
+    pub(crate) fn remove_shortcut(&self, id: ShortcutHandle) {
         let mut shortcuts = self.shortcuts.borrow_mut();
         if let Some(callbacks) = shortcuts.get_mut(&id.id) {
-            callbacks.remove(id.number);
-            if callbacks.is_empty() {
+            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);
                 }
@@ -121,31 +129,6 @@ impl ShortcutRegistry {
     }
 }
 
-/// An error that can occur when registering a shortcut.
-#[non_exhaustive]
-#[derive(Debug, Clone)]
-pub enum ShortcutRegistryError {
-    /// The shortcut is invalid.
-    InvalidShortcut(String),
-    /// An unknown error occurred.
-    Other(Rc<dyn std::error::Error>),
-}
-
-/// An global id for a shortcut.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct ShortcutId {
-    id: u32,
-    number: usize,
-}
-
-/// 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,
-}
-
 pub trait IntoAccelerator {
     fn accelerator(&self) -> HotKey;
 }
@@ -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;
 }

+ 5 - 21
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::<DesktopContext>().unwrap();
-        // let provider: Rc<dyn EvalProvider> = 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<dyn EvalProvider> =
             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();

+ 13 - 19
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 {

+ 0 - 6
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::*;
 

+ 10 - 0
packages/hooks/src/use_on_destroy.rs

@@ -100,3 +100,13 @@ pub fn use_on_destroy<D: FnOnce() + 'static>(destroy: D) {
 pub fn use_on_drop<D: FnOnce() + 'static>(ondrop: D) {
     use_on_destroy(ondrop);
 }
+
+pub fn use_hook_with_cleanup<T: Clone + 'static>(
+    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
+}

+ 14 - 5
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 = []

+ 4 - 4
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 = []