docs: improve layout related docs (#1948)

Adds module level docs and more comprehensive docs on all the types in
the layout module

Fixes #1937
This commit is contained in:
Josh McKinney 2025-06-27 14:31:11 -07:00 committed by GitHub
parent d02995fda1
commit 8e2d568428
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 603 additions and 71 deletions

View File

@ -1,5 +1,311 @@
#![warn(clippy::missing_const_for_fn)]
//! Provides types and traits for working with layout and positioning in the terminal.
//! Layout and positioning in terminal user interfaces.
//!
//! This module provides a comprehensive set of types and traits for working with layout and
//! positioning in terminal applications. It implements a flexible layout system that allows you to
//! divide the terminal screen into different areas using constraints, manage positioning and
//! sizing, and handle complex UI arrangements.
//!
//! The layout system in Ratatui is based on the Cassowary constraint solver algorithm, implemented
//! through the [`kasuari`] crate. This allows for sophisticated constraint-based layouts where
//! multiple requirements can be satisfied simultaneously, with priorities determining which
//! constraints take precedence when conflicts arise.
//!
//! [`kasuari`]: https://crates.io/crates/kasuari
//!
//! # Core Concepts
//!
//! ## Coordinate System
//!
//! The coordinate system runs left to right, top to bottom, with the origin `(0, 0)` in the top
//! left corner of the terminal. The x and y coordinates are represented by `u16` values.
//!
//! ```text
//! x (columns)
//! ┌─────────────→
//! y │ (0,0)
//! │
//! (rows)
//! ↓
//! ```
//!
//! ## Layout Fundamentals
//!
//! Layouts form the structural foundation of your terminal UI. The [`Layout`] struct divides
//! available screen space into rectangular areas using a constraint-based approach. You define
//! multiple constraints for how space should be allocated, and the Cassowary solver determines
//! the optimal layout that satisfies as many constraints as possible. These areas can then be
//! used to render widgets or nested layouts.
//!
//! Note that the [`Layout`] struct is not required to create layouts - you can also manually
//! calculate and create [`Rect`] areas using simple mathematics to divide up the terminal space
//! if you prefer direct control over positioning and sizing.
//!
//! ## Rectangular Areas
//!
//! All layout operations work with rectangular areas represented by the [`Rect`] type. A [`Rect`]
//! defines a position and size in the terminal, specified by its top-left corner coordinates and
//! dimensions.
//!
//! # Available Types
//!
//! ## Core Layout Types
//!
//! - [`Layout`] - The primary layout engine that divides space using constraints and direction
//! - [`Rect`] - Represents a rectangular area with position and dimensions
//! - [`Constraint`] - Defines how space should be allocated (length, percentage, ratio, etc.)
//! - [`Direction`] - Specifies layout orientation (horizontal or vertical)
//! - [`Flex`] - Controls space distribution when constraints are satisfied
//!
//! ## Positioning and Sizing
//!
//! - [`Position`] - Represents a point in the terminal coordinate system
//! - [`Size`] - Represents dimensions (width and height)
//! - [`Margin`] - Defines spacing around rectangular areas
//! - [`Offset`] - Represents relative movement in the coordinate system
//! - [`Spacing`] - Controls spacing or overlap between layout segments
//!
//! ## Alignment
//!
//! - [`Alignment`] (alias for [`HorizontalAlignment`]) - Horizontal text/content alignment
//! - [`HorizontalAlignment`] - Horizontal alignment options (left, center, right)
//! - [`VerticalAlignment`] - Vertical alignment options (top, center, bottom)
//!
//! ## Iteration Support
//!
//! - [`Rows`] - Iterator over horizontal rows within a rectangular area
//! - [`Columns`] - Iterator over vertical columns within a rectangular area
//! - [`Positions`] - Iterator over all positions within a rectangular area
//!
//! # Quick Start
//!
//! Here's a simple example of creating a basic layout using the [`Layout`] struct:
//!
//! ```rust
//! use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
//!
//! // Create a terminal area
//! let area = Rect::new(0, 0, 80, 24);
//!
//! // Divide it vertically into two equal parts using Layout
//! let layout = Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]);
//! let [top, bottom] = layout.areas(area);
//!
//! // Now you have two areas: top and bottom
//! ```
//!
//! **Note**: When the number of layout areas is known at compile time, use destructuring
//! assignment with descriptive variable names for better readability:
//!
//! ```rust
//! use ratatui_core::layout::{Constraint, Layout, Rect};
//!
//! let area = Rect::new(0, 0, 80, 24);
//! let [header, content, footer] = Layout::vertical([
//! Constraint::Length(3),
//! Constraint::Fill(1),
//! Constraint::Length(1),
//! ])
//! .areas(area);
//! ```
//!
//! Use [`Layout::split`] when the number of areas is only known at runtime.
//!
//! Alternatively, you can create layouts manually using mathematics:
//!
//! ```rust
//! use ratatui_core::layout::Rect;
//!
//! // Create a terminal area
//! let area = Rect::new(0, 0, 80, 24);
//!
//! // Manually divide into two equal parts
//! let top_half = Rect::new(area.x, area.y, area.width, area.height / 2);
//! let bottom_half = Rect::new(
//! area.x,
//! area.y + area.height / 2,
//! area.width,
//! area.height / 2,
//! );
//! ```
//!
//! # Layout Examples
//!
//! ## Basic Vertical Split
//!
//! ```rust
//! use ratatui_core::layout::{Constraint, Layout, Rect};
//!
//! let area = Rect::new(0, 0, 80, 24);
//! let [header, content, footer] = Layout::vertical([
//! Constraint::Length(3), // Header: fixed height
//! Constraint::Fill(1), // Content: flexible
//! Constraint::Length(1), // Footer: fixed height
//! ])
//! .areas(area);
//! ```
//!
//! ## Horizontal Sidebar Layout
//!
//! ```rust
//! use ratatui_core::layout::{Constraint, Layout, Rect};
//!
//! let area = Rect::new(0, 0, 80, 24);
//! let [sidebar, main] = Layout::horizontal([
//! Constraint::Length(20), // Sidebar: fixed width
//! Constraint::Fill(1), // Main content: flexible
//! ])
//! .areas(area);
//! ```
//!
//! ## Complex Nested Layout
//!
//! ```rust
//! use ratatui_core::layout::{Constraint, Layout, Rect};
//!
//! fn create_complex_layout(area: Rect) -> [Rect; 4] {
//! // First, split vertically
//! let [header, body, footer] = Layout::vertical([
//! Constraint::Length(3), // Header
//! Constraint::Fill(1), // Body
//! Constraint::Length(1), // Footer
//! ])
//! .areas(area);
//!
//! // Then split the body horizontally
//! let [sidebar, main] = Layout::horizontal([
//! Constraint::Length(20), // Sidebar
//! Constraint::Fill(1), // Main
//! ])
//! .areas(body);
//!
//! [header, sidebar, main, footer]
//! }
//! ```
//!
//! # Working with Constraints
//!
//! [`Constraint`]s define how space is allocated within a layout using the Cassowary constraint
//! solver algorithm. The constraint solver attempts to satisfy all constraints simultaneously,
//! with priorities determining which constraints take precedence when conflicts arise. Different
//! constraint types serve different purposes:
//!
//! - [`Constraint::Min`] - Minimum size constraint
//! - [`Constraint::Max`] - Maximum size constraint
//! - [`Constraint::Length`] - Fixed size in character cells
//! - [`Constraint::Percentage`] - Relative size as a percentage of available space
//! - [`Constraint::Ratio`] - Proportional size using ratios
//! - [`Constraint::Fill`] - Proportional fill of remaining space
//!
//! Constraints are resolved in priority order, with [`Constraint::Min`] having the highest
//! priority and [`Constraint::Fill`] having the lowest. The constraint solver will satisfy as
//! many constraints as possible while respecting these priorities.
//!
//! # Flexible Space Distribution
//!
//! The [`Flex`] enum controls how extra space is distributed when constraints are satisfied:
//!
//! - [`Flex::Start`] - Align content to the start, leaving excess space at the end
//! - [`Flex::End`] - Align content to the end, leaving excess space at the start
//! - [`Flex::Center`] - Center content, distributing excess space equally on both sides
//! - [`Flex::SpaceBetween`] - Distribute excess space evenly between elements
//! - [`Flex::SpaceAround`] - Distribute excess space evenly around each element
//! - [`Flex::Legacy`] - Legacy behavior (puts excess space in the last element)
//!
//! # Positioning and Alignment
//!
//! Use [`Position`] to represent specific points in the terminal, [`Size`] for dimensions, and the
//! alignment types for controlling content positioning within areas:
//!
//! ```rust
//! use ratatui_core::layout::{Alignment, Position, Rect, Size};
//!
//! let pos = Position::new(10, 5);
//! let size = Size::new(80, 24);
//! let rect = Rect::new(pos.x, pos.y, size.width, size.height);
//!
//! // Alignment for content within areas
//! let center = Alignment::Center;
//! ```
//!
//! # Advanced Features
//!
//! ## Margins and Spacing
//!
//! Add spacing around areas using uniform margins or between layout segments using [`Spacing`]:
//!
//! ```rust
//! use ratatui_core::layout::{Constraint, Layout, Margin, Rect, Spacing};
//!
//! let layout = Layout::vertical([Constraint::Fill(1), Constraint::Fill(1)])
//! .margin(2) // 2-cell margin on all sides
//! .spacing(Spacing::Space(1)); // 1-cell spacing between segments
//!
//! // For asymmetric margins, use the Rect inner method directly
//! let area = Rect::new(0, 0, 80, 24).inner(Margin::new(2, 1));
//! ```
//!
//! ## Area Iteration
//!
//! Iterate over rows, columns, or all positions within a rectangular area. The `rows()` and
//! `columns()` iterators return full [`Rect`] regions that can be used to render widgets or
//! passed to other layout methods for more complex nested layouts. The `positions()` iterator
//! returns [`Position`] values representing individual cell coordinates:
//!
//! ```rust
//! use ratatui_core::buffer::Buffer;
//! use ratatui_core::layout::{Constraint, Layout, Rect};
//! use ratatui_core::widgets::Widget;
//!
//! let area = Rect::new(0, 0, 20, 10);
//! let mut buffer = Buffer::empty(area);
//!
//! // Renders "Row 0", "Row 1", etc. in each horizontal row
//! for (i, row) in area.rows().enumerate() {
//! format!("Row {i}").render(row, &mut buffer);
//! }
//!
//! // Renders column indices (0-9 repeating) in each vertical column
//! for (i, col) in area.columns().enumerate() {
//! format!("{}", i % 10).render(col, &mut buffer);
//! }
//!
//! // Renders position indices (0-9 repeating) at each cell position
//! for (i, pos) in area.positions().enumerate() {
//! buffer[pos].set_symbol(&format!("{}", i % 10));
//! }
//! ```
//!
//! # Performance Considerations
//!
//! The layout system includes optional caching to improve performance for repeated layout
//! calculations. Layout caching is enabled by default in the main `ratatui` crate, but requires
//! explicitly enabling the `layout-cache` feature when using `ratatui-core` directly. When
//! enabled, layout results are cached based on the area and layout configuration.
//!
//! # Related Documentation
//!
//! For more detailed information and practical examples:
//!
//! - [Layout Concepts](https://ratatui.rs/concepts/layout/) - Comprehensive guide to layout
//! concepts
//! - [Layout Recipes](https://ratatui.rs/recipes/layout/) - Practical layout examples and patterns
//! - [Grid Layout Recipe](https://ratatui.rs/recipes/layout/grid/) - Creating grid-based layouts
//! - [Center a Widget Recipe](https://ratatui.rs/recipes/layout/center-a-widget/) - Centering
//! content
//! - [Dynamic Layouts Recipe](https://ratatui.rs/recipes/layout/dynamic/) - Creating responsive
//! layouts
//!
//! # Examples
//!
//! See the Ratatui repository for complete examples:
//!
//! - [`constraints`](https://github.com/ratatui/ratatui/blob/main/examples/apps/constraints/) -
//! Demonstrates different constraint types
//! - [`flex`](https://github.com/ratatui/ratatui/blob/main/examples/apps/flex/) - Shows flex space
//! distribution
//! - [`layout`](https://github.com/ratatui/ratatui/blob/main/examples/apps/layout/) - Basic layout
//! examples
mod alignment;
mod constraint;

View File

@ -7,11 +7,17 @@ use strum::{Display, EnumString};
/// and libraries, it's unlikely that this alias will be removed in the future.
pub type Alignment = HorizontalAlignment;
/// A type representing horizontal alignment.
/// Horizontal content alignment within a layout area.
///
/// Prior to Ratatui 0.30.0, this type was named `Alignment`. In Ratatui 0.30.0, the name was
/// changed to `HorizontalAlignment` to make it more descriptive. The old name is still available as
/// an alias for backwards compatibility.
///
/// This type is used throughout Ratatui to control how content is positioned horizontally within
/// available space. It's commonly used with widgets to control text alignment, but can also be
/// used in layout calculations.
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
pub enum HorizontalAlignment {
#[default]
@ -20,7 +26,12 @@ pub enum HorizontalAlignment {
Right,
}
/// A type representing vertical alignment.
/// Vertical content alignment within a layout area.
///
/// This type is used to control how content is positioned vertically within available space.
/// It complements [`HorizontalAlignment`] to provide full 2D positioning control.
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
pub enum VerticalAlignment {
#[default]

View File

@ -5,10 +5,9 @@ use strum::EnumIs;
/// A constraint that defines the size of a layout element.
///
/// Constraints can be used to specify a fixed size, a percentage of the available space, a ratio of
/// the available space, a minimum or maximum size or a fill proportional value for a layout
/// element.
///
/// Constraints are the core mechanism for defining how space should be allocated within a
/// [`Layout`](crate::layout::Layout). They can specify fixed sizes (length), proportional sizes
/// (percentage, ratio), size limits (min, max), or proportional fill values for layout elements.
/// Relative constraints (percentage, ratio) are calculated relative to the entire space being
/// divided, rather than the space available after applying more fixed constraints (min, max,
/// length).
@ -22,6 +21,27 @@ use strum::EnumIs;
/// 5. [`Constraint::Ratio`]
/// 6. [`Constraint::Fill`]
///
/// # Size Calculation
///
/// - [`apply`](Self::apply) - Apply the constraint to a length and return the resulting size
///
/// # Collection Creation
///
/// - [`from_lengths`](Self::from_lengths) - Create a collection of length constraints
/// - [`from_ratios`](Self::from_ratios) - Create a collection of ratio constraints
/// - [`from_percentages`](Self::from_percentages) - Create a collection of percentage constraints
/// - [`from_maxes`](Self::from_maxes) - Create a collection of maximum constraints
/// - [`from_mins`](Self::from_mins) - Create a collection of minimum constraints
/// - [`from_fills`](Self::from_fills) - Create a collection of fill constraints
///
/// # Conversion and Construction
///
/// - [`from(u16)`](Self::from) - Create a [`Length`](Self::Length) constraint from `u16`
/// - [`from(&Constraint)`](Self::from) - Create from `&Constraint` (copy)
/// - [`as_ref()`](Self::as_ref) - Get a reference to self
/// - [`default()`](Self::default) - Create default constraint
/// ([`Percentage(100)`](Self::Percentage))
///
/// # Examples
///
/// `Constraint` provides helper methods to create lists of constraints from various input formats.
@ -45,6 +65,8 @@ use strum::EnumIs;
/// // Create a layout with fill proportional sizes for each element
/// let constraints = Constraint::from_fills([1, 2, 1]);
/// ```
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, EnumIs)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Constraint {

View File

@ -1,5 +1,14 @@
use strum::{Display, EnumString};
/// Defines the direction of a layout.
///
/// This enumeration is used with [`Layout`](crate::layout::Layout) to specify whether layout
/// segments should be arranged horizontally or vertically.
///
/// - `Horizontal`: Layout segments are arranged side by side (left to right)
/// - `Vertical`: Layout segments are arranged top to bottom (default)
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Direction {

View File

@ -5,7 +5,12 @@ use crate::layout::Constraint;
/// Defines the options for layout flex justify content in a container.
///
/// This enumeration controls the distribution of space when layout constraints are met.
/// This enumeration controls the distribution of space when layout constraints are met and there
/// is excess space available. The `Flex` option is used with [`Layout`](crate::layout::Layout) to
/// control how extra space is distributed among layout segments, which is particularly useful for
/// creating responsive layouts that adapt to different terminal sizes.
///
/// Available options:
///
/// - `Legacy`: Fills the available space within the container, putting excess space into the last
/// element.
@ -14,6 +19,8 @@ use crate::layout::Constraint;
/// - `Center`: Centers items within the container.
/// - `SpaceBetween`: Adds excess space between each element.
/// - `SpaceAround`: Adds excess space around each element.
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Copy, Debug, Default, Display, EnumString, Clone, Eq, PartialEq, Hash, EnumIs)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Flex {

View File

@ -109,47 +109,57 @@ impl From<i16> for Spacing {
}
}
/// The primary layout engine for dividing terminal space using constraints and direction.
///
/// A layout is a set of constraints that can be applied to a given area to split it into smaller
/// ones.
/// rectangular areas. This is the core building block for creating structured user interfaces in
/// terminal applications.
///
/// A layout is composed of:
/// - a direction (horizontal or vertical)
/// - a set of constraints (length, ratio, percentage, fill, min, max)
/// - a margin (horizontal and vertical), the space between the edge of the main area and the split
/// areas
/// - a flex option
/// - a spacing option
/// - a flex option that controls space distribution
/// - a spacing option that controls gaps between segments
///
/// The algorithm used to compute the layout is based on the [`kasuari`] solver. It is a simple
/// linear solver that can be used to solve linear equations and inequalities. In our case, we
/// define a set of constraints that are applied to split the provided area into Rects aligned in a
/// single direction, and the solver computes the values of the position and sizes that satisfy as
/// many of the constraints in order of their priorities.
/// The algorithm used to compute the layout is based on the [`kasuari`] solver, a linear constraint
/// solver that computes positions and sizes to satisfy as many constraints as possible in order of
/// their priorities.
///
/// When the layout is computed, the result is cached in a thread-local cache, so that subsequent
/// calls with the same parameters are faster. The cache is a `LruCache`, and the size of the cache
/// can be configured using [`Layout::init_cache()`].
///
/// # Constructors
/// # Construction
///
/// There are four ways to create a new layout:
/// - [`default`](Default::default) - Create a layout with default values (vertical direction, no
/// constraints, no margin)
/// - [`new`](Self::new) - Create a new layout with a given direction and constraints
/// - [`vertical`](Self::vertical) - Create a new vertical layout with the given constraints
/// - [`horizontal`](Self::horizontal) - Create a new horizontal layout with the given constraints
///
/// - [`Layout::default`]: create a new layout with default values
/// - [`Layout::new`]: create a new layout with a given direction and constraints
/// - [`Layout::vertical`]: create a new vertical layout with the given constraints
/// - [`Layout::horizontal`]: create a new horizontal layout with the given constraints
/// # Configuration
///
/// # Setters
/// - [`direction`](Self::direction) - Set the direction of the layout
/// - [`constraints`](Self::constraints) - Set the constraints of the layout
/// - [`margin`](Self::margin) - Set uniform margin on all sides
/// - [`horizontal_margin`](Self::horizontal_margin) - Set the horizontal margin of the layout
/// - [`vertical_margin`](Self::vertical_margin) - Set the vertical margin of the layout
/// - [`flex`](Self::flex) - Set the way space is distributed when constraints are satisfied
/// - [`spacing`](Self::spacing) - Set the gap between the constraints of the layout
///
/// There are several setters to modify the layout:
/// # Layout Operations
///
/// - [`Layout::direction`]: set the direction of the layout
/// - [`Layout::constraints`]: set the constraints of the layout
/// - [`Layout::margin`]: set the margin of the layout
/// - [`Layout::horizontal_margin`]: set the horizontal margin of the layout
/// - [`Layout::vertical_margin`]: set the vertical margin of the layout
/// - [`Layout::flex`]: set the way the space is distributed when the constraints are satisfied
/// - [`Layout::spacing`]: sets the gap between the constraints of the layout
/// - [`areas`](Self::areas) - Split area into fixed number of rectangles (compile-time known)
/// - [`spacers`](Self::spacers) - Get spacer rectangles between layout areas
/// - [`split`](Self::split) - Split area into rectangles (runtime determined count)
/// - [`split_with_spacers`](Self::split_with_spacers) - Split area and return both areas and
/// spacers
///
/// # Cache Management
///
/// - [`init_cache`](Self::init_cache) - Initialize layout cache with custom size
///
/// # Example
///
@ -160,16 +170,18 @@ impl From<i16> for Spacing {
/// use ratatui_core::widgets::Widget;
///
/// fn render(area: Rect, buf: &mut Buffer) {
/// let layout = Layout::vertical([Constraint::Length(5), Constraint::Min(0)]);
/// let [left, right] = layout.areas(area);
/// Text::from("foo").render(left, buf);
/// Text::from("bar").render(right, buf);
/// let layout = Layout::vertical([Constraint::Length(5), Constraint::Fill(1)]);
/// let [top, bottom] = layout.areas(area);
/// Text::from("foo").render(top, buf);
/// Text::from("bar").render(bottom, buf);
/// }
/// ```
///
/// See the `layout`, `flex`, and `constraints` examples in the [Examples] folder for more details
/// about how to use layouts.
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
///
/// ![layout
/// example](https://camo.githubusercontent.com/77d22f3313b782a81e5e033ef82814bb48d786d2598699c27f8e757ccee62021/68747470733a2f2f7668732e636861726d2e73682f7668732d315a4e6f4e4c4e6c4c746b4a58706767396e435635652e676966)
///
@ -215,7 +227,7 @@ impl Layout {
///
/// Layout::new(
/// Direction::Horizontal,
/// [Constraint::Length(5), Constraint::Min(0)],
/// [Constraint::Length(5), Constraint::Fill(1)],
/// );
///
/// Layout::new(
@ -247,7 +259,7 @@ impl Layout {
/// ```rust
/// use ratatui_core::layout::{Constraint, Layout};
///
/// let layout = Layout::vertical([Constraint::Length(5), Constraint::Min(0)]);
/// let layout = Layout::vertical([Constraint::Length(5), Constraint::Fill(1)]);
/// ```
pub fn vertical<I>(constraints: I) -> Self
where
@ -267,7 +279,7 @@ impl Layout {
/// ```rust
/// use ratatui_core::layout::{Constraint, Layout};
///
/// let layout = Layout::horizontal([Constraint::Length(5), Constraint::Min(0)]);
/// let layout = Layout::horizontal([Constraint::Length(5), Constraint::Fill(1)]);
/// ```
pub fn horizontal<I>(constraints: I) -> Self
where
@ -299,13 +311,13 @@ impl Layout {
///
/// let layout = Layout::default()
/// .direction(Direction::Horizontal)
/// .constraints([Constraint::Length(5), Constraint::Min(0)])
/// .constraints([Constraint::Length(5), Constraint::Fill(1)])
/// .split(Rect::new(0, 0, 10, 10));
/// assert_eq!(layout[..], [Rect::new(0, 0, 5, 10), Rect::new(5, 0, 5, 10)]);
///
/// let layout = Layout::default()
/// .direction(Direction::Vertical)
/// .constraints([Constraint::Length(5), Constraint::Min(0)])
/// .constraints([Constraint::Length(5), Constraint::Fill(1)])
/// .split(Rect::new(0, 0, 10, 10));
/// assert_eq!(layout[..], [Rect::new(0, 0, 10, 5), Rect::new(0, 5, 10, 5)]);
/// ```
@ -352,10 +364,10 @@ impl Layout {
/// ]
/// );
///
/// Layout::default().constraints([Constraint::Min(0)]);
/// Layout::default().constraints(&[Constraint::Min(0)]);
/// Layout::default().constraints(vec![Constraint::Min(0)]);
/// Layout::default().constraints([Constraint::Min(0)].iter().filter(|_| true));
/// Layout::default().constraints([Constraint::Fill(1)]);
/// Layout::default().constraints(&[Constraint::Fill(1)]);
/// Layout::default().constraints(vec![Constraint::Fill(1)]);
/// Layout::default().constraints([Constraint::Fill(1)].iter().filter(|_| true));
/// Layout::default().constraints([1, 2, 3].iter().map(|&c| Constraint::Length(c)));
/// Layout::default().constraints([1, 2, 3]);
/// Layout::default().constraints(vec![1, 2, 3]);
@ -378,7 +390,7 @@ impl Layout {
/// use ratatui_core::layout::{Constraint, Layout, Rect};
///
/// let layout = Layout::default()
/// .constraints([Constraint::Min(0)])
/// .constraints([Constraint::Fill(1)])
/// .margin(2)
/// .split(Rect::new(0, 0, 10, 10));
/// assert_eq!(layout[..], [Rect::new(2, 2, 6, 6)]);
@ -400,7 +412,7 @@ impl Layout {
/// use ratatui_core::layout::{Constraint, Layout, Rect};
///
/// let layout = Layout::default()
/// .constraints([Constraint::Min(0)])
/// .constraints([Constraint::Fill(1)])
/// .horizontal_margin(2)
/// .split(Rect::new(0, 0, 10, 10));
/// assert_eq!(layout[..], [Rect::new(2, 0, 6, 10)]);
@ -419,7 +431,7 @@ impl Layout {
/// use ratatui_core::layout::{Constraint, Layout, Rect};
///
/// let layout = Layout::default()
/// .constraints([Constraint::Min(0)])
/// .constraints([Constraint::Fill(1)])
/// .vertical_margin(2)
/// .split(Rect::new(0, 0, 10, 10));
/// assert_eq!(layout[..], [Rect::new(0, 2, 10, 6)]);
@ -525,14 +537,15 @@ impl Layout {
/// # Examples
///
/// ```rust
/// use ratatui_core::layout::{Layout, Constraint, Rect};
/// use ratatui_core::layout::{Constraint, Layout, Rect};
///
/// let area = Rect::new(0, 0, 10, 10);
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]);
/// let [top, main] = layout.areas(area);
///
/// // or explicitly specify the number of constraints:
/// let areas = layout.areas::<2>(area);
/// ```
pub fn areas<const N: usize>(&self, area: Rect) -> [Rect; N] {
let (areas, _) = self.split_with_spacers(area);
areas.as_ref().try_into().expect("invalid number of rects")
@ -559,7 +572,7 @@ impl Layout {
/// use ratatui_core::layout::{Constraint, Layout, Rect};
///
/// let area = Rect::new(0, 0, 10, 10);
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]);
/// let [top, main] = layout.areas(area);
/// let [before, inbetween, after] = layout.spacers(area);
///
@ -598,7 +611,7 @@ impl Layout {
/// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
/// let layout = Layout::default()
/// .direction(Direction::Vertical)
/// .constraints([Constraint::Length(5), Constraint::Min(0)])
/// .constraints([Constraint::Length(5), Constraint::Fill(1)])
/// .split(Rect::new(2, 2, 10, 10));
/// assert_eq!(layout[..], [Rect::new(2, 2, 10, 5), Rect::new(2, 7, 10, 5)]);
///
@ -631,7 +644,7 @@ impl Layout {
///
/// let (areas, spacers) = Layout::default()
/// .direction(Direction::Vertical)
/// .constraints([Constraint::Length(5), Constraint::Min(0)])
/// .constraints([Constraint::Length(5), Constraint::Fill(1)])
/// .split_with_spacers(Rect::new(2, 2, 10, 10));
/// assert_eq!(areas[..], [Rect::new(2, 2, 10, 5), Rect::new(2, 7, 10, 5)]);
/// assert_eq!(
@ -950,7 +963,7 @@ fn configure_flex_constraints(
/// │abcdef││abcdef│
/// └──────┘└──────┘
///
/// [Min(0), Fill(2)]
/// [Fill(1), Fill(2)]
/// ┌──────┐┌────────────┐
/// │abcdef││abcdefabcdef│
/// └──────┘└────────────┘

View File

@ -1,5 +1,38 @@
use core::fmt;
/// Represents spacing around rectangular areas.
///
/// `Margin` defines the horizontal and vertical spacing that should be applied around a rectangular
/// area. It's commonly used with [`Layout`](crate::layout::Layout) to add space between the
/// layout's boundaries and its contents, or with [`Rect::inner`](crate::layout::Rect::inner) and
/// [`Rect::outer`](crate::layout::Rect::outer) to create padded areas.
///
/// The margin values represent the number of character cells to add on each side. For horizontal
/// 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.
///
/// # Construction
///
/// - [`new`](Self::new) - Create a new margin with horizontal and vertical spacing
/// - [`default`](Default::default) - Create with zero margin
///
/// # Examples
///
/// ```rust
/// use ratatui_core::layout::{Constraint, Layout, Margin, Rect};
///
/// // Create a margin of 2 cells horizontally and 1 cell vertically
/// let margin = Margin::new(2, 1);
///
/// // Apply directly to a rectangle
/// let area = Rect::new(0, 0, 80, 24);
/// let inner_area = area.inner(margin);
///
/// // Or use with a layout (which only accepts uniform margins)
/// let layout = Layout::vertical([Constraint::Fill(1)]).margin(2);
/// ```
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Margin {

View File

@ -3,12 +3,26 @@ use core::fmt;
use crate::layout::Rect;
/// Position in the terminal
/// Position in the terminal coordinate system.
///
/// The position is relative to the top left corner of the terminal window, with the top left corner
/// being (0, 0). The x axis is horizontal increasing to the right, and the y axis is vertical
/// increasing downwards.
///
/// `Position` is used throughout the layout system to represent specific points in the terminal.
/// It can be created from coordinates, tuples, or extracted from rectangular areas.
///
/// # Construction
///
/// - [`new`](Self::new) - Create a new position from x and y coordinates
/// - [`default`](Default::default) - Create at origin (0, 0)
///
/// # Conversion
///
/// - [`from((u16, u16))`](Self::from) - Create from `(u16, u16)` tuple
/// - [`from(Rect)`](Self::from) - Create from [`Rect`] (uses top-left corner)
/// - [`into((u16, u16))`] - Convert to `(u16, u16)` tuple
///
/// # Examples
///
/// ```
@ -23,6 +37,8 @@ use crate::layout::Rect;
/// // position can be converted back into the components when needed
/// let (x, y) = position.into();
/// ```
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Position {

View File

@ -9,10 +9,77 @@ pub use iter::*;
use super::{Constraint, Flex, Layout};
/// A Rectangular area.
/// A rectangular area in the terminal.
///
/// 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.
/// A `Rect` represents a rectangular region in the terminal coordinate system, defined by its
/// top-left corner position and dimensions. This is the fundamental building block for all layout
/// operations and widget rendering in Ratatui.
///
/// Rectangles are used throughout the layout system to define areas where widgets can be rendered.
/// They are typically created by [`Layout`] operations that divide terminal space, but can also be
/// manually constructed for specific positioning needs.
///
/// The coordinate system uses the top-left corner as the origin (0, 0), with x increasing to the
/// right and y increasing downward. All measurements are in character cells.
///
/// # Construction and Conversion
///
/// - [`new`](Self::new) - Create a new rectangle from coordinates and dimensions
/// - [`as_position`](Self::as_position) - Convert to a position at the top-left corner
/// - [`as_size`](Self::as_size) - Convert to a size representing the dimensions
/// - [`from((Position, Size))`](Self::from) - Create from `(Position, Size)` tuple
/// - [`from(((u16, u16), (u16, u16)))`](Self::from) - Create from `((u16, u16), (u16, u16))`
/// coordinate and dimension tuples
/// - [`into((Position, Size))`] - Convert to `(Position, Size)` tuple
/// - [`default`](Self::default) - Create a zero-sized rectangle at origin
///
/// # Geometry and Properties
///
/// - [`area`](Self::area) - Calculate the total area in character cells
/// - [`is_empty`](Self::is_empty) - Check if the rectangle has zero area
/// - [`left`](Self::left), [`right`](Self::right), [`top`](Self::top), [`bottom`](Self::bottom) -
/// Get edge coordinates
///
/// # Spatial Operations
///
/// - [`inner`](Self::inner), [`outer`](Self::outer) - Apply margins to shrink or expand
/// - [`offset`](Self::offset) - Move the rectangle by a relative amount
/// - [`union`](Self::union) - Combine with another rectangle to create a bounding box
/// - [`intersection`](Self::intersection) - Find the overlapping area with another rectangle
/// - [`clamp`](Self::clamp) - Constrain the rectangle to fit within another
///
/// # Positioning and Centering
///
/// - [`centered_horizontally`](Self::centered_horizontally) - Center horizontally within a
/// constraint
/// - [`centered_vertically`](Self::centered_vertically) - Center vertically within a constraint
/// - [`centered`](Self::centered) - Center both horizontally and vertically
///
/// # Testing and Iteration
///
/// - [`contains`](Self::contains) - Check if a position is within the rectangle
/// - [`intersects`](Self::intersects) - Check if it overlaps with another rectangle
/// - [`rows`](Self::rows) - Iterate over horizontal rows within the rectangle
/// - [`columns`](Self::columns) - Iterate over vertical columns within the rectangle
/// - [`positions`](Self::positions) - Iterate over all positions within the rectangle
///
/// # Examples
///
/// ```rust
/// use ratatui_core::layout::{Position, Rect, Size};
///
/// // Create a rectangle manually
/// let rect = Rect::new(10, 5, 80, 20);
/// assert_eq!(rect.x, 10);
/// assert_eq!(rect.y, 5);
/// assert_eq!(rect.width, 80);
/// assert_eq!(rect.height, 20);
///
/// // Create from position and size
/// let rect = Rect::from((Position::new(10, 5), Size::new(80, 20)));
/// ```
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Rect {
@ -298,17 +365,31 @@ impl Rect {
/// An iterator over rows within the `Rect`.
///
/// Each row is a full `Rect` region with height 1 that can be used for rendering widgets
/// or as input to further layout methods.
///
/// # Example
///
/// ```
/// use ratatui_core::buffer::Buffer;
/// use ratatui_core::layout::Rect;
/// use ratatui_core::text::Line;
/// use ratatui_core::layout::{Constraint, Layout, Rect};
/// use ratatui_core::widgets::Widget;
///
/// fn render(area: Rect, buf: &mut Buffer) {
/// for row in area.rows() {
/// Line::raw("Hello, world!").render(row, buf);
/// fn render_list(area: Rect, buf: &mut Buffer) {
/// // Renders "Item 0", "Item 1", etc. in each row
/// for (i, row) in area.rows().enumerate() {
/// format!("Item {i}").render(row, buf);
/// }
/// }
///
/// fn render_with_nested_layout(area: Rect, buf: &mut Buffer) {
/// // Splits each row into left/right areas and renders labels and content
/// for (i, row) in area.rows().take(3).enumerate() {
/// let [left, right] =
/// Layout::horizontal([Constraint::Percentage(30), Constraint::Fill(1)]).areas(row);
///
/// format!("{i}:").render(left, buf);
/// "Content".render(right, buf);
/// }
/// }
/// ```
@ -318,17 +399,20 @@ impl Rect {
/// An iterator over columns within the `Rect`.
///
/// Each column is a full `Rect` region with width 1 that can be used for rendering widgets
/// or as input to further layout methods.
///
/// # Example
///
/// ```
/// use ratatui_core::buffer::Buffer;
/// use ratatui_core::layout::Rect;
/// use ratatui_core::text::Text;
/// use ratatui_core::widgets::Widget;
///
/// fn render(area: Rect, buf: &mut Buffer) {
/// fn render_columns(area: Rect, buf: &mut Buffer) {
/// // Renders column indices (0-9 repeating) in each column
/// for (i, column) in area.columns().enumerate() {
/// Text::from(format!("{}", i)).render(column, buf);
/// format!("{}", i % 10).render(column, buf);
/// }
/// }
/// ```
@ -339,16 +423,19 @@ impl Rect {
/// An iterator over the positions within the `Rect`.
///
/// The positions are returned in a row-major order (left-to-right, top-to-bottom).
/// Each position is a `Position` that represents a single cell coordinate.
///
/// # Example
///
/// ```
/// use ratatui_core::buffer::Buffer;
/// use ratatui_core::layout::Rect;
/// use ratatui_core::layout::{Position, Rect};
/// use ratatui_core::widgets::Widget;
///
/// fn render(area: Rect, buf: &mut Buffer) {
/// for position in area.positions() {
/// buf[(position.x, position.y)].set_symbol("x");
/// fn render_positions(area: Rect, buf: &mut Buffer) {
/// // Renders position indices (0-9 repeating) at each cell position
/// for (i, position) in area.positions().enumerate() {
/// buf[position].set_symbol(&format!("{}", i % 10));
/// }
/// }
/// ```

View File

@ -3,10 +3,38 @@ use core::fmt;
use crate::layout::Rect;
/// A simple size struct
/// A simple size struct for representing dimensions in the terminal.
///
/// The width and height are stored as `u16` values and represent the number of columns and rows
/// respectively.
/// respectively. This is used throughout the layout system to represent dimensions of rectangular
/// areas and other layout elements.
///
/// Size can be created from tuples, extracted from rectangular areas, or constructed directly.
/// It's commonly used in conjunction with [`Position`](crate::layout::Position) to define
/// rectangular areas.
///
/// # Construction
///
/// - [`new`](Self::new) - Create a new size from width and height
/// - [`default`](Default::default) - Create with zero dimensions
///
/// # Conversion
///
/// - [`from((u16, u16))`](Self::from) - Create from `(u16, u16)` tuple
/// - [`from(Rect)`](Self::from) - Create from [`Rect`] (uses width and height)
/// - [`into((u16, u16))`] - Convert to `(u16, u16)` tuple
///
/// # Examples
///
/// ```rust
/// use ratatui_core::layout::{Rect, Size};
///
/// let size = Size::new(80, 24);
/// let size = Size::from((80, 24));
/// let size = Size::from(Rect::new(0, 0, 80, 24));
/// ```
///
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Size {