revue/widget/macros.rs
1//! Declarative UI macros
2
3/// Create a vertical stack with children
4///
5/// # Examples
6///
7/// ```ignore
8/// use revue::prelude::*;
9///
10/// let view = vstack![
11/// Text::heading("Title"),
12/// Text::new("Content"),
13/// ];
14///
15/// // With gap
16/// let view = vstack![gap: 2;
17/// Text::new("Line 1"),
18/// Text::new("Line 2"),
19/// ];
20/// ```
21#[macro_export]
22macro_rules! vstack {
23 // With gap
24 (gap: $gap:expr; $($child:expr),* $(,)?) => {{
25 $crate::widget::vstack()
26 .gap($gap)
27 $(.child($child))*
28 }};
29 // Without gap
30 ($($child:expr),* $(,)?) => {{
31 $crate::widget::vstack()
32 $(.child($child))*
33 }};
34}
35
36/// Create a horizontal stack with children
37///
38/// # Examples
39///
40/// ```ignore
41/// use revue::prelude::*;
42///
43/// let view = hstack![
44/// Text::new("Left"),
45/// Text::new("Right"),
46/// ];
47///
48/// // With gap
49/// let view = hstack![gap: 1;
50/// Text::new("A"),
51/// Text::new("B"),
52/// ];
53/// ```
54#[macro_export]
55macro_rules! hstack {
56 // With gap
57 (gap: $gap:expr; $($child:expr),* $(,)?) => {{
58 $crate::widget::hstack()
59 .gap($gap)
60 $(.child($child))*
61 }};
62 // Without gap
63 ($($child:expr),* $(,)?) => {{
64 $crate::widget::hstack()
65 $(.child($child))*
66 }};
67}
68
69/// Create a border with a child
70///
71/// # Examples
72///
73/// ```ignore
74/// use revue::prelude::*;
75///
76/// let view = bordered![
77/// Text::new("Content")
78/// ];
79///
80/// // With title
81/// let view = bordered!["My Panel";
82/// Text::new("Content")
83/// ];
84///
85/// // With type and title
86/// let view = bordered![rounded, "Card Title";
87/// Text::new("Card content")
88/// ];
89/// ```
90#[macro_export]
91macro_rules! bordered {
92 // Border type + title + child
93 ($border_type:ident, $title:expr; $child:expr) => {{
94 $crate::widget::Border::$border_type()
95 .title($title)
96 .child($child)
97 }};
98 // Title + child
99 ($title:expr; $child:expr) => {{
100 $crate::widget::Border::single().title($title).child($child)
101 }};
102 // Just child
103 ($child:expr) => {{
104 $crate::widget::Border::single().child($child)
105 }};
106}
107
108/// Create text with common styling
109///
110/// # Examples
111///
112/// ```ignore
113/// use revue::prelude::*;
114///
115/// let t = text!("Hello");
116/// let t = text!("Error!", red);
117/// let t = text!("Success", green, bold);
118/// ```
119#[macro_export]
120macro_rules! text {
121 // Text with color and modifiers
122 ($content:expr, $color:ident, bold) => {{
123 $crate::widget::Text::new($content)
124 .fg($crate::style::Color::$color)
125 .bold()
126 }};
127 ($content:expr, $color:ident, italic) => {{
128 $crate::widget::Text::new($content)
129 .fg($crate::style::Color::$color)
130 .italic()
131 }};
132 // Text with color
133 ($content:expr, red) => {{
134 $crate::widget::Text::error($content)
135 }};
136 ($content:expr, green) => {{
137 $crate::widget::Text::success($content)
138 }};
139 ($content:expr, yellow) => {{
140 $crate::widget::Text::warning($content)
141 }};
142 ($content:expr, cyan) => {{
143 $crate::widget::Text::info($content)
144 }};
145 ($content:expr, $color:ident) => {{
146 $crate::widget::Text::new($content).fg($crate::style::Color::$color)
147 }};
148 // Plain text
149 ($content:expr) => {{
150 $crate::widget::Text::new($content)
151 }};
152}
153
154/// Build a complete UI layout declaratively
155///
156/// # Examples
157///
158/// ```ignore
159/// use revue::prelude::*;
160///
161/// let ui = ui! {
162/// vstack(gap: 1) {
163/// Text::heading("Dashboard")
164/// hstack {
165/// bordered!["Stats"; Text::new("100")]
166/// bordered!["Users"; Text::new("42")]
167/// }
168/// Text::muted("Press 'q' to quit")
169/// }
170/// };
171/// ```
172#[macro_export]
173macro_rules! ui {
174 // VStack with options
175 (vstack(gap: $gap:expr) { $($child:tt)* }) => {{
176 $crate::widget::vstack()
177 .gap($gap)
178 $(.child($crate::ui!(@child $child)))*
179 }};
180 // VStack without options
181 (vstack { $($child:tt)* }) => {{
182 $crate::widget::vstack()
183 $(.child($crate::ui!(@child $child)))*
184 }};
185 // HStack with options
186 (hstack(gap: $gap:expr) { $($child:tt)* }) => {{
187 $crate::widget::hstack()
188 .gap($gap)
189 $(.child($crate::ui!(@child $child)))*
190 }};
191 // HStack without options
192 (hstack { $($child:tt)* }) => {{
193 $crate::widget::hstack()
194 $(.child($crate::ui!(@child $child)))*
195 }};
196 // Child processing - recursive ui! call
197 (@child vstack $($rest:tt)*) => {{
198 $crate::ui!(vstack $($rest)*)
199 }};
200 (@child hstack $($rest:tt)*) => {{
201 $crate::ui!(hstack $($rest)*)
202 }};
203 // Child processing - direct expression
204 (@child $expr:expr) => {{
205 $expr
206 }};
207}