use crate::{prelude::*, widget::WidgetTree, widget_tree::TreeArena};
use indextree::{Arena, NodeId};
use std::{
cell::RefCell,
cmp::Ordering,
collections::{HashMap, HashSet},
rc::Rc,
};
use super::dispatcher::Dispatcher;
#[derive(Debug)]
pub(crate) struct FocusManager {
focusing: Option<WidgetId>,
request_focusing: Option<Option<WidgetId>>,
node_ids: HashMap<WidgetId, NodeId>,
arena: Arena<FocusNodeInfo>,
root: NodeId,
}
pub struct FocusHandle {
wid: WidgetId,
mgr: Rc<RefCell<FocusManager>>,
}
impl FocusHandle {
pub(crate) fn request_focus(&self) {
self.mgr.borrow_mut().request_focusing = Some(Some(self.wid));
}
pub(crate) fn unfocus(&self) {
if self.mgr.borrow().focusing == Some(self.wid) {
self.mgr.borrow_mut().request_focusing = Some(None);
}
}
}
impl Default for FocusManager {
fn default() -> Self {
let mut arena = Arena::new();
let root = arena.new_node(FocusNodeInfo { node_cnt: 0, scope_cnt: 1, wid: None });
Self {
request_focusing: None,
focusing: None,
node_ids: HashMap::<WidgetId, NodeId>::new(),
arena,
root,
}
}
}
#[derive(Eq, PartialEq, Copy, Clone)]
pub(crate) enum FocusType {
Scope,
Node,
}
#[derive(Debug)]
pub(crate) struct FocusNodeInfo {
pub scope_cnt: u32,
pub node_cnt: u32,
pub wid: Option<WidgetId>,
}
impl FocusNodeInfo {
fn new(wid: WidgetId) -> Self {
FocusNodeInfo {
scope_cnt: 0,
node_cnt: 0,
wid: Some(wid),
}
}
fn has_focus_node(&self) -> bool { self.node_cnt > 0 }
fn has_focus_scope(&self) -> bool { self.scope_cnt > 0 }
fn add_focus(&mut self, ft: FocusType) {
match ft {
FocusType::Node => self.node_cnt += 1,
FocusType::Scope => self.scope_cnt += 1,
}
}
fn remove_focus(&mut self, ft: FocusType) {
match ft {
FocusType::Node => self.node_cnt -= 1,
FocusType::Scope => self.scope_cnt -= 1,
}
}
fn is_empty(&self) -> bool { self.node_cnt + self.scope_cnt == 0 }
}
impl FocusManager {
pub(crate) fn add_focus_node(
&mut self,
wid: WidgetId,
auto_focus: bool,
focus_type: FocusType,
arena: &TreeArena,
) {
if let Some(id) = self.node_ids.get(&wid) {
let node = self.arena[*id].get_mut();
node.add_focus(focus_type);
} else {
let mut node = FocusNodeInfo::new(wid);
node.add_focus(focus_type);
let node_id = self.arena.new_node(node);
self.node_ids.insert(wid, node_id);
let it = wid.ancestors(arena).skip(1);
let parent = it
.filter_map(|id| self.node_ids.get(&id))
.next()
.unwrap_or(&self.root);
self.insert_node(*parent, node_id, wid, arena);
}
if auto_focus
&& focus_type == FocusType::Node
&& self.focusing.is_none()
&& self.request_focusing.is_none()
{
self.request_focusing = Some(Some(wid));
}
}
pub(crate) fn focus_handle(this: &Rc<RefCell<Self>>, wid: WidgetId) -> FocusHandle {
FocusHandle { mgr: this.clone(), wid }
}
pub(crate) fn remove_focus_node(&mut self, wid: WidgetId, focus_type: FocusType) {
let Some(id) = self.node_ids.get(&wid).cloned() else { return; };
let node = self.arena[id].get_mut();
node.remove_focus(focus_type);
if Some(wid) == self.focusing && !node.has_focus_node() {
self.request_focusing = Some(None);
}
if node.is_empty() {
id.remove(&mut self.arena);
self.node_ids.remove(&wid);
}
}
pub fn refresh(&mut self, arena: &TreeArena) {
let Some(focusing) = self.request_focusing.take() else { return };
if focusing.is_none() {
self.focusing = None;
return;
}
let focusing = focusing.filter(|node_id| self.ignore_scope_id(*node_id, arena).is_none());
let focus_node = focusing.and_then(|wid| self.node_ids.get(&wid));
let info = focus_node.and_then(|id: &NodeId| self.get(*id));
let focus_to = if let Some(node) = info {
if node.has_focus_scope() {
let scope = self.scope_property(node.wid, arena);
if node.has_focus_node() && scope.can_focus {
node.wid
} else if !scope.skip_descendants {
self
.focus_step_in_scope(*focus_node.unwrap(), None, false, arena)
.and_then(|id| self.assert_get(id).wid)
} else {
None
}
} else {
node.wid
}
} else {
None
};
self.focusing = focus_to;
}
pub(crate) fn request_next_focus(&mut self, arena: &TreeArena) {
self.focus_move_circle(false, arena);
}
pub(crate) fn request_prev_focus(&mut self, arena: &TreeArena) {
self.focus_move_circle(true, arena);
}
fn focus_move_circle(&mut self, backward: bool, arena: &TreeArena) {
let has_focus = self.focusing.is_some();
let mut wid = self.focus_step(self.focusing, backward, arena);
if wid.is_none() && has_focus {
wid = self.focus_step(wid, backward, arena);
}
self.request_focusing = Some(wid);
}
fn focus_step(
&mut self,
focusing: Option<WidgetId>,
backward: bool,
arena: &TreeArena,
) -> Option<WidgetId> {
let mut node_id = focusing.and_then(|id| self.node_ids.get(&id)).copied();
let mut scope_id = node_id.and_then(|id| self.scope_id(id)).or(Some(self.root));
loop {
scope_id?;
let next = self.focus_step_in_scope(scope_id.unwrap(), node_id, backward, arena);
if let Some(id) = next {
return self.get(id).and_then(|n| n.wid);
} else {
node_id = scope_id;
scope_id = self.scope_id(node_id.unwrap());
}
}
}
fn collect_tab_index_in_scope(
&self,
scope_id: NodeId,
backward: bool,
arena: &TreeArena,
) -> Vec<(i16, NodeId, FocusType)> {
let scope_tab_type = |id, has_focus_node: bool| {
let mut v = vec![];
let node = self.scope_property(id, arena);
if has_focus_node && node.can_focus {
v.push(FocusType::Node);
}
if !node.skip_descendants {
v.push(FocusType::Scope);
}
v
};
let is_scope = |id| self.assert_get(id).has_focus_scope();
let node_type = |id| {
self.arena.get(id).map(|n| n.get()).map_or(vec![], |node| {
if node.has_focus_scope() {
scope_tab_type(node.wid, node.has_focus_node())
} else if node.has_focus_node() {
vec![FocusType::Node]
} else {
vec![]
}
})
};
let next_sibling = |level: &mut u32, mut id| {
while *level > 0 {
let next = self.arena[id].next_sibling();
if next.is_some() {
return next;
} else {
id = self.arena[id].parent().unwrap();
*level -= 1;
}
}
None
};
let mut tab_indexs = vec![];
let mut node = self.arena[scope_id].first_child();
let mut level = 1;
while let Some(id) = node {
let tab_index = self.tab_index(id, arena);
if tab_index >= 0 {
node_type(id)
.into_iter()
.for_each(|t| tab_indexs.push((tab_index, id, t)));
}
if !is_scope(id) {
level += 1;
node = self.arena[id].first_child().or_else(|| {
level -= 1;
next_sibling(&mut level, id)
});
} else {
node = next_sibling(&mut level, id);
}
}
tab_indexs.sort_by(|lh, rh| {
if lh.0 == rh.0 {
Ordering::Equal
} else if lh.0 == 0 {
Ordering::Greater
} else if rh.0 == 0 {
Ordering::Less
} else {
lh.0.cmp(&rh.0)
}
});
if backward {
tab_indexs.reverse();
}
tab_indexs
}
fn focus_step_in_scope(
&self,
scope_id: NodeId,
node_id: Option<NodeId>,
backward: bool,
arena: &TreeArena,
) -> Option<NodeId> {
let mut iter = self
.collect_tab_index_in_scope(scope_id, backward, arena)
.into_iter();
let mut tmp;
let it: &mut dyn Iterator<Item = (i16, NodeId, FocusType)> = if let Some(node_id) = node_id {
tmp = iter.skip_while(move |(_, id, _)| *id != node_id).skip(1);
&mut tmp
} else {
&mut iter
};
for (_, id, focus_type) in it {
let next = if focus_type == FocusType::Scope {
self.focus_step_in_scope(id, None, backward, arena)
} else {
Some(id)
};
if next.is_some() {
return next;
}
}
None
}
fn scope_id(&self, node_id: NodeId) -> Option<NodeId> { self.scope_list(node_id).next() }
fn scope_list(&self, node_id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
node_id
.ancestors(&self.arena)
.skip(1)
.filter(|n| self.assert_get(*n).has_focus_scope())
}
fn ignore_scope_id(&self, wid: WidgetId, arena: &TreeArena) -> Option<NodeId> {
let node_id = wid
.ancestors(arena)
.filter_map(|wid| self.node_ids.get(&wid).copied())
.next();
node_id.and_then(|node_id| {
self.scope_list(node_id).find(|id| {
let mut has_ignore = false;
self.get(*id).and_then(|n| n.wid).map(|wid| {
wid.get(arena).map(|r| {
r.query_all_type(
|s: &FocusScope| {
has_ignore = s.skip_descendants;
!has_ignore
},
QueryOrder::InnerFirst,
)
})
});
has_ignore
})
})
}
fn scope_property(&self, scope_id: Option<WidgetId>, arena: &TreeArena) -> FocusScope {
scope_id
.and_then(|wid| {
wid.get(arena).and_then(|r| {
let mut node = None;
r.query_on_first_type(QueryOrder::InnerFirst, |s: &FocusScope| {
if node.is_none() {
node = Some(s.clone());
} else {
let n = node.as_mut().unwrap();
n.skip_descendants |= s.skip_descendants;
n.can_focus &= s.can_focus;
}
});
node
})
})
.unwrap_or(FocusScope::default())
}
fn tab_index(&self, node_id: NodeId, arena: &TreeArena) -> i16 {
let mut index = 0;
if let Some(r) = self
.get(node_id)
.and_then(|n| n.wid)
.and_then(|wid| wid.get(arena))
{
r.query_on_first_type(QueryOrder::OutsideFirst, |s: &FocusNode| {
index = s.tab_index;
});
};
index
}
fn insert_node(&mut self, parent: NodeId, node_id: NodeId, wid: WidgetId, arena: &TreeArena) {
enum TreePosition {
BeforeSibling,
SubTree,
AfterSibling,
}
fn locate_position(
dst: &Vec<WidgetId>,
base: &Vec<WidgetId>,
arena: &TreeArena,
) -> TreePosition {
assert!(dst.len() > 1 && base.len() > 1);
let cnt = dst
.iter()
.rev()
.zip(base.iter().rev())
.take_while(|(wid1, wid2)| wid1 == wid2)
.count();
if dst.len() == cnt {
return TreePosition::SubTree;
}
let parent = dst[dst.len() - cnt];
let lh = dst[dst.len() - cnt - 1];
let rh = base[base.len() - cnt - 1];
for id in parent.children(arena) {
if id == lh {
return TreePosition::BeforeSibling;
} else if id == rh {
return TreePosition::AfterSibling;
}
}
TreePosition::AfterSibling
}
fn collect_sub_ancestors(
wid: WidgetId,
pid: Option<WidgetId>,
arena: &TreeArena,
) -> Vec<WidgetId> {
if let Some(pid) = pid {
let mut arr: Vec<WidgetId> = wid.ancestors(arena).take_while(|id| *id != pid).collect();
arr.push(pid);
arr
} else {
wid.ancestors(arena).collect()
}
}
let pwid = self.assert_get(parent).wid;
let path = collect_sub_ancestors(wid, pwid, arena);
let mut before_sibling = None;
let mut afrer_sibling = None;
let mut children = vec![];
for id in parent.children(&self.arena) {
let wid = self.arena.get(id).and_then(|node| node.get().wid).unwrap();
let path2 = collect_sub_ancestors(wid, pwid, arena);
match locate_position(&path, &path2, arena) {
TreePosition::BeforeSibling => before_sibling = Some(id),
TreePosition::SubTree => children.push(id),
TreePosition::AfterSibling => afrer_sibling = Some(id),
}
if before_sibling.is_some() {
break;
}
}
if let Some(id) = before_sibling {
id.insert_before(node_id, &mut self.arena);
} else if let Some(id) = afrer_sibling {
id.insert_after(node_id, &mut self.arena);
} else {
parent.append(node_id, &mut self.arena);
}
for child in children {
node_id.append(child, &mut self.arena);
}
}
fn get(&self, node_id: NodeId) -> Option<&FocusNodeInfo> {
self.arena.get(node_id).map(|n| n.get())
}
fn assert_get(&self, node_id: NodeId) -> &FocusNodeInfo {
self.get(node_id).expect("focus not exists in the `tree`")
}
}
impl Dispatcher {
pub fn next_focus_widget(&mut self, tree: &WidgetTree) {
self.focus_mgr.borrow_mut().request_next_focus(&tree.arena);
self.refresh_focus(tree);
}
pub fn prev_focus_widget(&mut self, tree: &WidgetTree) {
self.focus_mgr.borrow_mut().request_prev_focus(&tree.arena);
self.refresh_focus(tree);
}
pub fn blur(&mut self, tree: &mut WidgetTree) -> Option<WidgetId> {
self.change_focusing_to(None, tree)
}
pub fn focusing(&self) -> Option<WidgetId> { self.focus_mgr.borrow_mut().focusing }
pub fn refresh_focus(&mut self, tree: &WidgetTree) {
self.focus_mgr.borrow_mut().refresh(&tree.arena);
let focusing = self.focus_mgr.borrow().focusing;
if self.focus_widgets.get(0) != focusing.as_ref() {
self.change_focusing_to(focusing, tree);
}
}
pub fn focus(&mut self, wid: WidgetId, tree: &WidgetTree) {
self.change_focusing_to(Some(wid), tree);
}
fn change_focusing_to(&mut self, node: Option<WidgetId>, tree: &WidgetTree) -> Option<WidgetId> {
let Self { focus_mgr, info, .. } = self;
let old_widgets = &self.focus_widgets;
let new_widgets = node.map_or(vec![], |wid| wid.ancestors(&tree.arena).collect::<Vec<_>>());
let old = old_widgets
.get(0)
.filter(|wid| !(*wid).is_dropped(&tree.arena))
.copied();
if let Some(wid) = old {
let mut focus_event = FocusEvent::new(wid, tree, info);
wid
.assert_get(&tree.arena)
.query_on_first_type(QueryOrder::InnerFirst, |blur: &BlurListener| {
blur.dispatch(&mut focus_event)
})
};
let common_ancestors = common_ancestors(&new_widgets, old_widgets);
if let Some(wid) = old_widgets
.iter()
.find(|wid| !(*wid).is_dropped(&tree.arena))
{
let mut focus_event = FocusEvent::new(*wid, tree, info);
tree.capture_event_with(
&mut focus_event,
|focus_out_capture: &FocusOutCaptureListener, event| {
if !common_ancestors.contains(&event.current_target()) {
focus_out_capture.dispatch(event);
}
},
);
tree.bubble_event_with(&mut focus_event, |focus_out: &FocusOutListener, event| {
if common_ancestors.contains(&event.current_target()) {
event.stop_propagation();
} else {
focus_out.dispatch(event);
}
});
};
if let Some(wid) = node {
let mut focus_event = FocusEvent::new(wid, tree, info);
wid
.assert_get(&tree.arena)
.query_on_first_type(QueryOrder::InnerFirst, |focus: &FocusListener| {
focus.dispatch(&mut focus_event)
});
let mut focus_event = FocusEvent::new(wid, tree, info);
tree.capture_event_with(
&mut focus_event,
|focus_in_capture: &FocusInCaptureListener, event| {
if !common_ancestors.contains(&event.current_target()) {
focus_in_capture.dispatch(event);
}
},
);
tree.bubble_event_with(&mut focus_event, |focus_in: &FocusInListener, event| {
if common_ancestors.contains(&event.current_target()) {
event.stop_propagation();
} else {
focus_in.dispatch(event);
}
});
}
self.focus_widgets = new_widgets;
focus_mgr.borrow_mut().focusing = node;
old
}
}
fn common_ancestors(path: &[WidgetId], path2: &[WidgetId]) -> HashSet<WidgetId> {
let it = path
.iter()
.rev()
.zip(path2.iter().rev())
.take_while(|(a, b)| a == b)
.map(|(a, _)| a);
let mut set = HashSet::new();
set.extend(it);
set
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helper::*;
use std::{cell::RefCell, rc::Rc};
#[test]
fn two_auto_focus() {
let size = Size::zero();
let widget = widget! {
MockMulti {
MockBox { size, auto_focus: true, }
MockBox { size, auto_focus: true, }
}
};
let mut wnd = TestWindow::new(widget);
let Window { dispatcher, widget_tree, .. } = &mut *wnd;
dispatcher.refresh_focus(widget_tree);
let id = widget_tree.root().first_child(&widget_tree.arena);
assert!(id.is_some());
assert_eq!(dispatcher.focusing(), id);
}
#[test]
fn on_auto_focus() {
let size = Size::zero();
let widget = widget! {
MockMulti {
MockBox { size }
MockBox { size, auto_focus: true}
}
};
let mut wnd = TestWindow::new(widget);
let Window { dispatcher, widget_tree, .. } = &mut *wnd;
let id = widget_tree
.root()
.first_child(&widget_tree.arena)
.and_then(|p| p.next_sibling(&widget_tree.arena));
assert!(id.is_some());
dispatcher.refresh_focus(widget_tree);
assert_eq!(dispatcher.focusing(), id);
}
#[test]
fn tab_index() {
let size = Size::zero();
let widget = widget! {
MockMulti {
MockBox { size, tab_index: -1, }
MockBox { size, tab_index: 0, }
MockBox { size, tab_index: 1, auto_focus: true}
MockBox { size, tab_index: 2, }
MockMulti { tab_index: 4, MockBox { size, tab_index: 3, } }
MockBox { size, tab_index: 0 }
}
};
let mut wnd = TestWindow::new(widget);
let Window { dispatcher, widget_tree, .. } = &mut *wnd;
dispatcher.refresh_focus(widget_tree);
let arena = &widget_tree.arena;
let negative = widget_tree.root().first_child(arena).unwrap();
let id0 = negative.next_sibling(arena).unwrap();
let id1 = id0.next_sibling(arena).unwrap();
let id2 = id1.next_sibling(arena).unwrap();
let id4 = id2.next_sibling(arena).unwrap();
let id3 = id4.first_child(arena).unwrap();
let id0_0 = id4.next_sibling(arena).unwrap();
{
dispatcher.next_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id2));
dispatcher.next_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id3));
dispatcher.next_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id4));
dispatcher.next_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id0));
dispatcher.next_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id0_0));
dispatcher.next_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id1));
dispatcher.prev_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id0_0));
dispatcher.prev_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id0));
dispatcher.prev_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id4));
dispatcher.prev_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id3));
dispatcher.prev_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id2));
dispatcher.prev_focus_widget(widget_tree);
assert_eq!(dispatcher.focusing(), Some(id1));
}
}
#[test]
fn focus_event() {
#[derive(Debug, Default)]
struct EmbedFocus {
log: Rc<RefCell<Vec<&'static str>>>,
}
impl Compose for EmbedFocus {
fn compose(this: State<Self>) -> Widget {
widget! {
states { this: this.into_writable() }
MockBox {
size: INFINITY_SIZE,
on_focus: move |_| { this.log.borrow_mut().push("focus parent"); },
on_blur: move |_| { this.log.borrow_mut().push("blur parent"); },
on_focus_in: move |_| { this.log.borrow_mut().push("focusin parent"); },
on_focus_out: move |_| { this.log.borrow_mut().push("focusout parent"); },
MockBox {
size: Size::zero(),
on_focus: move |_| { this.log.borrow_mut().push("focus child"); },
on_blur: move |_| { this.log.borrow_mut().push("blur child"); },
on_focus_in: move |_| { this.log.borrow_mut().push("focusin child"); },
on_focus_out: move |_| { this.log.borrow_mut().push("focusout child"); },
}
}
}
}
}
let widget = EmbedFocus::default();
let log = widget.log.clone();
let mut wnd = TestWindow::new(widget);
let Window { dispatcher, widget_tree: tree, .. } = &mut *wnd;
let parent = tree.root();
let child = parent.first_child(&tree.arena).unwrap();
dispatcher.refresh_focus(tree);
dispatcher.focus(child, tree);
assert_eq!(
&*log.borrow(),
&["focus child", "focusin child", "focusin parent"]
);
log.borrow_mut().clear();
dispatcher.focus(parent, tree);
assert_eq!(
&*log.borrow(),
&["blur child", "focusout child", "focus parent",]
);
log.borrow_mut().clear();
dispatcher.blur(tree);
assert_eq!(&*log.borrow(), &["blur parent", "focusout parent",]);
}
#[test]
fn dynamic_focus() {
let visible = Stateful::new(Some(()));
let w = widget! {
states { visible: visible.clone_stateful() }
MockMulti{
Option::map(*visible, |_| widget!{
MockBox {
size: Size::default(),
on_tap: move |_| {},
}
})
}
};
let mut wnd = TestWindow::new(w);
let focus_id = wnd.dispatcher.focusing();
wnd.draw_frame();
*visible.state_ref() = None;
wnd.draw_frame();
*visible.state_ref() = Some(());
wnd.draw_frame();
assert_eq!(wnd.dispatcher.focusing(), focus_id);
}
#[test]
fn scope_node_request_focus() {
let w = widget! {
MockMulti{
MockBox{
size: Size::zero(),
on_key_down: move |_| {}
}
FocusScope {
MockBox{
size: Size::zero(),
MockBox{
size: Size::zero(),
on_key_down: move |_| {}
}
}
}
MockBox{
size: Size::zero(),
on_key_down: move |_| {}
}
}
};
let mut wnd = TestWindow::new(w);
wnd.draw_frame();
let Window { dispatcher, widget_tree: tree, .. } = &mut *wnd;
let first_box = tree.root().first_child(&tree.arena);
let focus_scope = first_box.unwrap().next_sibling(&tree.arena);
dispatcher.focus_mgr.borrow_mut().request_focusing = Some(focus_scope);
let inner_box = focus_scope.unwrap().first_child(&tree.arena);
dispatcher.refresh_focus(tree);
assert_eq!(dispatcher.focusing(), inner_box);
}
}