intigrate focus system with tui

This commit is contained in:
Evan Almloff 2022-05-03 11:02:35 -05:00
parent 7a17683447
commit 35ee243d0d
5 changed files with 132 additions and 20 deletions

59
examples/tui_buttons.rs Normal file
View file

@ -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{},
}
}
})
}

View file

@ -550,7 +550,6 @@ impl RinkInputHandler {
}) })
.map(|evt| (evt.0, evt.1.into_any())); .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(); let mut hm: FxHashMap<&'static str, Vec<Arc<dyn Any + Send + Sync>>> = FxHashMap::default();
for (event, data) in events { for (event, data) in events {
if let Some(v) = hm.get_mut(event) { if let Some(v) = hm.get_mut(event) {
@ -562,6 +561,7 @@ impl RinkInputHandler {
for (event, datas) in hm { for (event, datas) in hm {
for node in dom.get_listening_sorted(event) { for node in dom.get_listening_sorted(event) {
for data in &datas { for data in &datas {
if node.state.focused {
resolved_events.push(UserEvent { resolved_events.push(UserEvent {
scope_id: None, scope_id: None,
priority: EventPriority::Medium, priority: EventPriority::Medium,
@ -572,6 +572,7 @@ impl RinkInputHandler {
} }
} }
} }
}
resolved_events resolved_events
} }

View file

@ -7,7 +7,9 @@ use crossterm::{
}; };
use dioxus_core::exports::futures_channel::mpsc::unbounded; use dioxus_core::exports::futures_channel::mpsc::unbounded;
use dioxus_core::*; 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 dioxus_native_core_macro::State;
use futures::{ use futures::{
channel::mpsc::{UnboundedReceiver, UnboundedSender}, channel::mpsc::{UnboundedReceiver, UnboundedSender},
@ -42,6 +44,7 @@ struct NodeState {
// depends on attributes, the C component of it's parent and a u8 context // depends on attributes, the C component of it's parent and a u8 context
#[parent_dep_state(style)] #[parent_dep_state(style)]
style: StyleModifier, style: StyleModifier,
focused: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -130,7 +133,10 @@ fn render_vdom(
} }
let to_rerender: fxhash::FxHashSet<usize> = vec![0].into_iter().collect(); 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 { loop {
/* /*
@ -144,8 +150,8 @@ fn render_vdom(
todo: lazy re-rendering todo: lazy re-rendering
*/ */
if !to_rerender.is_empty() || resized { if !to_rerender.is_empty() || updated {
resized = false; updated = false;
fn resize(dims: Rect, stretch: &mut Stretch, rdom: &Dom) { fn resize(dims: Rect, stretch: &mut Stretch, rdom: &Dom) {
let width = dims.width; let width = dims.width;
let height = dims.height; let height = dims.height;
@ -192,6 +198,8 @@ fn render_vdom(
// //
} }
Either::Right((evt, _o)) => { Either::Right((evt, _o)) => {
let mut evt_intersepted = false;
match evt.as_ref().unwrap() { match evt.as_ref().unwrap() {
InputEvent::UserInput(event) => match event { InputEvent::UserInput(event) => match event {
TermEvent::Key(key) => { TermEvent::Key(key) => {
@ -201,19 +209,55 @@ fn render_vdom(
{ {
break; 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;
} }
TermEvent::Resize(_, _) => resized = true, }
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(_, _) => updated = true,
TermEvent::Mouse(_) => {} TermEvent::Mouse(_) => {}
}, },
InputEvent::Close => break, InputEvent::Close => break,
}; };
if !evt_intersepted {
if let InputEvent::UserInput(evt) = evt.unwrap() { if let InputEvent::UserInput(evt) = evt.unwrap() {
register_event(evt); register_event(evt);
} }
} }
} }
} }
}
{ {
let evts = handler.get_events(&stretch.borrow(), &mut rdom); let evts = handler.get_events(&stretch.borrow(), &mut rdom);
@ -221,6 +265,9 @@ fn render_vdom(
vdom.handle_message(SchedulerMsg::Event(e)); vdom.handle_message(SchedulerMsg::Event(e));
} }
let mutations = vdom.work_with_deadline(|| false); let mutations = vdom.work_with_deadline(|| false);
for m in &mutations {
focus_iter.prune(m, &rdom);
}
// updates the dom's nodes // updates the dom's nodes
let to_update = rdom.apply_mutations(mutations); let to_update = rdom.apply_mutations(mutations);
// update the style and layout // update the style and layout

View file

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

View file

@ -41,7 +41,7 @@ impl<'a> RinkBuffer<'a> {
} }
pub trait RinkWidget { pub trait RinkWidget {
fn render(self, area: Rect, buf: RinkBuffer); fn render(self, area: Rect, buf: &mut RinkBuffer);
} }
pub struct WidgetWithContext<T: RinkWidget> { pub struct WidgetWithContext<T: RinkWidget> {
@ -57,7 +57,8 @@ impl<T: RinkWidget> WidgetWithContext<T> {
impl<T: RinkWidget> Widget for WidgetWithContext<T> { impl<T: RinkWidget> Widget for WidgetWithContext<T> {
fn render(self, area: Rect, buf: &mut Buffer) { 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);
} }
} }