1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
use ratatui::layout::{Columns, Margin, Offset, Position, Positions, Rect, Rows, Size};
/// Extended Rectangle with a z-order added.
///
/// Its sole use is to allow for correct mouse-handling
/// when processing focus-events.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ZRect {
/// Coordinates
pub x: u16,
pub y: u16,
pub z: u16,
pub width: u16,
pub height: u16,
}
impl From<Rect> for ZRect {
fn from(base_plane: Rect) -> Self {
Self {
x: base_plane.x,
y: base_plane.y,
z: 0,
width: base_plane.width,
height: base_plane.height,
}
}
}
impl From<(u16, Rect)> for ZRect {
#[inline]
fn from(plane_rect: (u16, Rect)) -> Self {
Self {
x: plane_rect.1.x,
y: plane_rect.1.y,
z: plane_rect.0,
width: plane_rect.1.width,
height: plane_rect.1.height,
}
}
}
impl From<(Position, Size)> for ZRect {
fn from((position, size): (Position, Size)) -> Self {
Self {
x: position.x,
y: position.y,
z: 0,
width: size.width,
height: size.height,
}
}
}
impl ZRect {
pub const ZERO: Self = Self {
x: 0,
y: 0,
z: 0,
width: 0,
height: 0,
};
#[inline]
pub const fn new(x: u16, y: u16, width: u16, height: u16, z: u16) -> Self {
Self {
x,
y,
width,
height,
z,
}
}
#[inline]
pub const fn as_rect(self) -> Rect {
Rect {
x: self.x,
y: self.y,
width: self.width,
height: self.height,
}
}
/// Returns true if the `Rect` has no area.
pub const fn is_empty(self) -> bool {
self.width == 0 || self.height == 0
}
/// Returns the left coordinate of the `Rect`.
pub const fn left(self) -> u16 {
self.x
}
/// Returns the right coordinate of the `Rect`. This is the first coordinate outside of the
/// `Rect`.
///
/// If the right coordinate is larger than the maximum value of u16, it will be clamped to
/// `u16::MAX`.
pub const fn right(self) -> u16 {
self.x.saturating_add(self.width)
}
/// Returns the top coordinate of the `Rect`.
pub const fn top(self) -> u16 {
self.y
}
/// Returns the bottom coordinate of the `Rect`. This is the first coordinate outside of the
/// `Rect`.
///
/// If the bottom coordinate is larger than the maximum value of u16, it will be clamped to
/// `u16::MAX`.
pub const fn bottom(self) -> u16 {
self.y.saturating_add(self.height)
}
/// Returns a new `Rect` inside the current one, with the given margin on each side.
///
/// If the margin is larger than the `Rect`, the returned `Rect` will have no area.
#[must_use = "method returns the modified value"]
#[inline]
pub fn inner(self, margin: Margin) -> Self {
ZRect::from((self.z, self.as_rect().inner(margin)))
}
/// 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.
#[must_use = "method returns the modified value"]
#[inline]
pub fn offset(self, offset: Offset) -> Self {
ZRect::from((self.z, self.as_rect().offset(offset)))
}
// TODO: unclear what this means for different z.
//
// /// Returns a new `Rect` that contains both the current one and the given one.
// #[must_use = "method returns the modified value"]
// pub fn union(self, other: Self) -> Self {
// ZRect::from((self.z, self.as_rect().union(other.as_rect())))
// }
//
// /// Returns a new `Rect` that is the intersection of the current one and the given one.
// ///
// /// If the two `Rect`s do not intersect, the returned `Rect` will have no area.
// #[must_use = "method returns the modified value"]
// pub fn intersection(self, other: Self) -> Self {
// ZRect::from((self.z, self.as_rect().intersection(other.as_rect())))
// }
//
// /// Returns true if the two `Rect`s intersect.
// pub const fn intersects(self, other: Self) -> bool {
// self.as_rect().intersects(other.as_rect())
// }
/// Returns true if the given position is inside the `Rect`.
///
/// The position is considered inside the `Rect` if it is on the `Rect`'s border.
///
/// # Examples
///
/// ```rust
/// # use ratatui::{prelude::*, layout::Position};
/// let rect = Rect::new(1, 2, 3, 4);
/// assert!(rect.contains(Position { x: 1, y: 2 }));
/// ````
pub const fn contains(self, position: Position) -> bool {
self.as_rect().contains(position)
}
// TODO: unclear what this means for different z.
// /// Clamp this `Rect` to fit inside the other `Rect`.
// ///
// /// If the width or height of this `Rect` is larger than the other `Rect`, it will be clamped to
// /// the other `Rect`'s width or height.
// ///
// /// If the left or top coordinate of this `Rect` is smaller than the other `Rect`, it will be
// /// clamped to the other `Rect`'s left or top coordinate.
// ///
// /// If the right or bottom coordinate of this `Rect` is larger than the other `Rect`, it will be
// /// clamped to the other `Rect`'s right or bottom coordinate.
// ///
// /// This is different from [`Rect::intersection`] because it will move this `Rect` to fit inside
// /// the other `Rect`, while [`Rect::intersection`] instead would keep this `Rect`'s position and
// /// truncate its size to only that which is inside the other `Rect`.
// ///
// /// # Examples
// ///
// /// ```rust
// /// # use ratatui::prelude::*;
// /// # fn render(frame: &mut Frame) {
// /// let area = frame.size();
// /// let rect = Rect::new(0, 0, 100, 100).clamp(area);
// /// # }
// /// ```
// #[must_use = "method returns the modified value"]
// pub fn clamp(self, other: Self) -> Self {
// ZRect::from((self.z, self.as_rect().clamp(other.as_rect())))
// }
/// An iterator over rows within the `Rect`.
///
/// # Example
///
/// ```
/// # use ratatui::prelude::*;
/// fn render(area: Rect, buf: &mut Buffer) {
/// for row in area.rows() {
/// Line::raw("Hello, world!").render(row, buf);
/// }
/// }
/// ```
pub const fn rows(self) -> Rows {
self.as_rect().rows()
}
/// An iterator over columns within the `Rect`.
///
/// # Example
///
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// fn render(area: Rect, buf: &mut Buffer) {
/// if let Some(left) = area.columns().next() {
/// Block::new().borders(Borders::LEFT).render(left, buf);
/// }
/// }
/// ```
pub const fn columns(self) -> Columns {
self.as_rect().columns()
}
/// An iterator over the positions within the `Rect`.
///
/// The positions are returned in a row-major order (left-to-right, top-to-bottom).
///
/// # Example
///
/// ```
/// # use ratatui::prelude::*;
/// fn render(area: Rect, buf: &mut Buffer) {
/// for position in area.positions() {
/// buf.get_mut(position.x, position.y).set_symbol("x");
/// }
/// }
/// ```
pub const fn positions(self) -> Positions {
self.as_rect().positions()
}
/// Returns a [`Position`] with the same coordinates as this `Rect`.
///
/// # Examples
///
/// ```
/// # use ratatui::prelude::*;
/// let rect = Rect::new(1, 2, 3, 4);
/// let position = rect.as_position();
/// ````
pub const fn as_position(self) -> Position {
self.as_rect().as_position()
}
/// Converts the `Rect` into a size struct.
pub const fn as_size(self) -> Size {
self.as_rect().as_size()
}
}