ratatui/widgets/block/
title.rs

1//! This module holds the [`Title`] element and its related configuration types.
2//! A title is a piece of [`Block`](crate::widgets::Block) configuration.
3
4use strum::{Display, EnumString};
5
6use crate::{layout::Alignment, text::Line};
7
8/// A [`Block`](crate::widgets::Block) title.
9///
10/// It can be aligned (see [`Alignment`]) and positioned (see [`Position`]).
11///
12/// # Future Deprecation
13///
14/// This type is deprecated and will be removed in a future release. The reason for this is that the
15/// position of the title should be stored in the block itself, not in the title. The `Line` type
16/// has an alignment method that can be used to align the title. For more information see
17/// <https://github.com/ratatui/ratatui/issues/738>.
18///
19/// Use [`Line`] instead, when the position is not defined as part of the title. When a specific
20/// position is needed, use [`Block::title_top`](crate::widgets::Block::title_top) or
21/// [`Block::title_bottom`](crate::widgets::Block::title_bottom) instead.
22///
23/// # Example
24///
25/// Title with no style.
26/// ```
27/// use ratatui::widgets::block::Title;
28///
29/// Title::from("Title");
30/// ```
31///
32/// Blue title on a white background (via [`Stylize`](crate::style::Stylize) trait).
33/// ```
34/// use ratatui::{style::Stylize, widgets::block::Title};
35///
36/// Title::from("Title".blue().on_white());
37/// ```
38///
39/// Title with multiple styles (see [`Line`] and [`Stylize`](crate::style::Stylize)).
40/// ```
41/// use ratatui::{style::Stylize, text::Line, widgets::block::Title};
42///
43/// Title::from(Line::from(vec!["Q".white().underlined(), "uit".gray()]));
44/// ```
45///
46/// Complete example
47/// ```
48/// use ratatui::{
49///     layout::Alignment,
50///     widgets::{
51///         block::{Position, Title},
52///         Block,
53///     },
54/// };
55///
56/// Title::from("Title")
57///     .position(Position::Top)
58///     .alignment(Alignment::Right);
59/// ```
60#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
61pub struct Title<'a> {
62    /// Title content
63    pub content: Line<'a>,
64    /// Title alignment
65    ///
66    /// If [`None`], defaults to the alignment defined with
67    /// [`Block::title_alignment`](crate::widgets::Block::title_alignment) in the associated
68    /// [`Block`](crate::widgets::Block).
69    pub alignment: Option<Alignment>,
70
71    /// Title position
72    ///
73    /// If [`None`], defaults to the position defined with
74    /// [`Block::title_position`](crate::widgets::Block::title_position) in the associated
75    /// [`Block`](crate::widgets::Block).
76    pub position: Option<Position>,
77}
78
79/// Defines the [title](crate::widgets::block::Title) position.
80///
81/// The title can be positioned on top or at the bottom of the block.
82/// Defaults to [`Position::Top`].
83///
84/// # Example
85///
86/// ```
87/// use ratatui::widgets::{
88///     block::{Position, Title},
89///     Block,
90/// };
91///
92/// Block::new().title(Title::from("title").position(Position::Bottom));
93/// ```
94#[derive(Debug, Default, Display, EnumString, Clone, Copy, PartialEq, Eq, Hash)]
95pub enum Position {
96    /// Position the title at the top of the block.
97    ///
98    /// This is the default.
99    #[default]
100    Top,
101    /// Position the title at the bottom of the block.
102    Bottom,
103}
104
105#[deprecated = "use Block::title_top() or Block::title_bottom() instead. This will be removed in a future release."]
106impl<'a> Title<'a> {
107    /// Set the title content.
108    #[must_use = "method moves the value of self and returns the modified value"]
109    pub fn content<T>(mut self, content: T) -> Self
110    where
111        T: Into<Line<'a>>,
112    {
113        self.content = content.into();
114        self
115    }
116
117    /// Set the title alignment.
118    #[must_use = "method moves the value of self and returns the modified value"]
119    pub const fn alignment(mut self, alignment: Alignment) -> Self {
120        self.alignment = Some(alignment);
121        self
122    }
123
124    /// Set the title position.
125    #[must_use = "method moves the value of self and returns the modified value"]
126    pub const fn position(mut self, position: Position) -> Self {
127        self.position = Some(position);
128        self
129    }
130}
131
132impl<'a, T> From<T> for Title<'a>
133where
134    T: Into<Line<'a>>,
135{
136    fn from(value: T) -> Self {
137        let content = value.into();
138        let alignment = content.alignment;
139        Self {
140            content,
141            alignment,
142            position: None,
143        }
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use rstest::rstest;
150    use strum::ParseError;
151
152    use super::*;
153
154    #[test]
155    fn position_to_string() {
156        assert_eq!(Position::Top.to_string(), "Top");
157        assert_eq!(Position::Bottom.to_string(), "Bottom");
158    }
159
160    #[test]
161    fn position_from_str() {
162        assert_eq!("Top".parse::<Position>(), Ok(Position::Top));
163        assert_eq!("Bottom".parse::<Position>(), Ok(Position::Bottom));
164        assert_eq!("".parse::<Position>(), Err(ParseError::VariantNotFound));
165    }
166
167    #[test]
168    fn title_from_line() {
169        let title = Title::from(Line::raw("Title"));
170        assert_eq!(title.content, Line::from("Title"));
171        assert_eq!(title.alignment, None);
172        assert_eq!(title.position, None);
173    }
174
175    #[rstest]
176    #[case::left(Alignment::Left)]
177    #[case::center(Alignment::Center)]
178    #[case::right(Alignment::Right)]
179    fn title_from_line_with_alignment(#[case] alignment: Alignment) {
180        let line = Line::raw("Title").alignment(alignment);
181        let title = Title::from(line.clone());
182        assert_eq!(title.content, line);
183        assert_eq!(title.alignment, Some(alignment));
184        assert_eq!(title.position, None);
185    }
186}