use super::{TreeNode, TreeNodeCollection, TreeNodeCollectionImpl, TreeNodeKind};
use crate::diagnostics::controls::TopLevelGroup;
use crate::diagnostics::views::MainWindow;
use ferroui_base::collections::{FerroList, FerroListExtensions};
use ferroui_base::reactive::{CompositeDisposable, Disposable, IDisposable};
use ferroui_base::{FerroObject, Nullable, Ref, StyledElement};
use ferroui_controls::TopLevel;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub struct LogicalTreeNode;
impl LogicalTreeNode {
pub fn new(ferro_object: Ref<FerroObject>, parent: Option<&Rc<TreeNode>>) -> Rc<TreeNode> {
let logical = ferro_object.cast::<StyledElement>();
let host = ferro_object.cast::<TopLevelGroup>();
TreeNode::new_node(ferro_object, parent, None, TreeNodeKind::Logical, move |this| {
if let Some(logical) = logical {
LogicalTreeNodeCollection::new(this, logical)
} else if let Some(host) = host {
TopLevelGroupHostLogical::new(this, host)
} else {
TreeNodeCollection::empty()
}
})
}
pub fn create(control: impl Into<Nullable<FerroObject>>) -> Vec<Rc<TreeNode>> {
match control.into().0 {
Some(logical) => vec![LogicalTreeNode::new(logical, None)],
None => Vec::new(),
}
}
}
pub struct LogicalTreeNodeCollection {
control: Ref<StyledElement>,
subscription: RefCell<Option<Rc<dyn IDisposable>>>,
}
impl LogicalTreeNodeCollection {
pub fn new(owner: Weak<TreeNode>, control: Ref<StyledElement>) -> Rc<TreeNodeCollection> {
TreeNodeCollection::new(owner, Box::new(Self { control, subscription: RefCell::new(None) }))
}
}
impl TreeNodeCollectionImpl for LogicalTreeNodeCollection {
fn dispose(&self, this: &TreeNodeCollection) {
this.base_dispose();
let subscription = self.subscription.borrow().clone();
if let Some(subscription) = subscription {
subscription.dispose();
}
}
fn initialize(&self, this: &TreeNodeCollection, nodes: &FerroList<Rc<TreeNode>>) {
let owner = this.owner();
let owner = owner.as_ref().map(Rc::downgrade).unwrap_or_default();
let (added, removed, reset) = (nodes.clone(), nodes.clone(), nodes.clone());
let subscription = FerroListExtensions::for_each_item_indexed(
self.control.logical_children(),
move |i, item: &Ref<StyledElement>| {
let owner = owner.upgrade();
added.insert(i, LogicalTreeNode::new(item.clone().upcast(), owner.as_ref()));
},
move |i, _| removed.remove_at(i),
move || reset.clear(),
false,
);
*self.subscription.borrow_mut() = Some(subscription);
}
}
pub struct TopLevelGroupHostLogical {
group: Ref<TopLevelGroup>,
subscriptions: CompositeDisposable,
factory: fn(Ref<FerroObject>, Option<&Rc<TreeNode>>) -> Rc<TreeNode>,
}
impl TopLevelGroupHostLogical {
pub fn new(owner: Weak<TreeNode>, host: Ref<TopLevelGroup>) -> Rc<TreeNodeCollection> {
Self::new_with(owner, host, LogicalTreeNode::new)
}
pub fn new_with(
owner: Weak<TreeNode>,
host: Ref<TopLevelGroup>,
factory: fn(Ref<FerroObject>, Option<&Rc<TreeNode>>) -> Rc<TreeNode>,
) -> Rc<TreeNodeCollection> {
TreeNodeCollection::new(
owner,
Box::new(Self { group: host, subscriptions: CompositeDisposable::with_capacity(1), factory }),
)
}
}
impl TreeNodeCollectionImpl for TopLevelGroupHostLogical {
fn initialize(&self, this: &TreeNodeCollection, nodes: &FerroList<Rc<TreeNode>>) {
let owner = this.owner();
let items = self.group.items();
for window in items.iter() {
if window.is::<MainWindow>() {
continue;
}
nodes.add((self.factory)(window.clone().upcast(), owner.as_ref()));
}
let owner = owner.as_ref().map(Rc::downgrade).unwrap_or_default();
let factory = self.factory;
let group_on_added = {
let nodes = nodes.clone();
let owner = owner.clone();
move |e: &Ref<TopLevel>| {
if e.is::<MainWindow>() {
return;
}
nodes.add(factory(e.clone().upcast(), owner.upgrade().as_ref()));
}
};
let group_on_removed = {
let nodes = nodes.clone();
move |e: &Ref<TopLevel>| {
if e.is::<MainWindow>() {
return;
}
nodes.add(factory(e.clone().upcast(), owner.upgrade().as_ref()));
}
};
let added = self.group.added(group_on_added);
let removed = self.group.removed(group_on_removed);
self.subscriptions.add(Disposable::create(move || {
added.dispose();
removed.dispose();
}));
}
fn dispose(&self, this: &TreeNodeCollection) {
self.subscriptions.dispose();
this.base_dispose();
}
}