Răsfoiți Sursa

move focus into event system

Evan Almloff 3 ani în urmă
părinte
comite
23376fef8e
4 a modificat fișierele cu 99 adăugiri și 82 ștergeri
  1. 1 0
      examples/tui_buttons.rs
  2. 21 17
      packages/tui/src/focus.rs
  3. 72 48
      packages/tui/src/hooks.rs
  4. 5 17
      packages/tui/src/lib.rs

+ 1 - 0
examples/tui_buttons.rs

@@ -20,6 +20,7 @@ fn Button(cx: Scope) -> Element {
             justify_content: "center",
             align_items: "center",
             onkeydown: |_| state.modify(|s| !s),
+            onclick: |_| state.modify(|s| !s),
 
             "{text}"
         }

+ 21 - 17
packages/tui/src/focus.rs

@@ -2,6 +2,7 @@ use crate::Dom;
 
 use dioxus_core::ElementId;
 use dioxus_native_core::utils::{ElementProduced, PersistantElementIter};
+use dioxus_native_core_macro::sorted_str_slice;
 
 use std::num::NonZeroU16;
 
@@ -100,9 +101,8 @@ impl NodeDepState for Focus {
     }
 }
 
-// must be sorted
-const FOCUS_EVENTS: &[&str] = &["keydown", "keypress", "keyup"];
-const FOCUS_ATTRIBUTES: &[&str] = &["dioxus-prevent-default", "tabindex"];
+const FOCUS_EVENTS: &[&str] = &sorted_str_slice!(["keydown", "keypress", "keyup"]);
+const FOCUS_ATTRIBUTES: &[&str] = &sorted_str_slice!(["dioxus-prevent-default", "tabindex"]);
 
 #[derive(Default)]
 pub(crate) struct FocusState {
@@ -123,6 +123,7 @@ impl FocusState {
         let focus_level = &mut self.focus_level;
         let mut next_focus = None;
         let starting_focus_level = *focus_level;
+        let mut focus_level_changed = false;
 
         loop {
             let new = if forward {
@@ -131,22 +132,21 @@ impl FocusState {
                 self.focus_iter.prev(&rdom)
             };
             let new_id = new.id();
-            let current_level = rdom[new_id].state.focus.level;
             if let ElementProduced::Looped(_) = new {
                 let mut closest_level = None;
 
                 if forward {
                     // find the closest focusable element after the current level
                     rdom.traverse_depth_first(|n| {
-                        let current_level = n.state.focus.level;
-                        if current_level != *focus_level {
-                            if current_level > *focus_level {
+                        let node_level = n.state.focus.level;
+                        if node_level != *focus_level && node_level.focusable() {
+                            if node_level > *focus_level {
                                 if let Some(level) = &mut closest_level {
-                                    if current_level < *level {
-                                        *level = current_level;
+                                    if node_level < *level {
+                                        *level = node_level;
                                     }
                                 } else {
-                                    closest_level = Some(current_level);
+                                    closest_level = Some(node_level);
                                 }
                             }
                         }
@@ -154,15 +154,15 @@ impl FocusState {
                 } else {
                     // find the closest focusable element before the current level
                     rdom.traverse_depth_first(|n| {
-                        let current_level = n.state.focus.level;
-                        if current_level != *focus_level {
-                            if current_level < *focus_level {
+                        let node_level = n.state.focus.level;
+                        if node_level != *focus_level && node_level.focusable() {
+                            if node_level < *focus_level {
                                 if let Some(level) = &mut closest_level {
-                                    if current_level > *level {
-                                        *level = current_level;
+                                    if node_level > *level {
+                                        *level = node_level;
                                     }
                                 } else {
-                                    closest_level = Some(current_level);
+                                    closest_level = Some(node_level);
                                 }
                             }
                         }
@@ -174,6 +174,9 @@ impl FocusState {
 
                 if let Some(level) = closest_level {
                     *focus_level = level;
+                    if *focus_level != starting_focus_level {
+                        focus_level_changed = true;
+                    }
                 } else {
                     if forward {
                         *focus_level = FocusLevel::Unfocusable;
@@ -183,7 +186,7 @@ impl FocusState {
                 }
 
                 // if the focus level looped, we are done
-                if *focus_level == starting_focus_level {
+                if *focus_level == starting_focus_level && focus_level_changed {
                     break;
                 }
             }
@@ -197,6 +200,7 @@ impl FocusState {
                 loop_marker_id = Some(new_id);
             }
 
+            let current_level = rdom[new_id].state.focus.level;
             let after_previous_focused = if forward {
                 current_level >= *focus_level
             } else {

+ 72 - 48
packages/tui/src/hooks.rs

@@ -14,6 +14,7 @@ use std::{
 };
 use stretch2::{prelude::Layout, Stretch};
 
+use crate::FocusState;
 use crate::{Dom, Node};
 
 // a wrapper around the input state for easier access
@@ -78,6 +79,7 @@ pub struct InnerInputState {
     wheel: Option<WheelData>,
     last_key_pressed: Option<(KeyboardData, Instant)>,
     screen: Option<(u16, u16)>,
+    focus_state: FocusState,
     // subscribers: Vec<Rc<dyn Fn() + 'static>>,
 }
 
@@ -89,6 +91,7 @@ impl InnerInputState {
             last_key_pressed: None,
             screen: None,
             // subscribers: Vec::new(),
+            focus_state: FocusState::default(),
         }
     }
 
@@ -96,54 +99,56 @@ impl InnerInputState {
     fn apply_event(&mut self, evt: &mut EventCore) {
         match evt.1 {
             // limitations: only two buttons may be held at once
-            EventData::Mouse(ref mut m) => match &mut self.mouse {
-                Some(state) => {
-                    let mut buttons = state.0.buttons;
-                    state.0 = m.clone();
-                    match evt.0 {
-                        // this code only runs when there are no buttons down
-                        "mouseup" => {
-                            buttons = 0;
-                            state.1 = Vec::new();
-                        }
-                        "mousedown" => {
-                            if state.1.contains(&m.buttons) {
-                                // if we already pressed a button and there is another button released the button crossterm sends is the button remaining
-                                if state.1.len() > 1 {
-                                    evt.0 = "mouseup";
-                                    state.1 = vec![m.buttons];
-                                }
-                                // otherwise some other button was pressed. In testing it was consistantly this mapping
-                                else {
-                                    match m.buttons {
-                                        0x01 => state.1.push(0x02),
-                                        0x02 => state.1.push(0x01),
-                                        0x04 => state.1.push(0x01),
-                                        _ => (),
+            EventData::Mouse(ref mut m) => {
+                match &mut self.mouse {
+                    Some(state) => {
+                        let mut buttons = state.0.buttons;
+                        state.0 = m.clone();
+                        match evt.0 {
+                            // this code only runs when there are no buttons down
+                            "mouseup" => {
+                                buttons = 0;
+                                state.1 = Vec::new();
+                            }
+                            "mousedown" => {
+                                if state.1.contains(&m.buttons) {
+                                    // if we already pressed a button and there is another button released the button crossterm sends is the button remaining
+                                    if state.1.len() > 1 {
+                                        evt.0 = "mouseup";
+                                        state.1 = vec![m.buttons];
                                     }
+                                    // otherwise some other button was pressed. In testing it was consistantly this mapping
+                                    else {
+                                        match m.buttons {
+                                            0x01 => state.1.push(0x02),
+                                            0x02 => state.1.push(0x01),
+                                            0x04 => state.1.push(0x01),
+                                            _ => (),
+                                        }
+                                    }
+                                } else {
+                                    state.1.push(m.buttons);
                                 }
-                            } else {
-                                state.1.push(m.buttons);
-                            }
 
-                            buttons = state.1.iter().copied().reduce(|a, b| a | b).unwrap();
+                                buttons = state.1.iter().copied().reduce(|a, b| a | b).unwrap();
+                            }
+                            _ => (),
                         }
-                        _ => (),
+                        state.0.buttons = buttons;
+                        m.buttons = buttons;
+                    }
+                    None => {
+                        self.mouse = Some((
+                            m.clone(),
+                            if m.buttons == 0 {
+                                Vec::new()
+                            } else {
+                                vec![m.buttons]
+                            },
+                        ));
                     }
-                    state.0.buttons = buttons;
-                    m.buttons = buttons;
-                }
-                None => {
-                    self.mouse = Some((
-                        m.clone(),
-                        if m.buttons == 0 {
-                            Vec::new()
-                        } else {
-                            vec![m.buttons]
-                        },
-                    ));
                 }
-            },
+            }
             EventData::Wheel(ref w) => self.wheel = Some(w.clone()),
             EventData::Screen(ref s) => self.screen = Some(*s),
             EventData::Keyboard(ref mut k) => {
@@ -161,14 +166,27 @@ impl InnerInputState {
 
     fn update(
         &mut self,
-        evts: &mut [EventCore],
+        evts: &mut Vec<EventCore>,
         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
+                }
+                _ => true,
+            },
+            _ => true,
+        });
 
         for e in evts.iter_mut() {
             self.apply_event(e);
@@ -179,6 +197,7 @@ impl InnerInputState {
         // for s in &self.subscribers {
         //     s();
         // }
+        force_redraw
     }
 
     fn resolve_mouse_events(
@@ -517,10 +536,15 @@ impl RinkInputHandler {
         )
     }
 
-    pub(crate) fn get_events(&self, layout: &Stretch, dom: &mut Dom) -> Vec<UserEvent> {
+    pub(crate) fn prune(&self, mutations: &dioxus_core::Mutations, rdom: &Dom) {
+        self.state.borrow_mut().focus_state.prune(mutations, rdom);
+    }
+
+    // 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) {
         let mut resolved_events = Vec::new();
 
-        (*self.state).borrow_mut().update(
+        let rerender = (*self.state).borrow_mut().update(
             &mut (*self.queued_events).borrow_mut(),
             &mut resolved_events,
             layout,
@@ -574,7 +598,7 @@ impl RinkInputHandler {
             }
         }
 
-        resolved_events
+        (resolved_events, rerender)
     }
 }
 
@@ -759,7 +783,7 @@ fn translate_key_event(event: crossterm::event::KeyEvent) -> Option<EventData> {
                 12 => KeyCode::F12,
                 _ => return None,
             },
-            TermKeyCode::BackTab => return None,
+            TermKeyCode::BackTab => KeyCode::Tab,
             TermKeyCode::Null => return None,
             _ => return None,
         };

+ 5 - 17
packages/tui/src/lib.rs

@@ -139,8 +139,6 @@ fn render_vdom(
             let to_rerender: fxhash::FxHashSet<usize> = vec![0].into_iter().collect();
             let mut updated = true;
 
-            let mut focus_state = FocusState::default();
-
             loop {
                 /*
                 -> render the nodes in the right place with tui/crossterm
@@ -201,8 +199,6 @@ fn render_vdom(
                             //
                         }
                         Either::Right((evt, _o)) => {
-                            let mut evt_intersepted = false;
-
                             match evt.as_ref().unwrap() {
                                 InputEvent::UserInput(event) => match event {
                                     TermEvent::Key(key) => {
@@ -212,13 +208,6 @@ fn render_vdom(
                                         {
                                             break;
                                         }
-                                        if let KeyCode::BackTab = key.code {
-                                            evt_intersepted =
-                                                focus_state.progress(&mut rdom, false);
-                                        }
-                                        if let KeyCode::Tab = key.code {
-                                            evt_intersepted = focus_state.progress(&mut rdom, true);
-                                        }
                                     }
                                     TermEvent::Resize(_, _) => updated = true,
                                     TermEvent::Mouse(_) => {}
@@ -226,23 +215,22 @@ fn render_vdom(
                                 InputEvent::Close => break,
                             };
 
-                            if !evt_intersepted {
-                                if let InputEvent::UserInput(evt) = evt.unwrap() {
-                                    register_event(evt);
-                                }
+                            if let InputEvent::UserInput(evt) = evt.unwrap() {
+                                register_event(evt);
                             }
                         }
                     }
                 }
 
                 {
-                    let evts = handler.get_events(&stretch.borrow(), &mut rdom);
+                    let (evts, rerender) = handler.get_events(&stretch.borrow(), &mut rdom);
+                    updated |= rerender;
                     for e in evts {
                         vdom.handle_message(SchedulerMsg::Event(e));
                     }
                     let mutations = vdom.work_with_deadline(|| false);
                     for m in &mutations {
-                        focus_state.prune(m, &rdom);
+                        handler.prune(m, &rdom);
                     }
                     // updates the dom's nodes
                     let to_update = rdom.apply_mutations(mutations);