aga 2.0.0

AgenticGraphicsAcceleration — standalone agentic-first GPU rendering backend; wgpu replacement with Vulkan, OpenGL, and complete ontology
Documentation
//! TreeView widget with hierarchical expand/collapse nodes.

use crate::core::{Color, Position, Rect, TextStyle};
use crate::ontology::{
    AgentAction, AgentCapability, Discoverable, SemanticRole, UiNode, WidgetSchema,
};
use crate::paint::Painter;
use crate::widget::Widget;

/// A node in a tree hierarchy.
#[derive(Debug, Clone)]
pub struct TreeNode {
    pub id: String,
    pub label: String,
    pub children: Vec<TreeNode>,
    pub expanded: bool,
}

impl TreeNode {
    #[must_use]
    pub fn new(id: impl Into<String>, label: impl Into<String>) -> Self {
        Self {
            id: id.into(),
            label: label.into(),
            children: Vec::new(),
            expanded: false,
        }
    }

    #[must_use]
    pub fn child(mut self, child: TreeNode) -> Self {
        self.children.push(child);
        self
    }

    #[must_use]
    pub fn expanded(mut self, expanded: bool) -> Self {
        self.expanded = expanded;
        self
    }

    /// Count the total visible (expanded) nodes starting from this node.
    fn visible_count(&self) -> usize {
        let mut count = 1; // self
        if self.expanded {
            for child in &self.children {
                count += child.visible_count();
            }
        }
        count
    }

    /// Recursively draw this node and its visible children.
    fn draw_recursive(
        &self,
        painter: &mut dyn Painter,
        x: f32,
        y: &mut f32,
        row_height: f32,
        area: &Rect,
        indent: f32,
    ) {
        if *y + row_height < area.y || *y > area.y + area.height {
            *y += row_height;
            if self.expanded {
                for child in &self.children {
                    child.draw_recursive(painter, x + indent, y, row_height, area, indent);
                }
            }
            return;
        }

        let style = TextStyle {
            font_size: 13.0,
            color: Color::WHITE,
            ..TextStyle::default()
        };

        // Expand/collapse arrow if has children
        if !self.children.is_empty() {
            let arrow = if self.expanded { "" } else { "" };
            painter.text(
                Position::new(x, *y + (row_height - style.font_size) * 0.5),
                arrow,
                &style,
            );
        }

        painter.text(
            Position::new(x + 16.0, *y + (row_height - style.font_size) * 0.5),
            &self.label,
            &style,
        );

        *y += row_height;

        if self.expanded {
            for child in &self.children {
                child.draw_recursive(painter, x + indent, y, row_height, area, indent);
            }
        }
    }
}

/// A hierarchical tree view widget.
pub struct TreeView {
    pub id: String,
    pub root: TreeNode,
    bg_color: Option<Color>,
    fg_color: Option<Color>,
    corner_radius: Option<f32>,
    font_size: Option<f32>,
    is_bold: bool,
}

impl TreeView {
    #[must_use]
    pub fn new(id: impl Into<String>, root: TreeNode) -> Self {
        Self {
            id: id.into(),
            root,
            bg_color: None,
            fg_color: None,
            corner_radius: None,
            font_size: None,
            is_bold: false,
        }
    }

    #[must_use]
    pub fn bg(mut self, color: Color) -> Self {
        self.bg_color = Some(color);
        self
    }

    #[must_use]
    pub fn fg(mut self, color: Color) -> Self {
        self.fg_color = Some(color);
        self
    }

    #[must_use]
    pub fn rounded(mut self, radius: f32) -> Self {
        self.corner_radius = Some(radius);
        self
    }

    #[must_use]
    pub fn text_size(mut self, size: f32) -> Self {
        self.font_size = Some(size);
        self
    }

    #[must_use]
    pub fn bold(mut self) -> Self {
        self.is_bold = true;
        self
    }
}

impl Widget for TreeView {
    fn draw(&self, painter: &mut dyn Painter, area: Rect) {
        let bg = self.bg_color.unwrap_or(Color::rgba(0.1, 0.1, 0.13, 1.0));
        let radius = self.corner_radius.unwrap_or(3.0);
        painter.fill_rect(area, bg, radius);

        let row_height = 24.0;
        let indent = 18.0;
        let mut y = area.y;

        self.root
            .draw_recursive(painter, area.x + 4.0, &mut y, row_height, &area, indent);
    }

    fn ui_node(&self) -> UiNode {
        UiNode::new("TreeView", SemanticRole::TreeNode).with_id(&self.id)
    }
}

impl Discoverable for TreeView {
    fn schema(&self) -> WidgetSchema {
        WidgetSchema::new(
            "TreeView",
            "A hierarchical tree view",
            SemanticRole::TreeNode,
        )
    }

    fn capabilities(&self) -> Vec<AgentCapability> {
        vec![
            AgentCapability::Focusable,
            AgentCapability::Expandable {
                expanded: self.root.expanded,
            },
        ]
    }

    fn actions(&self) -> Vec<AgentAction> {
        vec![
            AgentAction::simple("expand", "Expand a node", true),
            AgentAction::simple("collapse", "Collapse a node", true),
        ]
    }

    fn semantic_role(&self) -> SemanticRole {
        SemanticRole::TreeNode
    }

    fn agent_state(&self) -> serde_json::Value {
        serde_json::json!({
            "root_label": self.root.label,
            "root_expanded": self.root.expanded,
            "visible_nodes": self.root.visible_count(),
        })
    }

    fn execute_action(
        &mut self,
        action: &str,
        _params: &serde_json::Value,
    ) -> Result<serde_json::Value, String> {
        match action {
            "expand" => {
                self.root.expanded = true;
                Ok(serde_json::json!({ "expanded": true }))
            }
            "collapse" => {
                self.root.expanded = false;
                Ok(serde_json::json!({ "expanded": false }))
            }
            _ => Err(format!("Unknown action: {action}")),
        }
    }

    fn agent_id(&self) -> Option<&str> {
        Some(&self.id)
    }
}