Compare commits
1 Commits
release-pl
...
joshka/pus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22f56ac43a |
@@ -20,6 +20,7 @@ This is a quick summary of the sections below:
|
||||
- 'layout::Alignment' is renamed to 'layout::HorizontalAlignment'
|
||||
- MSRV is now 1.86.0
|
||||
- `Backend` now requires an associated `Error` type and `clear_region` method
|
||||
- `Rect::inner` now takes `Into<Inset>` instead of `Margin`
|
||||
- `TestBackend` now uses `core::convert::Infallible` for error handling instead of `std::io::Error`
|
||||
- Disabling `default-features` will now disable layout cache, which can have a negative impact on performance
|
||||
- `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` are now only available if `layout-cache`
|
||||
@@ -126,6 +127,13 @@ behavior can be achieved by using `Flex::SpaceEvenly` instead.
|
||||
+ let rects = Layout::horizontal([Length(1), Length(2)]).flex(Flex::SpaceEvenly).split(area);
|
||||
```
|
||||
|
||||
### `Rect::inner` now takes `Into<Inset>` instead of `Margin`
|
||||
|
||||
`Rect::inner` accepts any type that converts into `Inset` (for example `Inset` or `Margin`). Calls
|
||||
that relied on inference for a `Margin` conversion may now need an explicit `Margin::new` (or
|
||||
`Inset::trbl`) to make type inference succeed. This change also removes `const` support for
|
||||
`Rect::inner`, so calls in const contexts need to move to runtime code.
|
||||
|
||||
### `block::Title` no longer exists ([#1926])
|
||||
|
||||
[#1926]: https://github.com/ratatui/ratatui/pull/1926
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
//! - [`Position`] - Represents a point in the terminal coordinate system
|
||||
//! - [`Size`] - Represents dimensions (width and height)
|
||||
//! - [`Margin`] - Defines spacing around rectangular areas
|
||||
//! - [`Inset`] - Defines side-specific spacing inside a rectangle
|
||||
//! - [`Offset`] - Represents relative movement in the coordinate system
|
||||
//! - [`Spacing`] - Controls spacing or overlap between layout segments
|
||||
//!
|
||||
@@ -314,6 +315,7 @@ mod alignment;
|
||||
mod constraint;
|
||||
mod direction;
|
||||
mod flex;
|
||||
mod inset;
|
||||
mod layout;
|
||||
mod margin;
|
||||
mod offset;
|
||||
@@ -325,6 +327,7 @@ pub use alignment::{Alignment, HorizontalAlignment, VerticalAlignment};
|
||||
pub use constraint::Constraint;
|
||||
pub use direction::Direction;
|
||||
pub use flex::Flex;
|
||||
pub use inset::Inset;
|
||||
pub use layout::{Layout, Spacing};
|
||||
pub use margin::Margin;
|
||||
pub use offset::Offset;
|
||||
|
||||
189
ratatui-core/src/layout/inset.rs
Normal file
189
ratatui-core/src/layout/inset.rs
Normal file
@@ -0,0 +1,189 @@
|
||||
#![warn(missing_docs)]
|
||||
use core::fmt;
|
||||
|
||||
use crate::layout::Margin;
|
||||
|
||||
/// Represents side-specific spacing inside rectangular areas.
|
||||
///
|
||||
/// `Inset` defines how much space to remove from each side of a rectangle. Unlike [`Margin`], which
|
||||
/// applies uniform spacing horizontally and vertically, `Inset` lets you specify independent
|
||||
/// amounts for the top, right, bottom, and left edges. The default constructor order is
|
||||
/// top-right-bottom-left (often remembered as “trbl” or “trouble”), matching the CSS spec and
|
||||
/// offering an easy clockwise mnemonic.
|
||||
///
|
||||
/// Use `Inset` when you need per-side control; choose [`Margin`](crate::layout::Margin) for the
|
||||
/// common symmetric case.
|
||||
///
|
||||
/// # Construction
|
||||
///
|
||||
/// - [`trbl`](Self::trbl) - Create a new inset with top/right/bottom/left values
|
||||
/// - [`default`](Default::default) - Create with zero inset on all sides
|
||||
/// - [`symmetric`](Self::symmetric) - Create with shared horizontal and vertical values
|
||||
/// - [`horizontal`](Self::horizontal) - Create with equal left and right values
|
||||
/// - [`vertical`](Self::vertical) - Create with equal top and bottom values
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Inset, Rect};
|
||||
///
|
||||
/// let inset = Inset::trbl(1, 2, 3, 4);
|
||||
/// let rect = Rect::new(0, 0, 10, 10).inner(inset);
|
||||
/// assert_eq!(rect, Rect::new(4, 1, 4, 6));
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Inset {
|
||||
/// Space to remove from the top edge
|
||||
pub top: u16,
|
||||
/// Space to remove from the right edge
|
||||
pub right: u16,
|
||||
/// Space to remove from the bottom edge
|
||||
pub bottom: u16,
|
||||
/// Space to remove from the left edge
|
||||
pub left: u16,
|
||||
}
|
||||
|
||||
impl Inset {
|
||||
/// Creates a new inset with explicit top/right/bottom/left values.
|
||||
pub const fn trbl(top: u16, right: u16, bottom: u16, left: u16) -> Self {
|
||||
Self {
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new inset with shared horizontal and vertical values.
|
||||
///
|
||||
/// The `horizontal` value is applied to `left` and `right`; the `vertical` value is applied to
|
||||
/// `top` and `bottom`. Note the order is `horizontal, vertical` (x then y), opposite of the CSS
|
||||
/// ordering; we keep a single ordering across helpers to avoid mixing patterns in code.
|
||||
pub const fn symmetric(horizontal: u16, vertical: u16) -> Self {
|
||||
Self {
|
||||
right: horizontal,
|
||||
left: horizontal,
|
||||
top: vertical,
|
||||
bottom: vertical,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new inset with equal left and right values.
|
||||
pub const fn horizontal(horizontal: u16) -> Self {
|
||||
Self {
|
||||
top: 0,
|
||||
right: horizontal,
|
||||
bottom: 0,
|
||||
left: horizontal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new inset with equal top and bottom values.
|
||||
pub const fn vertical(vertical: u16) -> Self {
|
||||
Self {
|
||||
top: vertical,
|
||||
right: 0,
|
||||
bottom: vertical,
|
||||
left: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Inset {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"t{} r{} b{} l{}",
|
||||
self.top, self.right, self.bottom, self.left
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Margin> for Inset {
|
||||
fn from(margin: Margin) -> Self {
|
||||
Self {
|
||||
top: margin.vertical,
|
||||
right: margin.horizontal,
|
||||
bottom: margin.vertical,
|
||||
left: margin.horizontal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::string::ToString;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
assert_eq!(
|
||||
Inset::trbl(1, 2, 3, 4),
|
||||
Inset {
|
||||
top: 1,
|
||||
right: 2,
|
||||
bottom: 3,
|
||||
left: 4
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display() {
|
||||
assert_eq!(Inset::trbl(1, 2, 3, 4).to_string(), "t1 r2 b3 l4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symmetric() {
|
||||
assert_eq!(
|
||||
Inset::symmetric(2, 3),
|
||||
Inset {
|
||||
top: 3,
|
||||
right: 2,
|
||||
bottom: 3,
|
||||
left: 2
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn horizontal() {
|
||||
assert_eq!(
|
||||
Inset::horizontal(2),
|
||||
Inset {
|
||||
top: 0,
|
||||
right: 2,
|
||||
bottom: 0,
|
||||
left: 2
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vertical() {
|
||||
assert_eq!(
|
||||
Inset::vertical(3),
|
||||
Inset {
|
||||
top: 3,
|
||||
right: 0,
|
||||
bottom: 3,
|
||||
left: 0
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_margin() {
|
||||
assert_eq!(
|
||||
Inset::from(Margin::new(2, 3)),
|
||||
Inset {
|
||||
top: 3,
|
||||
right: 2,
|
||||
bottom: 3,
|
||||
left: 2
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@ use core::fmt;
|
||||
/// margin, the space is applied to both the left and right sides. For vertical margin, the space
|
||||
/// is applied to both the top and bottom sides.
|
||||
///
|
||||
/// Use [`Inset`](crate::layout::Inset) when you need different values for each side; `Margin`
|
||||
/// provides the symmetric case.
|
||||
///
|
||||
/// # Construction
|
||||
///
|
||||
/// - [`new`](Self::new) - Create a new margin with horizontal and vertical spacing
|
||||
|
||||
@@ -4,7 +4,7 @@ use core::cmp::{max, min};
|
||||
use core::fmt;
|
||||
|
||||
pub use self::iter::{Columns, Positions, Rows};
|
||||
use crate::layout::{Margin, Offset, Position, Size};
|
||||
use crate::layout::{Inset, Margin, Offset, Position, Size};
|
||||
|
||||
mod iter;
|
||||
mod ops;
|
||||
@@ -44,7 +44,7 @@ use super::{Constraint, Flex, Layout};
|
||||
///
|
||||
/// # Spatial Operations
|
||||
///
|
||||
/// - [`inner`](Self::inner), [`outer`](Self::outer) - Apply margins to shrink or expand
|
||||
/// - [`inner`](Self::inner), [`outer`](Self::outer) - Apply insets or margins to shrink or expand
|
||||
/// - [`offset`](Self::offset) - Move the rectangle by a relative amount
|
||||
/// - [`resize`](Self::resize) - Change the rectangle size while keeping the bottom/right in range
|
||||
/// - [`union`](Self::union) - Combine with another rectangle to create a bounding box
|
||||
@@ -127,6 +127,15 @@ use super::{Constraint, Flex, Layout};
|
||||
/// assert_eq!(rect, Rect::new(u16::MAX - 1, u16::MAX - 1, 1, 1));
|
||||
/// ```
|
||||
///
|
||||
/// To inset a `Rect` with different values on each side, use [`Rect::inner`] with an [`Inset`].
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Inset, Rect};
|
||||
///
|
||||
/// let rect = Rect::new(0, 0, 10, 10).inner(Inset::trbl(1, 2, 3, 4));
|
||||
/// assert_eq!(rect, Rect::new(4, 1, 4, 6));
|
||||
/// ```
|
||||
///
|
||||
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
@@ -225,22 +234,48 @@ impl Rect {
|
||||
self.y.saturating_add(self.height)
|
||||
}
|
||||
|
||||
/// Returns a new `Rect` inside the current one, with the given margin on each side.
|
||||
/// Returns a new `Rect` inside the current one, with the given inset on each side.
|
||||
///
|
||||
/// If the margin is larger than the `Rect`, the returned `Rect` will have no area.
|
||||
/// Accepts any type that can convert into an [`Inset`], including [`Margin`]. If the inset is
|
||||
/// larger than the `Rect`, the returned `Rect` will have no area.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Using a margin (shared horizontal and vertical):
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Margin, Rect};
|
||||
///
|
||||
/// let area = Rect::new(0, 0, 10, 6);
|
||||
/// let inner = area.inner(Margin::new(1, 2));
|
||||
///
|
||||
/// assert_eq!(inner, Rect::new(1, 2, 8, 2));
|
||||
/// ```
|
||||
///
|
||||
/// Using an inset with side-specific values:
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Inset, Rect};
|
||||
///
|
||||
/// let area = Rect::new(0, 0, 10, 6);
|
||||
/// let inner = area.inner(Inset::trbl(1, 2, 3, 4));
|
||||
///
|
||||
/// assert_eq!(inner, Rect::new(4, 1, 4, 2));
|
||||
/// ```
|
||||
#[must_use = "method returns the modified value"]
|
||||
pub const fn inner(self, margin: Margin) -> Self {
|
||||
let doubled_margin_horizontal = margin.horizontal.saturating_mul(2);
|
||||
let doubled_margin_vertical = margin.vertical.saturating_mul(2);
|
||||
pub fn inner<I: Into<Inset>>(self, inset: I) -> Self {
|
||||
let inset = inset.into();
|
||||
let total_horizontal = inset.left.saturating_add(inset.right);
|
||||
let total_vertical = inset.top.saturating_add(inset.bottom);
|
||||
|
||||
if self.width < doubled_margin_horizontal || self.height < doubled_margin_vertical {
|
||||
if self.width < total_horizontal || self.height < total_vertical {
|
||||
Self::ZERO
|
||||
} else {
|
||||
Self {
|
||||
x: self.x.saturating_add(margin.horizontal),
|
||||
y: self.y.saturating_add(margin.vertical),
|
||||
width: self.width.saturating_sub(doubled_margin_horizontal),
|
||||
height: self.height.saturating_sub(doubled_margin_vertical),
|
||||
x: self.x.saturating_add(inset.left),
|
||||
y: self.y.saturating_add(inset.top),
|
||||
width: self.width.saturating_sub(total_horizontal),
|
||||
height: self.height.saturating_sub(total_vertical),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -777,6 +812,22 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_supports_inset() {
|
||||
assert_eq!(
|
||||
Rect::new(10, 20, 50, 60).inner(Inset::trbl(1, 2, 3, 4)),
|
||||
Rect::new(14, 21, 44, 56),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_inset_zero_on_overflow() {
|
||||
assert_eq!(
|
||||
Rect::new(0, 0, 2, 2).inner(Inset::trbl(1, 1, 2, 2)),
|
||||
Rect::ZERO,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn offset() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user