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}