tuigui 0.23.0

An easy-to-use, highly extensible, and speedy TUI library.
Documentation
use crate::preludes::widget_creation::*;

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
/// Orientation
pub enum Orientation {
    /// Side to side
    Horizontal,
    /// Up and down
    Vertical,
}

/// Return: (Single, Opposite)
fn get_single_position(position: Position, orien: Orientation) -> (i16, i16) {
    match orien {
        Orientation::Horizontal => (position.col, position.row),
        Orientation::Vertical => (position.row, position.col),
    }
}

/// Return: (Single, Opposite)
fn get_single_size(size: Size, orien: Orientation) -> (u16, u16) {
    match orien {
        Orientation::Horizontal => (size.cols, size.rows),
        Orientation::Vertical => (size.rows, size.cols),
    }
}

fn from_single_position(single: i16, opposite: i16, orien: Orientation) -> Position {
    return Position {
        col: match orien {
            Orientation::Horizontal => single,
            Orientation::Vertical => opposite,
        },
        row: match orien {
            Orientation::Horizontal => opposite,
            Orientation::Vertical => single,
        },
    };
}

fn from_single_size(single: u16, opposite: u16, orien: Orientation) -> Size {
    return Size {
        cols: match orien {
            Orientation::Horizontal => single,
            Orientation::Vertical => opposite,
        },
        rows: match orien {
            Orientation::Horizontal => opposite,
            Orientation::Vertical => single,
        },
    };
}

fn is_single_col(orien: Orientation) -> bool {
    return orien == Orientation::Horizontal;
}

/// Stack a bunch of widgets horizontally or vertically
pub struct BoxContainer<W: Widget> {
    pub orientation: Orientation,
    pub widgets: Vec<W>,
    widget_data: WidgetData,
}

impl<W: Widget> BoxContainer<W> {
    pub fn new(orientation: Orientation, widgets: Vec<W>) -> Self {
        Self {
            orientation,
            widgets,
            widget_data: WidgetData::new(),
        }
    }
}

impl<W: Widget> Widget for BoxContainer<W> {
    fn draw(&mut self, canvas: &mut Canvas, state_frame: Option<&EventStateFrame>) {
        if canvas.is_visible() == false {
            return;
        }

        let count = self.widgets.len();

        let (mut empty_space, canvas_single_opposite) = get_single_size(canvas.transform.size, self.orientation);

        let default_size: u16 = empty_space / (count as u16);

        let mut canvas_vec: Vec<Canvas> = Vec::new();

        let mut can_be_increased: Vec<usize> = Vec::new();

        for (widget_index, widget) in self.widgets.iter().enumerate() {
            let widget_info = widget.widget_info();

            let mut inner = canvas.new_child(Transform::new(
                Position::zero(),
                from_single_size(default_size, canvas_single_opposite, self.orientation),
            ));

            inner.transform.size = widget_info.size_info.correct_size(inner.transform.size);

            empty_space = empty_space.saturating_sub(get_single_size(inner.transform.size, self.orientation).0);

            for change_potential in widget_info.size_info.can_change_size(inner.transform.size) {
                if let WidgetSizeInfoCanChange::CanGrow { cols, rows } = change_potential {
                    let check = match is_single_col(self.orientation) {
                        true => cols,
                        false => rows,
                    };

                    if check {
                        can_be_increased.push(widget_index);

                        break;
                    }
                }
            }

            canvas_vec.push(inner);
        }

        let mut prev_empty: Option<u16> = None;

        loop {
            if empty_space > 0 {
                let mut to_be_removed: Vec<usize> = Vec::new();

                let increase = empty_space / can_be_increased.len() as u16;

                for (cbi, widget_index) in can_be_increased.iter().enumerate() {
                    let widget_index = *widget_index;

                    let widget_info = self.widgets[widget_index].widget_info();

                    let inner = &mut canvas_vec[widget_index];

                    let (mut single, opposite) = get_single_size(inner.transform.size, self.orientation);

                    let original_single = single;

                    single += increase;

                    inner.transform.size = from_single_size(single, opposite, self.orientation);

                    inner.transform.size = self.widgets[widget_index]
                        .widget_info()
                        .size_info
                        .correct_size(inner.transform.size);

                    let single = get_single_size(inner.transform.size, self.orientation).0;

                    let mut increased_by = single - original_single;

                    if empty_space.overflowing_sub(increased_by).1 {
                        let increased_by_original = increased_by;
                        increased_by = empty_space;
                        let diff = increased_by_original - increased_by;

                        let (mut s, o) = get_single_size(inner.transform.size, self.orientation);

                        s -= diff;

                        let corrected_size = from_single_size(s, o, self.orientation);

                        inner.transform.size = corrected_size;
                    }

                    empty_space = empty_space.saturating_sub(increased_by);

                    for change_potential in widget_info.size_info.can_change_size(inner.transform.size) {
                        if let WidgetSizeInfoCanChange::CanGrow { cols, rows } = change_potential {
                            let check = match is_single_col(self.orientation) {
                                true => cols,
                                false => rows,
                            };

                            if check == false {
                                to_be_removed.push(cbi);

                                break;
                            }
                        }
                    }

                    if empty_space == 0 {
                        break;
                    }
                }

                for tbr in to_be_removed {
                    can_be_increased.remove(tbr);
                }
            }

            if let Some(prev_empty) = prev_empty {
                if empty_space == prev_empty {
                    break;
                }
            }

            prev_empty = Some(empty_space);
        }

        if empty_space > 0 {
            let canvas_vec_len = canvas_vec.len(); // Borrow checker bit me in the ass...
            let inner = &mut canvas_vec[canvas_vec_len - 1];

            let (mut single, opposite) = get_single_size(inner.transform.size, self.orientation);

            single += empty_space;

            inner.transform.size = from_single_size(single, opposite, self.orientation);
        }

        let mut offset: i16 = 0;

        for inner in canvas_vec.iter_mut() {
            let single_size = get_single_size(inner.transform.size, self.orientation).0;

            let opposite_position = get_single_position(inner.transform.position, self.orientation).1;

            inner.transform.position = from_single_position(offset, opposite_position, self.orientation);

            offset += single_size as i16;
        }

        for i in 0..self.widgets.len() {
            self.widgets[i].draw(&mut canvas_vec[i], state_frame);
        }
    }

    fn widget_info(&self) -> WidgetInfo {
        let mut size_min: Option<Size> = None;

        for widget in self.widgets.iter() {
            let sm = size_min.unwrap_or(Size::new(0, 0));
            match widget.widget_info().size_info {
                WidgetSizeInfo::Fixed(fixed_size) => {
                    size_min = Some(sm + fixed_size);
                },
                WidgetSizeInfo::Dynamic { min, max: _ } => {
                    size_min = Some(sm + min.unwrap_or(Size::new(0, 0)));
                },
            };
        }

        return WidgetInfo {
            size_info: WidgetSizeInfo::Dynamic {
                min: size_min,
                max: None,
            },
        };
    }

    fn widget_data(&mut self) -> &mut WidgetData {
        return &mut self.widget_data;
    }
}