move focus into event system
This commit is contained in:
1 parent
ce5ade9fa6
commit
23376fef8e
4 files changed
+100
-83
No files matched your search
@@ -20,6 +20,7 @@ fn Button(cx: Scope) -> Element {
|
|||||||
justify_content: "center",
|
justify_content: "center",
|
||||||
align_items: "center",
|
align_items: "center",
|
||||||
onkeydown: |_| state.modify(|s| !s),
|
onkeydown: |_| state.modify(|s| !s),
|
||||||
|
onclick: |_| state.modify(|s| !s),
|
||||||
|
|
||||||
"{text}"
|
"{text}"
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-17
@@ -2,6 +2,7 @@ use crate::Dom;
|
|||||||
|
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::ElementId;
|
||||||
use dioxus_native_core::utils::{ElementProduced, PersistantElementIter};
|
use dioxus_native_core::utils::{ElementProduced, PersistantElementIter};
|
||||||
|
use dioxus_native_core_macro::sorted_str_slice;
|
||||||
|
|
||||||
use std::num::NonZeroU16;
|
use std::num::NonZeroU16;
|
||||||
|
|
||||||
@@ -100,9 +101,8 @@ impl NodeDepState for Focus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be sorted
|
const FOCUS_EVENTS: &[&str] = &sorted_str_slice!(["keydown", "keypress", "keyup"]);
|
||||||
const FOCUS_EVENTS: &[&str] = &["keydown", "keypress", "keyup"];
|
const FOCUS_ATTRIBUTES: &[&str] = &sorted_str_slice!(["dioxus-prevent-default", "tabindex"]);
|
||||||
const FOCUS_ATTRIBUTES: &[&str] = &["dioxus-prevent-default", "tabindex"];
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct FocusState {
|
pub(crate) struct FocusState {
|
||||||
@@ -123,6 +123,7 @@ impl FocusState {
|
|||||||
let focus_level = &mut self.focus_level;
|
let focus_level = &mut self.focus_level;
|
||||||
let mut next_focus = None;
|
let mut next_focus = None;
|
||||||
let starting_focus_level = *focus_level;
|
let starting_focus_level = *focus_level;
|
||||||
|
let mut focus_level_changed = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let new = if forward {
|
let new = if forward {
|
||||||
@@ -131,22 +132,21 @@ impl FocusState {
|
|||||||
self.focus_iter.prev(&rdom)
|
self.focus_iter.prev(&rdom)
|
||||||
};
|
};
|
||||||
let new_id = new.id();
|
let new_id = new.id();
|
||||||
let current_level = rdom[new_id].state.focus.level;
|
|
||||||
if let ElementProduced::Looped(_) = new {
|
if let ElementProduced::Looped(_) = new {
|
||||||
let mut closest_level = None;
|
let mut closest_level = None;
|
||||||
|
|
||||||
if forward {
|
if forward {
|
||||||
// find the closest focusable element after the current level
|
// find the closest focusable element after the current level
|
||||||
rdom.traverse_depth_first(|n| {
|
rdom.traverse_depth_first(|n| {
|
||||||
let current_level = n.state.focus.level;
|
let node_level = n.state.focus.level;
|
||||||
if current_level != *focus_level {
|
if node_level != *focus_level && node_level.focusable() {
|
||||||
if current_level > *focus_level {
|
if node_level > *focus_level {
|
||||||
if let Some(level) = &mut closest_level {
|
if let Some(level) = &mut closest_level {
|
||||||
if current_level < *level {
|
if node_level < *level {
|
||||||
*level = current_level;
|
*level = node_level;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
closest_level = Some(current_level);
|
closest_level = Some(node_level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,15 +154,15 @@ impl FocusState {
|
|||||||
} else {
|
} else {
|
||||||
// find the closest focusable element before the current level
|
// find the closest focusable element before the current level
|
||||||
rdom.traverse_depth_first(|n| {
|
rdom.traverse_depth_first(|n| {
|
||||||
let current_level = n.state.focus.level;
|
let node_level = n.state.focus.level;
|
||||||
if current_level != *focus_level {
|
if node_level != *focus_level && node_level.focusable() {
|
||||||
if current_level < *focus_level {
|
if node_level < *focus_level {
|
||||||
if let Some(level) = &mut closest_level {
|
if let Some(level) = &mut closest_level {
|
||||||
if current_level > *level {
|
if node_level > *level {
|
||||||
*level = current_level;
|
*level = node_level;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
closest_level = Some(current_level);
|
closest_level = Some(node_level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,6 +174,9 @@ impl FocusState {
|
|||||||
|
|
||||||
if let Some(level) = closest_level {
|
if let Some(level) = closest_level {
|
||||||
*focus_level = level;
|
*focus_level = level;
|
||||||
|
if *focus_level != starting_focus_level {
|
||||||
|
focus_level_changed = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if forward {
|
if forward {
|
||||||
*focus_level = FocusLevel::Unfocusable;
|
*focus_level = FocusLevel::Unfocusable;
|
||||||
@@ -183,7 +186,7 @@ impl FocusState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if the focus level looped, we are done
|
// if the focus level looped, we are done
|
||||||
if *focus_level == starting_focus_level {
|
if *focus_level == starting_focus_level && focus_level_changed {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,6 +200,7 @@ impl FocusState {
|
|||||||
loop_marker_id = Some(new_id);
|
loop_marker_id = Some(new_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let current_level = rdom[new_id].state.focus.level;
|
||||||
let after_previous_focused = if forward {
|
let after_previous_focused = if forward {
|
||||||
current_level >= *focus_level
|
current_level >= *focus_level
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+73
-49
@@ -14,6 +14,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use stretch2::{prelude::Layout, Stretch};
|
use stretch2::{prelude::Layout, Stretch};
|
||||||
|
|
||||||
|
use crate::FocusState;
|
||||||
use crate::{Dom, Node};
|
use crate::{Dom, Node};
|
||||||
|
|
||||||
// a wrapper around the input state for easier access
|
// a wrapper around the input state for easier access
|
||||||
@@ -78,6 +79,7 @@ pub struct InnerInputState {
|
|||||||
wheel: Option<WheelData>,
|
wheel: Option<WheelData>,
|
||||||
last_key_pressed: Option<(KeyboardData, Instant)>,
|
last_key_pressed: Option<(KeyboardData, Instant)>,
|
||||||
screen: Option<(u16, u16)>,
|
screen: Option<(u16, u16)>,
|
||||||
|
focus_state: FocusState,
|
||||||
// subscribers: Vec<Rc<dyn Fn() + 'static>>,
|
// subscribers: Vec<Rc<dyn Fn() + 'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +91,7 @@ impl InnerInputState {
|
|||||||
last_key_pressed: None,
|
last_key_pressed: None,
|
||||||
screen: None,
|
screen: None,
|
||||||
// subscribers: Vec::new(),
|
// subscribers: Vec::new(),
|
||||||
|
focus_state: FocusState::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,54 +99,56 @@ impl InnerInputState {
|
|||||||
fn apply_event(&mut self, evt: &mut EventCore) {
|
fn apply_event(&mut self, evt: &mut EventCore) {
|
||||||
match evt.1 {
|
match evt.1 {
|
||||||
// limitations: only two buttons may be held at once
|
// limitations: only two buttons may be held at once
|
||||||
EventData::Mouse(ref mut m) => match &mut self.mouse {
|
EventData::Mouse(ref mut m) => {
|
||||||
Some(state) => {
|
match &mut self.mouse {
|
||||||
let mut buttons = state.0.buttons;
|
Some(state) => {
|
||||||
state.0 = m.clone();
|
let mut buttons = state.0.buttons;
|
||||||
match evt.0 {
|
state.0 = m.clone();
|
||||||
// this code only runs when there are no buttons down
|
match evt.0 {
|
||||||
"mouseup" => {
|
// this code only runs when there are no buttons down
|
||||||
buttons = 0;
|
"mouseup" => {
|
||||||
state.1 = Vec::new();
|
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);
|
|
||||||
}
|
}
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
|
||||||
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::Wheel(ref w) => self.wheel = Some(w.clone()),
|
||||||
EventData::Screen(ref s) => self.screen = Some(*s),
|
EventData::Screen(ref s) => self.screen = Some(*s),
|
||||||
EventData::Keyboard(ref mut k) => {
|
EventData::Keyboard(ref mut k) => {
|
||||||
@@ -161,14 +166,27 @@ impl InnerInputState {
|
|||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
evts: &mut [EventCore],
|
evts: &mut Vec<EventCore>,
|
||||||
resolved_events: &mut Vec<UserEvent>,
|
resolved_events: &mut Vec<UserEvent>,
|
||||||
layout: &Stretch,
|
layout: &Stretch,
|
||||||
dom: &mut Dom,
|
dom: &mut Dom,
|
||||||
) {
|
) -> bool {
|
||||||
let previous_mouse = self.mouse.as_ref().map(|m| (m.0.clone(), m.1.clone()));
|
let previous_mouse = self.mouse.as_ref().map(|m| (m.0.clone(), m.1.clone()));
|
||||||
|
|
||||||
self.wheel = None;
|
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() {
|
for e in evts.iter_mut() {
|
||||||
self.apply_event(e);
|
self.apply_event(e);
|
||||||
@@ -179,6 +197,7 @@ impl InnerInputState {
|
|||||||
// for s in &self.subscribers {
|
// for s in &self.subscribers {
|
||||||
// s();
|
// s();
|
||||||
// }
|
// }
|
||||||
|
force_redraw
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_mouse_events(
|
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();
|
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 (*self.queued_events).borrow_mut(),
|
||||||
&mut resolved_events,
|
&mut resolved_events,
|
||||||
layout,
|
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,
|
12 => KeyCode::F12,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
TermKeyCode::BackTab => return None,
|
TermKeyCode::BackTab => KeyCode::Tab,
|
||||||
TermKeyCode::Null => return None,
|
TermKeyCode::Null => return None,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|||||||
+5
-17
@@ -139,8 +139,6 @@ 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 updated = true;
|
let mut updated = true;
|
||||||
|
|
||||||
let mut focus_state = FocusState::default();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
/*
|
/*
|
||||||
-> render the nodes in the right place with tui/crossterm
|
-> render the nodes in the right place with tui/crossterm
|
||||||
@@ -201,8 +199,6 @@ 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) => {
|
||||||
@@ -212,13 +208,6 @@ fn render_vdom(
|
|||||||
{
|
{
|
||||||
break;
|
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::Resize(_, _) => updated = true,
|
||||||
TermEvent::Mouse(_) => {}
|
TermEvent::Mouse(_) => {}
|
||||||
@@ -226,23 +215,22 @@ fn render_vdom(
|
|||||||
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, rerender) = handler.get_events(&stretch.borrow(), &mut rdom);
|
||||||
|
updated |= rerender;
|
||||||
for e in evts {
|
for e in evts {
|
||||||
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 {
|
for m in &mutations {
|
||||||
focus_state.prune(m, &rdom);
|
handler.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);
|
||||||
|
|||||||
Reference in new issue
Block a user