Bläddra i källkod

feat: add update functionality to useref

Jonathan Kelley 3 år sedan
förälder
incheckning
a2b0c50

+ 1 - 0
Cargo.toml

@@ -47,6 +47,7 @@ fxhash = "0.2.1"
 anyhow = "1.0.42"
 reqwest = "0.11.4"
 serde_json = "1.0.68"
+simple_logger = "1.13.0"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
 argh = "0.1.5"

+ 2 - 2
examples/calculator.rs

@@ -3,7 +3,7 @@ This example is a simple iOS-style calculator. This particular example can run a
 This calculator version uses React-style state management. All state is held as individual use_states.
 */
 
-use dioxus::events::on::*;
+use dioxus::events::*;
 use dioxus::prelude::*;
 
 fn main() {
@@ -56,7 +56,7 @@ const APP: FC<()> = |cx, _| {
         }
     };
 
-    let keydownhandler = move |evt: KeyboardEvent| match evt.key_code() {
+    let keydownhandler = move |evt: KeyboardEvent| match evt.key_code {
         KeyCode::Add => operator.set(Some("+")),
         KeyCode::Subtract => operator.set(Some("-")),
         KeyCode::Divide => operator.set(Some("/")),

+ 3 - 3
examples/crm.rs

@@ -59,13 +59,13 @@ static App: FC<()> = |cx, _| {
                 h2 {"Add new client" margin_bottom: "10px" }
                 form { class: "pure-form"
                     input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
-                        oninput: move |e| firstname.set(e.value())
+                        oninput: move |e| firstname.set(e.value.clone())
                     }
                     input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
-                        oninput: move |e| lastname.set(e.value())
+                        oninput: move |e| lastname.set(e.value.clone())
                     }
                     textarea { class: "new-client description" placeholder: "Description" value: "{description}"
-                        oninput: move |e| description.set(e.value())
+                        oninput: move |e| description.set(e.value.clone())
                     }
                 }
                 button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }

+ 8 - 4
examples/file_explorer.rs

@@ -8,11 +8,11 @@
 use dioxus::prelude::*;
 
 fn main() {
-    env_logger::init();
+    simple_logger::init_with_level(log::Level::Debug);
     dioxus::desktop::launch(App, |c| {
         c.with_window(|w| {
-            w.with_resizable(false).with_inner_size(
-                dioxus::desktop::wry::application::dpi::LogicalSize::new(800.0, 400.0),
+            w.with_resizable(true).with_inner_size(
+                dioxus::desktop::wry::application::dpi::LogicalSize::new(400.0, 800.0),
             )
         })
     })
@@ -70,6 +70,7 @@ impl Files {
 
     fn reload_path_list(&mut self) {
         let cur_path = self.path_stack.last().unwrap();
+        log::info!("Reloading path list for {:?}", cur_path);
         let paths = match std::fs::read_dir(cur_path) {
             Ok(e) => e,
             Err(err) => {
@@ -79,15 +80,18 @@ impl Files {
                 return;
             }
         };
+        let collected = paths.collect::<Vec<_>>();
+        log::info!("Path list reloaded {:#?}", collected);
 
         // clear the current state
         self.clear_err();
         self.path_names.clear();
 
-        for path in paths {
+        for path in collected {
             self.path_names
                 .push(path.unwrap().path().display().to_string());
         }
+        log::info!("path namees are {:#?}", self.path_names);
     }
 
     fn go_up(&mut self) {

+ 1 - 1
examples/manual_edits.rs

@@ -30,7 +30,7 @@ fn main() {
         AppendChildren { many: 1 },
     ];
 
-    dioxus_desktop::WebviewRenderer::run_with_edits(APP, (), |c| c, Some(edits)).expect("failed");
+    dioxus_desktop::run(APP, (), |c| c.with_edits(edits)).unwrap();
 }
 
 const APP: FC<()> = |cx, _props| {

+ 2 - 2
examples/pattern_model.rs

@@ -16,7 +16,7 @@
 //! RefMuts at the same time.
 
 use dioxus::desktop::wry::application::dpi::LogicalSize;
-use dioxus::events::on::*;
+use dioxus::events::{on::*, KeyCode};
 use dioxus::prelude::*;
 
 const STYLE: &str = include_str!("./assets/calculator.css");
@@ -169,7 +169,7 @@ impl Calculator {
         self.waiting_for_operand = true;
     }
     fn handle_keydown(&mut self, evt: KeyboardEvent) {
-        match evt.key_code() {
+        match evt.key_code {
             KeyCode::Backspace => self.backspace(),
             KeyCode::Num0 => self.input_digit(0),
             KeyCode::Num1 => self.input_digit(1),

+ 2 - 2
examples/todomvc.rs

@@ -57,7 +57,7 @@ const App: FC<()> = |cx, props| {
                     class: "new-todo"
                     placeholder: "What needs to be done?"
                     value: "{draft}"
-                    oninput: move |evt| draft.set(evt.value())
+                    oninput: move |evt| draft.set(evt.value.clone())
                 }
             }
             {todolist}
@@ -100,7 +100,7 @@ pub fn TodoEntry<'a>(cx: Context<'a>, props: &TodoEntryProps) -> DomTree<'a> {
        {is_editing.then(|| rsx!{
             input {
                 value: "{contents}"
-                oninput: move |evt| contents.set(evt.value())
+                oninput: move |evt| contents.set(evt.value.clone())
             }
         })}
     })

+ 2 - 0
packages/core/src/events.rs

@@ -17,6 +17,8 @@ use std::{
     sync::Arc,
 };
 
+pub use on::*;
+
 #[derive(Debug)]
 pub struct UserEvent {
     /// The originator of the event trigger

+ 2 - 1
packages/desktop/src/cfg.rs

@@ -30,8 +30,9 @@ impl<'a> DesktopConfig<'a> {
         }
     }
 
-    pub fn with_edits(&mut self, edits: Vec<DomEdit<'a>>) {
+    pub fn with_edits(&mut self, edits: Vec<DomEdit<'a>>) -> &mut Self {
         self.manual_edits = Some(edits);
+        self
     }
 
     pub fn with_prerendered(&mut self, content: String) -> &mut Self {

+ 1 - 1
packages/desktop/src/index.html

@@ -9,7 +9,7 @@
     <div id="_dioxusroot">
     </div>
 </body>
-<script type="text/javascript" src="/index.js">
+<script type="text/javascript" src="index.js">
 </script>
 
 </html>

+ 4 - 0
packages/desktop/src/index.js

@@ -153,6 +153,10 @@ class Interpreter {
       this.root.addEventListener(event_name, (event) => {
         const target = event.target;
         const val = target.getAttribute(`dioxus-event-${event_name}`);
+        if (val == null) {
+          return;
+        }
+
         const fields = val.split(".");
         const scope_id = parseInt(fields[0]);
         const real_id = parseInt(fields[1]);

+ 6 - 15
packages/desktop/src/lib.rs

@@ -93,7 +93,7 @@ pub fn run<T: Properties + 'static + Send + Sync>(
     let locked_receiver = Rc::new(RefCell::new(event_rx));
 
     let webview = WebViewBuilder::new(window)?
-        .with_url("wry://src/index.html")?
+        .with_url("wry://index.html")?
         .with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
             //
             match req.method.as_str() {
@@ -130,20 +130,11 @@ pub fn run<T: Properties + 'static + Send + Sync>(
             use wry::http::ResponseBuilder;
             // Remove url scheme
             let path = request.uri().replace("wry://", "");
-            // Read the file content from file path
-            let content = read(canonicalize(&path)?)?;
-
-            // Return asset contents and mime types based on file extentions
-            // If you don't want to do this manually, there are some crates for you.
-            // Such as `infer` and `mime_guess`.
-            let (data, meta) = if path.ends_with(".html") {
-                (content, "text/html")
-            } else if path.ends_with(".js") {
-                (content, "text/javascript")
-            } else if path.ends_with(".png") {
-                (content, "image/png")
-            } else {
-                unimplemented!();
+
+            let (data, meta) = match path.as_str() {
+                "index.html" => (include_bytes!("./index.html").to_vec(), "text/html"),
+                "index.html/index.js" => (include_bytes!("./index.js").to_vec(), "text/javascript"),
+                _ => unimplemented!("path {}", path),
             };
 
             ResponseBuilder::new().mimetype(meta).body(data)

+ 36 - 9
packages/hooks/src/useref.rs

@@ -1,14 +1,37 @@
-use std::cell::{Ref, RefCell, RefMut};
+use std::{
+    cell::{Cell, Ref, RefCell, RefMut},
+    rc::Rc,
+};
 
 use dioxus_core::Context;
 
+pub fn use_ref<T: 'static>(cx: Context, f: impl FnOnce() -> T) -> UseRef<T> {
+    cx.use_hook(
+        |_| UseRefInner {
+            update_scheuled: Cell::new(false),
+            update_callback: cx.schedule_update(),
+            value: RefCell::new(f()),
+        },
+        |inner| {
+            inner.update_scheuled.set(false);
+            UseRef { inner }
+        },
+        |_| {},
+    )
+}
+
 pub struct UseRef<'a, T> {
-    inner: &'a RefCell<T>,
+    inner: &'a UseRefInner<T>,
+}
+struct UseRefInner<T> {
+    update_scheuled: Cell<bool>,
+    update_callback: Rc<dyn Fn()>,
+    value: RefCell<T>,
 }
 
 impl<'a, T> UseRef<'a, T> {
     pub fn read(&self) -> Ref<'_, T> {
-        self.inner.borrow()
+        self.inner.value.borrow()
     }
 
     pub fn read_write(&self) -> (Ref<'_, T>, &Self) {
@@ -17,12 +40,20 @@ impl<'a, T> UseRef<'a, T> {
 
     /// Calling "write" will force the component to re-render
     pub fn write(&self) -> RefMut<'_, T> {
-        self.inner.borrow_mut()
+        self.needs_update();
+        self.inner.value.borrow_mut()
     }
 
     /// Allows the ability to write the value without forcing a re-render
     pub fn write_silent(&self) -> RefMut<'_, T> {
-        self.inner.borrow_mut()
+        self.inner.value.borrow_mut()
+    }
+
+    pub fn needs_update(&self) {
+        if !self.inner.update_scheuled.get() {
+            self.inner.update_scheuled.set(true);
+            (self.inner.update_callback)();
+        }
     }
 }
 
@@ -32,7 +63,3 @@ impl<T> Clone for UseRef<'_, T> {
     }
 }
 impl<T> Copy for UseRef<'_, T> {}
-
-pub fn use_ref<T: 'static>(cx: Context, f: impl FnOnce() -> T) -> UseRef<T> {
-    cx.use_hook(|_| RefCell::new(f()), |f| UseRef { inner: f }, |_| {})
-}

+ 2 - 2
packages/mobile/src/lib.rs

@@ -29,7 +29,7 @@ static HTML_CONTENT: &'static str = include_str!("../../desktop/src/index.html")
 pub fn launch(root: FC<()>, builder: fn(WindowBuilder) -> WindowBuilder) -> anyhow::Result<()> {
     launch_with_props(root, (), builder)
 }
-pub fn launch_with_props<P: Properties + 'static>(
+pub fn launch_with_props<P: 'static + Send>(
     root: FC<P>,
     props: P,
     builder: fn(WindowBuilder) -> WindowBuilder,
@@ -50,7 +50,7 @@ enum RpcEvent<'a> {
     },
 }
 
-impl<T: Properties + 'static> WebviewRenderer<T> {
+impl<T: 'static + Send> WebviewRenderer<T> {
     pub fn run(
         root: FC<T>,
         props: T,

+ 3 - 3
packages/web/examples/basic.rs

@@ -26,7 +26,7 @@ static APP: FC<()> = |cx, _| {
             input {
                 r#type: "text",
                 value: "{text_content}"
-                oninput: move |e| text_content.set(e.value())
+                oninput: move |e| text_content.set(e.value.clone())
             }
 
             {(0..10).map(|_| {
@@ -45,8 +45,8 @@ static APP: FC<()> = |cx, _| {
                 id: "cars"
                 value: "{content}"
                 oninput: move |ev| {
-                    content.set(ev.value());
-                    match ev.value().as_str() {
+                    content.set(ev.value.clone());
+                    match ev.value.as_str() {
                         "h1" => count.set(0),
                         "h2" => count.set(5),
                         "h3" => count.set(10),

+ 3 - 3
packages/web/examples/crm2.rs

@@ -68,13 +68,13 @@ static App: FC<()> = |cx, _| {
                 h2 {"Add new client" margin_bottom: "10px" }
                 form { class: "pure-form"
                     input { class: "new-client firstname" placeholder: "First name" value: "{firstname}"
-                        oninput: move |evt| firstname.set(evt.value())
+                        oninput: move |evt| firstname.set(evt.value.clone())
                     }
                     input { class: "new-client lastname" placeholder: "Last name" value: "{lastname}"
-                        oninput: move |evt| lastname.set(evt.value())
+                        oninput: move |evt| lastname.set(evt.value.clone())
                     }
                     textarea { class: "new-client description" placeholder: "Description" value: "{description}"
-                        oninput: move |evt| description.set(evt.value())
+                        oninput: move |evt| description.set(evt.value.clone())
                     }
                 }
                 button { class: "pure-button pure-button-primary", onclick: {add_new}, "Add New" }