hui/frame/
rect.rs

1use glam::Vec2;
2use crate::{
3  color,
4  draw::{ImageHandle, RoundedCorners, UiDrawCommand, UiDrawCommandList},
5  rect::{Rect, Corners, FillColor},
6};
7use super::{Frame, point::FramePoint2d};
8
9/// A rectangular frame
10///
11/// Can optionally be tinted, textured, and have rounded corners
12#[derive(Clone, Copy)]
13pub struct RectFrame {
14  /// Background color of the frame\
15  ///
16  /// If the container has a background texture, it will be multiplied by this color
17  pub color: FillColor,
18
19  /// Background texture of the frame
20  ///
21  /// Can be used in conjunction with the background color\
22  /// In this case, the texture will be shaded by the color
23  ///
24  /// Please note that if the background color is NOT set (or set to transparent), the texture will NOT be visible\
25  /// This is because the texture is multiplied by the color, and if the color is transparent, the texture will be too\
26  pub image: Option<ImageHandle>,
27
28  /// Top left corner of the rectangle
29  pub top_left: FramePoint2d,
30
31  /// Bottom right corner of the rectangle
32  pub bottom_right: FramePoint2d,
33
34  /// Corner radius of the frame
35  pub corner_radius: Corners<f32>,
36}
37
38// impl<T: Into<FillColor>> From<T> for RectFrame {
39//   fn from(color: T) -> Self {
40//     Self::from_color(color)
41//   }
42// }
43
44impl From<FillColor> for RectFrame {
45  fn from(color: FillColor) -> Self {
46    Self::color(color)
47  }
48}
49
50impl From<ImageHandle> for RectFrame {
51  fn from(image: ImageHandle) -> Self {
52    Self::image(image)
53  }
54}
55
56impl RectFrame {
57  /// Create a new [`RectFrame`] with the given color
58  pub fn color(color: impl Into<FillColor>) -> Self {
59    Self {
60      color: color.into(),
61      ..Self::default()
62    }
63  }
64
65  /// Create a new [`RectFrame`] with the given image\
66  ///
67  /// Color will be set to [`WHITE`](crate::color::WHITE) to ensure the image is visible
68  pub fn image(image: ImageHandle) -> Self {
69    Self {
70      color: color::WHITE.into(),
71      image: Some(image),
72      ..Self::default()
73    }
74  }
75
76  /// Create a new [`RectFrame`] with the given color and image
77  pub fn color_image(color: impl Into<FillColor>, image: ImageHandle) -> Self {
78    Self {
79      color: color.into(),
80      image: Some(image),
81      ..Self::default()
82    }
83  }
84
85  /// Set the corner radius of the [`RectFrame`]
86  pub fn with_corner_radius(self, radius: impl Into<Corners<f32>>) -> Self {
87    Self {
88      corner_radius: radius.into(),
89      ..self
90    }
91  }
92
93  //TODO: deprecate and replace
94
95  /// Inset the rectangle by the given amount in pixels
96  pub fn with_inset(self, inset: f32) -> Self {
97    Self {
98      top_left: self.top_left + Vec2::splat(inset).into(),
99      bottom_right: self.bottom_right - Vec2::splat(inset).into(),
100      ..self
101    }
102  }
103}
104
105impl Default for RectFrame {
106  fn default() -> Self {
107    Self {
108      color: FillColor::transparent(),
109      image: None,
110      top_left: FramePoint2d::TOP_LEFT,
111      bottom_right: FramePoint2d::BOTTOM_RIGHT,
112      corner_radius: Corners::all(0.),
113    }
114  }
115}
116
117impl Frame for RectFrame {
118  fn draw(&self, draw: &mut UiDrawCommandList, rect: Rect) {
119    if self.color.is_transparent() {
120      return
121    }
122    //TODO: handle bottom_right < top_left
123    let top_left = self.top_left.resolve(rect.size);
124    let bottom_right = self.bottom_right.resolve(rect.size);
125    draw.add(UiDrawCommand::Rectangle {
126      position: rect.position + top_left,
127      size: bottom_right - top_left,
128      color: self.color.corners(),
129      texture: self.image,
130      texture_uv: None,
131      rounded_corners: (self.corner_radius.max_f32() > 0.).then_some(
132        RoundedCorners::from_radius(self.corner_radius)
133      ),
134    });
135  }
136
137  fn covers_opaque(&self) -> bool {
138    self.top_left.x.relative <= 0. &&
139    self.top_left.x.absolute <= 0. &&
140    self.top_left.y.relative <= 0. &&
141    self.top_left.y.absolute <= 0. &&
142    self.bottom_right.x.relative >= 1. &&
143    self.bottom_right.x.absolute >= 0. &&
144    self.bottom_right.y.relative >= 1. &&
145    self.bottom_right.y.absolute >= 0. &&
146    self.color.is_opaque() &&
147    self.image.is_none() &&
148    self.corner_radius.max_f32() == 0.
149  }
150}