ratatui_macros/layout.rs
1/// Creates a single constraint.
2///
3/// If creating an array of constraints, you probably want to use
4/// [`constraints!`] instead.
5///
6/// # Examples
7///
8/// ```
9/// # use ratatui_core::layout::Constraint;
10/// use ratatui_macros::constraint;
11/// assert_eq!(constraint!(>= 3 + 4), Constraint::Min(7));
12/// assert_eq!(constraint!(<= 3 + 4), Constraint::Max(7));
13/// assert_eq!(constraint!(== 1 / 3), Constraint::Ratio(1, 3));
14/// assert_eq!(constraint!(== 3), Constraint::Length(3));
15/// assert_eq!(constraint!(== 10 %), Constraint::Percentage(10));
16/// assert_eq!(constraint!(*= 1), Constraint::Fill(1));
17/// ```
18///
19/// [`constraints!`]: crate::constraints
20#[macro_export]
21macro_rules! constraint {
22 (== $token:tt %) => {
23 $crate::ratatui_core::layout::Constraint::Percentage($token)
24 };
25 (>= $expr:expr) => {
26 $crate::ratatui_core::layout::Constraint::Min($expr)
27 };
28 (<= $expr:expr) => {
29 $crate::ratatui_core::layout::Constraint::Max($expr)
30 };
31 (== $num:tt / $denom:tt) => {
32 $crate::ratatui_core::layout::Constraint::Ratio($num as u32, $denom as u32)
33 };
34 (== $expr:expr) => {
35 $crate::ratatui_core::layout::Constraint::Length($expr)
36 };
37 (*= $expr:expr) => {
38 $crate::ratatui_core::layout::Constraint::Fill($expr)
39 };
40}
41
42/// Creates an array of constraints.
43///
44/// See [`constraint!`] for more information.
45///
46/// If you want to solve the constraints, see
47/// [`vertical!`] and [`horizontal!`] macros.
48///
49/// # Examples
50///
51/// ```rust
52/// use ratatui_macros::constraints;
53/// assert_eq!(constraints![==5, ==30%, >=3, <=1, ==1/2].len(), 5);
54/// assert_eq!(constraints![==5; 5].len(), 5);
55/// ```
56///
57/// ```rust
58/// # use ratatui_core::layout::Constraint;
59/// # use ratatui_macros::constraints;
60/// assert_eq!(
61/// constraints![==50, ==30%, >=3, <=1, ==1/2, *=1],
62/// [
63/// Constraint::Length(50),
64/// Constraint::Percentage(30),
65/// Constraint::Min(3),
66/// Constraint::Max(1),
67/// Constraint::Ratio(1, 2),
68/// Constraint::Fill(1),
69/// ]
70/// )
71/// ```
72///
73/// [`constraint!`]: crate::constraint
74/// [`vertical!`]: crate::vertical
75/// [`horizontal!`]: crate::horizontal
76#[macro_export]
77macro_rules! constraints {
78 // Note: this implementation forgoes speed for the sake of simplicity. Adding variations of the
79 // comma and semicolon rules for each constraint type would be faster, but would result in a lot
80 // of duplicated code.
81
82 // Cannot start the constraints macro with a ,
83 ([ , $($rest:tt)* ] -> () []) => {
84 compile_error!("No rules expected the token `,` while trying to match the end of the macro")
85 };
86
87 // Comma finishes a constraint element, so parse it and continue.
88 // When a comma is encountered, it marks the end of a constraint element, so this rule is responsible
89 // for parsing the constraint expression up to the comma and continuing the parsing process.
90 // It accumulated the $partial contains a Constraint and is parsed using a separate $crate::constraint! macro.
91 // The constraint is then appended to the list of parsed constraints.
92 //
93 // [ , $($rest:tt)* ] -> In the rule matcher, this pattern matches a comma followed
94 // by the rest of the tokens. The comma signals the end of
95 // the current constraint element.
96 // ($($partial:tt)*) -> In the rule matcher, this contains the partial tokens
97 // accumulated so far for the current constraint element.
98 // [$($parsed:tt)* ] -> This contains the constraints that have been successfully
99 // parsed so far.
100 // $crate::constraint!($($partial)*) -> This macro call parses and expands the accumulated
101 // partial tokens into a single Constraint expression.
102 // [$($parsed)* $crate::constraint!(...)] -> Appends the newly parsed constraint to the list of
103 // already parsed constraints.
104 ([ , $($rest:tt)* ] -> ($($partial:tt)*) [ $($parsed:tt)* ]) => {
105 $crate::constraints!([$($rest)*] -> () [$($parsed)* $crate::constraint!($($partial)*) ,])
106 };
107
108 // Semicolon indicates that there's repetition. The trailing comma is required because the 'entrypoint'
109 // rule adds a trailing comma.
110 // This rule is triggered when a semicolon is encountered, indicating that there is repetition of
111 // constraints. It handles the repetition logic by parsing the count and generating an array of
112 // constraints using the $crate::constraint! macro.
113 //
114 // [ ; $count:expr , ] -> In the rule matcher, this pattern matches a semicolon
115 // followed by an expression representing the count, and a
116 // trailing comma.
117 // ($($partial:tt)*) -> In the rule matcher, this contains the partial tokens
118 // accumulated so far for the current constraint element.
119 // This represents everything before the ;
120 // [] -> There will be no existed parsed constraints when using ;
121 // $crate::constraint!($($partial)*) -> This macro call parses and expands the accumulated
122 // partial tokens into a single Constraint expression.
123 // [$crate::constraint!(...) ; $count] -> Generates an array of constraints by repeating the
124 // parsed constraint count number of times.
125 ([ ; $count:expr , ] -> ($($partial:tt)*) []) => {
126 [$crate::constraint!($($partial)*); $count]
127 };
128
129 // Pull the first token (which can't be a comma or semicolon) onto the accumulator.
130 // if first token is a comma or semicolon, previous rules will match before this rule
131 //
132 // [ $head:tt $($rest:tt)* ] -> In the rule matcher, this pulls a single `head` token
133 // out of the previous rest, and puts
134 // the remaining into `rest`
135 // [ $($rest)* ] -> This is what is fed back into the `constraints!` macro
136 // as the first segment for the match rule
137 //
138 // ($($partial:tt)*) -> In the rule matcher, this contains previous partial
139 // tokens that will make up a `Constraint` expression
140 // ($($partial)* $head) -> This combines head with the previous partial tokens
141 // i.e. this is the accumulated tokens
142 //
143 // [ $($parsed:tt)* ] -> In the rule matcher, this contains all parsed exprs
144 // [$($parsed)* ] -> These are passed on to the next match untouched.
145 ([ $head:tt $($rest:tt)* ] -> ($($partial:tt)*) [ $($parsed:tt)* ]) => {
146 $crate::constraints!([$($rest)*] -> ($($partial)* $head) [$($parsed)* ])
147 };
148
149 // This rule is triggered when there are no more input tokens to process. It signals the end of the
150 // macro invocation and outputs the parsed constraints as a final array.
151 ([$(,)?] -> () [ $( $parsed:tt )* ]) => {
152 [$($parsed)*]
153 };
154
155 // Entrypoint where there's no comma at the end.
156 // We add a comma to make sure there's always a trailing comma.
157 // Right-hand side will accumulate the actual `Constraint` literals.
158 ($( $constraint:tt )+) => {
159 $crate::constraints!([ $($constraint)+ , ] -> () [])
160 };
161}
162
163/// Creates a vertical layout with specified constraints.
164///
165/// It accepts a series of constraints and applies them to create a vertical layout. The constraints
166/// can include fixed sizes, minimum and maximum sizes, percentages, and ratios.
167///
168/// See [`constraint!`] or [`constraints!`] for more information.
169///
170/// # Examples
171///
172/// ```
173/// // Vertical layout with a fixed size and a percentage constraint
174/// use ratatui_macros::vertical;
175/// vertical![== 50, == 30%];
176/// ```
177#[macro_export]
178macro_rules! vertical {
179 ($( $constraint:tt )+) => {
180 $crate::ratatui_core::layout::Layout::vertical($crate::constraints!( $($constraint)+ ))
181 };
182}
183
184/// Creates a horizontal layout with specified constraints.
185///
186/// It takes a series of constraints and applies them to create a horizontal layout. The constraints
187/// can include fixed sizes, minimum and maximum sizes, percentages, and ratios.
188///
189/// See [`constraint!`] or [`constraints!`] for more information.
190///
191/// # Examples
192///
193/// ```
194/// // Horizontal layout with a ratio constraint and a minimum size constraint
195/// use ratatui_macros::horizontal;
196/// horizontal![== 1/3, >= 100];
197/// ```
198#[macro_export]
199macro_rules! horizontal {
200 ($( $constraint:tt )+) => {
201 $crate::ratatui_core::layout::Layout::horizontal($crate::constraints!( $($constraint)+ ))
202 };
203}