1
0
Эх сурвалжийг харах

intigrate focus system with tui

Evan Almloff 3 жил өмнө
parent
commit
35ee243d0d

+ 59 - 0
examples/tui_buttons.rs

@@ -0,0 +1,59 @@
+#![allow(non_snake_case)]
+
+use dioxus::prelude::*;
+
+fn main() {
+    dioxus::tui::launch(app);
+}
+
+fn Button(cx: Scope) -> Element {
+    let state = use_state(&cx, || false);
+    let color = if *state.get() { "red" } else { "blue" };
+    let text = if *state.get() { "☐" } else { "☒" };
+
+    cx.render(rsx! {
+        div {
+            border_width: "1px",
+            width: "50%",
+            height: "100%",
+            background_color: "{color}",
+            justify_content: "center",
+            align_items: "center",
+            onkeydown: |_| state.modify(|s| !s),
+
+            "{text}"
+        }
+    })
+}
+
+fn app(cx: Scope) -> Element {
+    cx.render(rsx! {
+        div {
+            width: "100%",
+            height: "100%",
+            flex_direction: "column",
+
+            div {
+                width: "100%",
+                height: "50%",
+                flex_direction: "row",
+
+                Button{},
+                Button{},
+                Button{},
+                Button{},
+            }
+
+            div {
+                width: "100%",
+                height: "50%",
+                flex_direction: "row",
+
+                Button{},
+                Button{},
+                Button{},
+                Button{},
+            }
+        }
+    })
+}

+ 9 - 8
packages/tui/src/hooks.rs

@@ -550,7 +550,6 @@ impl RinkInputHandler {
             })
             .map(|evt| (evt.0, evt.1.into_any()));
 
-        // todo: currently resolves events in all nodes, but once the focus system is added it should filter by focus
         let mut hm: FxHashMap<&'static str, Vec<Arc<dyn Any + Send + Sync>>> = FxHashMap::default();
         for (event, data) in events {
             if let Some(v) = hm.get_mut(event) {
@@ -562,13 +561,15 @@ impl RinkInputHandler {
         for (event, datas) in hm {
             for node in dom.get_listening_sorted(event) {
                 for data in &datas {
-                    resolved_events.push(UserEvent {
-                        scope_id: None,
-                        priority: EventPriority::Medium,
-                        name: event,
-                        element: Some(node.id),
-                        data: data.clone(),
-                    });
+                    if node.state.focused {
+                        resolved_events.push(UserEvent {
+                            scope_id: None,
+                            priority: EventPriority::Medium,
+                            name: event,
+                            element: Some(node.id),
+                            data: data.clone(),
+                        });
+                    }
                 }
             }
         }

+ 54 - 7
packages/tui/src/lib.rs

@@ -7,7 +7,9 @@ use crossterm::{
 };
 use dioxus_core::exports::futures_channel::mpsc::unbounded;
 use dioxus_core::*;
-use dioxus_native_core::{real_dom::RealDom, state::*};
+use dioxus_native_core::real_dom::{NodeType, RealDom};
+use dioxus_native_core::state::*;
+use dioxus_native_core::utils::PersistantElementIter;
 use dioxus_native_core_macro::State;
 use futures::{
     channel::mpsc::{UnboundedReceiver, UnboundedSender},
@@ -42,6 +44,7 @@ struct NodeState {
     // depends on attributes, the C component of it's parent and a u8 context
     #[parent_dep_state(style)]
     style: StyleModifier,
+    focused: bool,
 }
 
 #[derive(Clone)]
@@ -130,7 +133,10 @@ fn render_vdom(
             }
 
             let to_rerender: fxhash::FxHashSet<usize> = vec![0].into_iter().collect();
-            let mut resized = true;
+            let mut updated = true;
+
+            let mut focus_iter = PersistantElementIter::new();
+            let mut focus_id = ElementId(0);
 
             loop {
                 /*
@@ -144,8 +150,8 @@ fn render_vdom(
                 todo: lazy re-rendering
                 */
 
-                if !to_rerender.is_empty() || resized {
-                    resized = false;
+                if !to_rerender.is_empty() || updated {
+                    updated = false;
                     fn resize(dims: Rect, stretch: &mut Stretch, rdom: &Dom) {
                         let width = dims.width;
                         let height = dims.height;
@@ -192,6 +198,8 @@ 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) => {
@@ -201,15 +209,51 @@ fn render_vdom(
                                         {
                                             break;
                                         }
+                                        if let KeyCode::BackTab = key.code {
+                                            let mut new_focused_id;
+                                            loop {
+                                                new_focused_id = focus_iter.prev(&rdom).id();
+                                                if let NodeType::Element { .. } =
+                                                    &rdom[new_focused_id].node_type
+                                                {
+                                                    break;
+                                                }
+                                            }
+                                            rdom[focus_id].state.focused = false;
+                                            focus_id = new_focused_id;
+                                            rdom[focus_id].state.focused = true;
+                                            evt_intersepted = true;
+                                            updated = true;
+                                            // println!("{:?}", focus_id);
+                                        }
+                                        if let KeyCode::Tab = key.code {
+                                            let mut new_focused_id;
+                                            loop {
+                                                new_focused_id = focus_iter.next(&rdom).id();
+                                                if let NodeType::Element { .. } =
+                                                    &rdom[new_focused_id].node_type
+                                                {
+                                                    break;
+                                                }
+                                            }
+                                            rdom[focus_id].state.focused = false;
+                                            focus_id = new_focused_id;
+                                            rdom[focus_id].state.focused = true;
+                                            evt_intersepted = true;
+                                            updated = true;
+                                            // println!("{:?}", focus_id);
+                                        }
                                     }
-                                    TermEvent::Resize(_, _) => resized = true,
+                                    TermEvent::Resize(_, _) => updated = true,
                                     TermEvent::Mouse(_) => {}
                                 },
                                 InputEvent::Close => break,
                             };
 
-                            if let InputEvent::UserInput(evt) = evt.unwrap() {
-                                register_event(evt);
+                            if !evt_intersepted {
+                                if let InputEvent::UserInput(evt) = evt.unwrap() {
+                                    register_event(evt);
+                                }
                             }
                         }
                     }
@@ -221,6 +265,9 @@ fn render_vdom(
                         vdom.handle_message(SchedulerMsg::Event(e));
                     }
                     let mutations = vdom.work_with_deadline(|| false);
+                    for m in &mutations {
+                        focus_iter.prune(m, &rdom);
+                    }
                     // updates the dom's nodes
                     let to_update = rdom.apply_mutations(mutations);
                     // update the style and layout

+ 7 - 3
packages/tui/src/render.rs

@@ -5,7 +5,7 @@ use stretch2::{
     prelude::{Layout, Size},
     Stretch,
 };
-use tui::{backend::CrosstermBackend, layout::Rect};
+use tui::{backend::CrosstermBackend, layout::Rect, style::Color};
 
 use crate::{
     style::{RinkColor, RinkStyle},
@@ -43,7 +43,7 @@ pub(crate) fn render_vnode(
             }
 
             impl<'a> RinkWidget for Label<'a> {
-                fn render(self, area: Rect, mut buf: RinkBuffer) {
+                fn render(self, area: Rect, buf: &mut RinkBuffer) {
                     for (i, c) in self.text.char_indices() {
                         let mut new_cell = RinkCell::default();
                         new_cell.set_style(self.style);
@@ -81,7 +81,7 @@ pub(crate) fn render_vnode(
 }
 
 impl RinkWidget for &Node {
-    fn render(self, area: Rect, mut buf: RinkBuffer<'_>) {
+    fn render(self, area: Rect, mut buf: &mut RinkBuffer<'_>) {
         use tui::symbols::line::*;
 
         enum Direction {
@@ -267,6 +267,10 @@ impl RinkWidget for &Node {
                 if let Some(c) = self.state.style.core.bg {
                     new_cell.bg = c;
                 }
+                if self.state.focused {
+                    new_cell.bg.alpha = 100;
+                    new_cell.bg.color = new_cell.bg.blend(Color::White);
+                }
                 buf.set(x, y, new_cell);
             }
         }

+ 3 - 2
packages/tui/src/widget.rs

@@ -41,7 +41,7 @@ impl<'a> RinkBuffer<'a> {
 }
 
 pub trait RinkWidget {
-    fn render(self, area: Rect, buf: RinkBuffer);
+    fn render(self, area: Rect, buf: &mut RinkBuffer);
 }
 
 pub struct WidgetWithContext<T: RinkWidget> {
@@ -57,7 +57,8 @@ impl<T: RinkWidget> WidgetWithContext<T> {
 
 impl<T: RinkWidget> Widget for WidgetWithContext<T> {
     fn render(self, area: Rect, buf: &mut Buffer) {
-        self.widget.render(area, RinkBuffer::new(buf, self.config))
+        let mut rbuf = RinkBuffer::new(buf, self.config);
+        self.widget.render(area, &mut rbuf);
     }
 }