use crate::component::{ComponentRc, ComponentVTable};
use crate::items::{ItemRef, ItemVTable};
use core::pin::Pin;
use vtable::*;
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum TraversalOrder {
BackToFront,
FrontToBack,
}
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct VisitChildrenResult(u64);
impl VisitChildrenResult {
pub const CONTINUE: Self = Self(u64::MAX);
pub fn abort(item_index: usize, index_within_repeater: usize) -> Self {
assert!(item_index < u32::MAX as usize);
assert!(index_within_repeater < u32::MAX as usize);
Self(item_index as u64 | (index_within_repeater as u64) << 32)
}
pub fn has_aborted(&self) -> bool {
self.0 != Self::CONTINUE.0
}
pub fn aborted_index(&self) -> Option<usize> {
if self.0 != Self::CONTINUE.0 {
Some((self.0 & 0xffff_ffff) as usize)
} else {
None
}
}
pub fn aborted_indexes(&self) -> Option<(usize, usize)> {
if self.0 != Self::CONTINUE.0 {
Some(((self.0 & 0xffff_ffff) as usize, (self.0 >> 32) as usize))
} else {
None
}
}
}
impl core::fmt::Debug for VisitChildrenResult {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.0 == Self::CONTINUE.0 {
write!(f, "CONTINUE")
} else {
write!(f, "({},{})", (self.0 & 0xffff_ffff) as usize, (self.0 >> 32) as usize)
}
}
}
#[repr(u8)]
#[derive(Debug)]
pub enum ItemTreeNode<T> {
Item {
item: vtable::VOffset<T, ItemVTable, vtable::AllowPin>,
children_count: u32,
children_index: u32,
parent_index: u32,
},
DynamicTree {
index: usize,
parent_index: u32,
},
}
impl<T> ItemTreeNode<T> {
pub fn parent_index(&self) -> usize {
match self {
ItemTreeNode::Item { parent_index, .. } => *parent_index as usize,
ItemTreeNode::DynamicTree { parent_index, .. } => *parent_index as usize,
}
}
}
#[repr(C)]
#[vtable]
pub struct ItemVisitorVTable {
visit_item: fn(
VRefMut<ItemVisitorVTable>,
component: &VRc<ComponentVTable, vtable::Dyn>,
index: usize,
item: Pin<VRef<ItemVTable>>,
) -> VisitChildrenResult,
drop: fn(VRefMut<ItemVisitorVTable>),
}
pub type ItemVisitorRefMut<'a> = vtable::VRefMut<'a, ItemVisitorVTable>;
impl<T: FnMut(&ComponentRc, usize, Pin<ItemRef>) -> VisitChildrenResult> ItemVisitor for T {
fn visit_item(
&mut self,
component: &ComponentRc,
index: usize,
item: Pin<ItemRef>,
) -> VisitChildrenResult {
self(component, index, item)
}
}
pub enum ItemVisitorResult<State> {
Continue(State),
Abort,
}
pub fn visit_items<State>(
component: &ComponentRc,
order: TraversalOrder,
mut visitor: impl FnMut(&ComponentRc, Pin<ItemRef>, usize, &State) -> ItemVisitorResult<State>,
state: State,
) -> VisitChildrenResult {
visit_internal(
component,
order,
&mut |component, item, index, state| (visitor(component, item, index, state), ()),
&mut |_, _, _, r| r,
-1,
&state,
)
}
pub fn visit_items_with_post_visit<State, PostVisitState>(
component: &ComponentRc,
order: TraversalOrder,
mut visitor: impl FnMut(
&ComponentRc,
Pin<ItemRef>,
usize,
&State,
) -> (ItemVisitorResult<State>, PostVisitState),
mut post_visitor: impl FnMut(
&ComponentRc,
Pin<ItemRef>,
PostVisitState,
VisitChildrenResult,
) -> VisitChildrenResult,
state: State,
) -> VisitChildrenResult {
visit_internal(component, order, &mut visitor, &mut post_visitor, -1, &state)
}
fn visit_internal<State, PostVisitState>(
component: &ComponentRc,
order: TraversalOrder,
visitor: &mut impl FnMut(
&ComponentRc,
Pin<ItemRef>,
usize,
&State,
) -> (ItemVisitorResult<State>, PostVisitState),
post_visitor: &mut impl FnMut(
&ComponentRc,
Pin<ItemRef>,
PostVisitState,
VisitChildrenResult,
) -> VisitChildrenResult,
index: isize,
state: &State,
) -> VisitChildrenResult {
let mut actual_visitor = |component: &ComponentRc,
index: usize,
item: Pin<ItemRef>|
-> VisitChildrenResult {
match visitor(component, item, index, state) {
(ItemVisitorResult::Continue(state), post_visit_state) => {
let r =
visit_internal(component, order, visitor, post_visitor, index as isize, &state);
post_visitor(component, item, post_visit_state, r)
}
(ItemVisitorResult::Abort, post_visit_state) => post_visitor(
component,
item,
post_visit_state,
VisitChildrenResult::abort(index, 0),
),
}
};
vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
VRc::borrow_pin(component).as_ref().visit_children_item(index, order, actual_visitor)
}
pub fn visit_item_tree<Base>(
base: Pin<&Base>,
component: &ComponentRc,
item_tree: &[ItemTreeNode<Base>],
index: isize,
order: TraversalOrder,
mut visitor: vtable::VRefMut<ItemVisitorVTable>,
visit_dynamic: impl Fn(
Pin<&Base>,
TraversalOrder,
vtable::VRefMut<ItemVisitorVTable>,
usize,
) -> VisitChildrenResult,
) -> VisitChildrenResult {
let mut visit_at_index = |idx: usize| -> VisitChildrenResult {
match &item_tree[idx] {
ItemTreeNode::Item { item, .. } => {
visitor.visit_item(component, idx, item.apply_pin(base))
}
ItemTreeNode::DynamicTree { index, .. } => {
if let Some(sub_idx) =
visit_dynamic(base, order, visitor.borrow_mut(), *index).aborted_index()
{
VisitChildrenResult::abort(idx, sub_idx)
} else {
VisitChildrenResult::CONTINUE
}
}
}
};
if index == -1 {
visit_at_index(0)
} else {
match &item_tree[index as usize] {
ItemTreeNode::Item { children_index, children_count, .. } => {
for c in 0..*children_count {
let idx = match order {
TraversalOrder::BackToFront => (*children_index + c),
TraversalOrder::FrontToBack => (*children_index + *children_count - c - 1),
} as usize;
let maybe_abort_index = visit_at_index(idx);
if maybe_abort_index.has_aborted() {
return maybe_abort_index;
}
}
}
ItemTreeNode::DynamicTree { .. } => panic!("should not be called with dynamic items"),
};
VisitChildrenResult::CONTINUE
}
}
#[cfg(feature = "ffi")]
pub(crate) mod ffi {
#![allow(unsafe_code)]
use super::*;
use crate::slice::Slice;
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_visit_item_tree(
component: &ComponentRc,
item_tree: Slice<ItemTreeNode<u8>>,
index: isize,
order: TraversalOrder,
visitor: VRefMut<ItemVisitorVTable>,
visit_dynamic: extern "C" fn(
base: &u8,
order: TraversalOrder,
visitor: vtable::VRefMut<ItemVisitorVTable>,
dyn_index: usize,
) -> VisitChildrenResult,
) -> VisitChildrenResult {
crate::item_tree::visit_item_tree(
Pin::new_unchecked(&*(&**component as *const Dyn as *const u8)),
component,
item_tree.as_slice(),
index,
order,
visitor,
|a, b, c, d| visit_dynamic(a.get_ref(), b, c, d),
)
}
}