pub mod tabindex;
#[cfg(test)]
mod tests;
use rdom_core::{NodeId, NodeType, Position, Selection};
use crate::node::{TuiNodeExt, is_descendant_or_self};
use crate::{TuiDispatchExt, TuiDom, TuiEvent};
pub fn focus_node(dom: &mut TuiDom, new_focus: Option<NodeId>) {
let old = dom.focused();
if old == new_focus {
return;
}
if let Some(old_id) = old {
let mut blur = TuiEvent::blur();
let _ = dom.dispatch_tui_event(old_id, &mut blur);
let mut out = TuiEvent::focusout();
let _ = dom.dispatch_tui_event(old_id, &mut out);
}
dom.set_focused(new_focus);
if let Some(new_id) = new_focus {
seed_caret_for_editable_focus(dom, new_id);
}
if let Some(new_id) = new_focus {
let mut foc = TuiEvent::focus();
let _ = dom.dispatch_tui_event(new_id, &mut foc);
let mut fin = TuiEvent::focusin();
let _ = dom.dispatch_tui_event(new_id, &mut fin);
}
}
fn seed_caret_for_editable_focus(dom: &mut TuiDom, id: NodeId) {
if !dom.node(id).is_editable() {
return;
}
if let Some(sel) = dom.selection()
&& is_descendant_or_self(dom, sel.focus.node, id)
{
return;
}
let text_child = dom
.node(id)
.child_nodes()
.find(|n| n.node_type() == NodeType::Text)
.map(|n| n.id());
if let Some(text_id) = text_child {
dom.set_selection(Some(Selection::caret(Position::new(text_id, 0))));
}
}
pub fn nearest_focusable_ancestor(dom: &TuiDom, start: NodeId) -> Option<NodeId> {
let mut cur = Some(start);
while let Some(id) = cur {
if tabindex::is_focusable(dom, id) {
return Some(id);
}
cur = dom.node(id).parent_node().map(|p| p.id());
}
None
}