From 9ed722ae4560e321582785c4c45f2dfb913c27e1 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 3 May 2022 17:19:16 -0500 Subject: [PATCH] change focus when an element is clicked --- packages/native-core/src/utils.rs | 6 ++- packages/tui/src/focus.rs | 25 ++++++++- packages/tui/src/hooks.rs | 84 ++++++++++++++++++------------- packages/tui/src/lib.rs | 6 ++- 4 files changed, 83 insertions(+), 38 deletions(-) diff --git a/packages/native-core/src/utils.rs b/packages/native-core/src/utils.rs index 41f79a04b..efd2a1d3b 100644 --- a/packages/native-core/src/utils.rs +++ b/packages/native-core/src/utils.rs @@ -64,7 +64,9 @@ impl PersistantElementIter { } /// remove stale element refreneces - pub fn prune(&mut self, mutations: &Mutations, rdom: &RealDom) { + /// returns true if the focused element is removed + pub fn prune(&mut self, mutations: &Mutations, rdom: &RealDom) -> 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 diff --git a/packages/tui/src/focus.rs b/packages/tui/src/focus.rs index 3590075dd..cb578a8f1 100644 --- a/packages/tui/src/focus.rs +++ b/packages/tui/src/focus.rs @@ -109,10 +109,11 @@ pub(crate) struct FocusState { pub(crate) focus_iter: PersistantElementIter, pub(crate) last_focused_id: Option, 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 + } } diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index e045a8f25..6dd3e88bb 100644 --- a/packages/tui/src/hooks.rs +++ b/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, last_key_pressed: Option<(KeyboardData, Instant)>, screen: Option<(u16, u16)>, - focus_state: FocusState, + pub(crate) focus_state: FocusState, // subscribers: Vec>, } @@ -170,19 +170,14 @@ impl InnerInputState { resolved_events: &mut Vec, 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)>, resolved_events: &mut Vec, 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, bool) { + pub(crate) fn get_events(&self, layout: &Stretch, dom: &mut Dom) -> Vec { 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 { + self.state.borrow_mut() } } diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index cb1628206..f8ee2ec80 100644 --- a/packages/tui/src/lib.rs +++ b/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)); }