@@ -24,7 +24,7 @@ pub struct Margin {
2424}
2525
2626/// A simple rectangle used in the computation of the layout and to give widgets an hint about the
27−/// area they are supposed to render to.
27+/// area they are supposed to render to. (x, y) = (0, 0) is at the top left corner of the screen.
2828#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
2929pub struct Rect {
3030 pub x: u16,
@@ -92,6 +92,57 @@ impl Rect {
9292 self.y.saturating_add(self.height)
9393 }
9494
95+ // Returns a new Rect with width reduced from the left side.
96+ // This changes the `x` coordinate and clamps it to the right
97+ // edge of the original Rect.
98+ pub fn clip_left(self, width: u16) -> Rect {
99+ let width = std::cmp::min(width, self.width);
100+ Rect {
101+ x: self.x.saturating_add(width),
102+ width: self.width.saturating_sub(width),
103+ ..self
104+ }
105+ }
106+
107+ // Returns a new Rect with width reduced from the right side.
108+ // This does _not_ change the `x` coordinate.
109+ pub fn clip_right(self, width: u16) -> Rect {
110+ Rect {
111+ width: self.width.saturating_sub(width),
112+ ..self
113+ }
114+ }
115+
116+ // Returns a new Rect with height reduced from the top.
117+ // This changes the `y` coordinate and clamps it to the bottom
118+ // edge of the original Rect.
119+ pub fn clip_top(self, height: u16) -> Rect {
120+ let height = std::cmp::min(height, self.height);
121+ Rect {
122+ y: self.y.saturating_add(height),
123+ height: self.height.saturating_sub(height),
124+ ..self
125+ }
126+ }
127+
128+ // Returns a new Rect with height reduced from the bottom.
129+ // This does _not_ change the `y` coordinate.
130+ pub fn clip_bottom(self, height: u16) -> Rect {
131+ Rect {
132+ height: self.height.saturating_sub(height),
133+ ..self
134+ }
135+ }
136+
137+ pub fn with_height(self, height: u16) -> Rect {
138+ // new height may make area > u16::max_value, so use new()
139+ Self::new(self.x, self.y, self.width, height)
140+ }
141+
142+ pub fn with_width(self, width: u16) -> Rect {
143+ Self::new(self.x, self.y, width, self.height)
144+ }
145+
95146 pub fn inner(self, margin: &Margin) -> Rect {
96147 if self.width < 2 * margin.horizontal || self.height < 2 * margin.vertical {
97148 Rect::default()
@@ -495,6 +546,40 @@ mod tests {
495546 assert_eq!(rect.height, 100);
496547 }
497548
549+ #[test]
550+ fn test_rect_chop_from_left() {
551+ let rect = Rect::new(0, 0, 20, 30);
552+ assert_eq!(Rect::new(10, 0, 10, 30), rect.clip_left(10));
553+ assert_eq!(
554+ Rect::new(20, 0, 0, 30),
555+ rect.clip_left(40),
556+ "x should be clamped to original width if new width is bigger"
557+ );
558+ }
559+
560+ #[test]
561+ fn test_rect_chop_from_right() {
562+ let rect = Rect::new(0, 0, 20, 30);
563+ assert_eq!(Rect::new(0, 0, 10, 30), rect.clip_right(10));
564+ }
565+
566+ #[test]
567+ fn test_rect_chop_from_top() {
568+ let rect = Rect::new(0, 0, 20, 30);
569+ assert_eq!(Rect::new(0, 10, 20, 20), rect.clip_top(10));
570+ assert_eq!(
571+ Rect::new(0, 30, 20, 0),
572+ rect.clip_top(50),
573+ "y should be clamped to original height if new height is bigger"
574+ );
575+ }
576+
577+ #[test]
578+ fn test_rect_chop_from_bottom() {
579+ let rect = Rect::new(0, 0, 20, 30);
580+ assert_eq!(Rect::new(0, 0, 20, 20), rect.clip_bottom(10));
581+ }
582+
498583 fn styles() -> Vec<Style> {
499584 vec![
500585 Style::default(),