ezui 0.0.2

An eazy to use small GUI library. main purpose of this crate is to help you to make GUI for VST audio plugins.
Documentation
use crate::drawable::*;
use crate::mouse::*;
use crate::widget::*;

use derive_builder::Builder;

use glium_text_rusttype::*;

#[derive(Builder)]
#[builder(pattern = "owned")]
pub struct UiTexture<'a> {
    pub position: (f32, f32),
    pub size: (f32, f32),
    pub rotation: f32,
    texture: &'a SimpleTexture,
}

impl<'a> Widget for UiTexture<'a> {
    fn position(&self) -> (f32, f32) {
        self.position
    }

    fn size(&self) -> (f32, f32) {
        self.size
    }

    fn drawable(&self) -> Option<Drawable> {
        Some(Drawable::Texture(self.texture))
    }
    fn matrix(&self) -> [[f32; 4]; 4] {
        let mat = [
            [1.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 0.0],
            [0.0, 0.0, 1.0, 0.0],
            [0.0, 0.0, 0.0, 1.0],
        ];
        let mat = self.scale(mat);
        let mat = self.translate(mat);
        let mat = self.rotate(mat);
        mat
    }
}

impl<'a> Rotatable for UiTexture<'a> {
    fn rotation(&self) -> f32 {
        self.rotation
    }
}

#[derive(Builder)]
#[builder(pattern = "owned")]
pub struct UiButton {
    pub position: (f32, f32),
    pub size: (f32, f32),
    #[builder(setter(skip))]
    left: ButtonState,
    #[builder(setter(skip))]
    middle: ButtonState,
    #[builder(setter(skip))]
    right: ButtonState,
}

impl Widget for UiButton {
    fn position(&self) -> (f32, f32) {
        self.position
    }

    fn size(&self) -> (f32, f32) {
        self.size
    }

    fn drawable(&self) -> Option<Drawable> {
        None
    }
}

impl Pressable for UiButton {
    fn left(&self) -> &ButtonState {
        &self.left
    }
    fn middle(&self) -> &ButtonState {
        &self.middle
    }
    fn right(&self) -> &ButtonState {
        &self.right
    }

    fn update(&mut self, mouse: &MouseStatus) {
        macro_rules! update {
            ($name:ident) => {
                self.$name = ButtonState::Released;
                if let ButtonState::Pressed(x, y) = mouse.button().$name() {
                    if collision_check_rect_point(self.position(), self.size(), (x, y)) {
                        let pos = mouse.position();
                        self.$name = ButtonState::Pressed(pos.0 - x, pos.1 - y);
                    }
                }
            };
        }

        update!(left);
        update!(middle);
        update!(right);
    }
}

#[derive(Builder)]
#[builder(build_fn(skip), pattern = "owned")]
pub struct UiText<'a> {
    pub position: (f32, f32),
    pub size: (f32, f32),
    pub color: (f32, f32, f32, f32),

    text: TextDisplay<&'a FontTexture>,
    #[builder(setter(skip))]
    text_size: (f32, f32),
}

#[allow(dead_code)]
impl<'a> UiTextBuilder<'a> {
    pub fn build(self) -> Result<UiText<'a>, String> {
        let text = self.text.ok_or("text must be inititalized")?;
        let text_size = (text.get_width(), text.get_height());
        Ok(UiText {
            position: self.position.ok_or("position must be inititalized")?,
            size: self.size.ok_or("size must be inititalized")?,
            color: self.color.ok_or("color must be inititalized")?,
            text: text,
            text_size: text_size,
        })
    }
}

impl<'a> UiText<'a> {
    pub fn set_text(&mut self, string: &'a str) {
        self.text.set_text(string);
        self.text_size = (self.text.get_width(), self.text.get_height());
    }
}

impl<'a> Widget for UiText<'a> {
    fn position(&self) -> (f32, f32) {
        self.position
    }

    fn size(&self) -> (f32, f32) {
        self.size
    }

    fn size_adjusted(&self) -> (f32, f32) {
        let (w, h) = self.size();
        let tw = self.text.get_width();
        let th = self.text.get_height();
        (2.0 * w / tw, 2.0 * h / th)
    }

    fn position_adjusted(&self) -> (f32, f32) {
        let (x, y) = self.position();
        let (x, y) = (x * 2.0 - 1.0, y * 2.0 - 1.0);

        let (_w, h) = self.size_adjusted();

        let (x, y) = (x, y + h);
        (x, y)
    }

    fn drawable(&self) -> Option<Drawable> {
        let text = &self.text;
        let color = self.color;
        Some(Drawable::from_font(text, color))
    }
}

fn collision_check_rect_point(
    rect_pos: (f32, f32),
    rect_size: (f32, f32),
    point: (f32, f32),
) -> bool {
    let (x, y) = point;
    let (left, top) = rect_pos;
    let (right, bottom) = (left + rect_size.0, top + rect_size.1);

    if left <= x && x <= right && top <= y && y <= bottom {
        true
    } else {
        false
    }
}