cursive-tree 0.0.9

Tree view for the Cursive TUI library
Documentation
use super::{super::backend::*, depth::*, kind::*, list::*, path::*, state::*, symbol::*};

use {
    cursive::{style::*, utils::span::*, *},
    std::{borrow::*, marker::*},
};

//
// Node
//

/// Tree node.
pub struct Node<BackendT>
where
    BackendT: TreeBackend,
{
    /// Depth.
    pub depth: usize,

    /// Kind.
    pub kind: NodeKind,

    /// ID.
    pub id: BackendT::ID,

    /// Label.
    pub label: SpannedString<Style>,

    /// Label size.
    ///
    /// Cached so we only have to calculate it once.
    ///
    /// > Note that labels can ostensibly have heights >1, i.e. they can be multiline, however this
    /// is *not* currently supported by [TreeView](super::super::TreeView).
    pub label_size: Vec2,

    /// State for [Branch](NodeKind::Branch) node.
    pub branch_state: BranchState,

    /// Children for [Branch](NodeKind::Branch) node.
    pub children: Option<NodeList<BackendT>>,

    /// Data.
    pub data: Option<BackendT::Data>,

    backend: PhantomData<BackendT>,
}

impl<BackendT> Node<BackendT>
where
    BackendT: TreeBackend,
{
    /// Constructor.
    pub fn new<IdT, LabelT>(depth: usize, kind: NodeKind, id: IdT, label: LabelT) -> Self
    where
        IdT: Into<BackendT::ID>,
        LabelT: Into<SpannedString<Style>>,
    {
        let id = id.into();
        let label = label.into();
        let label_size = (label.width(), 1).into();
        Self {
            depth,
            kind,
            id,
            label,
            label_size,
            branch_state: Default::default(),
            children: None,
            data: None,
            backend: Default::default(),
        }
    }

    /// Symbol.
    ///
    /// Its char count is always 1.
    pub fn symbol(&self, context: BackendT::Context) -> Symbol<'_> {
        BackendT::symbol(self, context)
    }

    /// Get node at path.
    pub fn at_path(&self, path: NodePath) -> Option<&Self> {
        if path.is_empty() { Some(self) } else { self.children.as_ref().and_then(|children| children.at_path(path)) }
    }

    /// Get node at path.
    pub fn at_path_mut(&mut self, path: NodePath) -> Option<&mut Self> {
        if path.is_empty() {
            Some(self)
        } else {
            self.children.as_mut().and_then(|children| children.at_path_mut(path))
        }
    }

    /// Fill path to node.
    ///
    /// Returns true if found.
    pub fn fill_path(&self, path: &mut NodePath, node: &Self) -> bool {
        self.children.as_ref().map(|children| children.fill_path(path, node)).unwrap_or_default()
    }

    /// Fetch the branch's children from backend.
    ///
    /// If depth is [None] will populate all depths.
    ///
    /// If depth is 0 or not a [Branch](NodeKind::Branch) will do nothing.
    ///
    /// Note that populating a node will *not* automatically expand it.
    pub fn populate(&mut self, mut depth: Option<usize>, context: BackendT::Context) -> Result<(), BackendT::Error>
    where
        BackendT::Context: Clone,
    {
        if depth.is_zero() || !self.kind.is_branch() {
            return Ok(());
        }

        if self.children.is_none() {
            BackendT::populate(self, context.clone())?;
        }

        depth.decrease();
        if !depth.is_zero()
            && let Some(children) = &mut self.children
        {
            for node in children {
                node.populate(depth, context.clone())?;
            }
        }

        Ok(())
    }

    /// Add a child node.
    ///
    /// Will do nothing if not a [Branch](NodeKind::Branch).
    pub fn add_child<IdT, LabelT>(&mut self, kind: NodeKind, id: IdT, label: LabelT)
    where
        IdT: Into<BackendT::ID>,
        LabelT: Into<SpannedString<Style>>,
    {
        if !self.kind.is_branch() {
            return;
        }

        if self.children.is_none() {
            self.children = Some(Default::default());
        }

        self.children.as_mut().expect("children").add(self.depth + 1, kind, id, label);
    }

    /// Inserts a child node.
    ///
    /// Will do nothing if not a [Branch](NodeKind::Branch).
    pub fn insert_child<IdT, LabelT>(&mut self, index: usize, kind: NodeKind, id: IdT, label: LabelT)
    where
        IdT: Into<BackendT::ID>,
        LabelT: Into<SpannedString<Style>>,
    {
        if !self.kind.is_branch() {
            return;
        }

        if self.children.is_none() {
            self.children = Some(Default::default());
        }

        self.children.as_mut().expect("children").insert(index, self.depth + 1, kind, id, label);
    }

    /// Expand the branch.
    ///
    /// If depth is [None] will expand all depths.
    ///
    /// If depth is 0 or not a [Branch](NodeKind::Branch) will do nothing.
    ///
    /// Note that this *will* populate expanded nodes from the backend.
    pub fn expand(&mut self, mut depth: Option<usize>, context: BackendT::Context) -> Result<(), BackendT::Error>
    where
        BackendT::Context: Clone,
    {
        if depth.is_zero() || !self.kind.is_branch() {
            return Ok(());
        }

        if !self.branch_state.is_expanded() {
            self.populate(Some(1), context.clone())?;
            self.branch_state = BranchState::Expanded;
        }

        depth.decrease();
        if !depth.is_zero()
            && let Some(children) = &mut self.children
        {
            children.expand(depth, context.clone())?;
        }

        Ok(())
    }

    /// Collapse the branch.
    ///
    /// If depth is [None] will collapse all depths.
    ///
    /// If depth is 0 or not a [Branch](NodeKind::Branch) will do nothing.
    pub fn collapse(&mut self, mut depth: Option<usize>) {
        if depth.is_zero() || !self.kind.is_branch() {
            return;
        }

        if !self.branch_state.is_collapsed() {
            self.branch_state = BranchState::Collapsed;
        }

        depth.decrease();
        if !depth.is_zero()
            && let Some(children) = &mut self.children
        {
            children.collapse(depth);
        }
    }

    /// Toggle the branch state.
    ///
    /// Expand if collapsed, collapse if expanded.
    ///
    /// Will do nothing if not a [Branch](NodeKind::Branch).
    pub fn toggle_branch_state(&mut self, context: BackendT::Context) -> Result<(), BackendT::Error>
    where
        BackendT::Context: Clone,
    {
        if !self.kind.is_branch() {
            return Ok(());
        }

        _ = match self.branch_state {
            BranchState::Collapsed => self.expand(Some(1), context)?,
            BranchState::Expanded => self.collapse(Some(1)),
        };

        Ok(())
    }

    /// Data.
    ///
    /// If not already cached will fetch it from the backend.
    pub fn data(&mut self, context: BackendT::Context) -> Result<Option<Cow<'_, BackendT::Data>>, BackendT::Error>
    where
        BackendT::Data: Clone,
    {
        if self.data.is_none() {
            if let Some((data, cache)) = BackendT::data(self, context)? {
                if cache {
                    self.data = Some(data);
                } else {
                    return Ok(Some(Cow::Owned(data)));
                }
            }
        }

        Ok(self.data.as_ref().map(Cow::Borrowed))
    }
}