chess/
button.rs

1use std::fmt;
2
3use ggez::{graphics, Context, GameResult};
4
5use crate::ChessGui;
6
7/// Indicate how align the text (GUI).
8#[derive(Copy, Clone, Eq, PartialEq, Default, Debug)]
9pub enum Align {
10    Left,
11    Right,
12    #[default]
13    Center,
14}
15
16/// A struct of button for interact with the GUI.
17#[derive(Copy, Clone)]
18pub struct Button {
19    /// The id is not unique, it's just a name to identify it.
20    pub id: &'static str,
21    enable: bool,
22    rect: graphics::Rect,
23    image_path: Option<&'static str>,
24    color: graphics::Color,
25    text: &'static str,
26    align: Align,
27    func: Option<fn(&mut ChessGui)>,
28}
29
30impl Button {
31    /// Create a new [`Button`].
32    #[allow(clippy::too_many_arguments)]
33    pub fn new(
34        id: &'static str,
35        enable: bool,
36        rect: graphics::Rect,
37        color: graphics::Color,
38        text: &'static str,
39        align: Align,
40        func: Option<fn(&mut ChessGui)>,
41    ) -> Self {
42        Button {
43            id,
44            enable,
45            rect,
46            image_path: None,
47            color,
48            text,
49            align,
50            func,
51        }
52    }
53
54    /// Verify if the button is enable.
55    pub fn is_enable(&self) -> bool {
56        self.enable
57    }
58
59    /// Enable the button.
60    pub fn enable(&mut self) {
61        self.enable = true;
62    }
63
64    /// Disable the button.
65    pub fn disable(&mut self) {
66        self.enable = false;
67    }
68
69    /// Draw the image at the given path rather than a rectangle.
70    pub fn set_image(&mut self, path: Option<&'static str>) -> Self {
71        self.image_path = path;
72        *self
73    }
74
75    /// Verify if a coordinate is in the button.
76    pub fn contains(&self, x: f32, y: f32) -> bool {
77        self.rect.contains([x, y])
78    }
79
80    /// Draw the button in the [`Context`].
81    pub fn draw(&self, ctx: &mut Context, font_path: &str, font_scale: f32) -> GameResult {
82        if self.enable {
83            if self.image_path.is_some() {
84                self.draw_image(ctx)?;
85            } else {
86                self.draw_rect(ctx)?;
87                self.draw_text(ctx, font_path, font_scale)?;
88            }
89        }
90        Ok(())
91    }
92
93    /// Draw the button without text.
94    fn draw_rect(&self, ctx: &mut Context) -> GameResult {
95        let mesh = graphics::MeshBuilder::new()
96            .rectangle(graphics::DrawMode::stroke(3.0), self.rect, self.color)?
97            .build(ctx)?;
98        graphics::draw(ctx, &mesh, graphics::DrawParam::default())?;
99        Ok(())
100    }
101
102    /// Draw the text of the button.
103    fn draw_text(&self, ctx: &mut Context, font_path: &str, font_scale: f32) -> GameResult {
104        let font = graphics::Font::new(ctx, font_path)?;
105        let text = graphics::Text::new((self.text, font, font_scale));
106        let dest_point = match self.align {
107            Align::Left => [self.rect.x, self.rect.y],
108            Align::Right => [
109                self.rect.x + self.rect.w - text.width(ctx),
110                self.rect.y + self.rect.h - text.height(ctx),
111            ],
112            Align::Center => [
113                self.rect.x + (self.rect.w - text.width(ctx)) / 2.0,
114                self.rect.y + (self.rect.h - text.height(ctx)) / 2.0,
115            ],
116        };
117        graphics::draw(ctx, &text, (dest_point, self.color))?;
118        Ok(())
119    }
120
121    /// Draw the button without text.
122    fn draw_image(&self, ctx: &mut Context) -> GameResult {
123        let image = graphics::Image::new(ctx, self.image_path.unwrap()).expect("Image load error");
124        let image_scale = [
125            self.rect.w / image.width() as f32,
126            self.rect.h / image.height() as f32,
127        ];
128        let dp = graphics::DrawParam::new()
129            .dest(self.rect.point())
130            .scale(image_scale);
131        graphics::draw(ctx, &image, dp)?;
132        Ok(())
133    }
134
135    /// Call the func when the button is clicked.
136    pub fn clicked(&self, chess_gui: &mut ChessGui) {
137        if self.enable {
138            if let Some(func) = self.func {
139                func(chess_gui);
140            }
141        }
142    }
143}
144
145impl fmt::Display for Button {
146    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147        write!(f, "{}", self.id)
148    }
149}
150
151impl fmt::Debug for Button {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        write!(f, "{}", self.id)
154    }
155}