Newer
Older
use bevy::{
prelude::{Component, Reflect, ReflectComponent},
utils::HashMap,
};
#[derive(Component, Reflect, Default, Clone, Copy)]
#[reflect(Component)]
#[derive(Debug, Default, PartialEq, Eq)]
/// A struct used to track and calculate widget focusability, based on the following rule:
///
/// > Focusability set by a widget itself will _always_ override focusability set by its parent.
#[derive(Debug, Default)]
pub(crate) struct FocusTracker {
/// The focusability as set by the parent (i.e. from its props)
/// The focusability as set by the widget itself (i.e. from its render function)
impl FocusTree {
/// Add the given focusable index to the tree
pub fn add(&mut self, index: WrappedIndex, widget_context: &Tree) {
// Cases to handle:
// 1. Tree empty -> insert root node
// 2. Tree not empty
// a. Contains parent -> insert child node
// b. Not contains parent -> demote and replace root node
let mut current_index = index;
while let Some(parent) = widget_context.get_parent(current_index) {
current_index = parent;
if self.contains(parent) {
self.tree.add(index, Some(parent));
return;
}
}
// Set root node
self.tree.add(index, None);
self.focus(index);
}
if self.current_focus == Some(index) {
self.blur();
}
if self.tree.root_node == Some(index) {
self.tree.remove(index);
} else {
self.tree.remove_and_reparent(index);
}
}
/// Checks if the given index is present in the tree
pub fn contains(&self, index: WrappedIndex) -> bool {
}
/// Clear the tree and remove the current focus
pub fn clear(&mut self) {
self.tree = Tree::default();
self.blur();
}
/// Set the current focus
self.current_focus = Some(index);
}
/// Remove the current focus
///
/// This returns focus to the root node
self.current_focus = self.tree.root_node;
self.current_focus
}
/// Change focus to the next focusable index
self.current_focus = self.peek_next();
self.current_focus
}
/// Change focus to the previous focusable index
self.current_focus = self.peek_prev();
self.current_focus
}
/// Peek the next focusable index without actually changing focus
if let Some(index) = self.current_focus {
// === Enter Children === //
if let Some(child) = self.tree.get_first_child(index) {
return Some(child);
}
// === Enter Siblings === //
if let Some(sibling) = self.tree.get_next_sibling(index) {
return Some(sibling);
}
// === Go Back Up === //
let mut next = index;
while let Some(parent) = self.tree.get_parent(next) {
if let Some(uncle) = self.tree.get_next_sibling(parent) {
return Some(uncle);
}
next = parent;
}
}
// Default to root node to begin the cycle again
}
/// Peek the previous focusable index without actually changing focus
if let Some(index) = self.current_focus {
// === Enter Siblings === //
if let Some(sibling) = self.tree.get_prev_sibling(index) {
let mut next = sibling;
while let Some(child) = self.tree.get_last_child(next) {
next = child;
}
return Some(next);
}
// === Enter Parent === //
if let Some(parent) = self.tree.get_parent(index) {
return Some(parent);
}
// === Go Back Down === //
let mut next = index;
while let Some(child) = self.tree.get_last_child(next) {
next = child;
}
return Some(next);
}
impl FocusTracker {
/// Set the focusability of a widget
///
/// The `is_parent_defined` parameter is important because it dictates how the focusability is stored
/// and calculated.
///
/// Focusability map:
/// * `Some(true)` - This widget is focusable
/// * `Some(false)` - This widget is not focusable
/// * `None` - This widget can be either focusable or not
///
/// # Arguments
///
/// * `index`: The widget ID
/// * `focusable`: The focusability of the widget
/// * `is_parent_defined`: Does this setting come from the parent or the widget itself?
///
/// returns: ()
let map = if is_parent_defined {
&mut self.parents
} else {
&mut self.widgets
};
if let Some(focusable) = focusable {
map.insert(index, focusable);
} else {
map.remove(&index);
}
}
/// Get the focusability for the given widget
///
/// # Arguments
///
/// * `index`: The widget ID
///
/// returns: Option<bool>
pub fn get_focusability(&self, index: WrappedIndex) -> Option<bool> {
if let Some(focusable) = self.widgets.get(&index) {
Some(*focusable)
} else {
self.parents.get(&index).copied()
}
#[cfg(test)]
mod tests {
use crate::focus_tree::FocusTree;
use crate::node::WrappedIndex;
use crate::tree::Tree;
use bevy::prelude::Entity;
#[test]
fn next_should_cycle() {
let mut focus_tree = FocusTree::default();
tree.add(a_b_a, Some(a_b));
focus_tree.add(a, &tree);
focus_tree.add(a_a, &tree);
focus_tree.add(a_b, &tree);
focus_tree.add(a_a_a, &tree);
focus_tree.add(a_a_a_a, &tree);
focus_tree.add(a_a_a_b, &tree);
focus_tree.add(a_b_a, &tree);
assert_eq!(Some(a_a), focus_tree.next());
assert_eq!(Some(a_a_a), focus_tree.next());
assert_eq!(Some(a_a_a_a), focus_tree.next());
assert_eq!(Some(a_a_a_b), focus_tree.next());
assert_eq!(Some(a_b), focus_tree.next());
assert_eq!(Some(a_b_a), focus_tree.next());
assert_eq!(Some(a), focus_tree.next());
assert_eq!(Some(a_a), focus_tree.next());
// etc.
}
#[test]
fn prev_should_cycle() {
let mut focus_tree = FocusTree::default();
tree.add(a_b_a, Some(a_b));
focus_tree.add(a, &tree);
focus_tree.add(a_a, &tree);
focus_tree.add(a_b, &tree);
focus_tree.add(a_a_a, &tree);
focus_tree.add(a_a_a_a, &tree);
focus_tree.add(a_a_a_b, &tree);
focus_tree.add(a_b_a, &tree);
assert_eq!(Some(a_b_a), focus_tree.prev());
assert_eq!(Some(a_b), focus_tree.prev());
assert_eq!(Some(a_a_a_b), focus_tree.prev());
assert_eq!(Some(a_a_a_a), focus_tree.prev());
assert_eq!(Some(a_a_a), focus_tree.prev());
assert_eq!(Some(a_a), focus_tree.prev());
assert_eq!(Some(a), focus_tree.prev());
assert_eq!(Some(a_b_a), focus_tree.prev());