diff --git a/ratatui-core/src/layout.rs b/ratatui-core/src/layout.rs index a6982607..a4ba7b48 100644 --- a/ratatui-core/src/layout.rs +++ b/ratatui-core/src/layout.rs @@ -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; diff --git a/ratatui-core/src/layout/alignment.rs b/ratatui-core/src/layout/alignment.rs index 849c9850..a7d52567 100644 --- a/ratatui-core/src/layout/alignment.rs +++ b/ratatui-core/src/layout/alignment.rs @@ -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] diff --git a/ratatui-core/src/layout/constraint.rs b/ratatui-core/src/layout/constraint.rs index d2d1fbb7..8e65457f 100644 --- a/ratatui-core/src/layout/constraint.rs +++ b/ratatui-core/src/layout/constraint.rs @@ -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 { diff --git a/ratatui-core/src/layout/direction.rs b/ratatui-core/src/layout/direction.rs index ba0bf4fb..6acc4d98 100644 --- a/ratatui-core/src/layout/direction.rs +++ b/ratatui-core/src/layout/direction.rs @@ -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 { diff --git a/ratatui-core/src/layout/flex.rs b/ratatui-core/src/layout/flex.rs index b4b16610..6b40c980 100644 --- a/ratatui-core/src/layout/flex.rs +++ b/ratatui-core/src/layout/flex.rs @@ -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 { diff --git a/ratatui-core/src/layout/layout.rs b/ratatui-core/src/layout/layout.rs index 6b8e1169..0f21c5cb 100644 --- a/ratatui-core/src/layout/layout.rs +++ b/ratatui-core/src/layout/layout.rs @@ -109,47 +109,57 @@ impl From 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 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(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(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(&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│ /// └──────┘└────────────┘ diff --git a/ratatui-core/src/layout/margin.rs b/ratatui-core/src/layout/margin.rs index 1b220dc1..e3d972aa 100644 --- a/ratatui-core/src/layout/margin.rs +++ b/ratatui-core/src/layout/margin.rs @@ -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 { diff --git a/ratatui-core/src/layout/position.rs b/ratatui-core/src/layout/position.rs index 9b3a7d2b..339cad0a 100644 --- a/ratatui-core/src/layout/position.rs +++ b/ratatui-core/src/layout/position.rs @@ -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 { diff --git a/ratatui-core/src/layout/rect.rs b/ratatui-core/src/layout/rect.rs index 34b58afe..d63ab2ac 100644 --- a/ratatui-core/src/layout/rect.rs +++ b/ratatui-core/src/layout/rect.rs @@ -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)); /// } /// } /// ``` diff --git a/ratatui-core/src/layout/size.rs b/ratatui-core/src/layout/size.rs index 688efac6..fa0d404c 100644 --- a/ratatui-core/src/layout/size.rs +++ b/ratatui-core/src/layout/size.rs @@ -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 {