weavetui_core 0.1.3

Core traits and utilities for weavetui TUI framework.
Documentation
//! This module provides utility functions for managing `Component` instances, including drawing, updating, event handling, and theme propagation across the component tree.

use ratatui::{layout::Rect, Frame};
use tokio::sync::mpsc::UnboundedSender;

use crate::{
    event::{Action, Event},
    keyboard::KeyBindings,
    theme::ThemeManager,
    Component,
};

/// Recursively draws a component and its children.
///
/// This function first checks if the component has an area and is active. If so, it calls
/// the component's `draw` method. It then recursively iterates over the children, sets their area if not
/// already set, and calls `handle_draw` for each child, effectively performing a depth-first traversal of the component tree.
///
/// # Arguments
///
/// * `c` - The component to draw.
/// * `f` - The frame to draw on.
pub fn handle_draw<T: Component + ?Sized>(c: &mut T, f: &mut Frame<'_>) {
    if let Some(area) = c.area() {
        if c.is_active() {
            c.draw(f, area);

            for child in c.get_children().values_mut() {
                if child.area().is_none() {
                    child.set_area(area);
                }
                handle_draw(child.as_mut(), f);
            }
        }
    }
}

/// Recursively updates a component and its children based on a received action.
///
/// This function calls the component's `update` method if it is active, and then
/// recursively calls `update` for all its children, ensuring that the action is propagated
/// throughout the entire component subtree.
///
/// # Arguments
///
/// * `c` - The component to update.
/// * `action` - The action to apply.
pub fn update<T: Component + ?Sized>(c: &mut T, action: &Action) {
    if c.is_active() {
        c.update(action);

        for child in c.get_children().values_mut() {
            update(child.as_mut(), action);
        }
    }
}

/// Recursively handles a string message for a component and its children.
///
/// This function calls the component's `on_event` method if it is active, and then
/// recursively calls `handle_message` for all its children, propagating the message down the component tree.
///
/// # Arguments
///
/// * `c` - The component to handle the message.
/// * `message` - The message to handle.
pub fn handle_message<T: Component + ?Sized>(c: &mut T, message: &str) {
    if c.is_active() {
        c.on_event(message);

        for child in c.get_children().values_mut() {
            handle_message(child.as_mut(), message);
        }
    }
}

/// Recursively initializes a component and its children.
///
/// This function calls the component's `init` method, and then recursively calls `init`
/// for all its children, ensuring the entire component subtree is initialized.
///
/// # Arguments
///
/// * `c` - The component to initialize.
/// * `area` - The area to initialize the component with.
pub fn init<T: Component + ?Sized>(c: &mut T, area: Rect) {
    c.init(area);

    for child in c.get_children().values_mut() {
        init(child.as_mut(), area);
    }
}

/// Recursively sets the action handler for a component and its children.
///
/// This function calls the component's `register_action_handler` method, and then
/// recursively calls `receive_action_handler` for all its children, ensuring that all components
/// in the subtree can send actions.
///
/// # Arguments
///
/// * `c` - The component to set the action handler for.
/// * `tx` - The sender part of the action channel.
pub fn receive_action_handler<T: Component + ?Sized>(c: &mut T, tx: UnboundedSender<Action>) {
    c.register_action_handler(tx.clone());

    for child in c.get_children().values_mut() {
        receive_action_handler(child.as_mut(), tx.clone());
    }
}

/// Recursively handles an event for a component and its children, collecting all resulting actions.
///
/// If the component is active, this function dispatches the event to the appropriate
/// handler (`handle_key_events`, `handle_mouse_events`, etc.). Any resulting action is
/// collected. It then recursively calls `handle_event_for` for all children and extends
/// the list of actions with the results, effectively gathering all actions from the component subtree.
///
/// # Arguments
///
/// * `c` - The component to handle the event for.
/// * `event` - The optional `Event` to handle.
///
/// # Returns
///
/// A `Vec<Action>` containing all actions generated by the component and its children.
pub fn handle_event_for<T: Component + ?Sized>(c: &mut T, event: &Option<Event>) -> Vec<Action> {
    if c.is_active() {
        let mut actions = vec![];

        let action = match event {
            Some(Event::Key(key_event)) => c.handle_key_events(*key_event),
            Some(Event::Mouse(mouse_event)) => c.handle_mouse_events(*mouse_event),
            Some(Event::Tick) => c.handle_tick_event(),
            Some(Event::Render) => c.handle_frame_event(),
            Some(Event::Paste(s)) => c.handle_paste_event(s),
            _ => None,
        };

        if let Some(action) = action {
            actions.push(action);
        }

        for child in c.get_children().values_mut() {
            let child_actions = handle_event_for(child.as_mut(), event);
            actions.extend(child_actions);
        }

        actions
    } else {
        vec![]
    }
}

/// Recursively collects custom keybindings from a component and its children.
///
/// This function gets the keybindings from the component and extends the provided `KeyBindings`
/// with them. It then recursively calls itself for all children, aggregating all keybindings
/// from the entire component subtree.
///
/// # Arguments
///
/// * `c` - The component to get the keybindings from.
/// * `kb` - The `KeyBindings` to extend.
pub fn custom_keybindings<T: Component + ?Sized>(c: &mut T, kb: &mut KeyBindings) {
    let other_kb = c.keybindings();
    kb.extend(other_kb);

    for child in c.get_children().values_mut() {
        custom_keybindings(child.as_mut(), kb);
    }
}

/// Recursively sets the theme manager for a component and its children.
///
/// This function sets the theme manager for the given component and then recursively
/// calls itself for all children, ensuring that the theme is propagated throughout
/// the entire component subtree.
///
/// # Arguments
///
/// * `c` - The component to set the theme manager for.
/// * `th` - The `ThemeManager` to set.
pub fn handle_theme<T: Component + ?Sized>(c: &mut T, th: &ThemeManager) {
    c.set_theme_manager(th.clone());

    for child in c.get_children().values_mut() {
        handle_theme(child.as_mut(), th);
    }
}