From 0c3872f1c5105153ebb0368e83e8b72cb68b8b34 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Wed, 25 Jun 2025 21:56:15 -0700 Subject: [PATCH] feat: add Rect::outer() (#1929) Fixes: https://github.com/ratatui/ratatui/issues/211 --- ratatui-core/src/layout/rect.rs | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/ratatui-core/src/layout/rect.rs b/ratatui-core/src/layout/rect.rs index cc8b22bd..34b58afe 100644 --- a/ratatui-core/src/layout/rect.rs +++ b/ratatui-core/src/layout/rect.rs @@ -155,6 +155,34 @@ impl Rect { } } + /// Returns a new `Rect` outside the current one, with the given margin applied on each side. + /// + /// If the margin causes the `Rect`'s bounds to outsdie the range of a `u16`, the `Rect` will + /// be truncated to keep the bounds within `u16`. This will cause the size of the `Rect` to + /// change. + /// + /// The generated `Rect` may not fit inside the buffer or containing area, so it consider + /// constraining the resulting `Rect` with [`Rect::clamp`] before using it. + #[must_use = "method returns the modified value"] + pub const fn outer(self, margin: Margin) -> Self { + let x = self.x.saturating_sub(margin.horizontal); + let y = self.y.saturating_sub(margin.vertical); + let width = self + .right() + .saturating_add(margin.horizontal) + .saturating_sub(x); + let height = self + .bottom() + .saturating_add(margin.vertical) + .saturating_sub(y); + Self { + x, + y, + width, + height, + } + } + /// Moves the `Rect` without modifying its size. /// /// Moves the `Rect` according to the given offset without modifying its [`width`](Rect::width) @@ -511,6 +539,27 @@ mod tests { ); } + #[test] + fn outer() { + // enough space to grow on all sides + assert_eq!( + Rect::new(100, 200, 10, 20).outer(Margin::new(20, 30)), + Rect::new(80, 170, 50, 80) + ); + + // left / top saturation should truncate the size (10 less on left / top) + assert_eq!( + Rect::new(10, 20, 10, 20).outer(Margin::new(20, 30)), + Rect::new(0, 0, 40, 70), + ); + + // right / bottom saturation should truncate the size (10 less on bottom / right) + assert_eq!( + Rect::new(u16::MAX - 20, u16::MAX - 40, 10, 20).outer(Margin::new(20, 30)), + Rect::new(u16::MAX - 40, u16::MAX - 70, 40, 70), + ); + } + #[test] fn offset() { assert_eq!(