pub struct BoxBuilder { /* private fields */ }Expand description
Builder for Box views.
Implementations§
Source§impl BoxBuilder
impl BoxBuilder
pub fn new() -> Self
Sourcepub fn child(self, view: View) -> Self
pub fn child(self, view: View) -> Self
Examples found in repository?
examples/09_syntax_comparison.rs (lines 72-76)
24 fn render(&self, cx: Scope) -> View {
25 let use_jsx = state!(cx, || false);
26 let show_help = state!(cx, || false);
27
28 // F1 toggles help
29 cx.use_command(
30 KeyBinding::key(KeyCode::F(1)),
31 with!(show_help => move || show_help.update(|v| *v = !*v)),
32 );
33
34 let toggle = with!(use_jsx => move || use_jsx.set(!use_jsx.get()));
35
36 // Show which syntax is currently displayed
37 let syntax_name = if use_jsx.get() {
38 "view! macro (JSX-like)"
39 } else {
40 "Builder pattern"
41 };
42
43 View::vstack()
44 .child(
45 View::styled_text("Syntax Comparison")
46 .color(Color::Cyan)
47 .bold()
48 .build(),
49 )
50 .child(
51 View::styled_text("Same UI, two ways to write it")
52 .dim()
53 .build(),
54 )
55 .child(View::gap(1))
56 .child(
57 View::hstack()
58 .child(View::text("Current syntax: "))
59 .child(
60 View::styled_text(syntax_name)
61 .color(Color::Yellow)
62 .bold()
63 .build(),
64 )
65 .build(),
66 )
67 .child(View::gap(1))
68 .child(
69 View::boxed()
70 .border(true)
71 .padding(1)
72 .child(if use_jsx.get() {
73 counter_jsx(cx.clone())
74 } else {
75 counter_builder(cx.clone())
76 })
77 .build(),
78 )
79 .child(View::gap(1))
80 .child(
81 View::button()
82 .label("Toggle Syntax")
83 .on_press(toggle)
84 .build(),
85 )
86 .child(View::gap(1))
87 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
88 .child(
89 View::modal()
90 .visible(show_help.get())
91 .title("Example 09: Syntax Comparison")
92 .on_dismiss(with!(show_help => move || show_help.set(false)))
93 .child(
94 View::vstack()
95 .child(View::styled_text("What you're seeing").bold().build())
96 .child(View::text("• Two syntaxes that produce identical output"))
97 .child(View::text("• Builder: View::vstack().child(...).build()"))
98 .child(View::text("• Macro: view! { <VStack>...</VStack> }"))
99 .child(View::gap(1))
100 .child(View::styled_text("Key concepts").bold().build())
101 .child(View::text("• Builder is Rust-native, IDE-friendly"))
102 .child(View::text("• view! macro is JSX-like, less boilerplate"))
103 .child(View::text("• Choose based on your preference"))
104 .child(View::gap(1))
105 .child(View::styled_text("Try this").bold().build())
106 .child(View::text("• Toggle between syntaxes"))
107 .child(View::text("• Notice the output is identical"))
108 .child(View::text("• Check the source code to see both styles"))
109 .child(View::gap(1))
110 .child(View::styled_text("Next up").bold().build())
111 .child(View::text("→ 10_state_explained: deep dive into state"))
112 .child(View::gap(1))
113 .child(View::styled_text("Press Escape to close").dim().build())
114 .build(),
115 )
116 .build(),
117 )
118 .build()
119 }More examples
examples/15_markdown.rs (lines 75-106)
54 fn render(&self, cx: Scope) -> View {
55 let show_help = state!(cx, || false);
56
57 // F1 toggles help
58 cx.use_command(
59 KeyBinding::key(KeyCode::F(1)),
60 with!(show_help => move || show_help.update(|v| *v = !*v)),
61 );
62
63 let rendered = telex::markdown::render(DEMO_MARKDOWN);
64
65 View::vstack()
66 .child(
67 View::styled_text("Markdown Rendering Demo")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::boxed()
74 .flex(1)
75 .child(
76 View::split()
77 .horizontal()
78 .ratio(0.4)
79 .first(
80 View::vstack()
81 .child(View::styled_text(" Source ").bold().build())
82 .child(
83 View::boxed()
84 .flex(1)
85 .border(true)
86 .scroll(true)
87 .child(View::text(DEMO_MARKDOWN))
88 .build(),
89 )
90 .build(),
91 )
92 .second(
93 View::vstack()
94 .child(View::styled_text(" Rendered ").bold().build())
95 .child(
96 View::boxed()
97 .flex(1)
98 .border(true)
99 .scroll(true)
100 .child(rendered)
101 .build(),
102 )
103 .build(),
104 )
105 .build(),
106 )
107 .build(),
108 )
109 .child(
110 View::styled_text("Tab: switch panes | ↑↓/jk: scroll | F1 help | Ctrl+Q: quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 15: Markdown")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• Side-by-side markdown source and rendered"))
123 .child(View::text("• Full markdown syntax support"))
124 .child(View::text("• Scrollable panes for long content"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• telex::markdown::render() parses markdown"))
128 .child(View::text("• Returns a View tree with styled text"))
129 .child(View::text("• Code blocks, lists, quotes, headers"))
130 .child(View::text("• Split view for comparison"))
131 .child(View::gap(1))
132 .child(View::styled_text("Try this").bold().build())
133 .child(View::text("• Tab between source and rendered panes"))
134 .child(View::text("• Scroll with arrow keys or j/k"))
135 .child(View::text("• Compare source with rendered output"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 16_progress: progress bars"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }examples/16_tree.rs (lines 80-87)
20 fn render(&self, cx: Scope) -> View {
21 let show_help = state!(cx, || false);
22
23 // F1 toggles help
24 cx.use_command(
25 KeyBinding::key(KeyCode::F(1)),
26 with!(show_help => move || show_help.update(|v| *v = !*v)),
27 );
28
29 // Track selected path
30 let selected = state!(cx, || vec![0usize]);
31
32 // Track expanded state for each node (by path prefix)
33 let expanded_paths = state!(cx, || {
34 vec![
35 vec![0], // src/ expanded
36 vec![0, 0], // src/components/ expanded
37 ]
38 });
39
40 // Build tree items with current expanded state
41 let items = build_tree(&expanded_paths.get());
42
43 let on_select = with!(selected => move |path: TreePath| {
44 selected.set(path);
45 });
46
47 let on_activate = with!(expanded_paths => move |path: TreePath| {
48 // Toggle expand/collapse for the activated item
49 let mut paths = expanded_paths.get().clone();
50 if let Some(pos) = paths.iter().position(|p| *p == path) {
51 // Currently expanded, collapse it
52 paths.remove(pos);
53 } else {
54 // Currently collapsed, expand it
55 paths.push(path.clone());
56 }
57 expanded_paths.set(paths);
58 });
59
60 let selected_label = get_item_at_path(&items, &selected.get())
61 .map(|item| item.label.clone())
62 .unwrap_or_else(|| "Nothing".to_string());
63
64 View::vstack()
65 .child(
66 View::styled_text("File Browser")
67 .color(Color::Cyan)
68 .bold()
69 .build(),
70 )
71 .child(
72 View::styled_text(format!("Selected: {}", selected_label))
73 .dim()
74 .build(),
75 )
76 .child(
77 View::boxed()
78 .flex(1)
79 .border(true)
80 .child(
81 View::tree()
82 .items(items)
83 .selected(selected.get().clone())
84 .on_select(on_select)
85 .on_activate(on_activate)
86 .build(),
87 )
88 .build(),
89 )
90 .child(
91 View::styled_text(
92 "↑↓/jk: navigate | Enter: expand/collapse | F1 help | Ctrl+Q: quit",
93 )
94 .dim()
95 .build(),
96 )
97 .child(
98 View::modal()
99 .visible(show_help.get())
100 .title("Example 16: Tree View")
101 .on_dismiss(with!(show_help => move || show_help.set(false)))
102 .child(
103 View::vstack()
104 .child(View::styled_text("What you're seeing").bold().build())
105 .child(View::text("• Hierarchical tree widget"))
106 .child(View::text("• Expand/collapse folders"))
107 .child(View::text("• Path-based selection tracking"))
108 .child(View::gap(1))
109 .child(View::styled_text("Key concepts").bold().build())
110 .child(View::text("• View::tree() for hierarchical data"))
111 .child(View::text("• TreeItem::new().child() builds hierarchy"))
112 .child(View::text("• on_select returns TreePath (Vec<usize>)"))
113 .child(View::text("• on_activate for expand/collapse"))
114 .child(View::gap(1))
115 .child(View::styled_text("Try this").bold().build())
116 .child(View::text("• Navigate with arrow keys"))
117 .child(View::text("• Press Enter to expand/collapse folders"))
118 .child(View::text("• Watch the 'Selected:' text update"))
119 .child(View::gap(1))
120 .child(View::styled_text("Next up").bold().build())
121 .child(View::text("→ 17_table: data tables with sorting"))
122 .child(View::gap(1))
123 .child(View::styled_text("Press Escape to close").dim().build())
124 .build(),
125 )
126 .build(),
127 )
128 .build()
129 }examples/10_state_explained.rs (lines 68-80)
21 fn render(&self, cx: Scope) -> View {
22 let count = state!(cx, || 0i32);
23 let show_help = state!(cx, || false);
24
25 // F1 toggles help
26 cx.use_command(
27 KeyBinding::key(KeyCode::F(1)),
28 with!(show_help => move || show_help.update(|v| *v = !*v)),
29 );
30
31 // Clone handles for closures (this is the pattern being explained)
32 let count_for_increment = count.clone();
33 let count_for_decrement = count.clone();
34 let count_for_reset = count.clone();
35
36 let increment = move || {
37 let current = count_for_increment.get();
38 count_for_increment.set(current + 1);
39 };
40
41 let decrement = move || {
42 let current = count_for_decrement.get();
43 count_for_decrement.set(current - 1);
44 };
45
46 let reset = move || {
47 count_for_reset.set(0);
48 };
49
50 let current_value = count.get();
51
52 // Hook ordering demo
53 let _always_called_1 = state!(cx, || "hook 1");
54 let _always_called_2 = state!(cx, || "hook 2");
55
56 View::vstack()
57 .spacing(1)
58 .child(
59 View::styled_text("State Explained")
60 .color(Color::Cyan)
61 .bold()
62 .build(),
63 )
64 .child(
65 View::boxed()
66 .border(true)
67 .padding(1)
68 .child(
69 View::vstack()
70 .child(View::styled_text("The Mental Model:").bold().build())
71 .child(View::gap(1))
72 .child(View::text(" State<T> is a HANDLE, not data"))
73 .child(View::text(" clone() copies the handle, not the data"))
74 .child(View::text(" All handles point to ONE value"))
75 .child(View::gap(1))
76 .child(View::text(" count ──────┐"))
77 .child(View::text(" ├──► i32: 0 (shared!)"))
78 .child(View::text(" count2 ─────┘"))
79 .build(),
80 )
81 .build(),
82 )
83 .child(
84 View::hstack()
85 .spacing(1)
86 .child(View::text("Current value:"))
87 .child(
88 View::styled_text(format!("{}", current_value))
89 .color(Color::Yellow)
90 .bold()
91 .build(),
92 )
93 .build(),
94 )
95 .child(
96 View::hstack()
97 .spacing(1)
98 .child(View::button().label(" - ").on_press(decrement).build())
99 .child(View::button().label(" + ").on_press(increment).build())
100 .child(View::button().label("Reset").on_press(reset).build())
101 .build(),
102 )
103 .child(
104 View::styled_text("All three buttons modify the SAME underlying i32")
105 .dim()
106 .build(),
107 )
108 .child(View::gap(1))
109 .child(
110 View::styled_text("Tab navigate • F1 help • Ctrl+Q quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 10: State Explained")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• State<T> as a handle/pointer concept"))
123 .child(View::text("• Multiple closures sharing one value"))
124 .child(View::text("• Visual diagram of the mental model"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• clone() copies handle, not data"))
128 .child(View::text("• All handles point to same underlying value"))
129 .child(View::text(
130 "• Hooks must be called in same order every render",
131 ))
132 .child(View::gap(1))
133 .child(View::styled_text("Important rule").bold().build())
134 .child(View::text("• NEVER put use_state inside an if block"))
135 .child(View::text("• Use with!() macro to simplify cloning"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 11_checkbox: toggle controls"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }examples/13_split_panes.rs (lines 59-100)
20 fn render(&self, cx: Scope) -> View {
21 let show_help = state!(cx, || false);
22
23 // F1 toggles help
24 cx.use_command(
25 KeyBinding::key(KeyCode::F(1)),
26 with!(show_help => move || show_help.update(|v| *v = !*v)),
27 );
28
29 let selected_item = state!(cx, || 0usize);
30
31 let items = vec![
32 "README.md".to_string(),
33 "Cargo.toml".to_string(),
34 "src/".to_string(),
35 "src/main.rs".to_string(),
36 "src/lib.rs".to_string(),
37 "tests/".to_string(),
38 ];
39
40 let on_select = with!(selected_item => move |idx: usize| {
41 selected_item.set(idx);
42 });
43
44 let detail_text = match selected_item.get() {
45 0 => "# README\n\nThis is a demo of split panes.\n\nThe left panel shows a file list,\nthe right panel shows details.",
46 1 => "[package]\nname = \"demo\"\nversion = \"0.1.0\"\nedition = \"2021\"",
47 2 => "Directory: src/\n\nContains the main source files.",
48 3 => "fn main() {\n println!(\"Hello, world!\");\n}",
49 4 => "pub mod utils;\npub mod widgets;",
50 5 => "Directory: tests/\n\nContains integration tests.",
51 _ => "Select an item to see details.",
52 };
53
54 // Horizontal split: file list on left, details on right
55 View::vstack()
56 .child(
57 View::boxed()
58 .flex(1)
59 .child(
60 View::split()
61 .horizontal()
62 .ratio(0.3)
63 .min_first(15)
64 .first(
65 View::vstack()
66 .child(
67 View::styled_text("Files")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::list()
74 .items(items)
75 .selected(selected_item.get())
76 .on_select(on_select)
77 .build(),
78 )
79 .child(View::gap(1))
80 .build(),
81 )
82 .second(
83 View::vstack()
84 .child(
85 View::styled_text("Details")
86 .color(Color::Green)
87 .bold()
88 .build(),
89 )
90 .child(
91 View::boxed()
92 .border(true)
93 .flex(1)
94 .child(View::text(detail_text))
95 .build(),
96 )
97 .build(),
98 )
99 .build(),
100 )
101 .build(),
102 )
103 .child(
104 View::styled_text("↑↓: select file | F1 help | Ctrl+Q: quit")
105 .dim()
106 .build(),
107 )
108 .child(
109 View::modal()
110 .visible(show_help.get())
111 .title("Example 13: Split Panes")
112 .on_dismiss(with!(show_help => move || show_help.set(false)))
113 .child(
114 View::vstack()
115 .child(View::styled_text("What you're seeing").bold().build())
116 .child(View::text("• Horizontal split: file list / details"))
117 .child(View::text("• ratio(0.3) = 30% left, 70% right"))
118 .child(View::text("• min_first(15) sets minimum pane width"))
119 .child(View::gap(1))
120 .child(View::styled_text("Key concepts").bold().build())
121 .child(View::text("• View::split() creates resizable panes"))
122 .child(View::text("• .horizontal() or .vertical() orientation"))
123 .child(View::text("• .first() and .second() set pane content"))
124 .child(View::text("• Splits can be nested"))
125 .child(View::gap(1))
126 .child(View::styled_text("Try this").bold().build())
127 .child(View::text("• Select different files to see details"))
128 .child(View::text("• Resize terminal to see layout adapt"))
129 .child(View::gap(1))
130 .child(View::styled_text("Next up").bold().build())
131 .child(View::text("→ 14_tabs: tabbed interfaces"))
132 .child(View::gap(1))
133 .child(View::styled_text("Press Escape to close").dim().build())
134 .build(),
135 )
136 .build(),
137 )
138 .build()
139 }examples/06_log_viewer.rs (line 114)
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 // Stream log entries
31 let logs = text_stream!(cx, || {
32 let log_messages = vec![
33 "[INFO] Application started",
34 "[INFO] Loading configuration...",
35 "[OK] Config loaded successfully",
36 "[INFO] Connecting to database...",
37 "[OK] Database connected",
38 "[INFO] Starting web server on :8080",
39 "[OK] Server listening",
40 "[INFO] Processing request GET /api/users",
41 "[OK] Response 200 in 45ms",
42 "[INFO] Processing request POST /api/login",
43 "[OK] Response 200 in 120ms",
44 "[WARN] High memory usage detected: 85%",
45 "[INFO] Running garbage collection...",
46 "[OK] Memory freed: 200MB",
47 "[INFO] Processing request GET /api/data",
48 "[ERROR] Database timeout after 5000ms",
49 "[INFO] Retrying database connection...",
50 "[OK] Database reconnected",
51 "[OK] Response 200 in 5045ms",
52 "[INFO] Scheduled backup starting...",
53 "[OK] Backup completed: 1.2GB",
54 "[INFO] Processing request GET /api/health",
55 "[OK] Response 200 in 12ms",
56 "[INFO] Processing request PUT /api/users/123",
57 "[OK] Response 200 in 89ms",
58 "[INFO] Cache invalidation triggered",
59 "[OK] Cache cleared: 50MB",
60 "[WARN] Slow query detected: 1200ms",
61 "[INFO] Query optimization suggested",
62 "[INFO] Processing request DELETE /api/sessions",
63 "[OK] Response 204 in 34ms",
64 "[INFO] SSL certificate check",
65 "[OK] Certificate valid for 45 days",
66 "[INFO] Processing request POST /api/upload",
67 "[OK] File uploaded: 15MB",
68 "[WARN] Disk usage at 78%",
69 "[INFO] Processing request GET /api/reports",
70 "[OK] Response 200 in 230ms",
71 "[INFO] Metrics exported to monitoring",
72 "[OK] Heartbeat sent successfully",
73 "------- Log stream completed -------",
74 ];
75
76 log_messages.into_iter().map(|msg| {
77 std::thread::sleep(Duration::from_millis(500));
78 format!("{}\n", msg)
79 })
80 });
81
82 let log_text = logs.get();
83 let is_streaming = logs.is_loading();
84
85 // Color the status indicator
86 let status = if is_streaming {
87 View::styled_text(" [LIVE]")
88 .color(Color::Green)
89 .bold()
90 .build()
91 } else {
92 View::styled_text(" [END]").dim().build()
93 };
94
95 View::vstack()
96 .child(
97 View::hstack()
98 .child(
99 View::styled_text("Log Viewer")
100 .color(Color::Cyan)
101 .bold()
102 .build(),
103 )
104 .child(status)
105 .build(),
106 )
107 .child(View::gap(1))
108 .child(
109 View::boxed()
110 .scroll(true)
111 .auto_scroll_bottom(true)
112 .min_height(15)
113 .max_height(15)
114 .child(View::text(&log_text))
115 .build(),
116 )
117 .child(View::gap(1))
118 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
119 .child(
120 View::modal()
121 .visible(show_help.get())
122 .title("Example 06: Log Viewer")
123 .on_dismiss(with!(show_help => move || show_help.set(false)))
124 .child(
125 View::vstack()
126 .child(View::styled_text("What you're seeing").bold().build())
127 .child(View::text("• text_stream!() macro for accumulating text"))
128 .child(View::text("• Auto-scrolling box that follows new content"))
129 .child(View::text("• Live/End indicator based on stream state"))
130 .child(View::gap(1))
131 .child(View::styled_text("Key concepts").bold().build())
132 .child(View::text("• text_stream! concatenates yielded strings"))
133 .child(View::text(
134 "• auto_scroll_bottom(true) keeps newest visible",
135 ))
136 .child(View::text("• Simulates tailing a log file"))
137 .child(View::gap(1))
138 .child(View::styled_text("Try this").bold().build())
139 .child(View::text("• Watch the [LIVE] indicator change to [END]"))
140 .child(View::text("• Notice auto-scroll keeps up with new entries"))
141 .child(View::gap(1))
142 .child(View::styled_text("Next up").bold().build())
143 .child(View::text("→ 07_file_browser: real filesystem navigation"))
144 .child(View::gap(1))
145 .child(View::styled_text("Press Escape to close").dim().build())
146 .build(),
147 )
148 .build(),
149 )
150 .build()
151 }Additional examples can be found in:
- examples/24_async_data.rs
- examples/25_context.rs
- examples/14_tabs.rs
- examples/11_checkbox.rs
- examples/17_table.rs
- examples/20_menu_bar.rs
- examples/26_radio_buttons.rs
- examples/21_toasts.rs
- examples/03_theme_switcher.rs
- examples/28_shared_state.rs
- examples/27_keyed_state.rs
- examples/22_forms.rs
- examples/23_modal.rs
Sourcepub fn border(self, border: bool) -> Self
pub fn border(self, border: bool) -> Self
Examples found in repository?
examples/09_syntax_comparison.rs (line 70)
24 fn render(&self, cx: Scope) -> View {
25 let use_jsx = state!(cx, || false);
26 let show_help = state!(cx, || false);
27
28 // F1 toggles help
29 cx.use_command(
30 KeyBinding::key(KeyCode::F(1)),
31 with!(show_help => move || show_help.update(|v| *v = !*v)),
32 );
33
34 let toggle = with!(use_jsx => move || use_jsx.set(!use_jsx.get()));
35
36 // Show which syntax is currently displayed
37 let syntax_name = if use_jsx.get() {
38 "view! macro (JSX-like)"
39 } else {
40 "Builder pattern"
41 };
42
43 View::vstack()
44 .child(
45 View::styled_text("Syntax Comparison")
46 .color(Color::Cyan)
47 .bold()
48 .build(),
49 )
50 .child(
51 View::styled_text("Same UI, two ways to write it")
52 .dim()
53 .build(),
54 )
55 .child(View::gap(1))
56 .child(
57 View::hstack()
58 .child(View::text("Current syntax: "))
59 .child(
60 View::styled_text(syntax_name)
61 .color(Color::Yellow)
62 .bold()
63 .build(),
64 )
65 .build(),
66 )
67 .child(View::gap(1))
68 .child(
69 View::boxed()
70 .border(true)
71 .padding(1)
72 .child(if use_jsx.get() {
73 counter_jsx(cx.clone())
74 } else {
75 counter_builder(cx.clone())
76 })
77 .build(),
78 )
79 .child(View::gap(1))
80 .child(
81 View::button()
82 .label("Toggle Syntax")
83 .on_press(toggle)
84 .build(),
85 )
86 .child(View::gap(1))
87 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
88 .child(
89 View::modal()
90 .visible(show_help.get())
91 .title("Example 09: Syntax Comparison")
92 .on_dismiss(with!(show_help => move || show_help.set(false)))
93 .child(
94 View::vstack()
95 .child(View::styled_text("What you're seeing").bold().build())
96 .child(View::text("• Two syntaxes that produce identical output"))
97 .child(View::text("• Builder: View::vstack().child(...).build()"))
98 .child(View::text("• Macro: view! { <VStack>...</VStack> }"))
99 .child(View::gap(1))
100 .child(View::styled_text("Key concepts").bold().build())
101 .child(View::text("• Builder is Rust-native, IDE-friendly"))
102 .child(View::text("• view! macro is JSX-like, less boilerplate"))
103 .child(View::text("• Choose based on your preference"))
104 .child(View::gap(1))
105 .child(View::styled_text("Try this").bold().build())
106 .child(View::text("• Toggle between syntaxes"))
107 .child(View::text("• Notice the output is identical"))
108 .child(View::text("• Check the source code to see both styles"))
109 .child(View::gap(1))
110 .child(View::styled_text("Next up").bold().build())
111 .child(View::text("→ 10_state_explained: deep dive into state"))
112 .child(View::gap(1))
113 .child(View::styled_text("Press Escape to close").dim().build())
114 .build(),
115 )
116 .build(),
117 )
118 .build()
119 }More examples
examples/15_markdown.rs (line 85)
54 fn render(&self, cx: Scope) -> View {
55 let show_help = state!(cx, || false);
56
57 // F1 toggles help
58 cx.use_command(
59 KeyBinding::key(KeyCode::F(1)),
60 with!(show_help => move || show_help.update(|v| *v = !*v)),
61 );
62
63 let rendered = telex::markdown::render(DEMO_MARKDOWN);
64
65 View::vstack()
66 .child(
67 View::styled_text("Markdown Rendering Demo")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::boxed()
74 .flex(1)
75 .child(
76 View::split()
77 .horizontal()
78 .ratio(0.4)
79 .first(
80 View::vstack()
81 .child(View::styled_text(" Source ").bold().build())
82 .child(
83 View::boxed()
84 .flex(1)
85 .border(true)
86 .scroll(true)
87 .child(View::text(DEMO_MARKDOWN))
88 .build(),
89 )
90 .build(),
91 )
92 .second(
93 View::vstack()
94 .child(View::styled_text(" Rendered ").bold().build())
95 .child(
96 View::boxed()
97 .flex(1)
98 .border(true)
99 .scroll(true)
100 .child(rendered)
101 .build(),
102 )
103 .build(),
104 )
105 .build(),
106 )
107 .build(),
108 )
109 .child(
110 View::styled_text("Tab: switch panes | ↑↓/jk: scroll | F1 help | Ctrl+Q: quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 15: Markdown")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• Side-by-side markdown source and rendered"))
123 .child(View::text("• Full markdown syntax support"))
124 .child(View::text("• Scrollable panes for long content"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• telex::markdown::render() parses markdown"))
128 .child(View::text("• Returns a View tree with styled text"))
129 .child(View::text("• Code blocks, lists, quotes, headers"))
130 .child(View::text("• Split view for comparison"))
131 .child(View::gap(1))
132 .child(View::styled_text("Try this").bold().build())
133 .child(View::text("• Tab between source and rendered panes"))
134 .child(View::text("• Scroll with arrow keys or j/k"))
135 .child(View::text("• Compare source with rendered output"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 16_progress: progress bars"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }examples/16_tree.rs (line 79)
20 fn render(&self, cx: Scope) -> View {
21 let show_help = state!(cx, || false);
22
23 // F1 toggles help
24 cx.use_command(
25 KeyBinding::key(KeyCode::F(1)),
26 with!(show_help => move || show_help.update(|v| *v = !*v)),
27 );
28
29 // Track selected path
30 let selected = state!(cx, || vec![0usize]);
31
32 // Track expanded state for each node (by path prefix)
33 let expanded_paths = state!(cx, || {
34 vec![
35 vec![0], // src/ expanded
36 vec![0, 0], // src/components/ expanded
37 ]
38 });
39
40 // Build tree items with current expanded state
41 let items = build_tree(&expanded_paths.get());
42
43 let on_select = with!(selected => move |path: TreePath| {
44 selected.set(path);
45 });
46
47 let on_activate = with!(expanded_paths => move |path: TreePath| {
48 // Toggle expand/collapse for the activated item
49 let mut paths = expanded_paths.get().clone();
50 if let Some(pos) = paths.iter().position(|p| *p == path) {
51 // Currently expanded, collapse it
52 paths.remove(pos);
53 } else {
54 // Currently collapsed, expand it
55 paths.push(path.clone());
56 }
57 expanded_paths.set(paths);
58 });
59
60 let selected_label = get_item_at_path(&items, &selected.get())
61 .map(|item| item.label.clone())
62 .unwrap_or_else(|| "Nothing".to_string());
63
64 View::vstack()
65 .child(
66 View::styled_text("File Browser")
67 .color(Color::Cyan)
68 .bold()
69 .build(),
70 )
71 .child(
72 View::styled_text(format!("Selected: {}", selected_label))
73 .dim()
74 .build(),
75 )
76 .child(
77 View::boxed()
78 .flex(1)
79 .border(true)
80 .child(
81 View::tree()
82 .items(items)
83 .selected(selected.get().clone())
84 .on_select(on_select)
85 .on_activate(on_activate)
86 .build(),
87 )
88 .build(),
89 )
90 .child(
91 View::styled_text(
92 "↑↓/jk: navigate | Enter: expand/collapse | F1 help | Ctrl+Q: quit",
93 )
94 .dim()
95 .build(),
96 )
97 .child(
98 View::modal()
99 .visible(show_help.get())
100 .title("Example 16: Tree View")
101 .on_dismiss(with!(show_help => move || show_help.set(false)))
102 .child(
103 View::vstack()
104 .child(View::styled_text("What you're seeing").bold().build())
105 .child(View::text("• Hierarchical tree widget"))
106 .child(View::text("• Expand/collapse folders"))
107 .child(View::text("• Path-based selection tracking"))
108 .child(View::gap(1))
109 .child(View::styled_text("Key concepts").bold().build())
110 .child(View::text("• View::tree() for hierarchical data"))
111 .child(View::text("• TreeItem::new().child() builds hierarchy"))
112 .child(View::text("• on_select returns TreePath (Vec<usize>)"))
113 .child(View::text("• on_activate for expand/collapse"))
114 .child(View::gap(1))
115 .child(View::styled_text("Try this").bold().build())
116 .child(View::text("• Navigate with arrow keys"))
117 .child(View::text("• Press Enter to expand/collapse folders"))
118 .child(View::text("• Watch the 'Selected:' text update"))
119 .child(View::gap(1))
120 .child(View::styled_text("Next up").bold().build())
121 .child(View::text("→ 17_table: data tables with sorting"))
122 .child(View::gap(1))
123 .child(View::styled_text("Press Escape to close").dim().build())
124 .build(),
125 )
126 .build(),
127 )
128 .build()
129 }examples/10_state_explained.rs (line 66)
21 fn render(&self, cx: Scope) -> View {
22 let count = state!(cx, || 0i32);
23 let show_help = state!(cx, || false);
24
25 // F1 toggles help
26 cx.use_command(
27 KeyBinding::key(KeyCode::F(1)),
28 with!(show_help => move || show_help.update(|v| *v = !*v)),
29 );
30
31 // Clone handles for closures (this is the pattern being explained)
32 let count_for_increment = count.clone();
33 let count_for_decrement = count.clone();
34 let count_for_reset = count.clone();
35
36 let increment = move || {
37 let current = count_for_increment.get();
38 count_for_increment.set(current + 1);
39 };
40
41 let decrement = move || {
42 let current = count_for_decrement.get();
43 count_for_decrement.set(current - 1);
44 };
45
46 let reset = move || {
47 count_for_reset.set(0);
48 };
49
50 let current_value = count.get();
51
52 // Hook ordering demo
53 let _always_called_1 = state!(cx, || "hook 1");
54 let _always_called_2 = state!(cx, || "hook 2");
55
56 View::vstack()
57 .spacing(1)
58 .child(
59 View::styled_text("State Explained")
60 .color(Color::Cyan)
61 .bold()
62 .build(),
63 )
64 .child(
65 View::boxed()
66 .border(true)
67 .padding(1)
68 .child(
69 View::vstack()
70 .child(View::styled_text("The Mental Model:").bold().build())
71 .child(View::gap(1))
72 .child(View::text(" State<T> is a HANDLE, not data"))
73 .child(View::text(" clone() copies the handle, not the data"))
74 .child(View::text(" All handles point to ONE value"))
75 .child(View::gap(1))
76 .child(View::text(" count ──────┐"))
77 .child(View::text(" ├──► i32: 0 (shared!)"))
78 .child(View::text(" count2 ─────┘"))
79 .build(),
80 )
81 .build(),
82 )
83 .child(
84 View::hstack()
85 .spacing(1)
86 .child(View::text("Current value:"))
87 .child(
88 View::styled_text(format!("{}", current_value))
89 .color(Color::Yellow)
90 .bold()
91 .build(),
92 )
93 .build(),
94 )
95 .child(
96 View::hstack()
97 .spacing(1)
98 .child(View::button().label(" - ").on_press(decrement).build())
99 .child(View::button().label(" + ").on_press(increment).build())
100 .child(View::button().label("Reset").on_press(reset).build())
101 .build(),
102 )
103 .child(
104 View::styled_text("All three buttons modify the SAME underlying i32")
105 .dim()
106 .build(),
107 )
108 .child(View::gap(1))
109 .child(
110 View::styled_text("Tab navigate • F1 help • Ctrl+Q quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 10: State Explained")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• State<T> as a handle/pointer concept"))
123 .child(View::text("• Multiple closures sharing one value"))
124 .child(View::text("• Visual diagram of the mental model"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• clone() copies handle, not data"))
128 .child(View::text("• All handles point to same underlying value"))
129 .child(View::text(
130 "• Hooks must be called in same order every render",
131 ))
132 .child(View::gap(1))
133 .child(View::styled_text("Important rule").bold().build())
134 .child(View::text("• NEVER put use_state inside an if block"))
135 .child(View::text("• Use with!() macro to simplify cloning"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 11_checkbox: toggle controls"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }examples/13_split_panes.rs (line 92)
20 fn render(&self, cx: Scope) -> View {
21 let show_help = state!(cx, || false);
22
23 // F1 toggles help
24 cx.use_command(
25 KeyBinding::key(KeyCode::F(1)),
26 with!(show_help => move || show_help.update(|v| *v = !*v)),
27 );
28
29 let selected_item = state!(cx, || 0usize);
30
31 let items = vec![
32 "README.md".to_string(),
33 "Cargo.toml".to_string(),
34 "src/".to_string(),
35 "src/main.rs".to_string(),
36 "src/lib.rs".to_string(),
37 "tests/".to_string(),
38 ];
39
40 let on_select = with!(selected_item => move |idx: usize| {
41 selected_item.set(idx);
42 });
43
44 let detail_text = match selected_item.get() {
45 0 => "# README\n\nThis is a demo of split panes.\n\nThe left panel shows a file list,\nthe right panel shows details.",
46 1 => "[package]\nname = \"demo\"\nversion = \"0.1.0\"\nedition = \"2021\"",
47 2 => "Directory: src/\n\nContains the main source files.",
48 3 => "fn main() {\n println!(\"Hello, world!\");\n}",
49 4 => "pub mod utils;\npub mod widgets;",
50 5 => "Directory: tests/\n\nContains integration tests.",
51 _ => "Select an item to see details.",
52 };
53
54 // Horizontal split: file list on left, details on right
55 View::vstack()
56 .child(
57 View::boxed()
58 .flex(1)
59 .child(
60 View::split()
61 .horizontal()
62 .ratio(0.3)
63 .min_first(15)
64 .first(
65 View::vstack()
66 .child(
67 View::styled_text("Files")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::list()
74 .items(items)
75 .selected(selected_item.get())
76 .on_select(on_select)
77 .build(),
78 )
79 .child(View::gap(1))
80 .build(),
81 )
82 .second(
83 View::vstack()
84 .child(
85 View::styled_text("Details")
86 .color(Color::Green)
87 .bold()
88 .build(),
89 )
90 .child(
91 View::boxed()
92 .border(true)
93 .flex(1)
94 .child(View::text(detail_text))
95 .build(),
96 )
97 .build(),
98 )
99 .build(),
100 )
101 .build(),
102 )
103 .child(
104 View::styled_text("↑↓: select file | F1 help | Ctrl+Q: quit")
105 .dim()
106 .build(),
107 )
108 .child(
109 View::modal()
110 .visible(show_help.get())
111 .title("Example 13: Split Panes")
112 .on_dismiss(with!(show_help => move || show_help.set(false)))
113 .child(
114 View::vstack()
115 .child(View::styled_text("What you're seeing").bold().build())
116 .child(View::text("• Horizontal split: file list / details"))
117 .child(View::text("• ratio(0.3) = 30% left, 70% right"))
118 .child(View::text("• min_first(15) sets minimum pane width"))
119 .child(View::gap(1))
120 .child(View::styled_text("Key concepts").bold().build())
121 .child(View::text("• View::split() creates resizable panes"))
122 .child(View::text("• .horizontal() or .vertical() orientation"))
123 .child(View::text("• .first() and .second() set pane content"))
124 .child(View::text("• Splits can be nested"))
125 .child(View::gap(1))
126 .child(View::styled_text("Try this").bold().build())
127 .child(View::text("• Select different files to see details"))
128 .child(View::text("• Resize terminal to see layout adapt"))
129 .child(View::gap(1))
130 .child(View::styled_text("Next up").bold().build())
131 .child(View::text("→ 14_tabs: tabbed interfaces"))
132 .child(View::gap(1))
133 .child(View::styled_text("Press Escape to close").dim().build())
134 .build(),
135 )
136 .build(),
137 )
138 .build()
139 }examples/24_async_data.rs (line 78)
38 fn render(&self, cx: Scope) -> View {
39 let show_help = state!(cx, || false);
40
41 // F1 toggles help
42 cx.use_command(
43 KeyBinding::key(KeyCode::F(1)),
44 with!(show_help => move || show_help.update(|v| *v = !*v)),
45 );
46
47 // Simulate fetching user profile (slow - 2 seconds)
48 let profile = async_data!(cx, || {
49 thread::sleep(Duration::from_secs(2));
50 Ok(UserProfile {
51 name: "Alice Johnson".to_string(),
52 email: "alice@example.com".to_string(),
53 member_since: "January 2024".to_string(),
54 })
55 });
56
57 // Simulate fetching user stats (medium - 1 second)
58 let stats = async_data!(cx, || {
59 thread::sleep(Duration::from_secs(1));
60 Ok(Stats {
61 posts: 142,
62 followers: 1234,
63 following: 567,
64 })
65 });
66
67 // Simulate a failing request
68 let failing_data = async_data!(cx, || {
69 thread::sleep(Duration::from_millis(500));
70 Err::<String, _>("Network error: Connection refused".to_string())
71 });
72
73 View::vstack()
74 .spacing(1)
75 .child(
76 // Header
77 View::boxed()
78 .border(true)
79 .padding(1)
80 .child(
81 View::vstack()
82 .child(View::styled_text("Async Data Loading Demo").bold().build())
83 .child(
84 View::styled_text(
85 "Demonstrates use_async for loading data with loading/error states",
86 )
87 .dim()
88 .build(),
89 )
90 .build(),
91 )
92 .build(),
93 )
94 .child(
95 // Main content - three columns
96 View::hstack()
97 .spacing(1)
98 // Profile section
99 .child(
100 View::boxed()
101 .flex(1)
102 .border(true)
103 .padding(1)
104 .child(render_profile_section(&profile))
105 .build(),
106 )
107 // Stats section
108 .child(
109 View::boxed()
110 .flex(1)
111 .border(true)
112 .padding(1)
113 .child(render_stats_section(&stats))
114 .build(),
115 )
116 // Error section
117 .child(
118 View::boxed()
119 .flex(1)
120 .border(true)
121 .padding(1)
122 .child(render_error_section(&failing_data))
123 .build(),
124 )
125 .build(),
126 )
127 .child(
128 // Status footer
129 View::boxed()
130 .border(true)
131 .padding(1)
132 .child(
133 View::vstack()
134 .child(View::text(format!(
135 "Overall status: {}",
136 if profile.is_loading() || stats.is_loading() {
137 "Loading..."
138 } else if profile.is_error() || stats.is_error() || failing_data.is_error() {
139 "Some requests failed"
140 } else {
141 "All data loaded"
142 }
143 )))
144 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
145 .build(),
146 )
147 .build(),
148 )
149 .child(
150 View::modal()
151 .visible(show_help.get())
152 .title("Example 24: Async Data")
153 .on_dismiss(with!(show_help => move || show_help.set(false)))
154 .child(
155 View::vstack()
156 .child(View::styled_text("What you're seeing").bold().build())
157 .child(View::text("• Three async data loads in parallel"))
158 .child(View::text("• Loading, success, and error states"))
159 .child(View::text("• Different load times for each"))
160 .child(View::gap(1))
161 .child(View::styled_text("Key concepts").bold().build())
162 .child(View::text("• async_data!() macro runs in background"))
163 .child(View::text("• Returns Async<T> enum"))
164 .child(View::text("• .is_loading() / .is_error() helpers"))
165 .child(View::text("• Pattern match for state handling"))
166 .child(View::gap(1))
167 .child(View::styled_text("Try this").bold().build())
168 .child(View::text("• Watch data load progressively"))
169 .child(View::text("• Notice the failing request"))
170 .child(View::gap(1))
171 .child(View::styled_text("Next up").bold().build())
172 .child(View::text("→ 25_context: context API"))
173 .child(View::gap(1))
174 .child(View::styled_text("Press Escape to close").dim().build())
175 .build()
176 )
177 .build()
178 )
179 .build()
180 }Sourcepub fn padding(self, padding: u16) -> Self
pub fn padding(self, padding: u16) -> Self
Examples found in repository?
examples/09_syntax_comparison.rs (line 71)
24 fn render(&self, cx: Scope) -> View {
25 let use_jsx = state!(cx, || false);
26 let show_help = state!(cx, || false);
27
28 // F1 toggles help
29 cx.use_command(
30 KeyBinding::key(KeyCode::F(1)),
31 with!(show_help => move || show_help.update(|v| *v = !*v)),
32 );
33
34 let toggle = with!(use_jsx => move || use_jsx.set(!use_jsx.get()));
35
36 // Show which syntax is currently displayed
37 let syntax_name = if use_jsx.get() {
38 "view! macro (JSX-like)"
39 } else {
40 "Builder pattern"
41 };
42
43 View::vstack()
44 .child(
45 View::styled_text("Syntax Comparison")
46 .color(Color::Cyan)
47 .bold()
48 .build(),
49 )
50 .child(
51 View::styled_text("Same UI, two ways to write it")
52 .dim()
53 .build(),
54 )
55 .child(View::gap(1))
56 .child(
57 View::hstack()
58 .child(View::text("Current syntax: "))
59 .child(
60 View::styled_text(syntax_name)
61 .color(Color::Yellow)
62 .bold()
63 .build(),
64 )
65 .build(),
66 )
67 .child(View::gap(1))
68 .child(
69 View::boxed()
70 .border(true)
71 .padding(1)
72 .child(if use_jsx.get() {
73 counter_jsx(cx.clone())
74 } else {
75 counter_builder(cx.clone())
76 })
77 .build(),
78 )
79 .child(View::gap(1))
80 .child(
81 View::button()
82 .label("Toggle Syntax")
83 .on_press(toggle)
84 .build(),
85 )
86 .child(View::gap(1))
87 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
88 .child(
89 View::modal()
90 .visible(show_help.get())
91 .title("Example 09: Syntax Comparison")
92 .on_dismiss(with!(show_help => move || show_help.set(false)))
93 .child(
94 View::vstack()
95 .child(View::styled_text("What you're seeing").bold().build())
96 .child(View::text("• Two syntaxes that produce identical output"))
97 .child(View::text("• Builder: View::vstack().child(...).build()"))
98 .child(View::text("• Macro: view! { <VStack>...</VStack> }"))
99 .child(View::gap(1))
100 .child(View::styled_text("Key concepts").bold().build())
101 .child(View::text("• Builder is Rust-native, IDE-friendly"))
102 .child(View::text("• view! macro is JSX-like, less boilerplate"))
103 .child(View::text("• Choose based on your preference"))
104 .child(View::gap(1))
105 .child(View::styled_text("Try this").bold().build())
106 .child(View::text("• Toggle between syntaxes"))
107 .child(View::text("• Notice the output is identical"))
108 .child(View::text("• Check the source code to see both styles"))
109 .child(View::gap(1))
110 .child(View::styled_text("Next up").bold().build())
111 .child(View::text("→ 10_state_explained: deep dive into state"))
112 .child(View::gap(1))
113 .child(View::styled_text("Press Escape to close").dim().build())
114 .build(),
115 )
116 .build(),
117 )
118 .build()
119 }More examples
examples/10_state_explained.rs (line 67)
21 fn render(&self, cx: Scope) -> View {
22 let count = state!(cx, || 0i32);
23 let show_help = state!(cx, || false);
24
25 // F1 toggles help
26 cx.use_command(
27 KeyBinding::key(KeyCode::F(1)),
28 with!(show_help => move || show_help.update(|v| *v = !*v)),
29 );
30
31 // Clone handles for closures (this is the pattern being explained)
32 let count_for_increment = count.clone();
33 let count_for_decrement = count.clone();
34 let count_for_reset = count.clone();
35
36 let increment = move || {
37 let current = count_for_increment.get();
38 count_for_increment.set(current + 1);
39 };
40
41 let decrement = move || {
42 let current = count_for_decrement.get();
43 count_for_decrement.set(current - 1);
44 };
45
46 let reset = move || {
47 count_for_reset.set(0);
48 };
49
50 let current_value = count.get();
51
52 // Hook ordering demo
53 let _always_called_1 = state!(cx, || "hook 1");
54 let _always_called_2 = state!(cx, || "hook 2");
55
56 View::vstack()
57 .spacing(1)
58 .child(
59 View::styled_text("State Explained")
60 .color(Color::Cyan)
61 .bold()
62 .build(),
63 )
64 .child(
65 View::boxed()
66 .border(true)
67 .padding(1)
68 .child(
69 View::vstack()
70 .child(View::styled_text("The Mental Model:").bold().build())
71 .child(View::gap(1))
72 .child(View::text(" State<T> is a HANDLE, not data"))
73 .child(View::text(" clone() copies the handle, not the data"))
74 .child(View::text(" All handles point to ONE value"))
75 .child(View::gap(1))
76 .child(View::text(" count ──────┐"))
77 .child(View::text(" ├──► i32: 0 (shared!)"))
78 .child(View::text(" count2 ─────┘"))
79 .build(),
80 )
81 .build(),
82 )
83 .child(
84 View::hstack()
85 .spacing(1)
86 .child(View::text("Current value:"))
87 .child(
88 View::styled_text(format!("{}", current_value))
89 .color(Color::Yellow)
90 .bold()
91 .build(),
92 )
93 .build(),
94 )
95 .child(
96 View::hstack()
97 .spacing(1)
98 .child(View::button().label(" - ").on_press(decrement).build())
99 .child(View::button().label(" + ").on_press(increment).build())
100 .child(View::button().label("Reset").on_press(reset).build())
101 .build(),
102 )
103 .child(
104 View::styled_text("All three buttons modify the SAME underlying i32")
105 .dim()
106 .build(),
107 )
108 .child(View::gap(1))
109 .child(
110 View::styled_text("Tab navigate • F1 help • Ctrl+Q quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 10: State Explained")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• State<T> as a handle/pointer concept"))
123 .child(View::text("• Multiple closures sharing one value"))
124 .child(View::text("• Visual diagram of the mental model"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• clone() copies handle, not data"))
128 .child(View::text("• All handles point to same underlying value"))
129 .child(View::text(
130 "• Hooks must be called in same order every render",
131 ))
132 .child(View::gap(1))
133 .child(View::styled_text("Important rule").bold().build())
134 .child(View::text("• NEVER put use_state inside an if block"))
135 .child(View::text("• Use with!() macro to simplify cloning"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 11_checkbox: toggle controls"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }examples/24_async_data.rs (line 79)
38 fn render(&self, cx: Scope) -> View {
39 let show_help = state!(cx, || false);
40
41 // F1 toggles help
42 cx.use_command(
43 KeyBinding::key(KeyCode::F(1)),
44 with!(show_help => move || show_help.update(|v| *v = !*v)),
45 );
46
47 // Simulate fetching user profile (slow - 2 seconds)
48 let profile = async_data!(cx, || {
49 thread::sleep(Duration::from_secs(2));
50 Ok(UserProfile {
51 name: "Alice Johnson".to_string(),
52 email: "alice@example.com".to_string(),
53 member_since: "January 2024".to_string(),
54 })
55 });
56
57 // Simulate fetching user stats (medium - 1 second)
58 let stats = async_data!(cx, || {
59 thread::sleep(Duration::from_secs(1));
60 Ok(Stats {
61 posts: 142,
62 followers: 1234,
63 following: 567,
64 })
65 });
66
67 // Simulate a failing request
68 let failing_data = async_data!(cx, || {
69 thread::sleep(Duration::from_millis(500));
70 Err::<String, _>("Network error: Connection refused".to_string())
71 });
72
73 View::vstack()
74 .spacing(1)
75 .child(
76 // Header
77 View::boxed()
78 .border(true)
79 .padding(1)
80 .child(
81 View::vstack()
82 .child(View::styled_text("Async Data Loading Demo").bold().build())
83 .child(
84 View::styled_text(
85 "Demonstrates use_async for loading data with loading/error states",
86 )
87 .dim()
88 .build(),
89 )
90 .build(),
91 )
92 .build(),
93 )
94 .child(
95 // Main content - three columns
96 View::hstack()
97 .spacing(1)
98 // Profile section
99 .child(
100 View::boxed()
101 .flex(1)
102 .border(true)
103 .padding(1)
104 .child(render_profile_section(&profile))
105 .build(),
106 )
107 // Stats section
108 .child(
109 View::boxed()
110 .flex(1)
111 .border(true)
112 .padding(1)
113 .child(render_stats_section(&stats))
114 .build(),
115 )
116 // Error section
117 .child(
118 View::boxed()
119 .flex(1)
120 .border(true)
121 .padding(1)
122 .child(render_error_section(&failing_data))
123 .build(),
124 )
125 .build(),
126 )
127 .child(
128 // Status footer
129 View::boxed()
130 .border(true)
131 .padding(1)
132 .child(
133 View::vstack()
134 .child(View::text(format!(
135 "Overall status: {}",
136 if profile.is_loading() || stats.is_loading() {
137 "Loading..."
138 } else if profile.is_error() || stats.is_error() || failing_data.is_error() {
139 "Some requests failed"
140 } else {
141 "All data loaded"
142 }
143 )))
144 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
145 .build(),
146 )
147 .build(),
148 )
149 .child(
150 View::modal()
151 .visible(show_help.get())
152 .title("Example 24: Async Data")
153 .on_dismiss(with!(show_help => move || show_help.set(false)))
154 .child(
155 View::vstack()
156 .child(View::styled_text("What you're seeing").bold().build())
157 .child(View::text("• Three async data loads in parallel"))
158 .child(View::text("• Loading, success, and error states"))
159 .child(View::text("• Different load times for each"))
160 .child(View::gap(1))
161 .child(View::styled_text("Key concepts").bold().build())
162 .child(View::text("• async_data!() macro runs in background"))
163 .child(View::text("• Returns Async<T> enum"))
164 .child(View::text("• .is_loading() / .is_error() helpers"))
165 .child(View::text("• Pattern match for state handling"))
166 .child(View::gap(1))
167 .child(View::styled_text("Try this").bold().build())
168 .child(View::text("• Watch data load progressively"))
169 .child(View::text("• Notice the failing request"))
170 .child(View::gap(1))
171 .child(View::styled_text("Next up").bold().build())
172 .child(View::text("→ 25_context: context API"))
173 .child(View::gap(1))
174 .child(View::styled_text("Press Escape to close").dim().build())
175 .build()
176 )
177 .build()
178 )
179 .build()
180 }examples/25_context.rs (line 131)
71 fn render(&self, cx: Scope) -> View {
72 let show_help = state!(cx, || false);
73
74 // F1 toggles help
75 cx.use_command(
76 KeyBinding::key(KeyCode::F(1)),
77 with!(show_help => move || show_help.update(|v| *v = !*v)),
78 );
79
80 // State that we'll provide via context
81 let theme = state!(cx, || ColorTheme::Default);
82 let user = state!(cx, || User {
83 name: "Guest".to_string(),
84 logged_in: false,
85 });
86
87 // Provide static config via context
88 cx.provide_context(AppConfig {
89 app_name: "Context Demo".to_string(),
90 version: "1.0.0".to_string(),
91 });
92
93 // Provide dynamic state via context (current values)
94 cx.provide_context(theme.get());
95 cx.provide_context(user.get());
96
97 // Theme switching handlers
98 let set_default = with!(theme => move || theme.set(ColorTheme::Default));
99 let set_ocean = with!(theme => move || theme.set(ColorTheme::Ocean));
100 let set_forest = with!(theme => move || theme.set(ColorTheme::Forest));
101 let set_sunset = with!(theme => move || theme.set(ColorTheme::Sunset));
102
103 // Login/logout handlers
104 let toggle_login = with!(user => move || {
105 let current = user.get();
106 if current.logged_in {
107 user.set(User {
108 name: "Guest".to_string(),
109 logged_in: false,
110 });
111 } else {
112 user.set(User {
113 name: "Alice".to_string(),
114 logged_in: true,
115 });
116 }
117 });
118
119 // Get current theme for styling
120 let current_theme = theme.get();
121
122 View::vstack()
123 .spacing(1)
124 // Header - uses context
125 .child(render_header(&cx))
126 .child(
127 // Main content
128 View::boxed()
129 .flex(1)
130 .border(true)
131 .padding(1)
132 .child(
133 View::vstack()
134 .spacing(1)
135 .child(
136 View::styled_text("Theme Selection:")
137 .color(current_theme.primary())
138 .bold()
139 .build(),
140 )
141 .child(
142 View::hstack()
143 .spacing(2)
144 .child(
145 View::button()
146 .label("Default")
147 .on_press(set_default)
148 .build(),
149 )
150 .child(
151 View::button().label("Ocean").on_press(set_ocean).build(),
152 )
153 .child(
154 View::button().label("Forest").on_press(set_forest).build(),
155 )
156 .child(
157 View::button().label("Sunset").on_press(set_sunset).build(),
158 )
159 .build(),
160 )
161 .child(View::gap(1))
162 .child(
163 View::styled_text("User Actions:")
164 .color(current_theme.primary())
165 .bold()
166 .build(),
167 )
168 .child(
169 View::button()
170 .label(if user.get().logged_in {
171 "Logout"
172 } else {
173 "Login as Alice"
174 })
175 .on_press(toggle_login)
176 .build(),
177 )
178 .child(View::spacer())
179 // User info panel - uses context
180 .child(render_user_panel(&cx))
181 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
182 .build(),
183 )
184 .build(),
185 )
186 // Status bar - uses context
187 .child(render_status_bar(&cx))
188 .child(
189 View::modal()
190 .visible(show_help.get())
191 .title("Example 25: Context")
192 .on_dismiss(with!(show_help => move || show_help.set(false)))
193 .child(
194 View::vstack()
195 .child(View::styled_text("What you're seeing").bold().build())
196 .child(View::text("• Context API for global state"))
197 .child(View::text("• Theme colors propagate everywhere"))
198 .child(View::text("• User state shared across components"))
199 .child(View::gap(1))
200 .child(View::styled_text("Key concepts").bold().build())
201 .child(View::text("• cx.provide_context() adds to context"))
202 .child(View::text("• cx.use_context::<T>() reads context"))
203 .child(View::text("• Avoids prop drilling"))
204 .child(View::text("• Great for themes, user, config"))
205 .child(View::gap(1))
206 .child(View::styled_text("Try this").bold().build())
207 .child(View::text("• Switch themes - colors update"))
208 .child(View::text("• Login/logout - panel updates"))
209 .child(View::text("• Header and status bar read context"))
210 .child(View::gap(1))
211 .child(View::styled_text("Next up").bold().build())
212 .child(View::text("→ 26_radio_buttons: radio selections"))
213 .child(View::gap(1))
214 .child(View::styled_text("Press Escape to close").dim().build())
215 .build(),
216 )
217 .build(),
218 )
219 .build()
220 }
221}
222
223// Helper function that reads from context
224fn render_header(cx: &Scope) -> View {
225 // Read config and theme from context
226 let config = cx.use_context::<AppConfig>();
227 let theme = cx
228 .use_context::<ColorTheme>()
229 .unwrap_or(ColorTheme::Default);
230
231 let title = config
232 .map(|c| format!("{} v{}", c.app_name, c.version))
233 .unwrap_or_else(|| "No config".to_string());
234
235 View::boxed()
236 .border(true)
237 .padding(1)
238 .child(
239 View::vstack()
240 .child(
241 View::styled_text(title)
242 .bold()
243 .color(theme.primary())
244 .build(),
245 )
246 .child(
247 View::styled_text("Demonstrates provide_context and use_context")
248 .dim()
249 .build(),
250 )
251 .build(),
252 )
253 .build()
254}
255
256// Helper function that reads user from context
257fn render_user_panel(cx: &Scope) -> View {
258 let user = cx.use_context::<User>();
259 let theme = cx
260 .use_context::<ColorTheme>()
261 .unwrap_or(ColorTheme::Default);
262
263 let (status_text, status_color) = match &user {
264 Some(u) if u.logged_in => (format!("Logged in as: {}", u.name), theme.accent()),
265 Some(_) => ("Not logged in".to_string(), Color::DarkGrey),
266 None => ("User context not available".to_string(), Color::Red),
267 };
268
269 View::boxed()
270 .border(true)
271 .padding(1)
272 .child(
273 View::vstack()
274 .child(
275 View::styled_text("User Panel (reads from context)")
276 .bold()
277 .color(theme.primary())
278 .build(),
279 )
280 .child(View::styled_text(status_text).color(status_color).build())
281 .build(),
282 )
283 .build()
284}examples/11_checkbox.rs (line 53)
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 // Settings state
31 let dark_mode = state!(cx, || true);
32 let notifications = state!(cx, || true);
33 let auto_save = state!(cx, || false);
34 let telemetry = state!(cx, || false);
35
36 View::vstack()
37 .spacing(1)
38 .child(
39 View::styled_text("Settings")
40 .color(Color::Cyan)
41 .bold()
42 .build(),
43 )
44 .child(
45 View::styled_text("Use Tab to navigate, Enter/Space to toggle")
46 .dim()
47 .build(),
48 )
49 .child(View::gap(1))
50 .child(
51 View::boxed()
52 .border(true)
53 .padding(1)
54 .child(
55 View::vstack()
56 .spacing(1)
57 .child(View::styled_text("Appearance").bold().build())
58 .child(
59 View::checkbox()
60 .checked(dark_mode.get())
61 .label("Dark mode")
62 .on_toggle(with!(dark_mode => move |checked| {
63 dark_mode.set(checked);
64 if checked {
65 set_theme(Theme::dark());
66 } else {
67 set_theme(Theme::light());
68 }
69 }))
70 .build(),
71 )
72 .build(),
73 )
74 .build(),
75 )
76 .child(
77 View::boxed()
78 .border(true)
79 .padding(1)
80 .child(
81 View::vstack()
82 .spacing(1)
83 .child(View::styled_text("Behavior").bold().build())
84 .child(
85 View::checkbox()
86 .checked(notifications.get())
87 .label("Enable notifications")
88 .on_toggle(with!(notifications => move |checked| {
89 notifications.set(checked);
90 }))
91 .build(),
92 )
93 .child(
94 View::checkbox()
95 .checked(auto_save.get())
96 .label("Auto-save documents")
97 .on_toggle(with!(auto_save => move |checked| {
98 auto_save.set(checked);
99 }))
100 .build(),
101 )
102 .build(),
103 )
104 .build(),
105 )
106 .child(
107 View::boxed()
108 .border(true)
109 .padding(1)
110 .child(
111 View::vstack()
112 .spacing(1)
113 .child(View::styled_text("Privacy").bold().build())
114 .child(
115 View::checkbox()
116 .checked(telemetry.get())
117 .label("Send anonymous usage data")
118 .on_toggle(with!(telemetry => move |checked| {
119 telemetry.set(checked);
120 }))
121 .build(),
122 )
123 .build(),
124 )
125 .build(),
126 )
127 .child(View::gap(1))
128 .child(
129 View::hstack()
130 .spacing(2)
131 .child(View::text("Current settings:"))
132 .child(
133 View::styled_text(format!(
134 "dark={} notify={} autosave={} telemetry={}",
135 dark_mode.get(),
136 notifications.get(),
137 auto_save.get(),
138 telemetry.get()
139 ))
140 .color(Color::Yellow)
141 .build(),
142 )
143 .build(),
144 )
145 .child(View::gap(1))
146 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
147 .child(
148 View::modal()
149 .visible(show_help.get())
150 .title("Example 11: Checkbox")
151 .on_dismiss(with!(show_help => move || show_help.set(false)))
152 .child(
153 View::vstack()
154 .child(View::styled_text("What you're seeing").bold().build())
155 .child(View::text("• Checkbox widget for boolean toggles"))
156 .child(View::text("• Grouped settings in boxed sections"))
157 .child(View::text("• Dark mode toggle that changes theme live"))
158 .child(View::gap(1))
159 .child(View::styled_text("Key concepts").bold().build())
160 .child(View::text("• View::checkbox() with checked state"))
161 .child(View::text("• on_toggle callback receives new value"))
162 .child(View::text("• set_theme() for live theme switching"))
163 .child(View::gap(1))
164 .child(View::styled_text("Try this").bold().build())
165 .child(View::text("• Toggle Dark mode to see theme change"))
166 .child(View::text("• Watch the status line update"))
167 .child(View::text("• Tab between checkboxes"))
168 .child(View::gap(1))
169 .child(View::styled_text("Next up").bold().build())
170 .child(View::text("→ 12_text_area: multi-line text editing"))
171 .child(View::gap(1))
172 .child(View::styled_text("Press Escape to close").dim().build())
173 .build(),
174 )
175 .build(),
176 )
177 .build()
178 }examples/20_menu_bar.rs (line 139)
26 fn render(&self, cx: Scope) -> View {
27 let show_help = state!(cx, || false);
28
29 // F1 toggles help
30 cx.use_command(
31 KeyBinding::key(KeyCode::F(1)),
32 with!(show_help => move || show_help.update(|v| *v = !*v)),
33 );
34
35 // Menu state
36 let active_menu = state!(cx, || Option::<usize>::None);
37 let highlighted_menu = state!(cx, || 0usize);
38 let selected_item = state!(cx, || 0usize);
39
40 // App state
41 let message = state!(cx, || "Use Tab to focus menu bar, then Enter to open".to_string());
42 let counter = state!(cx, || 0i32);
43
44 // Command handler - executes menu commands
45 let handle_command = with!(message, counter, active_menu, selected_item => move |cmd_id: &'static str| {
46 let msg = match cmd_id {
47 "file.new" => "Created new file".to_string(),
48 "file.open" => "Opening file...".to_string(),
49 "file.save" => "File saved".to_string(),
50 "file.quit" => "Use Ctrl+Q to quit".to_string(),
51 "edit.undo" => "Undone".to_string(),
52 "edit.redo" => "Redone".to_string(),
53 "edit.cut" => "Cut to clipboard".to_string(),
54 "edit.copy" => "Copied to clipboard".to_string(),
55 "edit.paste" => "Pasted from clipboard".to_string(),
56 "counter.increment" => {
57 counter.update(|n| *n += 1);
58 format!("Counter: {}", counter.get())
59 }
60 "counter.decrement" => {
61 counter.update(|n| *n -= 1);
62 format!("Counter: {}", counter.get())
63 }
64 "counter.reset" => {
65 counter.set(0);
66 "Counter reset".to_string()
67 }
68 _ => format!("Unknown: {}", cmd_id),
69 };
70 message.set(msg);
71 // Close menu after executing command
72 active_menu.set(None);
73 selected_item.set(0);
74 });
75
76 // Menu change handler - opens/closes menus
77 let on_menu_change = with!(active_menu, highlighted_menu, selected_item => move |idx: usize| {
78 if active_menu.get() == Some(idx) {
79 // Clicking same menu toggles it closed
80 active_menu.set(None);
81 } else {
82 active_menu.set(Some(idx));
83 highlighted_menu.set(idx); // Keep highlight in sync
84 selected_item.set(0);
85 }
86 });
87
88 // Highlight change handler - arrow key navigation when no menu is open
89 let on_highlight_change = with!(highlighted_menu => move |idx: usize| {
90 highlighted_menu.set(idx);
91 });
92
93 // Item change handler - navigates within menu
94 let on_item_change = with!(selected_item => move |idx: usize| {
95 selected_item.set(idx);
96 });
97
98 // Build menus
99 let file_menu = Menu::new("File")
100 .command_with_shortcut("file.new", "New", "Ctrl+N")
101 .command_with_shortcut("file.open", "Open", "Ctrl+O")
102 .command_with_shortcut("file.save", "Save", "Ctrl+S")
103 .separator()
104 .command_with_shortcut("file.quit", "Quit", "Ctrl+Q");
105
106 let edit_menu = Menu::new("Edit")
107 .command_with_shortcut("edit.undo", "Undo", "Ctrl+Z")
108 .command_with_shortcut("edit.redo", "Redo", "Ctrl+Y")
109 .separator()
110 .command_with_shortcut("edit.cut", "Cut", "Ctrl+X")
111 .command_with_shortcut("edit.copy", "Copy", "Ctrl+C")
112 .command_with_shortcut("edit.paste", "Paste", "Ctrl+V");
113
114 let counter_menu = Menu::new("Counter")
115 .command("counter.increment", "Increment")
116 .command("counter.decrement", "Decrement")
117 .separator()
118 .command("counter.reset", "Reset to Zero");
119
120 View::vstack()
121 .child(
122 View::menu_bar()
123 .menu(file_menu)
124 .menu(edit_menu)
125 .menu(counter_menu)
126 .active_menu(active_menu.get())
127 .highlighted_menu(highlighted_menu.get())
128 .selected_item(selected_item.get())
129 .on_select(handle_command)
130 .on_menu_change(on_menu_change)
131 .on_highlight_change(on_highlight_change)
132 .on_item_change(on_item_change)
133 .build(),
134 )
135 .child(
136 View::boxed()
137 .flex(1)
138 .border(true)
139 .padding(2)
140 .child(
141 View::vstack()
142 .spacing(1)
143 .child(View::styled_text("Menu Bar Demo").bold().build())
144 .child(
145 View::styled_text(format!("Counter: {}", counter.get()))
146 .color(Color::Cyan)
147 .bold()
148 .build(),
149 )
150 .child(
151 View::styled_text(format!("Status: {}", message.get()))
152 .dim()
153 .build(),
154 )
155 .child(View::spacer())
156 .child(View::styled_text("Keyboard Navigation:").bold().build())
157 .child(View::text(" Tab Focus menu bar"))
158 .child(View::text(" Enter Open menu / Execute item"))
159 .child(View::text(" Up/Down Navigate menu items"))
160 .child(View::text(" Left/Right Switch between menus"))
161 .child(View::text(" Escape Close menu"))
162 .child(View::text(" Ctrl+Q Quit"))
163 .child(View::text(" F1 Help"))
164 .build(),
165 )
166 .build(),
167 )
168 .child(
169 View::modal()
170 .visible(show_help.get())
171 .title("Example 20: Menu Bar")
172 .on_dismiss(with!(show_help => move || show_help.set(false)))
173 .child(
174 View::vstack()
175 .child(View::styled_text("What you're seeing").bold().build())
176 .child(View::text("• Dropdown menu bar with keyboard nav"))
177 .child(View::text("• Menu items with shortcuts"))
178 .child(View::text("• Separators in menus"))
179 .child(View::gap(1))
180 .child(View::styled_text("Key concepts").bold().build())
181 .child(View::text("• View::menu_bar() creates menu system"))
182 .child(View::text("• Menu::new().command() adds items"))
183 .child(View::text("• .command_with_shortcut() shows key hints"))
184 .child(View::text("• on_select receives command ID"))
185 .child(View::gap(1))
186 .child(View::styled_text("Try this").bold().build())
187 .child(View::text("• Tab to menu, Enter to open"))
188 .child(View::text("• Arrow keys navigate menus"))
189 .child(View::text("• Try the Counter menu"))
190 .child(View::gap(1))
191 .child(View::styled_text("Next up").bold().build())
192 .child(View::text("→ 21_toasts: toast notifications"))
193 .child(View::gap(1))
194 .child(View::styled_text("Press Escape to close").dim().build())
195 .build(),
196 )
197 .build(),
198 )
199 .build()
200 }Sourcepub fn flex(self, flex: u16) -> Self
pub fn flex(self, flex: u16) -> Self
Examples found in repository?
examples/15_markdown.rs (line 74)
54 fn render(&self, cx: Scope) -> View {
55 let show_help = state!(cx, || false);
56
57 // F1 toggles help
58 cx.use_command(
59 KeyBinding::key(KeyCode::F(1)),
60 with!(show_help => move || show_help.update(|v| *v = !*v)),
61 );
62
63 let rendered = telex::markdown::render(DEMO_MARKDOWN);
64
65 View::vstack()
66 .child(
67 View::styled_text("Markdown Rendering Demo")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::boxed()
74 .flex(1)
75 .child(
76 View::split()
77 .horizontal()
78 .ratio(0.4)
79 .first(
80 View::vstack()
81 .child(View::styled_text(" Source ").bold().build())
82 .child(
83 View::boxed()
84 .flex(1)
85 .border(true)
86 .scroll(true)
87 .child(View::text(DEMO_MARKDOWN))
88 .build(),
89 )
90 .build(),
91 )
92 .second(
93 View::vstack()
94 .child(View::styled_text(" Rendered ").bold().build())
95 .child(
96 View::boxed()
97 .flex(1)
98 .border(true)
99 .scroll(true)
100 .child(rendered)
101 .build(),
102 )
103 .build(),
104 )
105 .build(),
106 )
107 .build(),
108 )
109 .child(
110 View::styled_text("Tab: switch panes | ↑↓/jk: scroll | F1 help | Ctrl+Q: quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 15: Markdown")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• Side-by-side markdown source and rendered"))
123 .child(View::text("• Full markdown syntax support"))
124 .child(View::text("• Scrollable panes for long content"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• telex::markdown::render() parses markdown"))
128 .child(View::text("• Returns a View tree with styled text"))
129 .child(View::text("• Code blocks, lists, quotes, headers"))
130 .child(View::text("• Split view for comparison"))
131 .child(View::gap(1))
132 .child(View::styled_text("Try this").bold().build())
133 .child(View::text("• Tab between source and rendered panes"))
134 .child(View::text("• Scroll with arrow keys or j/k"))
135 .child(View::text("• Compare source with rendered output"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 16_progress: progress bars"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }More examples
examples/16_tree.rs (line 78)
20 fn render(&self, cx: Scope) -> View {
21 let show_help = state!(cx, || false);
22
23 // F1 toggles help
24 cx.use_command(
25 KeyBinding::key(KeyCode::F(1)),
26 with!(show_help => move || show_help.update(|v| *v = !*v)),
27 );
28
29 // Track selected path
30 let selected = state!(cx, || vec![0usize]);
31
32 // Track expanded state for each node (by path prefix)
33 let expanded_paths = state!(cx, || {
34 vec![
35 vec![0], // src/ expanded
36 vec![0, 0], // src/components/ expanded
37 ]
38 });
39
40 // Build tree items with current expanded state
41 let items = build_tree(&expanded_paths.get());
42
43 let on_select = with!(selected => move |path: TreePath| {
44 selected.set(path);
45 });
46
47 let on_activate = with!(expanded_paths => move |path: TreePath| {
48 // Toggle expand/collapse for the activated item
49 let mut paths = expanded_paths.get().clone();
50 if let Some(pos) = paths.iter().position(|p| *p == path) {
51 // Currently expanded, collapse it
52 paths.remove(pos);
53 } else {
54 // Currently collapsed, expand it
55 paths.push(path.clone());
56 }
57 expanded_paths.set(paths);
58 });
59
60 let selected_label = get_item_at_path(&items, &selected.get())
61 .map(|item| item.label.clone())
62 .unwrap_or_else(|| "Nothing".to_string());
63
64 View::vstack()
65 .child(
66 View::styled_text("File Browser")
67 .color(Color::Cyan)
68 .bold()
69 .build(),
70 )
71 .child(
72 View::styled_text(format!("Selected: {}", selected_label))
73 .dim()
74 .build(),
75 )
76 .child(
77 View::boxed()
78 .flex(1)
79 .border(true)
80 .child(
81 View::tree()
82 .items(items)
83 .selected(selected.get().clone())
84 .on_select(on_select)
85 .on_activate(on_activate)
86 .build(),
87 )
88 .build(),
89 )
90 .child(
91 View::styled_text(
92 "↑↓/jk: navigate | Enter: expand/collapse | F1 help | Ctrl+Q: quit",
93 )
94 .dim()
95 .build(),
96 )
97 .child(
98 View::modal()
99 .visible(show_help.get())
100 .title("Example 16: Tree View")
101 .on_dismiss(with!(show_help => move || show_help.set(false)))
102 .child(
103 View::vstack()
104 .child(View::styled_text("What you're seeing").bold().build())
105 .child(View::text("• Hierarchical tree widget"))
106 .child(View::text("• Expand/collapse folders"))
107 .child(View::text("• Path-based selection tracking"))
108 .child(View::gap(1))
109 .child(View::styled_text("Key concepts").bold().build())
110 .child(View::text("• View::tree() for hierarchical data"))
111 .child(View::text("• TreeItem::new().child() builds hierarchy"))
112 .child(View::text("• on_select returns TreePath (Vec<usize>)"))
113 .child(View::text("• on_activate for expand/collapse"))
114 .child(View::gap(1))
115 .child(View::styled_text("Try this").bold().build())
116 .child(View::text("• Navigate with arrow keys"))
117 .child(View::text("• Press Enter to expand/collapse folders"))
118 .child(View::text("• Watch the 'Selected:' text update"))
119 .child(View::gap(1))
120 .child(View::styled_text("Next up").bold().build())
121 .child(View::text("→ 17_table: data tables with sorting"))
122 .child(View::gap(1))
123 .child(View::styled_text("Press Escape to close").dim().build())
124 .build(),
125 )
126 .build(),
127 )
128 .build()
129 }examples/13_split_panes.rs (line 58)
20 fn render(&self, cx: Scope) -> View {
21 let show_help = state!(cx, || false);
22
23 // F1 toggles help
24 cx.use_command(
25 KeyBinding::key(KeyCode::F(1)),
26 with!(show_help => move || show_help.update(|v| *v = !*v)),
27 );
28
29 let selected_item = state!(cx, || 0usize);
30
31 let items = vec![
32 "README.md".to_string(),
33 "Cargo.toml".to_string(),
34 "src/".to_string(),
35 "src/main.rs".to_string(),
36 "src/lib.rs".to_string(),
37 "tests/".to_string(),
38 ];
39
40 let on_select = with!(selected_item => move |idx: usize| {
41 selected_item.set(idx);
42 });
43
44 let detail_text = match selected_item.get() {
45 0 => "# README\n\nThis is a demo of split panes.\n\nThe left panel shows a file list,\nthe right panel shows details.",
46 1 => "[package]\nname = \"demo\"\nversion = \"0.1.0\"\nedition = \"2021\"",
47 2 => "Directory: src/\n\nContains the main source files.",
48 3 => "fn main() {\n println!(\"Hello, world!\");\n}",
49 4 => "pub mod utils;\npub mod widgets;",
50 5 => "Directory: tests/\n\nContains integration tests.",
51 _ => "Select an item to see details.",
52 };
53
54 // Horizontal split: file list on left, details on right
55 View::vstack()
56 .child(
57 View::boxed()
58 .flex(1)
59 .child(
60 View::split()
61 .horizontal()
62 .ratio(0.3)
63 .min_first(15)
64 .first(
65 View::vstack()
66 .child(
67 View::styled_text("Files")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::list()
74 .items(items)
75 .selected(selected_item.get())
76 .on_select(on_select)
77 .build(),
78 )
79 .child(View::gap(1))
80 .build(),
81 )
82 .second(
83 View::vstack()
84 .child(
85 View::styled_text("Details")
86 .color(Color::Green)
87 .bold()
88 .build(),
89 )
90 .child(
91 View::boxed()
92 .border(true)
93 .flex(1)
94 .child(View::text(detail_text))
95 .build(),
96 )
97 .build(),
98 )
99 .build(),
100 )
101 .build(),
102 )
103 .child(
104 View::styled_text("↑↓: select file | F1 help | Ctrl+Q: quit")
105 .dim()
106 .build(),
107 )
108 .child(
109 View::modal()
110 .visible(show_help.get())
111 .title("Example 13: Split Panes")
112 .on_dismiss(with!(show_help => move || show_help.set(false)))
113 .child(
114 View::vstack()
115 .child(View::styled_text("What you're seeing").bold().build())
116 .child(View::text("• Horizontal split: file list / details"))
117 .child(View::text("• ratio(0.3) = 30% left, 70% right"))
118 .child(View::text("• min_first(15) sets minimum pane width"))
119 .child(View::gap(1))
120 .child(View::styled_text("Key concepts").bold().build())
121 .child(View::text("• View::split() creates resizable panes"))
122 .child(View::text("• .horizontal() or .vertical() orientation"))
123 .child(View::text("• .first() and .second() set pane content"))
124 .child(View::text("• Splits can be nested"))
125 .child(View::gap(1))
126 .child(View::styled_text("Try this").bold().build())
127 .child(View::text("• Select different files to see details"))
128 .child(View::text("• Resize terminal to see layout adapt"))
129 .child(View::gap(1))
130 .child(View::styled_text("Next up").bold().build())
131 .child(View::text("→ 14_tabs: tabbed interfaces"))
132 .child(View::gap(1))
133 .child(View::styled_text("Press Escape to close").dim().build())
134 .build(),
135 )
136 .build(),
137 )
138 .build()
139 }examples/24_async_data.rs (line 101)
38 fn render(&self, cx: Scope) -> View {
39 let show_help = state!(cx, || false);
40
41 // F1 toggles help
42 cx.use_command(
43 KeyBinding::key(KeyCode::F(1)),
44 with!(show_help => move || show_help.update(|v| *v = !*v)),
45 );
46
47 // Simulate fetching user profile (slow - 2 seconds)
48 let profile = async_data!(cx, || {
49 thread::sleep(Duration::from_secs(2));
50 Ok(UserProfile {
51 name: "Alice Johnson".to_string(),
52 email: "alice@example.com".to_string(),
53 member_since: "January 2024".to_string(),
54 })
55 });
56
57 // Simulate fetching user stats (medium - 1 second)
58 let stats = async_data!(cx, || {
59 thread::sleep(Duration::from_secs(1));
60 Ok(Stats {
61 posts: 142,
62 followers: 1234,
63 following: 567,
64 })
65 });
66
67 // Simulate a failing request
68 let failing_data = async_data!(cx, || {
69 thread::sleep(Duration::from_millis(500));
70 Err::<String, _>("Network error: Connection refused".to_string())
71 });
72
73 View::vstack()
74 .spacing(1)
75 .child(
76 // Header
77 View::boxed()
78 .border(true)
79 .padding(1)
80 .child(
81 View::vstack()
82 .child(View::styled_text("Async Data Loading Demo").bold().build())
83 .child(
84 View::styled_text(
85 "Demonstrates use_async for loading data with loading/error states",
86 )
87 .dim()
88 .build(),
89 )
90 .build(),
91 )
92 .build(),
93 )
94 .child(
95 // Main content - three columns
96 View::hstack()
97 .spacing(1)
98 // Profile section
99 .child(
100 View::boxed()
101 .flex(1)
102 .border(true)
103 .padding(1)
104 .child(render_profile_section(&profile))
105 .build(),
106 )
107 // Stats section
108 .child(
109 View::boxed()
110 .flex(1)
111 .border(true)
112 .padding(1)
113 .child(render_stats_section(&stats))
114 .build(),
115 )
116 // Error section
117 .child(
118 View::boxed()
119 .flex(1)
120 .border(true)
121 .padding(1)
122 .child(render_error_section(&failing_data))
123 .build(),
124 )
125 .build(),
126 )
127 .child(
128 // Status footer
129 View::boxed()
130 .border(true)
131 .padding(1)
132 .child(
133 View::vstack()
134 .child(View::text(format!(
135 "Overall status: {}",
136 if profile.is_loading() || stats.is_loading() {
137 "Loading..."
138 } else if profile.is_error() || stats.is_error() || failing_data.is_error() {
139 "Some requests failed"
140 } else {
141 "All data loaded"
142 }
143 )))
144 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
145 .build(),
146 )
147 .build(),
148 )
149 .child(
150 View::modal()
151 .visible(show_help.get())
152 .title("Example 24: Async Data")
153 .on_dismiss(with!(show_help => move || show_help.set(false)))
154 .child(
155 View::vstack()
156 .child(View::styled_text("What you're seeing").bold().build())
157 .child(View::text("• Three async data loads in parallel"))
158 .child(View::text("• Loading, success, and error states"))
159 .child(View::text("• Different load times for each"))
160 .child(View::gap(1))
161 .child(View::styled_text("Key concepts").bold().build())
162 .child(View::text("• async_data!() macro runs in background"))
163 .child(View::text("• Returns Async<T> enum"))
164 .child(View::text("• .is_loading() / .is_error() helpers"))
165 .child(View::text("• Pattern match for state handling"))
166 .child(View::gap(1))
167 .child(View::styled_text("Try this").bold().build())
168 .child(View::text("• Watch data load progressively"))
169 .child(View::text("• Notice the failing request"))
170 .child(View::gap(1))
171 .child(View::styled_text("Next up").bold().build())
172 .child(View::text("→ 25_context: context API"))
173 .child(View::gap(1))
174 .child(View::styled_text("Press Escape to close").dim().build())
175 .build()
176 )
177 .build()
178 )
179 .build()
180 }examples/25_context.rs (line 129)
71 fn render(&self, cx: Scope) -> View {
72 let show_help = state!(cx, || false);
73
74 // F1 toggles help
75 cx.use_command(
76 KeyBinding::key(KeyCode::F(1)),
77 with!(show_help => move || show_help.update(|v| *v = !*v)),
78 );
79
80 // State that we'll provide via context
81 let theme = state!(cx, || ColorTheme::Default);
82 let user = state!(cx, || User {
83 name: "Guest".to_string(),
84 logged_in: false,
85 });
86
87 // Provide static config via context
88 cx.provide_context(AppConfig {
89 app_name: "Context Demo".to_string(),
90 version: "1.0.0".to_string(),
91 });
92
93 // Provide dynamic state via context (current values)
94 cx.provide_context(theme.get());
95 cx.provide_context(user.get());
96
97 // Theme switching handlers
98 let set_default = with!(theme => move || theme.set(ColorTheme::Default));
99 let set_ocean = with!(theme => move || theme.set(ColorTheme::Ocean));
100 let set_forest = with!(theme => move || theme.set(ColorTheme::Forest));
101 let set_sunset = with!(theme => move || theme.set(ColorTheme::Sunset));
102
103 // Login/logout handlers
104 let toggle_login = with!(user => move || {
105 let current = user.get();
106 if current.logged_in {
107 user.set(User {
108 name: "Guest".to_string(),
109 logged_in: false,
110 });
111 } else {
112 user.set(User {
113 name: "Alice".to_string(),
114 logged_in: true,
115 });
116 }
117 });
118
119 // Get current theme for styling
120 let current_theme = theme.get();
121
122 View::vstack()
123 .spacing(1)
124 // Header - uses context
125 .child(render_header(&cx))
126 .child(
127 // Main content
128 View::boxed()
129 .flex(1)
130 .border(true)
131 .padding(1)
132 .child(
133 View::vstack()
134 .spacing(1)
135 .child(
136 View::styled_text("Theme Selection:")
137 .color(current_theme.primary())
138 .bold()
139 .build(),
140 )
141 .child(
142 View::hstack()
143 .spacing(2)
144 .child(
145 View::button()
146 .label("Default")
147 .on_press(set_default)
148 .build(),
149 )
150 .child(
151 View::button().label("Ocean").on_press(set_ocean).build(),
152 )
153 .child(
154 View::button().label("Forest").on_press(set_forest).build(),
155 )
156 .child(
157 View::button().label("Sunset").on_press(set_sunset).build(),
158 )
159 .build(),
160 )
161 .child(View::gap(1))
162 .child(
163 View::styled_text("User Actions:")
164 .color(current_theme.primary())
165 .bold()
166 .build(),
167 )
168 .child(
169 View::button()
170 .label(if user.get().logged_in {
171 "Logout"
172 } else {
173 "Login as Alice"
174 })
175 .on_press(toggle_login)
176 .build(),
177 )
178 .child(View::spacer())
179 // User info panel - uses context
180 .child(render_user_panel(&cx))
181 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
182 .build(),
183 )
184 .build(),
185 )
186 // Status bar - uses context
187 .child(render_status_bar(&cx))
188 .child(
189 View::modal()
190 .visible(show_help.get())
191 .title("Example 25: Context")
192 .on_dismiss(with!(show_help => move || show_help.set(false)))
193 .child(
194 View::vstack()
195 .child(View::styled_text("What you're seeing").bold().build())
196 .child(View::text("• Context API for global state"))
197 .child(View::text("• Theme colors propagate everywhere"))
198 .child(View::text("• User state shared across components"))
199 .child(View::gap(1))
200 .child(View::styled_text("Key concepts").bold().build())
201 .child(View::text("• cx.provide_context() adds to context"))
202 .child(View::text("• cx.use_context::<T>() reads context"))
203 .child(View::text("• Avoids prop drilling"))
204 .child(View::text("• Great for themes, user, config"))
205 .child(View::gap(1))
206 .child(View::styled_text("Try this").bold().build())
207 .child(View::text("• Switch themes - colors update"))
208 .child(View::text("• Login/logout - panel updates"))
209 .child(View::text("• Header and status bar read context"))
210 .child(View::gap(1))
211 .child(View::styled_text("Next up").bold().build())
212 .child(View::text("→ 26_radio_buttons: radio selections"))
213 .child(View::gap(1))
214 .child(View::styled_text("Press Escape to close").dim().build())
215 .build(),
216 )
217 .build(),
218 )
219 .build()
220 }examples/14_tabs.rs (line 69)
22 fn render(&self, cx: Scope) -> View {
23 let show_help = state!(cx, || false);
24
25 // F1 toggles help
26 cx.use_command(
27 KeyBinding::key(KeyCode::F(1)),
28 with!(show_help => move || show_help.update(|v| *v = !*v)),
29 );
30
31 let active_tab = state!(cx, || 0usize);
32
33 // Settings state
34 let notifications = state!(cx, || true);
35 let dark_mode = state!(cx, || true);
36 let auto_save = state!(cx, || true);
37
38 let on_change = with!(active_tab => move |idx: usize| {
39 active_tab.set(idx);
40 });
41
42 // Checkbox handlers
43 let on_notifications = with!(notifications => move |checked: bool| {
44 notifications.set(checked);
45 });
46
47 let on_dark_mode = with!(dark_mode => move |checked: bool| {
48 dark_mode.set(checked);
49 if checked {
50 set_theme(Theme::dark());
51 } else {
52 set_theme(Theme::light());
53 }
54 });
55
56 let on_auto_save = with!(auto_save => move |checked: bool| {
57 auto_save.set(checked);
58 });
59
60 View::vstack()
61 .child(
62 View::styled_text("Tabbed Interface Demo")
63 .color(Color::Cyan)
64 .bold()
65 .build(),
66 )
67 .child(
68 View::boxed()
69 .flex(1)
70 .child(
71 View::tabs()
72 .tab(
73 "Overview",
74 View::vstack()
75 .child(View::styled_text("Welcome!").bold().build())
76 .child(View::text(
77 "\nThis is the Overview tab.\n\n\
78 Use the keyboard to switch tabs:\n\
79 - Left/Right arrows\n\
80 - [ and ] keys\n\
81 - Number keys 1-3",
82 ))
83 .build(),
84 )
85 .tab(
86 "Settings",
87 View::vstack()
88 .child(View::styled_text("Settings").bold().build())
89 .child(View::text(""))
90 .child(
91 View::checkbox()
92 .label("Enable notifications")
93 .checked(notifications.get())
94 .on_toggle(on_notifications)
95 .build(),
96 )
97 .child(
98 View::checkbox()
99 .label("Dark mode")
100 .checked(dark_mode.get())
101 .on_toggle(on_dark_mode)
102 .build(),
103 )
104 .child(
105 View::checkbox()
106 .label("Auto-save")
107 .checked(auto_save.get())
108 .on_toggle(on_auto_save)
109 .build(),
110 )
111 .build(),
112 )
113 .tab(
114 "About",
115 View::vstack()
116 .child(View::styled_text("About").bold().build())
117 .child(View::text(""))
118 .child(View::text("Telex TUI Framework"))
119 .child(View::text("Version: 0.2.1"))
120 .child(View::text(""))
121 .child(
122 View::styled_text("A React-style TUI framework for Rust")
123 .dim()
124 .build(),
125 )
126 .build(),
127 )
128 .active(active_tab.get())
129 .on_change(on_change)
130 .build(),
131 )
132 .build(),
133 )
134 .child(
135 View::styled_text("←→ or []: switch tabs | F1 help | Ctrl+Q: quit")
136 .dim()
137 .build(),
138 )
139 .child(
140 View::modal()
141 .visible(show_help.get())
142 .title("Example 14: Tabs")
143 .on_dismiss(with!(show_help => move || show_help.set(false)))
144 .child(
145 View::vstack()
146 .child(View::styled_text("What you're seeing").bold().build())
147 .child(View::text("• Tabbed interface with three tabs"))
148 .child(View::text("• Settings tab with checkboxes"))
149 .child(View::text("• Keyboard navigation between tabs"))
150 .child(View::gap(1))
151 .child(View::styled_text("Key concepts").bold().build())
152 .child(View::text("• View::tabs() creates tabbed container"))
153 .child(View::text("• .tab(\"Title\", content) adds each tab"))
154 .child(View::text("• .active() and .on_change() for state"))
155 .child(View::text("• Arrow keys, [ ], or 1-3 switch tabs"))
156 .child(View::gap(1))
157 .child(View::styled_text("Try this").bold().build())
158 .child(View::text("• Switch tabs with arrow keys"))
159 .child(View::text("• Toggle checkboxes in Settings"))
160 .child(View::text("• Try [ and ] keys for tab switching"))
161 .child(View::gap(1))
162 .child(View::styled_text("Next up").bold().build())
163 .child(View::text("→ 15_markdown: markdown rendering"))
164 .child(View::gap(1))
165 .child(View::styled_text("Press Escape to close").dim().build())
166 .build(),
167 )
168 .build(),
169 )
170 .build()
171 }Additional examples can be found in:
Sourcepub fn scroll(self, scroll: bool) -> Self
pub fn scroll(self, scroll: bool) -> Self
Examples found in repository?
examples/15_markdown.rs (line 86)
54 fn render(&self, cx: Scope) -> View {
55 let show_help = state!(cx, || false);
56
57 // F1 toggles help
58 cx.use_command(
59 KeyBinding::key(KeyCode::F(1)),
60 with!(show_help => move || show_help.update(|v| *v = !*v)),
61 );
62
63 let rendered = telex::markdown::render(DEMO_MARKDOWN);
64
65 View::vstack()
66 .child(
67 View::styled_text("Markdown Rendering Demo")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::boxed()
74 .flex(1)
75 .child(
76 View::split()
77 .horizontal()
78 .ratio(0.4)
79 .first(
80 View::vstack()
81 .child(View::styled_text(" Source ").bold().build())
82 .child(
83 View::boxed()
84 .flex(1)
85 .border(true)
86 .scroll(true)
87 .child(View::text(DEMO_MARKDOWN))
88 .build(),
89 )
90 .build(),
91 )
92 .second(
93 View::vstack()
94 .child(View::styled_text(" Rendered ").bold().build())
95 .child(
96 View::boxed()
97 .flex(1)
98 .border(true)
99 .scroll(true)
100 .child(rendered)
101 .build(),
102 )
103 .build(),
104 )
105 .build(),
106 )
107 .build(),
108 )
109 .child(
110 View::styled_text("Tab: switch panes | ↑↓/jk: scroll | F1 help | Ctrl+Q: quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 15: Markdown")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• Side-by-side markdown source and rendered"))
123 .child(View::text("• Full markdown syntax support"))
124 .child(View::text("• Scrollable panes for long content"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• telex::markdown::render() parses markdown"))
128 .child(View::text("• Returns a View tree with styled text"))
129 .child(View::text("• Code blocks, lists, quotes, headers"))
130 .child(View::text("• Split view for comparison"))
131 .child(View::gap(1))
132 .child(View::styled_text("Try this").bold().build())
133 .child(View::text("• Tab between source and rendered panes"))
134 .child(View::text("• Scroll with arrow keys or j/k"))
135 .child(View::text("• Compare source with rendered output"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 16_progress: progress bars"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }More examples
examples/06_log_viewer.rs (line 110)
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 // Stream log entries
31 let logs = text_stream!(cx, || {
32 let log_messages = vec![
33 "[INFO] Application started",
34 "[INFO] Loading configuration...",
35 "[OK] Config loaded successfully",
36 "[INFO] Connecting to database...",
37 "[OK] Database connected",
38 "[INFO] Starting web server on :8080",
39 "[OK] Server listening",
40 "[INFO] Processing request GET /api/users",
41 "[OK] Response 200 in 45ms",
42 "[INFO] Processing request POST /api/login",
43 "[OK] Response 200 in 120ms",
44 "[WARN] High memory usage detected: 85%",
45 "[INFO] Running garbage collection...",
46 "[OK] Memory freed: 200MB",
47 "[INFO] Processing request GET /api/data",
48 "[ERROR] Database timeout after 5000ms",
49 "[INFO] Retrying database connection...",
50 "[OK] Database reconnected",
51 "[OK] Response 200 in 5045ms",
52 "[INFO] Scheduled backup starting...",
53 "[OK] Backup completed: 1.2GB",
54 "[INFO] Processing request GET /api/health",
55 "[OK] Response 200 in 12ms",
56 "[INFO] Processing request PUT /api/users/123",
57 "[OK] Response 200 in 89ms",
58 "[INFO] Cache invalidation triggered",
59 "[OK] Cache cleared: 50MB",
60 "[WARN] Slow query detected: 1200ms",
61 "[INFO] Query optimization suggested",
62 "[INFO] Processing request DELETE /api/sessions",
63 "[OK] Response 204 in 34ms",
64 "[INFO] SSL certificate check",
65 "[OK] Certificate valid for 45 days",
66 "[INFO] Processing request POST /api/upload",
67 "[OK] File uploaded: 15MB",
68 "[WARN] Disk usage at 78%",
69 "[INFO] Processing request GET /api/reports",
70 "[OK] Response 200 in 230ms",
71 "[INFO] Metrics exported to monitoring",
72 "[OK] Heartbeat sent successfully",
73 "------- Log stream completed -------",
74 ];
75
76 log_messages.into_iter().map(|msg| {
77 std::thread::sleep(Duration::from_millis(500));
78 format!("{}\n", msg)
79 })
80 });
81
82 let log_text = logs.get();
83 let is_streaming = logs.is_loading();
84
85 // Color the status indicator
86 let status = if is_streaming {
87 View::styled_text(" [LIVE]")
88 .color(Color::Green)
89 .bold()
90 .build()
91 } else {
92 View::styled_text(" [END]").dim().build()
93 };
94
95 View::vstack()
96 .child(
97 View::hstack()
98 .child(
99 View::styled_text("Log Viewer")
100 .color(Color::Cyan)
101 .bold()
102 .build(),
103 )
104 .child(status)
105 .build(),
106 )
107 .child(View::gap(1))
108 .child(
109 View::boxed()
110 .scroll(true)
111 .auto_scroll_bottom(true)
112 .min_height(15)
113 .max_height(15)
114 .child(View::text(&log_text))
115 .build(),
116 )
117 .child(View::gap(1))
118 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
119 .child(
120 View::modal()
121 .visible(show_help.get())
122 .title("Example 06: Log Viewer")
123 .on_dismiss(with!(show_help => move || show_help.set(false)))
124 .child(
125 View::vstack()
126 .child(View::styled_text("What you're seeing").bold().build())
127 .child(View::text("• text_stream!() macro for accumulating text"))
128 .child(View::text("• Auto-scrolling box that follows new content"))
129 .child(View::text("• Live/End indicator based on stream state"))
130 .child(View::gap(1))
131 .child(View::styled_text("Key concepts").bold().build())
132 .child(View::text("• text_stream! concatenates yielded strings"))
133 .child(View::text(
134 "• auto_scroll_bottom(true) keeps newest visible",
135 ))
136 .child(View::text("• Simulates tailing a log file"))
137 .child(View::gap(1))
138 .child(View::styled_text("Try this").bold().build())
139 .child(View::text("• Watch the [LIVE] indicator change to [END]"))
140 .child(View::text("• Notice auto-scroll keeps up with new entries"))
141 .child(View::gap(1))
142 .child(View::styled_text("Next up").bold().build())
143 .child(View::text("→ 07_file_browser: real filesystem navigation"))
144 .child(View::gap(1))
145 .child(View::styled_text("Press Escape to close").dim().build())
146 .build(),
147 )
148 .build(),
149 )
150 .build()
151 }Sourcepub fn auto_scroll_bottom(self, auto_scroll: bool) -> Self
pub fn auto_scroll_bottom(self, auto_scroll: bool) -> Self
Enable auto-scrolling to bottom (for chat-like UIs).
Examples found in repository?
examples/06_log_viewer.rs (line 111)
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 // Stream log entries
31 let logs = text_stream!(cx, || {
32 let log_messages = vec![
33 "[INFO] Application started",
34 "[INFO] Loading configuration...",
35 "[OK] Config loaded successfully",
36 "[INFO] Connecting to database...",
37 "[OK] Database connected",
38 "[INFO] Starting web server on :8080",
39 "[OK] Server listening",
40 "[INFO] Processing request GET /api/users",
41 "[OK] Response 200 in 45ms",
42 "[INFO] Processing request POST /api/login",
43 "[OK] Response 200 in 120ms",
44 "[WARN] High memory usage detected: 85%",
45 "[INFO] Running garbage collection...",
46 "[OK] Memory freed: 200MB",
47 "[INFO] Processing request GET /api/data",
48 "[ERROR] Database timeout after 5000ms",
49 "[INFO] Retrying database connection...",
50 "[OK] Database reconnected",
51 "[OK] Response 200 in 5045ms",
52 "[INFO] Scheduled backup starting...",
53 "[OK] Backup completed: 1.2GB",
54 "[INFO] Processing request GET /api/health",
55 "[OK] Response 200 in 12ms",
56 "[INFO] Processing request PUT /api/users/123",
57 "[OK] Response 200 in 89ms",
58 "[INFO] Cache invalidation triggered",
59 "[OK] Cache cleared: 50MB",
60 "[WARN] Slow query detected: 1200ms",
61 "[INFO] Query optimization suggested",
62 "[INFO] Processing request DELETE /api/sessions",
63 "[OK] Response 204 in 34ms",
64 "[INFO] SSL certificate check",
65 "[OK] Certificate valid for 45 days",
66 "[INFO] Processing request POST /api/upload",
67 "[OK] File uploaded: 15MB",
68 "[WARN] Disk usage at 78%",
69 "[INFO] Processing request GET /api/reports",
70 "[OK] Response 200 in 230ms",
71 "[INFO] Metrics exported to monitoring",
72 "[OK] Heartbeat sent successfully",
73 "------- Log stream completed -------",
74 ];
75
76 log_messages.into_iter().map(|msg| {
77 std::thread::sleep(Duration::from_millis(500));
78 format!("{}\n", msg)
79 })
80 });
81
82 let log_text = logs.get();
83 let is_streaming = logs.is_loading();
84
85 // Color the status indicator
86 let status = if is_streaming {
87 View::styled_text(" [LIVE]")
88 .color(Color::Green)
89 .bold()
90 .build()
91 } else {
92 View::styled_text(" [END]").dim().build()
93 };
94
95 View::vstack()
96 .child(
97 View::hstack()
98 .child(
99 View::styled_text("Log Viewer")
100 .color(Color::Cyan)
101 .bold()
102 .build(),
103 )
104 .child(status)
105 .build(),
106 )
107 .child(View::gap(1))
108 .child(
109 View::boxed()
110 .scroll(true)
111 .auto_scroll_bottom(true)
112 .min_height(15)
113 .max_height(15)
114 .child(View::text(&log_text))
115 .build(),
116 )
117 .child(View::gap(1))
118 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
119 .child(
120 View::modal()
121 .visible(show_help.get())
122 .title("Example 06: Log Viewer")
123 .on_dismiss(with!(show_help => move || show_help.set(false)))
124 .child(
125 View::vstack()
126 .child(View::styled_text("What you're seeing").bold().build())
127 .child(View::text("• text_stream!() macro for accumulating text"))
128 .child(View::text("• Auto-scrolling box that follows new content"))
129 .child(View::text("• Live/End indicator based on stream state"))
130 .child(View::gap(1))
131 .child(View::styled_text("Key concepts").bold().build())
132 .child(View::text("• text_stream! concatenates yielded strings"))
133 .child(View::text(
134 "• auto_scroll_bottom(true) keeps newest visible",
135 ))
136 .child(View::text("• Simulates tailing a log file"))
137 .child(View::gap(1))
138 .child(View::styled_text("Try this").bold().build())
139 .child(View::text("• Watch the [LIVE] indicator change to [END]"))
140 .child(View::text("• Notice auto-scroll keeps up with new entries"))
141 .child(View::gap(1))
142 .child(View::styled_text("Next up").bold().build())
143 .child(View::text("→ 07_file_browser: real filesystem navigation"))
144 .child(View::gap(1))
145 .child(View::styled_text("Press Escape to close").dim().build())
146 .build(),
147 )
148 .build(),
149 )
150 .build()
151 }Sourcepub fn focusable(self, focusable: bool) -> Self
pub fn focusable(self, focusable: bool) -> Self
Set whether this box participates in focus navigation.
By default, scrollable boxes are focusable. Use focusable(false) to
disable focus for a scrollable box (e.g., auto-scroll chat messages).
Sourcepub fn min_width(self, width: u16) -> Self
pub fn min_width(self, width: u16) -> Self
Examples found in repository?
examples/03_theme_switcher.rs (line 112)
20 fn render(&self, cx: Scope) -> View {
21 let selected = state!(cx, || 0usize);
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 let theme_names = vec![
31 "Dark".to_string(),
32 "Light".to_string(),
33 "Nord".to_string(),
34 "Monokai".to_string(),
35 "Catppuccin Mocha".to_string(),
36 "Catppuccin Latte".to_string(),
37 "Dracula".to_string(),
38 "Gruvbox Dark".to_string(),
39 "Solarized Dark".to_string(),
40 "Rosé Pine".to_string(),
41 "Tokyo Night".to_string(),
42 "HaX0R Blue".to_string(),
43 "HaX0R Green".to_string(),
44 "HaX0R Red".to_string(),
45 ];
46
47 // Apply theme when selection changes
48 let on_select = with!(selected => move |idx: usize| {
49 selected.set(idx);
50 let theme = match idx {
51 0 => Theme::dark(),
52 1 => Theme::light(),
53 2 => Theme::nord(),
54 3 => Theme::monokai(),
55 4 => Theme::catppuccin_mocha(),
56 5 => Theme::catppuccin_latte(),
57 6 => Theme::dracula(),
58 7 => Theme::gruvbox_dark(),
59 8 => Theme::solarized_dark(),
60 9 => Theme::rose_pine(),
61 10 => Theme::tokyo_night(),
62 11 => Theme::hax0r_blue(),
63 12 => Theme::hax0r_green(),
64 _ => Theme::hax0r_red(),
65 };
66 set_theme(theme);
67 });
68
69 let theme = current_theme();
70 let true_color = supports_true_color();
71
72 let mut stack = View::vstack();
73
74 // Show warning if true color isn't supported
75 if !true_color {
76 let term = terminal_name().unwrap_or_else(|| "Unknown".to_string());
77 stack = stack
78 .child(
79 View::styled_text(format!("Warning: {} doesn't support true color", term))
80 .color(theme.warning)
81 .bold()
82 .build(),
83 )
84 .child(
85 View::styled_text("Only 'Dark' and 'Light' themes will display correctly")
86 .color(theme.muted)
87 .build(),
88 )
89 .child(View::gap(1));
90 }
91
92 stack
93 .child(
94 View::styled_text("Theme Switcher")
95 .color(theme.primary)
96 .bold()
97 .build(),
98 )
99 .child(
100 View::styled_text("Select a theme from the list")
101 .color(theme.muted)
102 .italic()
103 .build(),
104 )
105 .child(View::gap(1))
106 .child(
107 View::hstack()
108 .spacing(2)
109 .child(
110 View::boxed()
111 .border(true)
112 .min_width(25)
113 .child(
114 View::list()
115 .items(theme_names)
116 .selected(selected.get())
117 .on_select(on_select)
118 .build(),
119 )
120 .build(),
121 )
122 .child(
123 View::boxed()
124 .border(true)
125 .padding(1)
126 .child(
127 View::vstack()
128 .child(View::styled_text("Preview").bold().build())
129 .child(View::gap(1))
130 .child(
131 View::hstack()
132 .child(
133 View::styled_text("Primary")
134 .color(theme.primary)
135 .build(),
136 )
137 .child(View::text(" "))
138 .child(
139 View::styled_text("Secondary")
140 .color(theme.secondary)
141 .build(),
142 )
143 .build(),
144 )
145 .child(
146 View::hstack()
147 .child(
148 View::styled_text("Muted")
149 .color(theme.muted)
150 .build(),
151 )
152 .child(View::text(" "))
153 .child(
154 View::styled_text("Success")
155 .color(theme.success)
156 .build(),
157 )
158 .build(),
159 )
160 .child(
161 View::hstack()
162 .child(
163 View::styled_text("Warning")
164 .color(theme.warning)
165 .build(),
166 )
167 .child(View::text(" "))
168 .child(
169 View::styled_text("Error")
170 .color(theme.error)
171 .build(),
172 )
173 .build(),
174 )
175 .build(),
176 )
177 .build(),
178 )
179 .build(),
180 )
181 .child(View::gap(1))
182 .child(
183 View::styled_text("↑/↓ select • F1 help • Ctrl+Q quit")
184 .color(theme.muted)
185 .build(),
186 )
187 .child(
188 View::modal()
189 .visible(show_help.get())
190 .title("Example 03: Theme Switcher")
191 .on_dismiss(with!(show_help => move || show_help.set(false)))
192 .child(
193 View::vstack()
194 .child(View::styled_text("What you're seeing").bold().build())
195 .child(View::text("• Built-in theme system with 14 themes"))
196 .child(View::text("• View::list() for selection UI"))
197 .child(View::text("• Live preview as you navigate"))
198 .child(View::gap(1))
199 .child(View::styled_text("Key concepts").bold().build())
200 .child(View::text("• current_theme() gets active theme colors"))
201 .child(View::text("• set_theme() changes theme globally"))
202 .child(View::text(
203 "• Themes provide semantic colors (primary, error, etc.)",
204 ))
205 .child(View::gap(1))
206 .child(View::styled_text("Try this").bold().build())
207 .child(View::text(
208 "• Navigate with ↑/↓ to see themes change instantly",
209 ))
210 .child(View::text(
211 "• Notice the preview panel updates with theme colors",
212 ))
213 .child(View::gap(1))
214 .child(View::styled_text("Next up").bold().build())
215 .child(View::text("→ 04_timer: streaming data without interaction"))
216 .child(View::gap(1))
217 .child(View::styled_text("Press Escape to close").dim().build())
218 .build(),
219 )
220 .build(),
221 )
222 .build()
223 }Sourcepub fn max_width(self, width: u16) -> Self
pub fn max_width(self, width: u16) -> Self
Examples found in repository?
examples/28_shared_state.rs (line 66)
29 fn render(&self, cx: Scope) -> View {
30 let show_help = state!(cx, || false);
31
32 // F1 toggles help
33 cx.use_command(
34 KeyBinding::key(KeyCode::F(1)),
35 with!(show_help => move || show_help.update(|v| *v = !*v)),
36 );
37
38 // Both panes will use the SAME key type = SAME state
39 // Note: using use_state_keyed directly with an explicit key type
40
41 // PANE A - gets the shared counter
42 let count_a = cx.use_state_keyed::<SharedCounterKey, _>(|| 0);
43 let inc_a = with!(count_a => move || count_a.update(|n| *n += 1));
44
45 // PANE B - uses the SAME key, so gets the SAME state!
46 let count_b = cx.use_state_keyed::<SharedCounterKey, _>(|| 0);
47 let inc_b = with!(count_b => move || count_b.update(|n| *n += 1));
48
49 View::vstack()
50 .spacing(1)
51 .child(
52 View::styled_text("Shared State Demo")
53 .color(Color::Cyan)
54 .bold()
55 .build(),
56 )
57 .child(View::gap(1))
58 .child(
59 View::hstack()
60 .spacing(2)
61 // Pane A
62 .child(
63 View::boxed()
64 .border(true)
65 .padding(1)
66 .max_width(25)
67 .child(
68 View::vstack()
69 .child(View::styled_text("Pane A").bold().build())
70 .child(View::gap(1))
71 .child(
72 View::hstack()
73 .spacing(1)
74 .child(View::text("Value:"))
75 .child(
76 View::styled_text(format!("{}", count_a.get()))
77 .color(Color::Yellow)
78 .bold()
79 .build(),
80 )
81 .child(View::button().label("+").on_press(inc_a).build())
82 .build(),
83 )
84 .build(),
85 )
86 .build(),
87 )
88 // Pane B
89 .child(
90 View::boxed()
91 .border(true)
92 .padding(1)
93 .max_width(25)
94 .child(
95 View::vstack()
96 .child(View::styled_text("Pane B").bold().build())
97 .child(View::gap(1))
98 .child(
99 View::hstack()
100 .spacing(1)
101 .child(View::text("Value:"))
102 .child(
103 View::styled_text(format!("{}", count_b.get()))
104 .color(Color::Yellow)
105 .bold()
106 .build(),
107 )
108 .child(View::button().label("+").on_press(inc_b).build())
109 .build(),
110 )
111 .build(),
112 )
113 .build(),
114 )
115 .build(),
116 )
117 .child(View::gap(1))
118 .child(View::styled_text("Try this:").bold().build())
119 .child(View::text(" 1. Click + on Pane A"))
120 .child(View::text(" 2. Watch Pane B update too!"))
121 .child(View::text(" 3. Click + on Pane B - same thing"))
122 .child(View::gap(1))
123 .child(
124 View::styled_text("Both panes share the SAME state.")
125 .color(Color::Green)
126 .build(),
127 )
128 .child(View::gap(1))
129 .child(
130 View::boxed()
131 .border(true)
132 .padding(1)
133 .child(
134 View::vstack()
135 .child(View::styled_text("The code:").bold().build())
136 .child(View::gap(1))
137 .child(
138 View::styled_text("struct SharedCounterKey; // Named key type")
139 .color(Color::Green)
140 .build(),
141 )
142 .child(View::gap(1))
143 .child(View::styled_text("// Pane A").color(Color::DarkGrey).build())
144 .child(
145 View::styled_text("let count_a = cx.use_state_keyed::<SharedCounterKey, _>(|| 0);")
146 .color(Color::Yellow)
147 .build(),
148 )
149 .child(View::gap(1))
150 .child(View::styled_text("// Pane B - SAME key type!").color(Color::DarkGrey).build())
151 .child(
152 View::styled_text("let count_b = cx.use_state_keyed::<SharedCounterKey, _>(|| 0);")
153 .color(Color::Yellow)
154 .build(),
155 )
156 .child(View::gap(1))
157 .child(View::text("Same key = same state. Both variables"))
158 .child(View::text("point to the same underlying value."))
159 .build(),
160 )
161 .build(),
162 )
163 .child(View::gap(1))
164 .child(
165 View::boxed()
166 .border(true)
167 .padding(1)
168 .child(
169 View::vstack()
170 .child(View::styled_text("Compare with example 27:").bold().build())
171 .child(View::gap(1))
172 .child(View::styled_text("state!(cx, || 0) // Anonymous key").color(Color::Magenta).build())
173 .child(View::text(" Each call = unique key = independent state"))
174 .child(View::gap(1))
175 .child(View::styled_text("cx.use_state_keyed::<MyKey, _>(|| 0) // Named key").color(Color::Yellow).build())
176 .child(View::text(" Same key = same state = shared everywhere"))
177 .build(),
178 )
179 .build(),
180 )
181 .child(View::gap(1))
182 .child(View::styled_text("Tab: navigate | F1 help | Ctrl+Q: quit").dim().build())
183 .child(
184 View::modal()
185 .visible(show_help.get())
186 .title("Example 28: Shared State")
187 .on_dismiss(with!(show_help => move || show_help.set(false)))
188 .child(
189 View::vstack()
190 .child(View::styled_text("What you're seeing").bold().build())
191 .child(View::text("• Two panes sharing ONE counter"))
192 .child(View::text("• Both + buttons increment same value"))
193 .child(View::text("• Named key type = shared state"))
194 .child(View::gap(1))
195 .child(View::styled_text("Key concepts").bold().build())
196 .child(View::text("• struct SharedCounterKey; defines key"))
197 .child(View::text("• use_state_keyed::<Key, _>() uses it"))
198 .child(View::text("• Same key = same underlying value"))
199 .child(View::text("• Opposite of state! (unique keys)"))
200 .child(View::gap(1))
201 .child(View::styled_text("Try this").bold().build())
202 .child(View::text("• Click + on Pane A"))
203 .child(View::text("• Watch Pane B update too!"))
204 .child(View::text("• Both buttons modify same state"))
205 .child(View::gap(1))
206 .child(View::styled_text("Next up").bold().build())
207 .child(View::text("→ 29_canvas: pixel graphics"))
208 .child(View::gap(1))
209 .child(View::styled_text("Press Escape to close").dim().build())
210 .build()
211 )
212 .build()
213 )
214 .build()
215 }More examples
examples/27_keyed_state.rs (line 113)
42 fn render(&self, cx: Scope) -> View {
43 let show_help = state!(cx, || false);
44
45 // F1 toggles help
46 cx.use_command(
47 KeyBinding::key(KeyCode::F(1)),
48 with!(show_help => move || show_help.update(|v| *v = !*v)),
49 );
50
51 // Two independent toggles
52 let show_a = state!(cx, || true);
53 let show_b = state!(cx, || true);
54
55 // COUNTER A - state created inside conditional
56 let counter_a = if show_a.get() {
57 let count = state!(cx, || 0);
58 let inc = with!(count => move || count.update(|n| *n += 1));
59
60 View::hstack()
61 .spacing(1)
62 .child(
63 View::styled_text(format!("{}", count.get()))
64 .color(Color::Yellow)
65 .bold()
66 .build(),
67 )
68 .child(View::button().label("+").on_press(inc).build())
69 .build()
70 } else {
71 View::styled_text("--").dim().build()
72 };
73
74 // COUNTER B - state created inside a DIFFERENT conditional
75 let counter_b = if show_b.get() {
76 let count = state!(cx, || 0);
77 let inc = with!(count => move || count.update(|n| *n += 1));
78
79 View::hstack()
80 .spacing(1)
81 .child(
82 View::styled_text(format!("{}", count.get()))
83 .color(Color::Magenta)
84 .bold()
85 .build(),
86 )
87 .child(View::button().label("+").on_press(inc).build())
88 .build()
89 } else {
90 View::styled_text("--").dim().build()
91 };
92
93 let toggle_a = with!(show_a => move |_: bool| show_a.update(|b| *b = !*b));
94 let toggle_b = with!(show_b => move |_: bool| show_b.update(|b| *b = !*b));
95
96 View::vstack()
97 .spacing(1)
98 .child(
99 View::styled_text("state! Demo")
100 .color(Color::Cyan)
101 .bold()
102 .build(),
103 )
104 .child(View::gap(1))
105 .child(
106 View::hstack()
107 .spacing(2)
108 // Counter A box
109 .child(
110 View::boxed()
111 .border(true)
112 .padding(1)
113 .max_width(25)
114 .child(
115 View::vstack()
116 .child(View::styled_text("Counter A").bold().build())
117 .child(View::gap(1))
118 .child(
119 View::hstack()
120 .spacing(1)
121 .child(View::text("Value:"))
122 .child(counter_a)
123 .build(),
124 )
125 .child(
126 View::hstack()
127 .spacing(1)
128 .child(View::text("Show:"))
129 .child(
130 View::checkbox()
131 .checked(show_a.get())
132 .on_toggle(toggle_a)
133 .build(),
134 )
135 .build(),
136 )
137 .build(),
138 )
139 .build(),
140 )
141 // Counter B box
142 .child(
143 View::boxed()
144 .border(true)
145 .padding(1)
146 .max_width(25)
147 .child(
148 View::vstack()
149 .child(View::styled_text("Counter B").bold().build())
150 .child(View::gap(1))
151 .child(
152 View::hstack()
153 .spacing(1)
154 .child(View::text("Value:"))
155 .child(counter_b)
156 .build(),
157 )
158 .child(
159 View::hstack()
160 .spacing(1)
161 .child(View::text("Show:"))
162 .child(
163 View::checkbox()
164 .checked(show_b.get())
165 .on_toggle(toggle_b)
166 .build(),
167 )
168 .build(),
169 )
170 .build(),
171 )
172 .build(),
173 )
174 .build(),
175 )
176 .child(View::gap(1))
177 .child(View::styled_text("Try this:").bold().build())
178 .child(View::text(
179 " 1. Increment both counters to different values",
180 ))
181 .child(View::text(" 2. Hide counter A (uncheck its box)"))
182 .child(View::text(" 3. Counter B continues to work just fine!"))
183 .child(View::text(" 4. Show A again - it remembers its value"))
184 .child(View::gap(1))
185 .child(
186 View::styled_text("They don't interfere with each other.")
187 .color(Color::Green)
188 .build(),
189 )
190 .child(View::gap(1))
191 .child(
192 View::boxed()
193 .border(true)
194 .padding(1)
195 .child(
196 View::vstack()
197 .child(View::styled_text("The code:").bold().build())
198 .child(View::gap(1))
199 .child(
200 View::styled_text("if show_a.get() {")
201 .color(Color::DarkGrey)
202 .build(),
203 )
204 .child(
205 View::styled_text(" let count = state!(cx, || 0);")
206 .color(Color::Yellow)
207 .build(),
208 )
209 .child(View::styled_text("}").color(Color::DarkGrey).build())
210 .child(
211 View::styled_text("if show_b.get() {")
212 .color(Color::DarkGrey)
213 .build(),
214 )
215 .child(
216 View::styled_text(" let count = state!(cx, || 0);")
217 .color(Color::Magenta)
218 .build(),
219 )
220 .child(View::styled_text("}").color(Color::DarkGrey).build())
221 .child(View::gap(1))
222 .child(View::text("With use_state, hiding A would CRASH B"))
223 .child(View::text("(hook indices would shift)."))
224 .build(),
225 )
226 .build(),
227 )
228 .child(View::gap(1))
229 .child(
230 View::styled_text("Tab: navigate | F1 help | Ctrl+Q: quit")
231 .dim()
232 .build(),
233 )
234 .child(
235 View::modal()
236 .visible(show_help.get())
237 .title("Example 27: Keyed State")
238 .on_dismiss(with!(show_help => move || show_help.set(false)))
239 .child(
240 View::vstack()
241 .child(View::styled_text("What you're seeing").bold().build())
242 .child(View::text("• state! macro for order-independent hooks"))
243 .child(View::text("• Conditional state that doesn't crash"))
244 .child(View::text("• Two counters with hide/show toggles"))
245 .child(View::gap(1))
246 .child(View::styled_text("Key concepts").bold().build())
247 .child(View::text("• state!(cx, || init) creates keyed state"))
248 .child(View::text("• Each call site gets unique key"))
249 .child(View::text("• Safe to use inside if blocks"))
250 .child(View::text("• Values persist when hidden/shown"))
251 .child(View::gap(1))
252 .child(View::styled_text("Try this").bold().build())
253 .child(View::text("• Increment both counters"))
254 .child(View::text("• Hide counter A"))
255 .child(View::text("• Counter B still works!"))
256 .child(View::text("• Show A again - value preserved"))
257 .child(View::gap(1))
258 .child(View::styled_text("Next up").bold().build())
259 .child(View::text("→ 28_shared_state: shared state via keys"))
260 .child(View::gap(1))
261 .child(View::styled_text("Press Escape to close").dim().build())
262 .build(),
263 )
264 .build(),
265 )
266 .build()
267 }Sourcepub fn min_height(self, height: u16) -> Self
pub fn min_height(self, height: u16) -> Self
Examples found in repository?
examples/06_log_viewer.rs (line 112)
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 // Stream log entries
31 let logs = text_stream!(cx, || {
32 let log_messages = vec![
33 "[INFO] Application started",
34 "[INFO] Loading configuration...",
35 "[OK] Config loaded successfully",
36 "[INFO] Connecting to database...",
37 "[OK] Database connected",
38 "[INFO] Starting web server on :8080",
39 "[OK] Server listening",
40 "[INFO] Processing request GET /api/users",
41 "[OK] Response 200 in 45ms",
42 "[INFO] Processing request POST /api/login",
43 "[OK] Response 200 in 120ms",
44 "[WARN] High memory usage detected: 85%",
45 "[INFO] Running garbage collection...",
46 "[OK] Memory freed: 200MB",
47 "[INFO] Processing request GET /api/data",
48 "[ERROR] Database timeout after 5000ms",
49 "[INFO] Retrying database connection...",
50 "[OK] Database reconnected",
51 "[OK] Response 200 in 5045ms",
52 "[INFO] Scheduled backup starting...",
53 "[OK] Backup completed: 1.2GB",
54 "[INFO] Processing request GET /api/health",
55 "[OK] Response 200 in 12ms",
56 "[INFO] Processing request PUT /api/users/123",
57 "[OK] Response 200 in 89ms",
58 "[INFO] Cache invalidation triggered",
59 "[OK] Cache cleared: 50MB",
60 "[WARN] Slow query detected: 1200ms",
61 "[INFO] Query optimization suggested",
62 "[INFO] Processing request DELETE /api/sessions",
63 "[OK] Response 204 in 34ms",
64 "[INFO] SSL certificate check",
65 "[OK] Certificate valid for 45 days",
66 "[INFO] Processing request POST /api/upload",
67 "[OK] File uploaded: 15MB",
68 "[WARN] Disk usage at 78%",
69 "[INFO] Processing request GET /api/reports",
70 "[OK] Response 200 in 230ms",
71 "[INFO] Metrics exported to monitoring",
72 "[OK] Heartbeat sent successfully",
73 "------- Log stream completed -------",
74 ];
75
76 log_messages.into_iter().map(|msg| {
77 std::thread::sleep(Duration::from_millis(500));
78 format!("{}\n", msg)
79 })
80 });
81
82 let log_text = logs.get();
83 let is_streaming = logs.is_loading();
84
85 // Color the status indicator
86 let status = if is_streaming {
87 View::styled_text(" [LIVE]")
88 .color(Color::Green)
89 .bold()
90 .build()
91 } else {
92 View::styled_text(" [END]").dim().build()
93 };
94
95 View::vstack()
96 .child(
97 View::hstack()
98 .child(
99 View::styled_text("Log Viewer")
100 .color(Color::Cyan)
101 .bold()
102 .build(),
103 )
104 .child(status)
105 .build(),
106 )
107 .child(View::gap(1))
108 .child(
109 View::boxed()
110 .scroll(true)
111 .auto_scroll_bottom(true)
112 .min_height(15)
113 .max_height(15)
114 .child(View::text(&log_text))
115 .build(),
116 )
117 .child(View::gap(1))
118 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
119 .child(
120 View::modal()
121 .visible(show_help.get())
122 .title("Example 06: Log Viewer")
123 .on_dismiss(with!(show_help => move || show_help.set(false)))
124 .child(
125 View::vstack()
126 .child(View::styled_text("What you're seeing").bold().build())
127 .child(View::text("• text_stream!() macro for accumulating text"))
128 .child(View::text("• Auto-scrolling box that follows new content"))
129 .child(View::text("• Live/End indicator based on stream state"))
130 .child(View::gap(1))
131 .child(View::styled_text("Key concepts").bold().build())
132 .child(View::text("• text_stream! concatenates yielded strings"))
133 .child(View::text(
134 "• auto_scroll_bottom(true) keeps newest visible",
135 ))
136 .child(View::text("• Simulates tailing a log file"))
137 .child(View::gap(1))
138 .child(View::styled_text("Try this").bold().build())
139 .child(View::text("• Watch the [LIVE] indicator change to [END]"))
140 .child(View::text("• Notice auto-scroll keeps up with new entries"))
141 .child(View::gap(1))
142 .child(View::styled_text("Next up").bold().build())
143 .child(View::text("→ 07_file_browser: real filesystem navigation"))
144 .child(View::gap(1))
145 .child(View::styled_text("Press Escape to close").dim().build())
146 .build(),
147 )
148 .build(),
149 )
150 .build()
151 }Sourcepub fn max_height(self, height: u16) -> Self
pub fn max_height(self, height: u16) -> Self
Examples found in repository?
examples/06_log_viewer.rs (line 113)
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 // Stream log entries
31 let logs = text_stream!(cx, || {
32 let log_messages = vec![
33 "[INFO] Application started",
34 "[INFO] Loading configuration...",
35 "[OK] Config loaded successfully",
36 "[INFO] Connecting to database...",
37 "[OK] Database connected",
38 "[INFO] Starting web server on :8080",
39 "[OK] Server listening",
40 "[INFO] Processing request GET /api/users",
41 "[OK] Response 200 in 45ms",
42 "[INFO] Processing request POST /api/login",
43 "[OK] Response 200 in 120ms",
44 "[WARN] High memory usage detected: 85%",
45 "[INFO] Running garbage collection...",
46 "[OK] Memory freed: 200MB",
47 "[INFO] Processing request GET /api/data",
48 "[ERROR] Database timeout after 5000ms",
49 "[INFO] Retrying database connection...",
50 "[OK] Database reconnected",
51 "[OK] Response 200 in 5045ms",
52 "[INFO] Scheduled backup starting...",
53 "[OK] Backup completed: 1.2GB",
54 "[INFO] Processing request GET /api/health",
55 "[OK] Response 200 in 12ms",
56 "[INFO] Processing request PUT /api/users/123",
57 "[OK] Response 200 in 89ms",
58 "[INFO] Cache invalidation triggered",
59 "[OK] Cache cleared: 50MB",
60 "[WARN] Slow query detected: 1200ms",
61 "[INFO] Query optimization suggested",
62 "[INFO] Processing request DELETE /api/sessions",
63 "[OK] Response 204 in 34ms",
64 "[INFO] SSL certificate check",
65 "[OK] Certificate valid for 45 days",
66 "[INFO] Processing request POST /api/upload",
67 "[OK] File uploaded: 15MB",
68 "[WARN] Disk usage at 78%",
69 "[INFO] Processing request GET /api/reports",
70 "[OK] Response 200 in 230ms",
71 "[INFO] Metrics exported to monitoring",
72 "[OK] Heartbeat sent successfully",
73 "------- Log stream completed -------",
74 ];
75
76 log_messages.into_iter().map(|msg| {
77 std::thread::sleep(Duration::from_millis(500));
78 format!("{}\n", msg)
79 })
80 });
81
82 let log_text = logs.get();
83 let is_streaming = logs.is_loading();
84
85 // Color the status indicator
86 let status = if is_streaming {
87 View::styled_text(" [LIVE]")
88 .color(Color::Green)
89 .bold()
90 .build()
91 } else {
92 View::styled_text(" [END]").dim().build()
93 };
94
95 View::vstack()
96 .child(
97 View::hstack()
98 .child(
99 View::styled_text("Log Viewer")
100 .color(Color::Cyan)
101 .bold()
102 .build(),
103 )
104 .child(status)
105 .build(),
106 )
107 .child(View::gap(1))
108 .child(
109 View::boxed()
110 .scroll(true)
111 .auto_scroll_bottom(true)
112 .min_height(15)
113 .max_height(15)
114 .child(View::text(&log_text))
115 .build(),
116 )
117 .child(View::gap(1))
118 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
119 .child(
120 View::modal()
121 .visible(show_help.get())
122 .title("Example 06: Log Viewer")
123 .on_dismiss(with!(show_help => move || show_help.set(false)))
124 .child(
125 View::vstack()
126 .child(View::styled_text("What you're seeing").bold().build())
127 .child(View::text("• text_stream!() macro for accumulating text"))
128 .child(View::text("• Auto-scrolling box that follows new content"))
129 .child(View::text("• Live/End indicator based on stream state"))
130 .child(View::gap(1))
131 .child(View::styled_text("Key concepts").bold().build())
132 .child(View::text("• text_stream! concatenates yielded strings"))
133 .child(View::text(
134 "• auto_scroll_bottom(true) keeps newest visible",
135 ))
136 .child(View::text("• Simulates tailing a log file"))
137 .child(View::gap(1))
138 .child(View::styled_text("Try this").bold().build())
139 .child(View::text("• Watch the [LIVE] indicator change to [END]"))
140 .child(View::text("• Notice auto-scroll keeps up with new entries"))
141 .child(View::gap(1))
142 .child(View::styled_text("Next up").bold().build())
143 .child(View::text("→ 07_file_browser: real filesystem navigation"))
144 .child(View::gap(1))
145 .child(View::styled_text("Press Escape to close").dim().build())
146 .build(),
147 )
148 .build(),
149 )
150 .build()
151 }Sourcepub fn build(self) -> View
pub fn build(self) -> View
Examples found in repository?
examples/09_syntax_comparison.rs (line 77)
24 fn render(&self, cx: Scope) -> View {
25 let use_jsx = state!(cx, || false);
26 let show_help = state!(cx, || false);
27
28 // F1 toggles help
29 cx.use_command(
30 KeyBinding::key(KeyCode::F(1)),
31 with!(show_help => move || show_help.update(|v| *v = !*v)),
32 );
33
34 let toggle = with!(use_jsx => move || use_jsx.set(!use_jsx.get()));
35
36 // Show which syntax is currently displayed
37 let syntax_name = if use_jsx.get() {
38 "view! macro (JSX-like)"
39 } else {
40 "Builder pattern"
41 };
42
43 View::vstack()
44 .child(
45 View::styled_text("Syntax Comparison")
46 .color(Color::Cyan)
47 .bold()
48 .build(),
49 )
50 .child(
51 View::styled_text("Same UI, two ways to write it")
52 .dim()
53 .build(),
54 )
55 .child(View::gap(1))
56 .child(
57 View::hstack()
58 .child(View::text("Current syntax: "))
59 .child(
60 View::styled_text(syntax_name)
61 .color(Color::Yellow)
62 .bold()
63 .build(),
64 )
65 .build(),
66 )
67 .child(View::gap(1))
68 .child(
69 View::boxed()
70 .border(true)
71 .padding(1)
72 .child(if use_jsx.get() {
73 counter_jsx(cx.clone())
74 } else {
75 counter_builder(cx.clone())
76 })
77 .build(),
78 )
79 .child(View::gap(1))
80 .child(
81 View::button()
82 .label("Toggle Syntax")
83 .on_press(toggle)
84 .build(),
85 )
86 .child(View::gap(1))
87 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
88 .child(
89 View::modal()
90 .visible(show_help.get())
91 .title("Example 09: Syntax Comparison")
92 .on_dismiss(with!(show_help => move || show_help.set(false)))
93 .child(
94 View::vstack()
95 .child(View::styled_text("What you're seeing").bold().build())
96 .child(View::text("• Two syntaxes that produce identical output"))
97 .child(View::text("• Builder: View::vstack().child(...).build()"))
98 .child(View::text("• Macro: view! { <VStack>...</VStack> }"))
99 .child(View::gap(1))
100 .child(View::styled_text("Key concepts").bold().build())
101 .child(View::text("• Builder is Rust-native, IDE-friendly"))
102 .child(View::text("• view! macro is JSX-like, less boilerplate"))
103 .child(View::text("• Choose based on your preference"))
104 .child(View::gap(1))
105 .child(View::styled_text("Try this").bold().build())
106 .child(View::text("• Toggle between syntaxes"))
107 .child(View::text("• Notice the output is identical"))
108 .child(View::text("• Check the source code to see both styles"))
109 .child(View::gap(1))
110 .child(View::styled_text("Next up").bold().build())
111 .child(View::text("→ 10_state_explained: deep dive into state"))
112 .child(View::gap(1))
113 .child(View::styled_text("Press Escape to close").dim().build())
114 .build(),
115 )
116 .build(),
117 )
118 .build()
119 }More examples
examples/15_markdown.rs (line 88)
54 fn render(&self, cx: Scope) -> View {
55 let show_help = state!(cx, || false);
56
57 // F1 toggles help
58 cx.use_command(
59 KeyBinding::key(KeyCode::F(1)),
60 with!(show_help => move || show_help.update(|v| *v = !*v)),
61 );
62
63 let rendered = telex::markdown::render(DEMO_MARKDOWN);
64
65 View::vstack()
66 .child(
67 View::styled_text("Markdown Rendering Demo")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::boxed()
74 .flex(1)
75 .child(
76 View::split()
77 .horizontal()
78 .ratio(0.4)
79 .first(
80 View::vstack()
81 .child(View::styled_text(" Source ").bold().build())
82 .child(
83 View::boxed()
84 .flex(1)
85 .border(true)
86 .scroll(true)
87 .child(View::text(DEMO_MARKDOWN))
88 .build(),
89 )
90 .build(),
91 )
92 .second(
93 View::vstack()
94 .child(View::styled_text(" Rendered ").bold().build())
95 .child(
96 View::boxed()
97 .flex(1)
98 .border(true)
99 .scroll(true)
100 .child(rendered)
101 .build(),
102 )
103 .build(),
104 )
105 .build(),
106 )
107 .build(),
108 )
109 .child(
110 View::styled_text("Tab: switch panes | ↑↓/jk: scroll | F1 help | Ctrl+Q: quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 15: Markdown")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• Side-by-side markdown source and rendered"))
123 .child(View::text("• Full markdown syntax support"))
124 .child(View::text("• Scrollable panes for long content"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• telex::markdown::render() parses markdown"))
128 .child(View::text("• Returns a View tree with styled text"))
129 .child(View::text("• Code blocks, lists, quotes, headers"))
130 .child(View::text("• Split view for comparison"))
131 .child(View::gap(1))
132 .child(View::styled_text("Try this").bold().build())
133 .child(View::text("• Tab between source and rendered panes"))
134 .child(View::text("• Scroll with arrow keys or j/k"))
135 .child(View::text("• Compare source with rendered output"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 16_progress: progress bars"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }examples/16_tree.rs (line 88)
20 fn render(&self, cx: Scope) -> View {
21 let show_help = state!(cx, || false);
22
23 // F1 toggles help
24 cx.use_command(
25 KeyBinding::key(KeyCode::F(1)),
26 with!(show_help => move || show_help.update(|v| *v = !*v)),
27 );
28
29 // Track selected path
30 let selected = state!(cx, || vec![0usize]);
31
32 // Track expanded state for each node (by path prefix)
33 let expanded_paths = state!(cx, || {
34 vec![
35 vec![0], // src/ expanded
36 vec![0, 0], // src/components/ expanded
37 ]
38 });
39
40 // Build tree items with current expanded state
41 let items = build_tree(&expanded_paths.get());
42
43 let on_select = with!(selected => move |path: TreePath| {
44 selected.set(path);
45 });
46
47 let on_activate = with!(expanded_paths => move |path: TreePath| {
48 // Toggle expand/collapse for the activated item
49 let mut paths = expanded_paths.get().clone();
50 if let Some(pos) = paths.iter().position(|p| *p == path) {
51 // Currently expanded, collapse it
52 paths.remove(pos);
53 } else {
54 // Currently collapsed, expand it
55 paths.push(path.clone());
56 }
57 expanded_paths.set(paths);
58 });
59
60 let selected_label = get_item_at_path(&items, &selected.get())
61 .map(|item| item.label.clone())
62 .unwrap_or_else(|| "Nothing".to_string());
63
64 View::vstack()
65 .child(
66 View::styled_text("File Browser")
67 .color(Color::Cyan)
68 .bold()
69 .build(),
70 )
71 .child(
72 View::styled_text(format!("Selected: {}", selected_label))
73 .dim()
74 .build(),
75 )
76 .child(
77 View::boxed()
78 .flex(1)
79 .border(true)
80 .child(
81 View::tree()
82 .items(items)
83 .selected(selected.get().clone())
84 .on_select(on_select)
85 .on_activate(on_activate)
86 .build(),
87 )
88 .build(),
89 )
90 .child(
91 View::styled_text(
92 "↑↓/jk: navigate | Enter: expand/collapse | F1 help | Ctrl+Q: quit",
93 )
94 .dim()
95 .build(),
96 )
97 .child(
98 View::modal()
99 .visible(show_help.get())
100 .title("Example 16: Tree View")
101 .on_dismiss(with!(show_help => move || show_help.set(false)))
102 .child(
103 View::vstack()
104 .child(View::styled_text("What you're seeing").bold().build())
105 .child(View::text("• Hierarchical tree widget"))
106 .child(View::text("• Expand/collapse folders"))
107 .child(View::text("• Path-based selection tracking"))
108 .child(View::gap(1))
109 .child(View::styled_text("Key concepts").bold().build())
110 .child(View::text("• View::tree() for hierarchical data"))
111 .child(View::text("• TreeItem::new().child() builds hierarchy"))
112 .child(View::text("• on_select returns TreePath (Vec<usize>)"))
113 .child(View::text("• on_activate for expand/collapse"))
114 .child(View::gap(1))
115 .child(View::styled_text("Try this").bold().build())
116 .child(View::text("• Navigate with arrow keys"))
117 .child(View::text("• Press Enter to expand/collapse folders"))
118 .child(View::text("• Watch the 'Selected:' text update"))
119 .child(View::gap(1))
120 .child(View::styled_text("Next up").bold().build())
121 .child(View::text("→ 17_table: data tables with sorting"))
122 .child(View::gap(1))
123 .child(View::styled_text("Press Escape to close").dim().build())
124 .build(),
125 )
126 .build(),
127 )
128 .build()
129 }examples/10_state_explained.rs (line 81)
21 fn render(&self, cx: Scope) -> View {
22 let count = state!(cx, || 0i32);
23 let show_help = state!(cx, || false);
24
25 // F1 toggles help
26 cx.use_command(
27 KeyBinding::key(KeyCode::F(1)),
28 with!(show_help => move || show_help.update(|v| *v = !*v)),
29 );
30
31 // Clone handles for closures (this is the pattern being explained)
32 let count_for_increment = count.clone();
33 let count_for_decrement = count.clone();
34 let count_for_reset = count.clone();
35
36 let increment = move || {
37 let current = count_for_increment.get();
38 count_for_increment.set(current + 1);
39 };
40
41 let decrement = move || {
42 let current = count_for_decrement.get();
43 count_for_decrement.set(current - 1);
44 };
45
46 let reset = move || {
47 count_for_reset.set(0);
48 };
49
50 let current_value = count.get();
51
52 // Hook ordering demo
53 let _always_called_1 = state!(cx, || "hook 1");
54 let _always_called_2 = state!(cx, || "hook 2");
55
56 View::vstack()
57 .spacing(1)
58 .child(
59 View::styled_text("State Explained")
60 .color(Color::Cyan)
61 .bold()
62 .build(),
63 )
64 .child(
65 View::boxed()
66 .border(true)
67 .padding(1)
68 .child(
69 View::vstack()
70 .child(View::styled_text("The Mental Model:").bold().build())
71 .child(View::gap(1))
72 .child(View::text(" State<T> is a HANDLE, not data"))
73 .child(View::text(" clone() copies the handle, not the data"))
74 .child(View::text(" All handles point to ONE value"))
75 .child(View::gap(1))
76 .child(View::text(" count ──────┐"))
77 .child(View::text(" ├──► i32: 0 (shared!)"))
78 .child(View::text(" count2 ─────┘"))
79 .build(),
80 )
81 .build(),
82 )
83 .child(
84 View::hstack()
85 .spacing(1)
86 .child(View::text("Current value:"))
87 .child(
88 View::styled_text(format!("{}", current_value))
89 .color(Color::Yellow)
90 .bold()
91 .build(),
92 )
93 .build(),
94 )
95 .child(
96 View::hstack()
97 .spacing(1)
98 .child(View::button().label(" - ").on_press(decrement).build())
99 .child(View::button().label(" + ").on_press(increment).build())
100 .child(View::button().label("Reset").on_press(reset).build())
101 .build(),
102 )
103 .child(
104 View::styled_text("All three buttons modify the SAME underlying i32")
105 .dim()
106 .build(),
107 )
108 .child(View::gap(1))
109 .child(
110 View::styled_text("Tab navigate • F1 help • Ctrl+Q quit")
111 .dim()
112 .build(),
113 )
114 .child(
115 View::modal()
116 .visible(show_help.get())
117 .title("Example 10: State Explained")
118 .on_dismiss(with!(show_help => move || show_help.set(false)))
119 .child(
120 View::vstack()
121 .child(View::styled_text("What you're seeing").bold().build())
122 .child(View::text("• State<T> as a handle/pointer concept"))
123 .child(View::text("• Multiple closures sharing one value"))
124 .child(View::text("• Visual diagram of the mental model"))
125 .child(View::gap(1))
126 .child(View::styled_text("Key concepts").bold().build())
127 .child(View::text("• clone() copies handle, not data"))
128 .child(View::text("• All handles point to same underlying value"))
129 .child(View::text(
130 "• Hooks must be called in same order every render",
131 ))
132 .child(View::gap(1))
133 .child(View::styled_text("Important rule").bold().build())
134 .child(View::text("• NEVER put use_state inside an if block"))
135 .child(View::text("• Use with!() macro to simplify cloning"))
136 .child(View::gap(1))
137 .child(View::styled_text("Next up").bold().build())
138 .child(View::text("→ 11_checkbox: toggle controls"))
139 .child(View::gap(1))
140 .child(View::styled_text("Press Escape to close").dim().build())
141 .build(),
142 )
143 .build(),
144 )
145 .build()
146 }examples/13_split_panes.rs (line 95)
20 fn render(&self, cx: Scope) -> View {
21 let show_help = state!(cx, || false);
22
23 // F1 toggles help
24 cx.use_command(
25 KeyBinding::key(KeyCode::F(1)),
26 with!(show_help => move || show_help.update(|v| *v = !*v)),
27 );
28
29 let selected_item = state!(cx, || 0usize);
30
31 let items = vec![
32 "README.md".to_string(),
33 "Cargo.toml".to_string(),
34 "src/".to_string(),
35 "src/main.rs".to_string(),
36 "src/lib.rs".to_string(),
37 "tests/".to_string(),
38 ];
39
40 let on_select = with!(selected_item => move |idx: usize| {
41 selected_item.set(idx);
42 });
43
44 let detail_text = match selected_item.get() {
45 0 => "# README\n\nThis is a demo of split panes.\n\nThe left panel shows a file list,\nthe right panel shows details.",
46 1 => "[package]\nname = \"demo\"\nversion = \"0.1.0\"\nedition = \"2021\"",
47 2 => "Directory: src/\n\nContains the main source files.",
48 3 => "fn main() {\n println!(\"Hello, world!\");\n}",
49 4 => "pub mod utils;\npub mod widgets;",
50 5 => "Directory: tests/\n\nContains integration tests.",
51 _ => "Select an item to see details.",
52 };
53
54 // Horizontal split: file list on left, details on right
55 View::vstack()
56 .child(
57 View::boxed()
58 .flex(1)
59 .child(
60 View::split()
61 .horizontal()
62 .ratio(0.3)
63 .min_first(15)
64 .first(
65 View::vstack()
66 .child(
67 View::styled_text("Files")
68 .color(Color::Cyan)
69 .bold()
70 .build(),
71 )
72 .child(
73 View::list()
74 .items(items)
75 .selected(selected_item.get())
76 .on_select(on_select)
77 .build(),
78 )
79 .child(View::gap(1))
80 .build(),
81 )
82 .second(
83 View::vstack()
84 .child(
85 View::styled_text("Details")
86 .color(Color::Green)
87 .bold()
88 .build(),
89 )
90 .child(
91 View::boxed()
92 .border(true)
93 .flex(1)
94 .child(View::text(detail_text))
95 .build(),
96 )
97 .build(),
98 )
99 .build(),
100 )
101 .build(),
102 )
103 .child(
104 View::styled_text("↑↓: select file | F1 help | Ctrl+Q: quit")
105 .dim()
106 .build(),
107 )
108 .child(
109 View::modal()
110 .visible(show_help.get())
111 .title("Example 13: Split Panes")
112 .on_dismiss(with!(show_help => move || show_help.set(false)))
113 .child(
114 View::vstack()
115 .child(View::styled_text("What you're seeing").bold().build())
116 .child(View::text("• Horizontal split: file list / details"))
117 .child(View::text("• ratio(0.3) = 30% left, 70% right"))
118 .child(View::text("• min_first(15) sets minimum pane width"))
119 .child(View::gap(1))
120 .child(View::styled_text("Key concepts").bold().build())
121 .child(View::text("• View::split() creates resizable panes"))
122 .child(View::text("• .horizontal() or .vertical() orientation"))
123 .child(View::text("• .first() and .second() set pane content"))
124 .child(View::text("• Splits can be nested"))
125 .child(View::gap(1))
126 .child(View::styled_text("Try this").bold().build())
127 .child(View::text("• Select different files to see details"))
128 .child(View::text("• Resize terminal to see layout adapt"))
129 .child(View::gap(1))
130 .child(View::styled_text("Next up").bold().build())
131 .child(View::text("→ 14_tabs: tabbed interfaces"))
132 .child(View::gap(1))
133 .child(View::styled_text("Press Escape to close").dim().build())
134 .build(),
135 )
136 .build(),
137 )
138 .build()
139 }examples/06_log_viewer.rs (line 115)
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 // Stream log entries
31 let logs = text_stream!(cx, || {
32 let log_messages = vec![
33 "[INFO] Application started",
34 "[INFO] Loading configuration...",
35 "[OK] Config loaded successfully",
36 "[INFO] Connecting to database...",
37 "[OK] Database connected",
38 "[INFO] Starting web server on :8080",
39 "[OK] Server listening",
40 "[INFO] Processing request GET /api/users",
41 "[OK] Response 200 in 45ms",
42 "[INFO] Processing request POST /api/login",
43 "[OK] Response 200 in 120ms",
44 "[WARN] High memory usage detected: 85%",
45 "[INFO] Running garbage collection...",
46 "[OK] Memory freed: 200MB",
47 "[INFO] Processing request GET /api/data",
48 "[ERROR] Database timeout after 5000ms",
49 "[INFO] Retrying database connection...",
50 "[OK] Database reconnected",
51 "[OK] Response 200 in 5045ms",
52 "[INFO] Scheduled backup starting...",
53 "[OK] Backup completed: 1.2GB",
54 "[INFO] Processing request GET /api/health",
55 "[OK] Response 200 in 12ms",
56 "[INFO] Processing request PUT /api/users/123",
57 "[OK] Response 200 in 89ms",
58 "[INFO] Cache invalidation triggered",
59 "[OK] Cache cleared: 50MB",
60 "[WARN] Slow query detected: 1200ms",
61 "[INFO] Query optimization suggested",
62 "[INFO] Processing request DELETE /api/sessions",
63 "[OK] Response 204 in 34ms",
64 "[INFO] SSL certificate check",
65 "[OK] Certificate valid for 45 days",
66 "[INFO] Processing request POST /api/upload",
67 "[OK] File uploaded: 15MB",
68 "[WARN] Disk usage at 78%",
69 "[INFO] Processing request GET /api/reports",
70 "[OK] Response 200 in 230ms",
71 "[INFO] Metrics exported to monitoring",
72 "[OK] Heartbeat sent successfully",
73 "------- Log stream completed -------",
74 ];
75
76 log_messages.into_iter().map(|msg| {
77 std::thread::sleep(Duration::from_millis(500));
78 format!("{}\n", msg)
79 })
80 });
81
82 let log_text = logs.get();
83 let is_streaming = logs.is_loading();
84
85 // Color the status indicator
86 let status = if is_streaming {
87 View::styled_text(" [LIVE]")
88 .color(Color::Green)
89 .bold()
90 .build()
91 } else {
92 View::styled_text(" [END]").dim().build()
93 };
94
95 View::vstack()
96 .child(
97 View::hstack()
98 .child(
99 View::styled_text("Log Viewer")
100 .color(Color::Cyan)
101 .bold()
102 .build(),
103 )
104 .child(status)
105 .build(),
106 )
107 .child(View::gap(1))
108 .child(
109 View::boxed()
110 .scroll(true)
111 .auto_scroll_bottom(true)
112 .min_height(15)
113 .max_height(15)
114 .child(View::text(&log_text))
115 .build(),
116 )
117 .child(View::gap(1))
118 .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
119 .child(
120 View::modal()
121 .visible(show_help.get())
122 .title("Example 06: Log Viewer")
123 .on_dismiss(with!(show_help => move || show_help.set(false)))
124 .child(
125 View::vstack()
126 .child(View::styled_text("What you're seeing").bold().build())
127 .child(View::text("• text_stream!() macro for accumulating text"))
128 .child(View::text("• Auto-scrolling box that follows new content"))
129 .child(View::text("• Live/End indicator based on stream state"))
130 .child(View::gap(1))
131 .child(View::styled_text("Key concepts").bold().build())
132 .child(View::text("• text_stream! concatenates yielded strings"))
133 .child(View::text(
134 "• auto_scroll_bottom(true) keeps newest visible",
135 ))
136 .child(View::text("• Simulates tailing a log file"))
137 .child(View::gap(1))
138 .child(View::styled_text("Try this").bold().build())
139 .child(View::text("• Watch the [LIVE] indicator change to [END]"))
140 .child(View::text("• Notice auto-scroll keeps up with new entries"))
141 .child(View::gap(1))
142 .child(View::styled_text("Next up").bold().build())
143 .child(View::text("→ 07_file_browser: real filesystem navigation"))
144 .child(View::gap(1))
145 .child(View::styled_text("Press Escape to close").dim().build())
146 .build(),
147 )
148 .build(),
149 )
150 .build()
151 }Additional examples can be found in:
- examples/24_async_data.rs
- examples/25_context.rs
- examples/14_tabs.rs
- examples/11_checkbox.rs
- examples/17_table.rs
- examples/20_menu_bar.rs
- examples/26_radio_buttons.rs
- examples/21_toasts.rs
- examples/03_theme_switcher.rs
- examples/28_shared_state.rs
- examples/27_keyed_state.rs
- examples/22_forms.rs
- examples/23_modal.rs
Trait Implementations§
Source§impl Default for BoxBuilder
impl Default for BoxBuilder
Source§fn default() -> BoxBuilder
fn default() -> BoxBuilder
Returns the “default value” for a type. Read more
Auto Trait Implementations§
impl Freeze for BoxBuilder
impl !RefUnwindSafe for BoxBuilder
impl !Send for BoxBuilder
impl !Sync for BoxBuilder
impl Unpin for BoxBuilder
impl !UnwindSafe for BoxBuilder
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.