From 1229b96e428df880a951ef57f53ca73e74ef1ea2 Mon Sep 17 00:00:00 2001 From: Valentin271 <36198422+Valentin271@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:03:18 +0100 Subject: [PATCH] feat(Rect): add `offset` method (#533) The offset method creates a new Rect that is moved by the amount specified in the x and y direction. These values can be positive or negative. This is useful for manual layout tasks. ```rust let rect = area.offset(Offset { x: 10, y -10 }); ``` --- src/layout/rect.rs | 57 +++++++++++++++++++++++++++++++++++++++ src/layout/rect/offset.rs | 12 +++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/layout/rect/offset.rs diff --git a/src/layout/rect.rs b/src/layout/rect.rs index b2d2b4b1..89a0b191 100644 --- a/src/layout/rect.rs +++ b/src/layout/rect.rs @@ -6,6 +6,10 @@ use std::{ use crate::prelude::*; +mod offset; + +pub use offset::*; + /// A simple rectangle used in the computation of the layout and to give widgets a hint about the /// area they are supposed to render to. #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] @@ -106,6 +110,26 @@ impl Rect { } } + /// Moves the `Rect` without modifying its size. + /// + /// Moves the `Rect` according to the given offset without modifying its [`width`](Rect::width) + /// or [`height`](Rect::height). + /// - Positive `x` moves the whole `Rect` to the right, negative to the left. + /// - Positive `y` moves the whole `Rect` to the bottom, negative to the top. + /// + /// See [`Offset`] for details. + pub fn offset(self, offset: Offset) -> Rect { + Rect { + x: i32::from(self.x) + .saturating_add(offset.x) + .clamp(0, (u16::MAX - self.width) as i32) as u16, + y: i32::from(self.y) + .saturating_add(offset.y) + .clamp(0, (u16::MAX - self.height) as i32) as u16, + ..self + } + } + /// Returns a new rect that contains both the current one and the given one. pub fn union(self, other: Rect) -> Rect { let x1 = min(self.x, other.x); @@ -207,6 +231,39 @@ mod tests { ); } + #[test] + fn offset() { + assert_eq!( + Rect::new(1, 2, 3, 4).offset(Offset { x: 5, y: 6 }), + Rect::new(6, 8, 3, 4), + ); + } + + #[test] + fn negative_offset() { + assert_eq!( + Rect::new(4, 3, 3, 4).offset(Offset { x: -2, y: -1 }), + Rect::new(2, 2, 3, 4), + ); + } + + #[test] + fn negative_offset_saturate() { + assert_eq!( + Rect::new(1, 2, 3, 4).offset(Offset { x: -5, y: -6 }), + Rect::new(0, 0, 3, 4), + ); + } + + /// Offsets a [`Rect`] making it go outside [`u16::MAX`], it should keep its size. + #[test] + fn offset_saturate_max() { + assert_eq!( + Rect::new(u16::MAX - 500, u16::MAX - 500, 100, 100).offset(Offset { x: 1000, y: 1000 }), + Rect::new(u16::MAX - 100, u16::MAX - 100, 100, 100), + ); + } + #[test] fn union() { assert_eq!( diff --git a/src/layout/rect/offset.rs b/src/layout/rect/offset.rs new file mode 100644 index 00000000..7d2ff8e9 --- /dev/null +++ b/src/layout/rect/offset.rs @@ -0,0 +1,12 @@ +/// Amounts by which to move a [`Rect`](super::Rect). +/// +/// Positive numbers move to the right/bottom and negative to the left/top. +/// +/// See [`Rect::offset`](super::Rect::offset) +#[derive(Debug, Default, Clone, Copy)] +pub struct Offset { + /// How much to move on the X axis + pub x: i32, + /// How much to move on the Y axis + pub y: i32, +}