gooey/interface/view/component/
canvas.rs

1use derive_more::{From, TryInto};
2
3use crate::interface::view::{dimensions, position, Border, Color, Coordinates};
4use super::impl_kind;
5
6#[derive(Clone, Debug, Eq, PartialEq)]
7pub struct Canvas {
8  pub coordinates : Coordinates,
9  pub clear_color : ClearColor,
10  pub border      : Option <Border>
11}
12impl_kind!(Canvas);
13
14/// Indicates to the `Presentation` backend how the `Canvas` should be cleared.
15///
16/// Default is `Appearance`, meaning the `Presentation` backend will clear the
17/// `Canvas` if the current `Appearance` has a `Style` bg `Color`.
18#[derive(Clone, Debug, Default, Eq, PartialEq, From, TryInto)]
19pub enum ClearColor {
20  /// Use the `Style` bg `Color` of the current `Appearance`.
21  ///
22  /// If the current `Appearance` does not have a `Style`, then the clear color is
23  /// unspecified and the `Presentation` backend may not clear the `Canvas`.
24  #[default]
25  Appearance,
26  /// Always clear with the given `Color`.
27  ///
28  /// If 'None', then the Presentation backend may not clear the `Canvas`.
29  Fixed (Option <Color>)
30}
31
32impl Canvas {
33  pub fn default_tile() -> Self {
34    Canvas {
35      coordinates: (position::Tile::default(), dimensions::Tile::default()).into(),
36      clear_color: ClearColor::default(),
37      border:      None
38    }
39  }
40  pub fn default_pixel() -> Self {
41    Canvas {
42      coordinates: (position::Pixel::default(), dimensions::Pixel::default()).into(),
43      clear_color: ClearColor::default(),
44      border:      None
45    }
46  }
47  pub fn body_wh (&self) -> (u32, u32) {
48    self.border.as_ref().map_or_else (||
49      ( self.coordinates.dimensions().horizontal(),
50        self.coordinates.dimensions().vertical() ),
51      |border|{
52        let (border_width, border_height) = border.total_wh();
53        let width  = self.coordinates.dimensions_horizontal();
54        let height = self.coordinates.dimensions_vertical();
55        ( width.saturating_sub  (border_width  as u32),
56          height.saturating_sub (border_height as u32)
57        )
58      })
59  }
60  /// Computes the coordinates (position, dimensions) of the inner body area (inside the
61  /// border)
62  pub fn body_coordinates (&self) -> Coordinates {
63    let border_left = self.border.as_ref().map_or (0, |border| border.thickness_left);
64    match self.coordinates {
65      Coordinates::Tile  (position, _) => {
66        let border_top = self.border.as_ref().map_or (0, |border| border.thickness_top);
67        let (columns, rows) = self.body_wh();
68        let row    = position.row()    + border_top  as i32;
69        let column = position.column() + border_left as i32;
70        ( position::Tile::new_rc (row, column),
71          dimensions::Tile::new_rc (rows, columns)
72        ).into()
73      }
74      Coordinates::Pixel (position, _) => {
75        let border_bottom = self.border.as_ref()
76          .map_or (0, |border| border.thickness_bottom);
77        let (width, height) = self.body_wh();
78        let x = position.0.x + border_left   as i32;
79        let y = position.0.y + border_bottom as i32;
80        ( position::Pixel::new_xy (x, y),
81          dimensions::Pixel::new_wh (width, height)
82        ).into()
83      }
84    }
85  }
86}