use dioxus_native_core::{prelude::NodeType, real_dom::NodeImmutable, tree::TreeRef, NodeId};
use freya_node_state::LayoutState;
use rustc_hash::FxHashMap;
use torin::prelude::*;
use crate::dom::DioxusDOM;
pub struct DioxusDOMAdapter<'a> {
pub rdom: &'a DioxusDOM,
valid_nodes_cache: Option<FxHashMap<NodeId, bool>>,
}
impl<'a> DioxusDOMAdapter<'a> {
pub fn new(rdom: &'a DioxusDOM) -> Self {
Self {
rdom,
valid_nodes_cache: None,
}
}
pub fn new_with_cache(rdom: &'a DioxusDOM) -> Self {
Self {
rdom,
valid_nodes_cache: Some(FxHashMap::default()),
}
}
}
impl DOMAdapter<NodeId> for DioxusDOMAdapter<'_> {
fn get_node(&self, node_id: &NodeId) -> Option<Node> {
let node = self.rdom.get(*node_id)?;
let mut size = node.get::<LayoutState>().unwrap().clone();
if *node_id == self.rdom.root_id() {
size.width = Size::Percentage(Length::new(100.0));
size.height = Size::Percentage(Length::new(100.0));
}
Some(Node {
width: size.width,
height: size.height,
minimum_width: size.minimum_width,
minimum_height: size.minimum_height,
maximum_width: size.maximum_width,
maximum_height: size.maximum_height,
direction: size.direction,
padding: size.padding,
margin: size.margin,
display: size.display,
offset_x: Length::new(size.offset_x),
offset_y: Length::new(size.offset_y),
has_layout_references: size.node_ref.is_some(),
})
}
fn height(&self, node_id: &NodeId) -> Option<u16> {
self.rdom.tree_ref().height(*node_id)
}
fn parent_of(&self, node_id: &NodeId) -> Option<NodeId> {
self.rdom.tree_ref().parent_id(*node_id)
}
fn children_of(&mut self, node_id: &NodeId) -> Vec<NodeId> {
self.rdom
.tree_ref()
.children_ids(*node_id)
.into_iter()
.filter(|id| is_node_valid(self.rdom, &mut self.valid_nodes_cache, id))
.collect::<Vec<NodeId>>()
}
fn is_node_valid(&mut self, node_id: &NodeId) -> bool {
is_node_valid(self.rdom, &mut self.valid_nodes_cache, node_id)
}
fn closest_common_parent(
&self,
node_id_a: &NodeId,
node_id_b: &NodeId,
) -> Option<(NodeId, Vec<NodeId>)> {
find_common_parent(self.rdom, *node_id_a, *node_id_b)
}
}
fn balance_heights(rdom: &DioxusDOM, base: NodeId, target: NodeId) -> Option<Vec<NodeId>> {
let tree = rdom.tree_ref();
let target_height = tree.height(target)?;
let mut current = base;
let mut path = Vec::new();
loop {
if tree.height(current)? == target_height {
break;
}
let parent_current = tree.parent_id(current);
if let Some(parent_current) = parent_current {
current = parent_current;
path.push(current);
}
}
Some(path)
}
fn find_common_parent(
rdom: &DioxusDOM,
node_a: NodeId,
node_b: NodeId,
) -> Option<(NodeId, Vec<NodeId>)> {
let tree = rdom.tree_ref();
let height_a = tree.height(node_a)?;
let height_b = tree.height(node_b)?;
let (path_a, path_b) = match height_a.cmp(&height_b) {
std::cmp::Ordering::Less => (
vec![node_a],
balance_heights(rdom, node_b, node_a).unwrap_or(vec![node_b]),
),
std::cmp::Ordering::Equal => (vec![node_a], vec![node_b]),
std::cmp::Ordering::Greater => (
balance_heights(rdom, node_a, node_b).unwrap_or(vec![node_a]),
vec![node_b],
),
};
let node_a = *path_a.last().unwrap();
let node_b = *path_b.last().unwrap();
let mut branch_a = vec![node_a];
let mut branch_b = vec![node_b];
loop {
if branch_a.last() == branch_b.last() {
let last = *branch_a.last().unwrap();
branch_a.extend(branch_b);
branch_a.extend(path_a);
branch_a.extend(path_b);
return Some((last, branch_a));
}
let parent_a = tree.parent_id(*branch_a.last().unwrap());
if let Some(parent_a) = parent_a {
branch_a.push(parent_a);
} else if rdom.root_id() != *branch_a.last().unwrap() {
break;
}
let parent_b = tree.parent_id(*branch_b.last().unwrap());
if let Some(parent_b) = parent_b {
branch_b.push(parent_b);
} else if rdom.root_id() != *branch_b.last().unwrap() {
break;
}
}
None
}
fn is_node_valid(
rdom: &DioxusDOM,
valid_nodes_cache: &mut Option<FxHashMap<NodeId, bool>>,
node_id: &NodeId,
) -> bool {
if let Some(valid_nodes_cache) = valid_nodes_cache {
if let Some(is_valid) = valid_nodes_cache.get(node_id) {
return *is_valid;
}
}
let node = rdom.get(*node_id);
let is_valid = 'validation: {
if let Some(node) = node {
let is_placeholder = matches!(*node.node_type(), NodeType::Placeholder);
if is_placeholder {
break 'validation false;
}
let tree = rdom.tree_ref();
let mut current = *node_id;
loop {
let height = tree.height(current);
if let Some(height) = height {
if height == 0 {
break;
}
}
let parent_current = tree.parent_id(current);
if let Some(parent_current) = parent_current {
current = parent_current;
}
}
current == rdom.root_id()
} else {
false
}
};
if let Some(valid_nodes_cache) = valid_nodes_cache {
valid_nodes_cache.insert(*node_id, is_valid);
}
is_valid
}