fp_tui 0.2.2

A very basic tui library
Documentation
use std::rc::Rc;
use std::cell::RefCell;

use crate::{ContainerWidget, DisplayBuffer, FrameWidget, PositioningAlgorithm, Widget};

use crate::common::{
    BoundingBox,
    BorderStyle
};

/// #### Description
/// A frame widget that has a border.
///
pub struct BorderedFrameWidget {
    /// #### Description
    /// A basic frame widget
    ///
    frame: Rc<RefCell<FrameWidget>>,
    
    /// #### Description
    /// The style for the border of the widget
    ///
    border_style: BorderStyle,

    /// #### Description
    /// The optional title of the frame
    ///
    title: Option<String>
}

impl BorderedFrameWidget {

    /// #### Description
    /// Creates a bordered frame widget
    /// 
    /// #### Arguments
    /// * `x`: [u32] - The x position of the widget
    /// * `y`: [u32] - The y position of the widget
    /// * `width`: [u32] - The width of the widget
    /// * `height`: [u32] - The height of the widget
    /// * `title`: [Option]<&[str]> - The optional title of the widget
    /// * `border_style`: [BorderStyle] - The border style for the widget
    ///
    /// #### Returns
    /// [Rc]<[RefCell]<[BorderedFrameWidget]>> - A bordered frame widget with 
    /// the specified attributes
    ///
    pub fn new(
        x: i32,
        y: i32,
        width: u32,
        height: u32,
        title: Option<&str>,
        border_style: BorderStyle
    ) -> Rc<RefCell<Self>> {
        let frame = FrameWidget::new(x, y, width, height);
        
        return Rc::new(RefCell::new(Self {
            frame,
            title: title.map(|str| {str.to_string()}),
            border_style
        }));
    }
}

impl Widget for BorderedFrameWidget {
    fn draw(
        &mut self, 
        positioning_algorithm: &Box<dyn PositioningAlgorithm>, 
        display_buffer: &mut DisplayBuffer
    ) {
        let bounds = self.frame.borrow().get_bounding_box();

        // The position of the widget relative to the viewport
        let abs_pos = positioning_algorithm.calculate_position(
            bounds.get_position()
        );

        self.frame.borrow_mut().draw(positioning_algorithm, display_buffer);

        // draw border and title
        let left_bound = abs_pos.get_x();
        let right_bound = left_bound + bounds.get_width() as i32;

        let top_bound = abs_pos.get_y();
        let bottom_bound = top_bound + bounds.get_height() as i32;

        for y in top_bound..bottom_bound {
            for x in left_bound..right_bound {
                if x == left_bound && y == top_bound {
                    // top left
                    display_buffer.set_char(
                        self.border_style.get_top_left(),
                        x, y
                    );
                }
                else if x == right_bound - 1 && y == top_bound {
                    // top right
                    display_buffer.set_char(
                        self.border_style.get_top_right(),
                        x, y
                    );
                }
                else if x == left_bound && y == bottom_bound - 1 {
                    // bottom left
                    display_buffer.set_char(
                        self.border_style.get_bottom_left(),
                        x, y
                    );
                }
                else if x == right_bound - 1 && y == bottom_bound - 1 {
                    // bottom right
                    display_buffer.set_char(
                        self.border_style.get_bottom_right(),
                        x, y
                    );
                }
                else if x == left_bound || x == right_bound - 1 {
                    // vertical
                    display_buffer.set_char(
                        self.border_style.get_vertical(),
                        x, y
                    );
                }
                else if y == top_bound || y == bottom_bound - 1 {
                    // horizontal
                    display_buffer.set_char(
                        self.border_style.get_horizontal(),
                        x, y
                    );
                }
            }
        }

        if let Some(title) = &self.title {
            if left_bound+1 <= right_bound-1 {
                let chars = title.chars().collect::<Vec<_>>();
                for x in left_bound+1..right_bound-1 {
                    if let Some(c) = chars.get((x - (left_bound + 1)) as usize) {
                        display_buffer.set_char(*c, x, top_bound);
                    }
                    else {
                        break;
                    }
                }
            }
        }
    }

    fn get_bounding_box(&self) -> BoundingBox {
        return self.frame.borrow().get_bounding_box();
    }
}

impl ContainerWidget for BorderedFrameWidget {
    fn add_child(
            &mut self,
            positioning_algorithm: Box<dyn PositioningAlgorithm>,
            widget: Rc<RefCell<dyn Widget>>
        ) {
        self.frame.borrow_mut().add_child(positioning_algorithm, widget);
    }
}