button_lib 0.1.0

A simple library for buttons with macroquad.
Documentation
use self::State::{Disabled, Hovered, Idle, Pressed};
use macroquad::color::Color;
use macroquad::input::{is_mouse_button_down, is_mouse_button_pressed};
use macroquad::math::{Vec2, vec2};
use macroquad::prelude::{Font, mouse_position};
use macroquad::shapes::{draw_ellipse, draw_rectangle};
use text_lib::text;

pub enum Shape {
    Rectangle,
    Ellipse,
}

#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Idle,
    Hovered,
    Pressed,
    Disabled,
}

pub struct Text {
    pub text: String,
    pub font: Font,
    pub size: u16,
    pub color: Color,
}

pub struct Button {
    pos: Vec2,
    size: Vec2,
    shape: Shape,
    color: Color,
    toggle: bool,
    text: text::Text,
    state: State,
}

impl Button {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        pos: Vec2,
        size: Vec2,
        shape: Shape,
        color: Color,
        toggle: bool,
        text: Text,
    ) -> Self {
        Button {
            pos,
            size,
            shape,
            color,
            text: text::Text::new(
                pos,
                size.x * 0.9,
                text.text,
                text.font,
                text::Alignment {
                    x: text::AlignX::Center,
                    y: text::AlignY::Center,
                },
                text.size,
                text.color,
            ),
            toggle,
            state: Idle,
        }
    }

    pub fn render(&self) {
        match self.shape {
            Shape::Rectangle => draw_rectangle(
                self.pos.x - self.size.x * 0.5,
                self.pos.y - self.size.y * 0.5,
                self.size.x,
                self.size.y,
                self.color,
            ),
            Shape::Ellipse => draw_ellipse(
                self.pos.x,
                self.pos.y,
                self.size.x * 0.5,
                self.size.y * 0.5,
                0.0,
                self.color,
            ),
        }
        self.text.draw();
    }

    pub fn set_pos(&mut self, pos: Vec2) {
        self.pos = pos;
        self.text.set_pos(pos);
    }

    pub fn set_size(&mut self, size: Vec2) {
        self.size = size;
        self.text.set_width(size.x * 0.9);
    }

    pub fn set_shape(&mut self, shape: Shape) {
        self.shape = shape;
    }

    pub fn set_color(&mut self, color: Color) {
        self.color = color;
    }

    pub fn set_text_text(&mut self, text: String) {
        self.text.set_text(text);
    }

    pub fn set_text_font(&mut self, font: Font) {
        self.text.set_font(font);
    }

    pub fn set_text_size(&mut self, size: u16) {
        self.text.set_size(size);
    }

    pub fn set_text_color(&mut self, color: Color) {
        self.text.set_color(color);
    }

    pub fn set_toggle(&mut self, toggle: bool) {
        self.toggle = toggle;
    }

    pub fn get_state(&mut self) -> State {
        //TODO: cleaner implementation
        if self.state != Disabled {
            let mouse_pos: Vec2 = vec2(mouse_position().0, mouse_position().1);
            let mut over: bool = false;
            match self.shape {
                Shape::Ellipse => {
                    if 1.0
                        >= (mouse_pos.x - self.pos.x) * (mouse_pos.x - self.pos.x)
                            / ((self.size.x * 0.5) * (self.size.x * 0.5))
                            + (mouse_pos.y - self.pos.y) * (mouse_pos.y - self.pos.y)
                                / ((self.size.y * 0.5) * (self.size.y * 0.5))
                    {
                        over = true;
                    }
                }
                Shape::Rectangle => {
                    if (mouse_pos.x - self.pos.x).abs() <= self.size.x * 0.5
                        && (mouse_pos.y - self.pos.y).abs() <= self.size.y * 0.5
                    {
                        over = true;
                    }
                }
            }
            if self.toggle {
                if over {
                    if is_mouse_button_pressed(macroquad::input::MouseButton::Left) {
                        if self.state == Pressed {
                            self.state = Hovered;
                        } else {
                            self.state = Pressed;
                        }
                    } else {
                        if self.state != Pressed {
                            self.state = Hovered;
                        }
                    }
                } else {
                    if self.state != Pressed {
                        self.state = Idle;
                    }
                }
            } else {
                if over {
                    if is_mouse_button_down(macroquad::input::MouseButton::Left) {
                        self.state = Pressed;
                    } else {
                        self.state = Hovered;
                    }
                } else {
                    self.state = Idle;
                }
            }
        }
        self.state
    }

    pub fn disable(&mut self) {
        self.state = Disabled;
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use macroquad::prelude::*;

    #[ignore = "Requires a graphical window, skip in CI"]
    #[test]
    fn button_test() {
        macroquad::Window::new("Integration Test", async {
            let content = "A quick brown fox jumps.".to_string();
            let b1 = Button::new(
                vec2(200.0, 200.0),
                vec2(200.0, 200.0),
                Shape::Ellipse,
                RED,
                false,
                Text {
                    text: content,
                    font: load_ttf_font("JetBrainsMono-VariableFont_wght.ttf")
                        .await
                        .unwrap(),
                    size: 24,
                    color: BLUE,
                },
            );
            loop {
                clear_background(BLACK);
                b1.render();
                next_frame().await;
            }
        });
    }
}