use teksilo_core::signal::Signal;
use crate::dnd_types::ItemKey;
use crate::dnd_types::{
DragEligibility, DragSource, DropCommit, DropPosition, DropQuery, DropResponse, RowState,
};
use crate::tree_change::NodeId;
use crate::tree_model::TreeModel;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FlatEntry<K: ItemKey = NodeId> {
pub node_id: K,
pub depth: usize,
pub has_children: bool,
pub is_expanded: bool,
}
pub trait TreeDataSource: 'static {
type Item: 'static;
type Key: ItemKey;
fn visible_count(&self) -> usize;
fn with_entry<R>(
&self,
flat_index: usize,
f: impl FnOnce(&Self::Item, &FlatEntry<Self::Key>) -> R,
) -> Option<R>;
fn key_at(&self, flat_index: usize) -> Option<Self::Key>;
fn flat_index_of(&self, key: &Self::Key) -> Option<usize>;
fn parent(&self, key: &Self::Key) -> Option<Self::Key>;
fn child_keys(&self, key: &Self::Key) -> Vec<Self::Key>;
fn version_signal(&self) -> Signal<u64>;
fn is_expanded(&self, key: &Self::Key) -> bool;
fn set_expanded(&self, key: &Self::Key, expanded: bool);
fn first_changed_index(&self) -> Option<usize> {
None
}
fn contains_key(&self, key: &Self::Key) -> bool {
self.flat_index_of(key).is_some()
}
fn drag(&self, _key: &Self::Key) -> DragEligibility {
DragEligibility::NoDrag
}
fn can_accept(&self, _query: &DropQuery<'_, Self::Key>) -> DropResponse {
DropResponse::Reject
}
fn accept_drop(&self, _commit: DropCommit<'_, Self::Key>) -> bool {
false
}
fn reorder_within(
&self,
sources: &[Self::Key],
target: &Self::Key,
position: DropPosition,
) -> bool {
let mut t = Some(target.clone());
while let Some(node) = t {
if sources.iter().any(|s| s == &node) {
return false;
}
t = self.parent(&node);
}
let top: Vec<Self::Key> = sources
.iter()
.filter(|k| {
let mut p = self.parent(k);
while let Some(ancestor) = p {
if sources.iter().any(|s| s == &ancestor) {
return false;
}
p = self.parent(&ancestor);
}
true
})
.cloned()
.collect();
let mut anchor = target.clone();
let mut pos = position;
let mut moved = false;
for key in &top {
if key == &anchor {
continue;
}
if self.accept_drop(DropCommit {
source: DragSource::SameView { key: key.clone() },
target: anchor.clone(),
position: pos,
}) {
moved = true;
anchor = key.clone();
pos = DropPosition::After;
}
}
moved
}
fn on_drag_out(&self, _key: &Self::Key) {}
fn row_state(&self, _flat_index: usize) -> RowState {
RowState::Ready
}
fn request_window(&self, _range: std::ops::Range<usize>) {}
fn can_fetch_more(&self) -> bool {
false
}
fn fetch_more(&self) {}
}
pub fn tree_is_desc_or_self<T: 'static>(
tree: &TreeModel<T>,
node: NodeId,
ancestor: NodeId,
) -> bool {
let mut cur = Some(node);
while let Some(n) = cur {
if n == ancestor {
return true;
}
cur = tree.parent(n);
}
false
}
pub fn tree_apply_reorder<T: 'static>(
tree: &TreeModel<T>,
source: NodeId,
target: NodeId,
position: DropPosition,
) -> bool {
if source == target {
return false;
}
if tree_is_desc_or_self(tree, target, source) {
return false;
}
match position {
DropPosition::Into => {
let mut idx = tree.child_count(target);
if tree.parent(source) == Some(target) {
idx -= 1;
}
tree.move_node(source, target, idx);
true
}
DropPosition::Before | DropPosition::After => {
let new_parent = tree.parent(target);
let siblings: Vec<NodeId> = match new_parent {
Some(p) => tree.children(p),
None => (0..tree.root_count()).map(|i| tree.root(i)).collect(),
};
let Some(pos) = siblings.iter().position(|&s| s == target) else {
return false;
};
let mut idx = if position == DropPosition::After {
pos + 1
} else {
pos
};
if tree.parent(source) == new_parent
&& let Some(sp) = siblings.iter().position(|&s| s == source)
&& sp < idx
{
idx -= 1;
}
match new_parent {
Some(p) => tree.move_node(source, p, idx),
None => tree.move_to_root(source, idx),
}
true
}
}
}