tuigui 0.23.0

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

/// All widgets implement this trait.
/// You can also create your own widgets by
/// implementing this trait yourself!
pub trait Widget {
    /// Logic for drawing to a canvas
    fn draw(&mut self, canvas: &mut Canvas, state_frame: Option<&EventStateFrame>);

    /// Information about the widget, such as the size information like,
    /// does your widget require a fixed size? Or is it dynamic with a
    /// minumum and maximum size?
    fn widget_info(&self) -> WidgetInfo;

    /// Returns a mutable reference to the widget's internal special data
    fn widget_data(&mut self) -> &mut WidgetData;
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
/// Widget data for internal use of the library
pub struct WidgetData {
    /// Animation data
    pub animation_data: AnimationData,
}

impl WidgetData {
    pub fn new() -> Self {
        Self {
            animation_data: AnimationData::new(),
        }
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
/// Animation data for widgets
pub struct AnimationData {
    creation_time: std::time::Instant,
}

impl AnimationData {
    pub fn new() -> Self {
        Self {
            creation_time: std::time::Instant::now(),
        }
    }

    /// Reset animation data
    pub fn reset(&mut self) {
        *self = Self::new();
    }

    /// The time the animation data was created
    pub fn creation_time(&self) -> std::time::Instant {
        return self.creation_time;
    }

    /// How long the animation data has existed for
    pub fn duration(&self) -> std::time::Duration {
        return std::time::Instant::now() - self.creation_time();
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
/// Widget information
pub struct WidgetInfo {
    /// Size information dictating whether or not
    /// the widget is fixed size or dynamic
    pub size_info: WidgetSizeInfo,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
/// Information on how a widget's size can be changed
pub enum WidgetSizeInfoCanChange {
    /// The widget size cannot grow or shrink
    CannotChange,
    /// The widget size can be bigger
    CanGrow {
        cols: bool,
        rows: bool,
    },
    /// The widget size can be smaller
    CanShrink {
        cols: bool,
        rows: bool,
    },
    /// The widget size is invalid to begin with
    Invalid,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
/// Widget size information
pub enum WidgetSizeInfo {
    Dynamic {
        min: Option<Size>,
        max: Option<Size>,
    },
    Fixed(Size),
}

impl WidgetSizeInfo {
    /// Get minimum size
    pub fn get_min(&self) -> Size {
        match self {
            Self::Dynamic { min, .. } => min.unwrap_or(Size::zero()),
            Self::Fixed(fixed) => *fixed,
        }
    }

    /// Get maximum size
    pub fn get_max(&self) -> Size {
        match self {
            Self::Dynamic { max, .. } => max.unwrap_or(Size::new(u16::MAX, u16::MAX)),
            Self::Fixed(fixed) => *fixed,
        }
    }

    /// Assuming the passed in size is for something such as the canvas
    /// that the widget will be drawn to, this function will return
    /// information on how that size can be potentially changed while
    /// still being valid
    pub fn can_change_size(&self, size: Size) -> Vec<WidgetSizeInfoCanChange> {
        if self.correct_size(size) != size {
            return vec![WidgetSizeInfoCanChange::Invalid];
        }

        match self {
            Self::Dynamic { min, max } => {
                let mut potentials: Vec<WidgetSizeInfoCanChange> = Vec::new();

                let min_size = min.unwrap_or(Size::zero());
                let max_size = max.unwrap_or(Size::same(u16::MAX));

                if min_size.area() < size.area() {
                    let cols: bool = min_size.cols < size.cols;
                    let rows: bool = min_size.rows < size.rows;

                    potentials.push(WidgetSizeInfoCanChange::CanShrink {
                        cols,
                        rows,
                    });
                }

                if max_size.area() > size.area() {
                    let cols: bool = max_size.cols > size.cols;
                    let rows: bool = max_size.rows > size.rows;

                    potentials.push(WidgetSizeInfoCanChange::CanGrow {
                        cols,
                        rows,
                    });
                }

                if potentials.is_empty() {
                    potentials.push(WidgetSizeInfoCanChange::CannotChange);
                }

                potentials
            },
            Self::Fixed(_) => vec![WidgetSizeInfoCanChange::CannotChange],
        }
    }

    /// Correct a size, this is used for things like correcting
    /// a canvas in a parent container widget. If the child's canvas is too
    /// small or too big, this function will correct the size according
    /// to the method owner's widget size information
    pub fn correct_size(&self, size: Size) -> Size {
        let mut new_size = size;

        match self {
            Self::Dynamic { min, max } => {
                let min = min.unwrap_or(Size::new(0, 0));

                let mut check_max = true;

                if new_size.cols < min.cols {
                    new_size.cols = min.cols;
                    check_max = false;
                }

                if new_size.rows < min.rows {
                    new_size.rows = min.rows;
                    check_max = false;
                }

                if check_max {
                    if let Some(max) = max {
                        if new_size.cols > max.cols {
                            new_size.cols = max.cols;
                        }

                        if new_size.rows > max.rows {
                            new_size.rows = max.rows;
                        }
                    }
                }
            },
            Self::Fixed(fixed_size) => new_size = *fixed_size,
        };

        return new_size;
    }
}