Sfoglia il codice sorgente

change focus when an element is clicked

Evan Almloff 3 anni fa
parent
commit
9ed722ae45

+ 5 - 1
packages/native-core/src/utils.rs

@@ -64,7 +64,9 @@ impl PersistantElementIter {
     }
 
     /// remove stale element refreneces
-    pub fn prune<S: State>(&mut self, mutations: &Mutations, rdom: &RealDom<S>) {
+    /// returns true if the focused element is removed
+    pub fn prune<S: State>(&mut self, mutations: &Mutations, rdom: &RealDom<S>) -> bool {
+        let mut changed = false;
         let ids_removed: Vec<_> = mutations
             .edits
             .iter()
@@ -82,6 +84,7 @@ impl PersistantElementIter {
             .position(|(el_id, _)| ids_removed.iter().any(|id| el_id.as_u64() == *id))
         {
             self.stack.truncate(r);
+            changed = true;
         }
         // if a child is removed or inserted before or at the current element, update the child index
         for (el_id, child_idx) in self.stack.iter_mut() {
@@ -122,6 +125,7 @@ impl PersistantElementIter {
                 }
             }
         }
+        changed
     }
 
     /// get the next element

+ 24 - 1
packages/tui/src/focus.rs

@@ -109,10 +109,11 @@ pub(crate) struct FocusState {
     pub(crate) focus_iter: PersistantElementIter,
     pub(crate) last_focused_id: Option<ElementId>,
     pub(crate) focus_level: FocusLevel,
+    pub(crate) dirty: bool,
 }
 
 impl FocusState {
-    // returns true if the focus has changed
+    /// Returns true if the focus has changed.
     pub fn progress(&mut self, rdom: &mut Dom, forward: bool) -> bool {
         if let Some(last) = self.last_focused_id {
             if !rdom[last].state.focus.pass_focus {
@@ -227,6 +228,7 @@ impl FocusState {
                     self.focus_iter.prev(&rdom).id()
                 } != id
                 {}
+                self.dirty = true;
                 return true;
             }
         }
@@ -253,6 +255,9 @@ impl FocusState {
                 }
             }
         }
+        if self.focus_iter.prune(mutations, rdom) {
+            self.dirty = true;
+        }
         for m in &mutations.edits {
             match m {
                 dioxus_core::DomEdit::ReplaceWith { root, .. } => remove_children(
@@ -269,4 +274,22 @@ impl FocusState {
             }
         }
     }
+
+    pub(crate) fn set_focus(&mut self, rdom: &mut Dom, id: ElementId) {
+        if let Some(old) = self.last_focused_id.replace(id) {
+            rdom[old].state.focused = false;
+        }
+        let state = &mut rdom[id].state;
+        state.focused = true;
+        self.focus_level = state.focus.level;
+        // reset the position to the currently focused element
+        while self.focus_iter.next(&rdom).id() != id {}
+        self.dirty = true;
+    }
+
+    pub(crate) fn clean(&mut self) -> bool {
+        let old = self.dirty;
+        self.dirty = false;
+        old
+    }
 }

+ 50 - 34
packages/tui/src/hooks.rs

@@ -7,7 +7,7 @@ use fxhash::{FxHashMap, FxHashSet};
 use dioxus_html::{on::*, KeyCode};
 use std::{
     any::Any,
-    cell::RefCell,
+    cell::{RefCell, RefMut},
     rc::Rc,
     sync::Arc,
     time::{Duration, Instant},
@@ -79,7 +79,7 @@ pub struct InnerInputState {
     wheel: Option<WheelData>,
     last_key_pressed: Option<(KeyboardData, Instant)>,
     screen: Option<(u16, u16)>,
-    focus_state: FocusState,
+    pub(crate) focus_state: FocusState,
     // subscribers: Vec<Rc<dyn Fn() + 'static>>,
 }
 
@@ -170,19 +170,14 @@ impl InnerInputState {
         resolved_events: &mut Vec<UserEvent>,
         layout: &Stretch,
         dom: &mut Dom,
-    ) -> bool {
+    ) {
         let previous_mouse = self.mouse.as_ref().map(|m| (m.0.clone(), m.1.clone()));
 
         self.wheel = None;
-        let mut force_redraw = false;
 
         evts.retain(|e| match &e.1 {
             EventData::Keyboard(k) => match k.key_code {
-                KeyCode::Tab => {
-                    let focus_event = self.focus_state.progress(dom, !k.shift_key);
-                    force_redraw |= focus_event;
-                    !focus_event
-                }
+                KeyCode::Tab => !self.focus_state.progress(dom, !k.shift_key),
                 _ => true,
             },
             _ => true,
@@ -197,11 +192,10 @@ impl InnerInputState {
         // for s in &self.subscribers {
         //     s();
         // }
-        force_redraw
     }
 
     fn resolve_mouse_events(
-        &self,
+        &mut self,
         previous_mouse: Option<(MouseData, Vec<u16>)>,
         resolved_events: &mut Vec<UserEvent>,
         layout: &Stretch,
@@ -343,14 +337,14 @@ impl InnerInputState {
                 }
             }
 
-            {
-                // mousedown
+            // mousedown
+            if data.clicked {
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("mousedown") {
                     let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
                     let currently_contains = layout_contains_point(node_layout, data.new_pos);
 
-                    if currently_contains && data.clicked {
+                    if currently_contains {
                         try_create_event(
                             "mousedown",
                             Arc::new(data.mouse_data.clone()),
@@ -363,14 +357,14 @@ impl InnerInputState {
                 }
             }
 
-            {
-                // mouseup
+            // mouseup
+            if data.released {
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("mouseup") {
                     let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
                     let currently_contains = layout_contains_point(node_layout, data.new_pos);
 
-                    if currently_contains && data.released {
+                    if currently_contains {
                         try_create_event(
                             "mouseup",
                             Arc::new(data.mouse_data.clone()),
@@ -383,14 +377,14 @@ impl InnerInputState {
                 }
             }
 
-            {
-                // click
+            // click
+            if data.released && data.mouse_data.button == 0 {
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("click") {
                     let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
                     let currently_contains = layout_contains_point(node_layout, data.new_pos);
 
-                    if currently_contains && data.released && data.mouse_data.button == 0 {
+                    if currently_contains {
                         try_create_event(
                             "click",
                             Arc::new(data.mouse_data.clone()),
@@ -403,14 +397,14 @@ impl InnerInputState {
                 }
             }
 
-            {
-                // contextmenu
+            // contextmenu
+            if data.released && data.mouse_data.button == 2 {
                 let mut will_bubble = FxHashSet::default();
                 for node in dom.get_listening_sorted("contextmenu") {
                     let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
                     let currently_contains = layout_contains_point(node_layout, data.new_pos);
 
-                    if currently_contains && data.released && data.mouse_data.button == 2 {
+                    if currently_contains {
                         try_create_event(
                             "contextmenu",
                             Arc::new(data.mouse_data.clone()),
@@ -423,15 +417,15 @@ impl InnerInputState {
                 }
             }
 
-            {
-                // wheel
-                let mut will_bubble = FxHashSet::default();
-                for node in dom.get_listening_sorted("wheel") {
-                    let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
-                    let currently_contains = layout_contains_point(node_layout, data.new_pos);
+            // wheel
+            if let Some(w) = data.wheel_data {
+                if data.wheel_delta != 0.0 {
+                    let mut will_bubble = FxHashSet::default();
+                    for node in dom.get_listening_sorted("wheel") {
+                        let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
+                        let currently_contains = layout_contains_point(node_layout, data.new_pos);
 
-                    if let Some(w) = data.wheel_data {
-                        if currently_contains && data.wheel_delta != 0.0 {
+                        if currently_contains {
                             try_create_event(
                                 "wheel",
                                 Arc::new(w.clone()),
@@ -492,6 +486,24 @@ impl InnerInputState {
                     }
                 }
             }
+
+            // update focus
+            if data.released {
+                let mut focus_id = None;
+                dom.traverse_depth_first(|node| {
+                    let node_layout = layout.layout(node.state.layout.node.unwrap()).unwrap();
+                    let currently_contains = layout_contains_point(node_layout, data.new_pos);
+
+                    if currently_contains {
+                        if node.state.focus.level.focusable() {
+                            focus_id = Some(node.id);
+                        }
+                    }
+                });
+                if let Some(id) = focus_id {
+                    self.focus_state.set_focus(dom, id);
+                }
+            }
         }
     }
 
@@ -541,10 +553,10 @@ impl RinkInputHandler {
     }
 
     // returns a list of events and if a event will force a rerender
-    pub(crate) fn get_events(&self, layout: &Stretch, dom: &mut Dom) -> (Vec<UserEvent>, bool) {
+    pub(crate) fn get_events(&self, layout: &Stretch, dom: &mut Dom) -> Vec<UserEvent> {
         let mut resolved_events = Vec::new();
 
-        let rerender = (*self.state).borrow_mut().update(
+        (*self.state).borrow_mut().update(
             &mut (*self.queued_events).borrow_mut(),
             &mut resolved_events,
             layout,
@@ -598,7 +610,11 @@ impl RinkInputHandler {
             }
         }
 
-        (resolved_events, rerender)
+        resolved_events
+    }
+
+    pub(crate) fn state(&self) -> RefMut<InnerInputState> {
+        self.state.borrow_mut()
     }
 }
 

+ 4 - 2
packages/tui/src/lib.rs

@@ -223,8 +223,10 @@ fn render_vdom(
                 }
 
                 {
-                    let (evts, rerender) = handler.get_events(&stretch.borrow(), &mut rdom);
-                    updated |= rerender;
+                    let evts = handler.get_events(&stretch.borrow(), &mut rdom);
+                    {
+                        updated |= handler.state().focus_state.clean();
+                    }
                     for e in evts {
                         vdom.handle_message(SchedulerMsg::Event(e));
                     }