26_radio_buttons/26_radio_buttons.rs
1//! Example 26: Radio Buttons
2//!
3//! Demonstrates radio button groups for mutually exclusive options.
4//! Use arrow keys or j/k to navigate within a group.
5//!
6//! Run with: `cargo run -p telex-tui --example 26_radio_buttons`
7
8use crossterm::event::KeyCode;
9use telex::prelude::*;
10use telex::Color;
11
12telex::require_api!(0, 2);
13
14fn main() {
15 telex::run_with_theme(App, telex::theme::Theme::nord()).unwrap();
16}
17
18struct App;
19
20impl Component for App {
21 fn render(&self, cx: Scope) -> View {
22 let show_help = state!(cx, || false);
23
24 // F1 toggles help
25 cx.use_command(
26 KeyBinding::key(KeyCode::F(1)),
27 with!(show_help => move || show_help.update(|v| *v = !*v)),
28 );
29
30 // State for different radio groups
31 let theme = state!(cx, || 0usize); // 0=Light, 1=Dark, 2=System
32 let font_size = state!(cx, || 1usize); // 0=Small, 1=Medium, 2=Large
33 let notification = state!(cx, || 0usize); // 0=All, 1=Important, 2=None
34
35 let theme_options = vec!["Light", "Dark", "System"];
36 let font_options = vec!["Small (12px)", "Medium (14px)", "Large (16px)"];
37 let notification_options = vec!["All notifications", "Important only", "None"];
38
39 View::vstack()
40 .spacing(1)
41 .child(
42 // Header
43 View::boxed()
44 .border(true)
45 .padding(1)
46 .child(
47 View::vstack()
48 .child(View::styled_text("Radio Buttons Demo").bold().build())
49 .child(
50 View::styled_text(
51 "Use Tab to switch groups, Up/Down or j/k to select",
52 )
53 .dim()
54 .build(),
55 )
56 .build(),
57 )
58 .build(),
59 )
60 .child(
61 // Main content - settings panel
62 View::boxed()
63 .flex(1)
64 .border(true)
65 .padding(1)
66 .child(
67 View::hstack()
68 .spacing(4)
69 // Theme selection
70 .child(
71 View::vstack()
72 .spacing(1)
73 .child(
74 View::styled_text("Theme")
75 .bold()
76 .color(Color::Cyan)
77 .build(),
78 )
79 .child(
80 View::radio_group()
81 .options(theme_options)
82 .selected(theme.get())
83 .on_change(with!(theme => move |idx| {
84 theme.set(idx);
85 }))
86 .build(),
87 )
88 .build(),
89 )
90 // Font size selection
91 .child(
92 View::vstack()
93 .spacing(1)
94 .child(
95 View::styled_text("Font Size")
96 .bold()
97 .color(Color::Green)
98 .build(),
99 )
100 .child(
101 View::radio_group()
102 .options(font_options)
103 .selected(font_size.get())
104 .on_change(with!(font_size => move |idx| {
105 font_size.set(idx);
106 }))
107 .build(),
108 )
109 .build(),
110 )
111 // Notification selection
112 .child(
113 View::vstack()
114 .spacing(1)
115 .child(
116 View::styled_text("Notifications")
117 .bold()
118 .color(Color::Yellow)
119 .build(),
120 )
121 .child(
122 View::radio_group()
123 .options(notification_options)
124 .selected(notification.get())
125 .on_change(with!(notification => move |idx| {
126 notification.set(idx);
127 }))
128 .build(),
129 )
130 .build(),
131 )
132 .build(),
133 )
134 .build(),
135 )
136 .child(
137 // Current selections display
138 View::boxed()
139 .border(true)
140 .padding(1)
141 .child(
142 View::vstack()
143 .child(View::styled_text("Current Settings:").bold().build())
144 .child(View::text(format!(
145 "Theme: {} | Font: {} | Notifications: {}",
146 match theme.get() {
147 0 => "Light",
148 1 => "Dark",
149 _ => "System",
150 },
151 match font_size.get() {
152 0 => "Small",
153 1 => "Medium",
154 _ => "Large",
155 },
156 match notification.get() {
157 0 => "All",
158 1 => "Important",
159 _ => "None",
160 }
161 )))
162 .build(),
163 )
164 .build(),
165 )
166 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
167 .child(
168 View::modal()
169 .visible(show_help.get())
170 .title("Example 26: Radio Buttons")
171 .on_dismiss(with!(show_help => move || show_help.set(false)))
172 .child(
173 View::vstack()
174 .child(View::styled_text("What you're seeing").bold().build())
175 .child(View::text("• Radio groups for mutually exclusive options"))
176 .child(View::text("• Three independent groups"))
177 .child(View::text("• Current selection shown below"))
178 .child(View::gap(1))
179 .child(View::styled_text("Key concepts").bold().build())
180 .child(View::text("• View::radio_group() creates groups"))
181 .child(View::text("• .options() takes Vec<&str>"))
182 .child(View::text("• .selected() binds to state (usize)"))
183 .child(View::text("• on_change receives new index"))
184 .child(View::gap(1))
185 .child(View::styled_text("Try this").bold().build())
186 .child(View::text("• Tab between groups"))
187 .child(View::text("• Up/Down or j/k to select"))
188 .child(View::text("• Watch current settings update"))
189 .child(View::gap(1))
190 .child(View::styled_text("Next up").bold().build())
191 .child(View::text("→ 27_keyed_state: order-independent hooks"))
192 .child(View::gap(1))
193 .child(View::styled_text("Press Escape to close").dim().build())
194 .build(),
195 )
196 .build(),
197 )
198 .build()
199 }
200}