use crate::compat::{HashMap, Rc, Vec};
use super::node::Node;
struct RetainedScope {
tree: Rc<Node>,
index: Option<HashMap<crate::compat::String, Option<Vec<usize>>>>,
}
thread_local! {
#[allow(clippy::missing_const_for_thread_local)]
static RETAINED: core::cell::RefCell<Option<RetainedScope>> =
const { core::cell::RefCell::new(None) };
}
pub(crate) fn with_retained_subtrees<R>(previous: Option<Rc<Node>>, f: impl FnOnce() -> R) -> R {
struct Restore(Option<RetainedScope>);
impl Drop for Restore {
fn drop(&mut self) {
let previous = self.0.take();
let _ = RETAINED.try_with(|cell| {
*cell.borrow_mut() = previous;
});
}
}
let saved = RETAINED
.try_with(|cell| cell.replace(previous.map(|tree| RetainedScope { tree, index: None })))
.ok()
.flatten();
let _restore = Restore(saved);
f()
}
pub(crate) fn retained_children(widget: &str, key: &str, revision: u64) -> Option<Vec<Node>> {
RETAINED
.try_with(|cell| {
let mut slot = cell.try_borrow_mut().ok()?;
let scope = slot.as_mut()?;
if scope.index.is_none() {
let index = index_revisioned(&scope.tree);
scope.index = Some(index);
}
let path = match scope.index.as_ref()?.get(key)? {
Some(path) => path.clone(),
None => return None,
};
let node = node_at_path(&scope.tree, &path)?;
if node.widget != widget || node.subtree_revision != Some(revision) {
return None;
}
Some(node.children.clone())
})
.ok()
.flatten()
}
fn index_revisioned(root: &Node) -> HashMap<crate::compat::String, Option<Vec<usize>>> {
let mut out = HashMap::new();
index_children(root, &mut Vec::new(), &mut out);
out
}
fn index_children(
node: &Node,
path: &mut Vec<usize>,
out: &mut HashMap<crate::compat::String, Option<Vec<usize>>>,
) {
if let (Some(key), Some(_)) = (node.key.as_ref(), node.subtree_revision) {
if out.contains_key(key) {
out.insert(key.clone(), None);
} else {
out.insert(key.clone(), Some(path.clone()));
}
}
for (index, child) in node.children.iter().enumerate() {
path.push(index);
index_children(child, path, out);
path.pop();
}
}
fn node_at_path<'a>(root: &'a Node, path: &[usize]) -> Option<&'a Node> {
let mut node = root;
for &index in path {
node = node.children.get(index)?;
}
Some(node)
}