cursive-image 0.0.6

Image view for the Cursive TUI library
Documentation
use super::{image::*, image_view::*, sizing::*, state::*, vec2f::*};

use cursive::*;

const TYPE_NAME: &str = "ImageView";

impl View for ImageView {
    fn type_name(&self) -> &'static str {
        TYPE_NAME
    }

    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        match &self.image {
            Some(image) => {
                let size = self.image_size_in_cells(image);
                match self.sizing {
                    Sizing::Shrink => {
                        let constraint = constraint.into_vec2f();
                        if size > constraint { size.scale_to_fit(constraint) } else { size }
                    }

                    Sizing::Fit => size.scale_to_fit(constraint.into_vec2f()),

                    Sizing::Scale(scale) => size.scale(scale),
                }
                .into_vec2()
            }

            None => (1, 1).into(),
        }
    }

    fn needs_relayout(&self) -> bool {
        false
    }

    fn layout(&mut self, size: Vec2) {
        if self.size != Some(size) {
            self.size = Some(size);
            self.set_content_offset(None);
            self.set_state(State::Ready);
        }
    }

    fn draw(&self, printer: &Printer) {
        let Some(image) = &self.image else {
            return;
        };

        if self.get_content_offset() != Some(printer.content_offset) {
            self.set_content_offset(Some(printer.content_offset));

            // Force draw
            self.set_state(State::Ready);
        }

        if self.get_state().is_ready() {
            let mut window = None;

            let constraint = printer.output_size.into_vec2f();
            let size = match self.sizing {
                Sizing::Shrink => {
                    let size = self.image_size_in_cells(image);
                    if size > constraint { size.scale_to_fit(constraint) } else { size }
                }

                Sizing::Fit => self.scale_to_fit_image_size_in_cells(image, constraint),

                Sizing::Scale(scale) => {
                    let mut size = self.image_size_in_cells(image).scale(scale);

                    let too_wide = size.x > constraint.x;
                    let too_high = size.y > constraint.y;

                    if too_wide || too_high {
                        if too_wide {
                            size.x = constraint.x;
                        }

                        if too_high {
                            size.y = constraint.y;
                        }

                        // Window is in pixels
                        window = Some(Rect::from_size(
                            self.into_pixels(printer.content_offset.into_vec2f()),
                            self.into_pixels(size),
                        ));
                    }

                    size
                }
            };

            let size = size.into_vec2();
            let position: Vec2 = (
                (printer.offset.x + self.align.h.get_offset(size.x, printer.output_size.x)),
                (printer.offset.y + self.align.v.get_offset(size.y, printer.output_size.y)),
            )
                .into();

            _ = image.show(position, Some(size), window);

            self.set_state(State::Visible);
        }
    }
}

impl ImageView {
    fn into_pixels(&self, vec: Vec2f) -> Vec2 {
        vec.scale_vec(self.cell_size).into_vec2()
    }

    fn image_size_in_cells(&self, image: &Image) -> Vec2f {
        image.size.into_vec2f().reverse_scale_vec(self.cell_size)
    }

    fn scale_to_fit_image_size_in_cells(&self, image: &Image, constraint: Vec2f) -> Vec2f {
        // Note that the image size is in pixels
        // Because we are already scaling it to cells there is no need to convert it to cells
        // However, we do need to convert it to the cell aspect ratio
        let size: Vec2f = (image.size.x as f64 * self.cell_aspect_ratio, image.size.y as f64).into();
        size.scale_to_fit(constraint)
    }
}

// Utils

// fn image_size_in_pixels(image: &DynamicImage) -> Vec2f {
//     (image.width() as f64, image.height() as f64).into()
// }