nightshade 0.14.1

A cross-platform data-oriented game engine.
Documentation
use nalgebra_glm::{Vec2, Vec4};

use crate::ecs::text::components::{TextAlignment, VerticalAlignment};
use crate::ecs::ui::builder::UiTreeBuilder;
use crate::ecs::ui::components::*;
use crate::ecs::ui::layout_types::FlowDirection;
use crate::ecs::ui::state::{UiBase, UiHover};
use crate::ecs::ui::units::{Ab, Rl};

use crate::prelude::*;
impl<'a> UiTreeBuilder<'a> {
    pub fn add_tree_view(&mut self, multi_select: bool) -> freecs::Entity {
        let scroll_entity = self.add_scroll_area_fill(2.0, 0.0);
        let (content_entity, thumb_entity, track_entity) =
            widget::<UiScrollAreaData>(self.world_mut(), scroll_entity)
                .map(|d| (d.content_entity, d.thumb_entity, d.track_entity))
                .unwrap_or((scroll_entity, scroll_entity, scroll_entity));

        self.world_mut().ui.set_ui_tree_view(
            scroll_entity,
            UiTreeViewData {
                selected_nodes: Vec::new(),
                multi_select,
                node_entities: Vec::new(),
                changed: false,
                content_entity,
                context_menu_node: None,
                filter_text: String::new(),
                filter_active: false,
                pre_filter_expanded: std::collections::HashMap::new(),
                scroll_offset: 0.0,
                thumb_entity,
                track_entity,
                thumb_dragging: false,
                thumb_drag_start_offset: 0.0,
            },
        );
        if let Some(interaction) = self
            .world_mut()
            .ui
            .get_ui_node_interaction_mut(scroll_entity)
        {
            interaction.accessible_role = Some(AccessibleRole::Tree);
        }

        scroll_entity
    }

    pub fn add_tree_node(
        &mut self,
        tree: freecs::Entity,
        parent_container: freecs::Entity,
        label: &str,
        depth: usize,
        user_data: u64,
    ) -> freecs::Entity {
        self.add_tree_node_with_icon(tree, parent_container, None, label, depth, user_data)
    }

    pub fn add_tree_node_with_icon(
        &mut self,
        tree: freecs::Entity,
        parent_container: freecs::Entity,
        icon: Option<crate::ecs::ui::icons::IconGlyph>,
        label: &str,
        depth: usize,
        user_data: u64,
    ) -> freecs::Entity {
        let theme = self
            .world_mut()
            .resources
            .retained_ui
            .theme_state
            .active_theme();
        let font_size = theme.font_size;
        let row_height = font_size * 1.4;
        let indent = depth as f32 * 16.0;

        let arrow_slot = self.world_mut().resources.text.cache.add_text("\u{25B6}");
        let label_slot = self.world_mut().resources.text.cache.add_text(label);

        let wrapper_entity = self
            .add_node()
            .flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, 0.0)))
            .flow(FlowDirection::Vertical, 0.0, 0.0)
            .entity();

        if let Some(parent) = self.world_mut().core.get_parent_mut(wrapper_entity) {
            *parent = crate::ecs::transform::components::Parent(Some(parent_container));
        }
        self.world_mut()
            .resources
            .transform_state
            .children_cache_valid = false;

        self.push_parent(wrapper_entity);

        let row_entity = self
            .add_node()
            .flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, row_height)))
            .with_rect(2.0, 0.0, Vec4::new(0.0, 0.0, 0.0, 0.0))
            .color_raw::<UiBase>(Vec4::new(0.0, 0.0, 0.0, 0.0))
            .with_theme_color::<UiHover>(ThemeColor::BackgroundHover)
            .with_interaction()
            .with_transition::<UiHover>(8.0, 6.0)
            .with_cursor_icon(winit::window::CursorIcon::Pointer)
            .flow(FlowDirection::Horizontal, 4.0, 2.0)
            .entity();

        self.push_parent(row_entity);

        if indent > 0.0 {
            self.add_node()
                .flow_child(Ab(Vec2::new(indent, row_height)))
                .entity();
        }

        let arrow_entity = self
            .add_node()
            .flow_child(Ab(Vec2::new(16.0, row_height)))
            .with_text_slot(arrow_slot, font_size * 0.8)
            .with_text_alignment(TextAlignment::Center, VerticalAlignment::Middle)
            .with_theme_color::<UiBase>(ThemeColor::Text)
            .entity();

        if let Some(icon) = icon {
            self.add_node()
                .flow_child(Ab(Vec2::new(18.0, row_height)))
                .with_icon(icon, font_size)
                .with_theme_color::<UiBase>(ThemeColor::Text)
                .entity();
        }

        self.add_node()
            .flow_child(Rl(Vec2::new(0.0, 100.0)))
            .flex_grow(1.0)
            .with_text_slot(label_slot, font_size)
            .with_text_alignment(TextAlignment::Left, VerticalAlignment::Middle)
            .with_theme_color::<UiBase>(ThemeColor::Text)
            .entity();

        self.pop_parent();

        let children_container = self
            .add_node()
            .flow_child(Rl(Vec2::new(100.0, 0.0)) + Ab(Vec2::new(0.0, 0.0)))
            .flow(FlowDirection::Vertical, 0.0, 0.0)
            .with_visible(false)
            .entity();

        self.pop_parent();

        let node_entity = row_entity;

        let existing_nodes = if let Some(tree_data) = self.world_mut().ui.get_ui_tree_view(tree) {
            tree_data.node_entities.clone()
        } else {
            Vec::new()
        };

        let parent_node = existing_nodes.iter().find_map(|&candidate| {
            if let Some(nd) = self.world_mut().ui.get_ui_tree_node(candidate)
                && nd.children_container == parent_container
            {
                return Some(candidate);
            }
            None
        });

        self.world_mut().ui.set_ui_tree_node(
            node_entity,
            UiTreeNodeData {
                label: label.to_string(),
                text_slot: label_slot,
                depth,
                expanded: false,
                selected: false,
                row_entity,
                arrow_entity,
                arrow_text_slot: arrow_slot,
                children_container,
                user_data,
                parent_node,
                wrapper_entity,
                lazy: false,
                lazy_loaded: false,
            },
        );
        if let Some(interaction) = self.world_mut().ui.get_ui_node_interaction_mut(node_entity) {
            interaction.accessible_role = Some(AccessibleRole::TreeItem);
        }

        if let Some(tree_data) = self.world_mut().ui.get_ui_tree_view_mut(tree) {
            tree_data.node_entities.push(node_entity);
        }

        node_entity
    }

    pub fn add_tree_node_lazy(
        &mut self,
        tree: freecs::Entity,
        parent_container: freecs::Entity,
        label: &str,
        depth: usize,
        user_data: u64,
    ) -> freecs::Entity {
        let node = self.add_tree_node(tree, parent_container, label, depth, user_data);
        if let Some(data) = self.world_mut().ui.get_ui_tree_node_mut(node) {
            data.lazy = true;
        }
        node
    }
}