nightshade 0.13.0

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};

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 = self
            .world_mut()
            .widget::<UiScrollAreaData>(scroll_entity)
            .map(|d| d.content_entity)
            .unwrap_or(scroll_entity);

        self.world_mut().ui.set_ui_widget_state(
            scroll_entity,
            UiWidgetState::TreeView(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(),
            }),
        );
        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 {
        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)
            .without_pointer_events()
            .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.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))
            .with_color::<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)))
                .without_pointer_events()
                .done();
        }

        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)
            .without_pointer_events()
            .done();

        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)
            .without_pointer_events()
            .done();

        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)
            .without_pointer_events()
            .entity();

        self.pop_parent();

        let node_entity = row_entity;

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

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

        self.world_mut().ui.set_ui_widget_state(
            node_entity,
            UiWidgetState::TreeNode(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(UiWidgetState::TreeView(tree_data)) =
            self.world_mut().ui.get_ui_widget_state_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(UiWidgetState::TreeNode(data)) =
            self.world_mut().ui.get_ui_widget_state_mut(node)
        {
            data.lazy = true;
        }
        node
    }
}