Przeglądaj źródła

bugs: fix web list issue

Jonathan Kelley 3 lat temu
rodzic
commit
da4423c

+ 2 - 2
examples/todomvc.rs

@@ -3,8 +3,8 @@ use dioxus::prelude::*;
 use im_rc::HashMap;
 use std::rc::Rc;
 
-fn main() -> anyhow::Result<()> {
-    dioxus::desktop::launch(App, |c| c)
+fn main() {
+    dioxus::desktop::launch(App, |c| c);
 }
 
 #[derive(PartialEq)]

+ 67 - 10
packages/core/src/diff.rs

@@ -330,9 +330,6 @@ impl<'bump> DiffMachine<'bump> {
         }
 
         if !children.is_empty() {
-            // self.stack.element_id_stack.push(real_id);
-            // push our element_id onto the stack
-            // drop our element off the stack
             self.stack.create_children(children, MountType::Append);
         }
     }
@@ -543,11 +540,15 @@ impl<'bump> DiffMachine<'bump> {
             }
         }
 
-        if has_comitted {
-            self.mutations.pop();
+        if old.children.len() == 0 && new.children.len() != 0 {
+            please_commit(&mut self.mutations.edits);
+            self.stack.create_children(new.children, MountType::Append);
+        } else {
+            self.diff_children(old.children, new.children);
+            if has_comitted {
+                self.mutations.pop();
+            }
         }
-
-        self.diff_children(old.children, new.children);
     }
 
     fn diff_component_nodes(
@@ -601,6 +602,9 @@ impl<'bump> DiffMachine<'bump> {
             return;
         }
 
+        debug_assert!(old.children.len() != 0);
+        debug_assert!(new.children.len() != 0);
+
         self.diff_children(old.children, new.children);
     }
 
@@ -633,6 +637,7 @@ impl<'bump> DiffMachine<'bump> {
         match (old, new) {
             ([], []) => {}
             ([], _) => {
+                // we need to push the
                 self.stack.create_children(new, MountType::Append);
             }
             (_, []) => {
@@ -761,13 +766,57 @@ impl<'bump> DiffMachine<'bump> {
             right_offset,
         );
 
+        log::debug!("stack before lo is {:#?}", self.stack.instructions);
         // Ok, we now hopefully have a smaller range of children in the middle
         // within which to re-order nodes with the same keys, remove old nodes with
         // now-unused keys, and create new nodes with fresh keys.
-        self.diff_keyed_middle(
-            &old[left_offset..(old.len() - right_offset)],
-            &new[left_offset..(new.len() - right_offset)],
+
+        let old_middle = &old[left_offset..(old.len() - right_offset)];
+        let new_middle = &new[left_offset..(new.len() - right_offset)];
+
+        debug_assert!(
+            !((old_middle.len() == new_middle.len()) && old_middle.len() == 0),
+            "keyed children must have the same number of children"
         );
+        if new_middle.len() == 0 {
+            // remove the old elements
+            self.remove_nodes(old_middle);
+        } else if old_middle.len() == 0 {
+            // there were no old elements, so just create the new elements
+            // we need to find the right "foothold" though - we shouldnt use the "append" at all
+            if left_offset == 0 {
+                // insert at the beginning of the old list
+                let foothold = &old[old.len() - right_offset];
+                self.stack.create_children(
+                    new_middle,
+                    MountType::InsertBefore {
+                        other_node: foothold,
+                    },
+                );
+            } else if right_offset == 0 {
+                // insert at the end  the old list
+                let foothold = old.last().unwrap();
+                self.stack.create_children(
+                    new_middle,
+                    MountType::InsertAfter {
+                        other_node: foothold,
+                    },
+                );
+            } else {
+                // inserting in the middle
+                let foothold = &old[left_offset - 1];
+                self.stack.create_children(
+                    new_middle,
+                    MountType::InsertAfter {
+                        other_node: foothold,
+                    },
+                );
+            }
+        } else {
+            self.diff_keyed_middle(old_middle, new_middle);
+        }
+
+        log::debug!("stack after km is {:#?}", self.stack.instructions);
     }
 
     /// Diff both ends of the children that share keys.
@@ -893,6 +942,14 @@ impl<'bump> DiffMachine<'bump> {
         // If none of the old keys are reused by the new children, then we remove all the remaining old children and
         // create the new children afresh.
         if shared_keys.is_empty() {
+            log::debug!(
+                "no shared keys, replacing and creating many with many, {:#?}, {:#?}",
+                old,
+                new
+            );
+            log::debug!("old_key_to_old_index, {:#?}", old_key_to_old_index);
+            log::debug!("new_index_to_old_index, {:#?}", new_index_to_old_index);
+            log::debug!("shared_keys, {:#?}", shared_keys);
             self.replace_and_create_many_with_many(old, new);
             return;
         }

+ 1 - 1
packages/core/src/diff_stack.rs

@@ -36,7 +36,7 @@ pub enum MountType<'a> {
 }
 
 pub(crate) struct DiffStack<'bump> {
-    instructions: Vec<DiffInstruction<'bump>>,
+    pub(crate) instructions: Vec<DiffInstruction<'bump>>,
     nodes_created_stack: SmallVec<[usize; 10]>,
     pub scope_stack: SmallVec<[ScopeId; 5]>,
 }

+ 15 - 2
packages/core/src/scheduler.rs

@@ -107,8 +107,6 @@ pub enum SchedulerMsg {
 }
 
 pub enum TaskMsg {
-    // SubmitTask(FiberTask, u64),
-    // SubmitTask(FiberTask, u64),
     ToggleTask(u64),
     PauseTask(u64),
     ResumeTask(u64),
@@ -322,6 +320,9 @@ impl Scheduler {
     unsafe fn load_work(&mut self) -> SavedDiffWork<'static> {
         self.saved_state.take().unwrap().extend()
     }
+    pub fn handle_task(&mut self, evt: TaskMsg) {
+        //
+    }
 
     pub fn handle_channel_msg(&mut self, msg: SchedulerMsg) {
         match msg {
@@ -480,6 +481,18 @@ impl Scheduler {
         let mut committed_mutations = Vec::<Mutations<'static>>::new();
 
         while self.has_any_work() {
+            while let Ok(Some(msg)) = self.receiver.try_next() {
+                match msg {
+                    SchedulerMsg::Task(t) => todo!(),
+                    SchedulerMsg::Immediate(im) => {
+                        self.dirty_scopes.insert(im);
+                    }
+                    SchedulerMsg::UiEvent(evt) => {
+                        self.ui_events.push_back(evt);
+                    }
+                }
+            }
+
             // switch our priority, pop off any work
             while let Some(event) = self.ui_events.pop_front() {
                 if let Some(scope) = self.pool.get_scope_mut(event.scope) {

+ 4 - 20
packages/core/src/virtual_dom.rs

@@ -360,25 +360,21 @@ impl VirtualDom {
     /// Waits for the scheduler to have work
     /// This lets us poll async tasks during idle periods without blocking the main thread.
     pub async fn wait_for_work(&mut self) {
+        // todo: poll the events once even if there is work to do to prevent starvation
         if self.scheduler.has_any_work() {
-            log::debug!("No need to wait for work, we already have some");
             return;
         }
 
-        log::debug!("No active work.... waiting for some...");
         use futures_util::StreamExt;
 
-        // right now this won't poll events if there is ongoing work
-        // in the future we want to prioritize some events over ongoing work
-        // this is coming in the "priorities" PR
-
         // Wait for any new events if we have nothing to do
-        // todo: poll the events once even if there is work to do to prevent starvation
         futures_util::select! {
             _ = self.scheduler.async_tasks.next() => {}
             msg = self.scheduler.receiver.next() => {
                 match msg.unwrap() {
-                    SchedulerMsg::Task(t) => todo!(),
+                    SchedulerMsg::Task(t) => {
+                        self.scheduler.handle_task(t);
+                    },
                     SchedulerMsg::Immediate(im) => {
                         self.scheduler.dirty_scopes.insert(im);
                     }
@@ -388,18 +384,6 @@ impl VirtualDom {
                 }
             },
         }
-
-        while let Ok(Some(msg)) = self.scheduler.receiver.try_next() {
-            match msg {
-                SchedulerMsg::Task(t) => todo!(),
-                SchedulerMsg::Immediate(im) => {
-                    self.scheduler.dirty_scopes.insert(im);
-                }
-                SchedulerMsg::UiEvent(evt) => {
-                    self.scheduler.ui_events.push_back(evt);
-                }
-            }
-        }
     }
 }
 

+ 2 - 2
packages/hooks/src/usestate.rs

@@ -121,8 +121,8 @@ impl<'a, T: 'static> UseState<'a, T> {
         self.inner.wip.borrow_mut()
     }
 
-    pub fn classic(self) -> (&'a T, &'a Rc<dyn Fn(T)>) {
-        todo!()
+    pub fn classic(self) -> (&'a T, Rc<dyn Fn(T)>) {
+        (&self.inner.current_val, self.setter())
     }
 
     pub fn setter(&self) -> Rc<dyn Fn(T)> {

+ 11 - 2
packages/html/src/lib.rs

@@ -1530,7 +1530,7 @@ builder_constructors! {
         // This has a manual implementation below
         // r#type: InputType,
 
-        value: String,
+        // value: String,
         width: isize,
     };
 
@@ -1538,7 +1538,7 @@ builder_constructors! {
     /// [`<label>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label)
     /// element.
     label {
-        r#for: Id,
+        // r#for: Id,
         form: Id,
     };
 
@@ -1694,6 +1694,10 @@ impl input {
     pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
         cx.attr("type", val, None, false)
     }
+
+    pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("value", val, None, true)
+    }
 }
 
 /*
@@ -1717,6 +1721,11 @@ impl textarea {
         cx.attr("value", val, None, true)
     }
 }
+impl label {
+    pub fn r#for<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("for", val, None, false)
+    }
+}
 
 pub trait SvgAttributes {
     aria_trait_methods! {

+ 10 - 9
packages/web/src/dom.rs

@@ -211,7 +211,7 @@ impl WebsysDom {
 
     fn create_placeholder(&mut self, id: u64) {
         self.create_element("pre", None, id);
-        self.set_attribute("hidden", "", None);
+        // self.set_attribute("hidden", "", None);
     }
 
     fn create_text_node(&mut self, text: &str, id: u64) {
@@ -244,6 +244,9 @@ impl WebsysDom {
                 .unwrap(),
         };
 
+        let el2 = el.dyn_ref::<Element>().unwrap();
+        el2.set_attribute("dioxus-id", &format!("{}", id)).unwrap();
+
         self.stack.push(el.clone());
         self.nodes[(id as usize)] = Some(el);
     }
@@ -400,18 +403,16 @@ impl WebsysDom {
     }
 
     fn insert_before(&mut self, n: u32, root: u64) {
-        let after = self.nodes[root as usize].as_ref().unwrap();
+        let anchor = self.nodes[root as usize].as_ref().unwrap();
 
         if n == 1 {
             let before = self.stack.pop();
 
-            after
+            anchor
                 .parent_node()
                 .unwrap()
-                .insert_before(&before, Some(&after))
+                .insert_before(&before, Some(&anchor))
                 .unwrap();
-
-            after.insert_before(&before, None).unwrap();
         } else {
             let arr: js_sys::Array = self
                 .stack
@@ -419,11 +420,11 @@ impl WebsysDom {
                 .drain((self.stack.list.len() - n as usize)..)
                 .collect();
 
-            if let Some(el) = after.dyn_ref::<Element>() {
+            if let Some(el) = anchor.dyn_ref::<Element>() {
                 el.before_with_node(&arr).unwrap();
-            } else if let Some(el) = after.dyn_ref::<web_sys::CharacterData>() {
+            } else if let Some(el) = anchor.dyn_ref::<web_sys::CharacterData>() {
                 el.before_with_node(&arr).unwrap();
-            } else if let Some(el) = after.dyn_ref::<web_sys::DocumentType>() {
+            } else if let Some(el) = anchor.dyn_ref::<web_sys::DocumentType>() {
                 el.before_with_node(&arr).unwrap();
             }
         }