tui-lipan 0.1.0

Opinionated, component-based TUI framework for Rust - declarative components, reconciliation, layout engine, focus, overlays, and rich widgets on top of ratatui.
Documentation
use std::sync::Arc;

use crate::callback::Callback;
use crate::core::node::{NodeKind, WidgetNode};
use crate::style::{Rect, Style};
use crate::widgets::Orientation;

use super::{Splitter, SplitterResizeEvent, sizes_to_weights};

#[derive(Clone)]
pub struct SplitterNode {
    pub orientation: Orientation,
    pub weights: Vec<f32>,
    pub weights_nonce: u32,
    pub split_id: Option<Arc<str>>,
    pub on_resize: Option<Callback<SplitterResizeEvent>>,
    pub min_size: u16,
    pub join_frame: bool,
    pub handle_symbol: char,
    pub handle_style: Style,
    pub handle_hover_style: Style,
    pub handle_active_style: Style,
    pub handle_rects: Vec<Rect>,
    pub pane_sizes: Vec<u16>,
    pub active_handle: Option<usize>,
}

impl SplitterNode {
    pub(crate) fn handle_at(&self, x: i16, y: i16) -> Option<usize> {
        self.handle_rects
            .iter()
            .position(|rect| rect.contains(x, y))
    }

    pub(crate) fn set_drag_sizes(&mut self, sizes: Vec<u16>) {
        self.pane_sizes = sizes;
        self.weights = sizes_to_weights(&self.pane_sizes);
    }
}

impl WidgetNode for SplitterNode {
    fn has_on_click(&self) -> bool {
        !self.handle_rects.is_empty()
    }

    fn is_hoverable(&self) -> bool {
        !self.handle_rects.is_empty()
    }

    fn hit_test_refinement(&self, x: i16, y: i16, _rect: Rect) -> Option<bool> {
        let hit = self.handle_at(x, y).is_some();
        Some(hit)
    }
}

impl From<Splitter> for SplitterNode {
    fn from(value: Splitter) -> Self {
        Self {
            orientation: value.orientation,
            weights: value.weights,
            weights_nonce: value.weights_nonce,
            split_id: value.split_id.clone(),
            on_resize: value.on_resize.clone(),
            min_size: value.min_size,
            join_frame: value.join_frame,
            handle_symbol: value.handle_symbol,
            handle_style: value.handle_style,
            handle_hover_style: value.handle_hover_style,
            handle_active_style: value.handle_active_style,
            handle_rects: Vec::new(),
            pane_sizes: Vec::new(),
            active_handle: None,
        }
    }
}

impl From<SplitterNode> for NodeKind {
    fn from(node: SplitterNode) -> Self {
        NodeKind::Splitter(node)
    }
}