Skip to main content

View

Enum View 

Source
pub enum View {
Show 30 variants Text(TextNode), VStack(VStackNode), HStack(HStackNode), Button(ButtonNode), Box(BoxNode), Spacer(SpacerNode), List(ListNode), TextInput(TextInputNode), TextArea(TextAreaNode), Checkbox(CheckboxNode), RadioGroup(RadioGroupNode), Modal(ModalNode), Split(SplitNode), Tabs(TabsNode), Tree(TreeNode), Table(TableNode), ProgressBar(ProgressBarNode), StatusBar(StatusBarNode), CommandPalette(CommandPaletteNode), MenuBar(MenuBarNode), ToastContainer(ToastContainerNode), Form(FormNode), FormField(FormFieldNode), Canvas(CanvasNode), Image(ImageNode), Terminal(TerminalNode), ErrorBoundary(ErrorBoundaryNode), Custom(CustomNode), Slider(SliderNode), Empty,
}
Expand description

The core view type - a node in the UI tree.

Variants§

§

Text(TextNode)

A text node displaying a string.

§

VStack(VStackNode)

A vertical stack of child views.

§

HStack(HStackNode)

A horizontal stack of child views.

§

Button(ButtonNode)

A clickable button.

§

Box(BoxNode)

A container with optional border, padding, and flex sizing.

§

Spacer(SpacerNode)

Flexible space that expands to fill available space.

§

List(ListNode)

A selectable list of items.

§

TextInput(TextInputNode)

A single-line text input.

§

TextArea(TextAreaNode)

A multi-line text area.

§

Checkbox(CheckboxNode)

A checkbox (toggle).

§

RadioGroup(RadioGroupNode)

A group of radio buttons (mutually exclusive options).

§

Modal(ModalNode)

A modal dialog overlay.

§

Split(SplitNode)

A split pane container with two resizable panels.

§

Tabs(TabsNode)

A tabbed interface container.

§

Tree(TreeNode)

A hierarchical tree view.

§

Table(TableNode)

A data table with columns and rows.

§

ProgressBar(ProgressBarNode)

A progress bar showing completion status.

§

StatusBar(StatusBarNode)

A status bar displayed at the bottom of the screen.

§

CommandPalette(CommandPaletteNode)

A command palette overlay for searching and executing commands.

§

MenuBar(MenuBarNode)

A horizontal menu bar with dropdown menus.

§

ToastContainer(ToastContainerNode)

A container for toast notifications.

§

Form(FormNode)

A form container with validation support.

§

FormField(FormFieldNode)

A form field with label and error display.

§

Canvas(CanvasNode)

A pixel-level canvas using Kitty graphics protocol.

§

Image(ImageNode)

An image display using Kitty graphics protocol.

§

Terminal(TerminalNode)

An interactive PTY terminal emulator.

§

ErrorBoundary(ErrorBoundaryNode)

An error boundary that catches panics in its child view.

§

Custom(CustomNode)

A user-defined custom widget.

§

Slider(SliderNode)

A slider for bounded numeric values.

§

Empty

An empty placeholder.

Implementations§

Source§

impl View

Source

pub fn text(content: impl Into<String>) -> Self

Create a text view with the given content.

Examples found in repository?
examples/33_terminal.rs (line 16)
5fn app(cx: Scope) -> View {
6    let terminal = terminal!(cx);
7
8    // Spawn bash on first render
9    if !terminal.is_started() {
10        if let Err(e) = terminal.spawn("bash", &[], 80, 24) {
11            eprintln!("Failed to spawn terminal: {}", e);
12        }
13    }
14
15    View::vstack()
16        .child(View::text("Telex Terminal Demo"))
17        .child(View::text(
18            "Press Ctrl+Shift+[ to escape terminal focus, Tab to navigate",
19        ))
20        .child(View::terminal().handle(terminal).build())
21        .build()
22}
More examples
Hide additional examples
examples/01_hello_world.rs (line 31)
19    fn render(&self, cx: Scope) -> View {
20        let show_help = state!(cx, || false);
21
22        // F1 toggles help
23        cx.use_command(
24            KeyBinding::key(KeyCode::F(1)),
25            with!(show_help => move || show_help.update(|v| *v = !*v)),
26        );
27
28        View::vstack()
29            .child(View::styled_text("Hello World").bold().build())
30            .child(View::gap(1))
31            .child(View::text("Welcome to Telex!"))
32            .child(View::gap(1))
33            .child(
34                View::styled_text("F1 for help • Ctrl+Q to quit")
35                    .dim()
36                    .build(),
37            )
38            .child(
39                View::modal()
40                    .visible(show_help.get())
41                    .title("Example 01: Hello World")
42                    .on_dismiss(with!(show_help => move || show_help.set(false)))
43                    .child(
44                        View::vstack()
45                            .child(View::styled_text("What you're seeing").bold().build())
46                            .child(View::text(
47                                "• Basic app structure with struct + Component trait",
48                            ))
49                            .child(View::text(
50                                "• View::text() and View::styled_text() for display",
51                            ))
52                            .child(View::text("• View::vstack() for vertical layout"))
53                            .child(View::gap(1))
54                            .child(View::styled_text("Key concepts").bold().build())
55                            .child(View::text("• Every Telex app implements Component"))
56                            .child(View::text("• render() returns a View tree"))
57                            .child(View::text("• No state yet - this is purely static"))
58                            .child(View::gap(1))
59                            .child(View::styled_text("Next up").bold().build())
60                            .child(View::text("→ 02_counter: add state and interactivity"))
61                            .child(View::gap(1))
62                            .child(View::styled_text("Press Escape to close").dim().build())
63                            .build(),
64                    )
65                    .build(),
66            )
67            .build()
68    }
examples/30_image.rs (line 45)
30    fn render(&self, cx: Scope) -> View {
31        let show_help = state!(cx, || false);
32
33        // F1 toggles help
34        cx.use_command(
35            KeyBinding::key(KeyCode::F(1)),
36            with!(show_help => move || show_help.update(|v| *v = !*v)),
37        );
38        View::vstack()
39            .spacing(1)
40            .child(
41                View::styled_text("Image Widget Demo (Kitty Graphics)")
42                    .bold()
43                    .build(),
44            )
45            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
46            .child(View::text(""))
47            // Load image from file path
48            .child(View::text("Logo (from file path):"))
49            .child(View::image().file("assets/telex-tui.png").build())
50            .child(View::text(""))
51            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
52            .child(
53                View::modal()
54                    .visible(show_help.get())
55                    .title("Example 30: Image")
56                    .on_dismiss(with!(show_help => move || show_help.set(false)))
57                    .child(
58                        View::vstack()
59                            .child(View::styled_text("What you're seeing").bold().build())
60                            .child(View::text("• Image display via Kitty protocol"))
61                            .child(View::text("• PNG/JPEG/GIF support"))
62                            .child(View::text("• Loaded from file path"))
63                            .child(View::gap(1))
64                            .child(View::styled_text("Key concepts").bold().build())
65                            .child(View::text("• View::image() displays images"))
66                            .child(View::text("• .file(\"path\") loads from disk"))
67                            .child(View::text("• .bytes(data) for embedded images"))
68                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
69                            .child(View::gap(1))
70                            .child(View::styled_text("Try this").bold().build())
71                            .child(View::text("• Run in compatible terminal"))
72                            .child(View::text("• See the Telex logo rendered"))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Next up").bold().build())
75                            .child(View::text("→ 31_animated_canvas: animations"))
76                            .child(View::gap(1))
77                            .child(View::styled_text("Press Escape to close").dim().build())
78                            .build(),
79                    )
80                    .build(),
81            )
82            .build()
83    }
examples/04_timer.rs (line 71)
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        // Stream that yields elapsed seconds
32        let elapsed = stream!(cx, || {
33            (0u64..).inspect(|&s| {
34                if s > 0 {
35                    std::thread::sleep(Duration::from_secs(1));
36                }
37            })
38        });
39
40        let seconds = elapsed.get();
41        let is_running = elapsed.is_loading();
42
43        // Format as MM:SS
44        let minutes = seconds / 60;
45        let secs = seconds % 60;
46        let time_display = format!("{:02}:{:02}", minutes, secs);
47
48        View::vstack()
49            .child(View::styled_text("Timer").color(Color::Cyan).bold().build())
50            .child(View::gap(1))
51            .child(
52                View::hstack()
53                    .child(View::styled_text(&time_display).bold().build())
54                    .child(if is_running {
55                        View::styled_text(" ●").color(Color::Green).build()
56                    } else {
57                        View::styled_text(" ○").dim().build()
58                    })
59                    .build(),
60            )
61            .child(View::gap(1))
62            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
63            .child(
64                View::modal()
65                    .visible(show_help.get())
66                    .title("Example 04: Timer")
67                    .on_dismiss(with!(show_help => move || show_help.set(false)))
68                    .child(
69                        View::vstack()
70                            .child(View::styled_text("What you're seeing").bold().build())
71                            .child(View::text("• stream!() macro for background data"))
72                            .child(View::text("• Auto-updating UI without user input"))
73                            .child(View::text("• Green dot = stream is running"))
74                            .child(View::gap(1))
75                            .child(View::styled_text("Key concepts").bold().build())
76                            .child(View::text("• Streams run in background threads"))
77                            .child(View::text("• Each yielded value triggers a re-render"))
78                            .child(View::text("• is_loading() tells you if stream is active"))
79                            .child(View::gap(1))
80                            .child(View::styled_text("Try this").bold().build())
81                            .child(View::text("• Just watch - the timer ticks automatically"))
82                            .child(View::text("• No button presses needed for updates"))
83                            .child(View::gap(1))
84                            .child(View::styled_text("Next up").bold().build())
85                            .child(View::text("→ 05_todo_list: text input and list management"))
86                            .child(View::gap(1))
87                            .child(View::styled_text("Press Escape to close").dim().build())
88                            .build(),
89                    )
90                    .build(),
91            )
92            .build()
93    }
examples/02_counter.rs (line 35)
19    fn render(&self, cx: Scope) -> View {
20        let count = state!(cx, || 0i32);
21        let show_help = state!(cx, || false);
22
23        let increment = with!(count => move || count.update(|n| *n += 1));
24        let decrement = with!(count => move || count.update(|n| *n -= 1));
25
26        // F1 toggles help
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        View::vstack()
33            .child(View::styled_text("Counter").bold().build())
34            .child(View::gap(1))
35            .child(View::text(format!("Count: {}", count.get())))
36            .child(View::gap(1))
37            .child(
38                View::hstack()
39                    .child(
40                        View::button()
41                            .label("Decrement")
42                            .on_press(decrement)
43                            .build(),
44                    )
45                    .child(View::text(" "))
46                    .child(
47                        View::button()
48                            .label("Increment")
49                            .on_press(increment)
50                            .build(),
51                    )
52                    .build(),
53            )
54            .child(View::gap(1))
55            .child(
56                View::styled_text("Tab to switch • Enter to press • F1 for help • Ctrl+Q to quit")
57                    .dim()
58                    .build(),
59            )
60            .child(
61                View::modal()
62                    .visible(show_help.get())
63                    .title("Example 02: Counter")
64                    .on_dismiss(with!(show_help => move || show_help.set(false)))
65                    .child(
66                        View::vstack()
67                            .child(View::styled_text("What you're seeing").bold().build())
68                            .child(View::text("• state!() macro for reactive state"))
69                            .child(View::text("• View::button() with on_press callbacks"))
70                            .child(View::text(
71                                "• The with!() macro for capturing state in closures",
72                            ))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Key concepts").bold().build())
75                            .child(View::text("• State persists across renders"))
76                            .child(View::text("• Updating state triggers a re-render"))
77                            .child(View::text("• Tab navigates between focusable elements"))
78                            .child(View::gap(1))
79                            .child(View::styled_text("Try this").bold().build())
80                            .child(View::text("• Press +/- rapidly - notice instant updates"))
81                            .child(View::text(
82                                "• The UI stays in sync with state automatically",
83                            ))
84                            .child(View::gap(1))
85                            .child(View::styled_text("Next up").bold().build())
86                            .child(View::text("→ 03_theme_switcher: styling and colors"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Press Escape to close").dim().build())
89                            .build(),
90                    )
91                    .build(),
92            )
93            .build()
94    }
examples/19_status_bar.rs (line 34)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        // F1 toggles help
28        cx.use_command(
29            KeyBinding::key(KeyCode::F(1)),
30            with!(show_help => move || show_help.update(|v| *v = !*v)),
31        );
32        View::vstack()
33            .child(View::styled_text("Status Bar Examples").bold().build())
34            .child(View::text(""))
35            .child(View::text("Basic status bar (left only):"))
36            .child(View::status_bar().left("NORMAL").build())
37            .child(View::text(""))
38            .child(View::text("Left and right sections:"))
39            .child(
40                View::status_bar()
41                    .left("INSERT")
42                    .right("Ln 42, Col 8")
43                    .build(),
44            )
45            .child(View::text(""))
46            .child(View::text("All three sections:"))
47            .child(
48                View::status_bar()
49                    .left("VISUAL")
50                    .center("main.rs")
51                    .right("UTF-8 | LF | Rust")
52                    .build(),
53            )
54            .child(View::text(""))
55            .child(View::text("Custom colors (green on dark):"))
56            .child(
57                View::status_bar()
58                    .left("SUCCESS")
59                    .center("All tests passed")
60                    .right("100%")
61                    .fg(Color::Green)
62                    .bg(Color::DarkGreen)
63                    .build(),
64            )
65            .child(View::text(""))
66            .child(View::text("Editor-style status bar:"))
67            .child(
68                View::status_bar()
69                    .left("-- INSERT --")
70                    .center("~/projects/myapp/src/main.rs [+]")
71                    .right("1/100 | 50%")
72                    .build(),
73            )
74            .child(View::spacer())
75            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
76            .child(
77                View::modal()
78                    .visible(show_help.get())
79                    .title("Example 19: Status Bar")
80                    .on_dismiss(with!(show_help => move || show_help.set(false)))
81                    .child(
82                        View::vstack()
83                            .child(View::styled_text("What you're seeing").bold().build())
84                            .child(View::text("• Status bars with left/center/right sections"))
85                            .child(View::text("• Custom foreground and background colors"))
86                            .child(View::text("• Editor-style status line example"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Key concepts").bold().build())
89                            .child(View::text("• View::status_bar() creates status lines"))
90                            .child(View::text("• .left(), .center(), .right() for sections"))
91                            .child(View::text("• .fg() and .bg() for custom colors"))
92                            .child(View::text("• Great for showing mode, file info, etc."))
93                            .child(View::gap(1))
94                            .child(View::styled_text("Try this").bold().build())
95                            .child(View::text("• Compare different status bar styles"))
96                            .child(View::text("• Notice how sections align"))
97                            .child(View::gap(1))
98                            .child(View::styled_text("Next up").bold().build())
99                            .child(View::text("→ 20_menu_bar: dropdown menus"))
100                            .child(View::gap(1))
101                            .child(View::styled_text("Press Escape to close").dim().build())
102                            .build(),
103                    )
104                    .build(),
105            )
106            .build()
107    }
Source

pub fn styled_text(content: impl Into<String>) -> TextBuilder

Create a styled text builder.

Examples found in repository?
examples/01_hello_world.rs (line 29)
19    fn render(&self, cx: Scope) -> View {
20        let show_help = state!(cx, || false);
21
22        // F1 toggles help
23        cx.use_command(
24            KeyBinding::key(KeyCode::F(1)),
25            with!(show_help => move || show_help.update(|v| *v = !*v)),
26        );
27
28        View::vstack()
29            .child(View::styled_text("Hello World").bold().build())
30            .child(View::gap(1))
31            .child(View::text("Welcome to Telex!"))
32            .child(View::gap(1))
33            .child(
34                View::styled_text("F1 for help • Ctrl+Q to quit")
35                    .dim()
36                    .build(),
37            )
38            .child(
39                View::modal()
40                    .visible(show_help.get())
41                    .title("Example 01: Hello World")
42                    .on_dismiss(with!(show_help => move || show_help.set(false)))
43                    .child(
44                        View::vstack()
45                            .child(View::styled_text("What you're seeing").bold().build())
46                            .child(View::text(
47                                "• Basic app structure with struct + Component trait",
48                            ))
49                            .child(View::text(
50                                "• View::text() and View::styled_text() for display",
51                            ))
52                            .child(View::text("• View::vstack() for vertical layout"))
53                            .child(View::gap(1))
54                            .child(View::styled_text("Key concepts").bold().build())
55                            .child(View::text("• Every Telex app implements Component"))
56                            .child(View::text("• render() returns a View tree"))
57                            .child(View::text("• No state yet - this is purely static"))
58                            .child(View::gap(1))
59                            .child(View::styled_text("Next up").bold().build())
60                            .child(View::text("→ 02_counter: add state and interactivity"))
61                            .child(View::gap(1))
62                            .child(View::styled_text("Press Escape to close").dim().build())
63                            .build(),
64                    )
65                    .build(),
66            )
67            .build()
68    }
More examples
Hide additional examples
examples/30_image.rs (line 41)
30    fn render(&self, cx: Scope) -> View {
31        let show_help = state!(cx, || false);
32
33        // F1 toggles help
34        cx.use_command(
35            KeyBinding::key(KeyCode::F(1)),
36            with!(show_help => move || show_help.update(|v| *v = !*v)),
37        );
38        View::vstack()
39            .spacing(1)
40            .child(
41                View::styled_text("Image Widget Demo (Kitty Graphics)")
42                    .bold()
43                    .build(),
44            )
45            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
46            .child(View::text(""))
47            // Load image from file path
48            .child(View::text("Logo (from file path):"))
49            .child(View::image().file("assets/telex-tui.png").build())
50            .child(View::text(""))
51            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
52            .child(
53                View::modal()
54                    .visible(show_help.get())
55                    .title("Example 30: Image")
56                    .on_dismiss(with!(show_help => move || show_help.set(false)))
57                    .child(
58                        View::vstack()
59                            .child(View::styled_text("What you're seeing").bold().build())
60                            .child(View::text("• Image display via Kitty protocol"))
61                            .child(View::text("• PNG/JPEG/GIF support"))
62                            .child(View::text("• Loaded from file path"))
63                            .child(View::gap(1))
64                            .child(View::styled_text("Key concepts").bold().build())
65                            .child(View::text("• View::image() displays images"))
66                            .child(View::text("• .file(\"path\") loads from disk"))
67                            .child(View::text("• .bytes(data) for embedded images"))
68                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
69                            .child(View::gap(1))
70                            .child(View::styled_text("Try this").bold().build())
71                            .child(View::text("• Run in compatible terminal"))
72                            .child(View::text("• See the Telex logo rendered"))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Next up").bold().build())
75                            .child(View::text("→ 31_animated_canvas: animations"))
76                            .child(View::gap(1))
77                            .child(View::styled_text("Press Escape to close").dim().build())
78                            .build(),
79                    )
80                    .build(),
81            )
82            .build()
83    }
examples/04_timer.rs (line 49)
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        // Stream that yields elapsed seconds
32        let elapsed = stream!(cx, || {
33            (0u64..).inspect(|&s| {
34                if s > 0 {
35                    std::thread::sleep(Duration::from_secs(1));
36                }
37            })
38        });
39
40        let seconds = elapsed.get();
41        let is_running = elapsed.is_loading();
42
43        // Format as MM:SS
44        let minutes = seconds / 60;
45        let secs = seconds % 60;
46        let time_display = format!("{:02}:{:02}", minutes, secs);
47
48        View::vstack()
49            .child(View::styled_text("Timer").color(Color::Cyan).bold().build())
50            .child(View::gap(1))
51            .child(
52                View::hstack()
53                    .child(View::styled_text(&time_display).bold().build())
54                    .child(if is_running {
55                        View::styled_text(" ●").color(Color::Green).build()
56                    } else {
57                        View::styled_text(" ○").dim().build()
58                    })
59                    .build(),
60            )
61            .child(View::gap(1))
62            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
63            .child(
64                View::modal()
65                    .visible(show_help.get())
66                    .title("Example 04: Timer")
67                    .on_dismiss(with!(show_help => move || show_help.set(false)))
68                    .child(
69                        View::vstack()
70                            .child(View::styled_text("What you're seeing").bold().build())
71                            .child(View::text("• stream!() macro for background data"))
72                            .child(View::text("• Auto-updating UI without user input"))
73                            .child(View::text("• Green dot = stream is running"))
74                            .child(View::gap(1))
75                            .child(View::styled_text("Key concepts").bold().build())
76                            .child(View::text("• Streams run in background threads"))
77                            .child(View::text("• Each yielded value triggers a re-render"))
78                            .child(View::text("• is_loading() tells you if stream is active"))
79                            .child(View::gap(1))
80                            .child(View::styled_text("Try this").bold().build())
81                            .child(View::text("• Just watch - the timer ticks automatically"))
82                            .child(View::text("• No button presses needed for updates"))
83                            .child(View::gap(1))
84                            .child(View::styled_text("Next up").bold().build())
85                            .child(View::text("→ 05_todo_list: text input and list management"))
86                            .child(View::gap(1))
87                            .child(View::styled_text("Press Escape to close").dim().build())
88                            .build(),
89                    )
90                    .build(),
91            )
92            .build()
93    }
examples/02_counter.rs (line 33)
19    fn render(&self, cx: Scope) -> View {
20        let count = state!(cx, || 0i32);
21        let show_help = state!(cx, || false);
22
23        let increment = with!(count => move || count.update(|n| *n += 1));
24        let decrement = with!(count => move || count.update(|n| *n -= 1));
25
26        // F1 toggles help
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        View::vstack()
33            .child(View::styled_text("Counter").bold().build())
34            .child(View::gap(1))
35            .child(View::text(format!("Count: {}", count.get())))
36            .child(View::gap(1))
37            .child(
38                View::hstack()
39                    .child(
40                        View::button()
41                            .label("Decrement")
42                            .on_press(decrement)
43                            .build(),
44                    )
45                    .child(View::text(" "))
46                    .child(
47                        View::button()
48                            .label("Increment")
49                            .on_press(increment)
50                            .build(),
51                    )
52                    .build(),
53            )
54            .child(View::gap(1))
55            .child(
56                View::styled_text("Tab to switch • Enter to press • F1 for help • Ctrl+Q to quit")
57                    .dim()
58                    .build(),
59            )
60            .child(
61                View::modal()
62                    .visible(show_help.get())
63                    .title("Example 02: Counter")
64                    .on_dismiss(with!(show_help => move || show_help.set(false)))
65                    .child(
66                        View::vstack()
67                            .child(View::styled_text("What you're seeing").bold().build())
68                            .child(View::text("• state!() macro for reactive state"))
69                            .child(View::text("• View::button() with on_press callbacks"))
70                            .child(View::text(
71                                "• The with!() macro for capturing state in closures",
72                            ))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Key concepts").bold().build())
75                            .child(View::text("• State persists across renders"))
76                            .child(View::text("• Updating state triggers a re-render"))
77                            .child(View::text("• Tab navigates between focusable elements"))
78                            .child(View::gap(1))
79                            .child(View::styled_text("Try this").bold().build())
80                            .child(View::text("• Press +/- rapidly - notice instant updates"))
81                            .child(View::text(
82                                "• The UI stays in sync with state automatically",
83                            ))
84                            .child(View::gap(1))
85                            .child(View::styled_text("Next up").bold().build())
86                            .child(View::text("→ 03_theme_switcher: styling and colors"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Press Escape to close").dim().build())
89                            .build(),
90                    )
91                    .build(),
92            )
93            .build()
94    }
examples/19_status_bar.rs (line 33)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        // F1 toggles help
28        cx.use_command(
29            KeyBinding::key(KeyCode::F(1)),
30            with!(show_help => move || show_help.update(|v| *v = !*v)),
31        );
32        View::vstack()
33            .child(View::styled_text("Status Bar Examples").bold().build())
34            .child(View::text(""))
35            .child(View::text("Basic status bar (left only):"))
36            .child(View::status_bar().left("NORMAL").build())
37            .child(View::text(""))
38            .child(View::text("Left and right sections:"))
39            .child(
40                View::status_bar()
41                    .left("INSERT")
42                    .right("Ln 42, Col 8")
43                    .build(),
44            )
45            .child(View::text(""))
46            .child(View::text("All three sections:"))
47            .child(
48                View::status_bar()
49                    .left("VISUAL")
50                    .center("main.rs")
51                    .right("UTF-8 | LF | Rust")
52                    .build(),
53            )
54            .child(View::text(""))
55            .child(View::text("Custom colors (green on dark):"))
56            .child(
57                View::status_bar()
58                    .left("SUCCESS")
59                    .center("All tests passed")
60                    .right("100%")
61                    .fg(Color::Green)
62                    .bg(Color::DarkGreen)
63                    .build(),
64            )
65            .child(View::text(""))
66            .child(View::text("Editor-style status bar:"))
67            .child(
68                View::status_bar()
69                    .left("-- INSERT --")
70                    .center("~/projects/myapp/src/main.rs [+]")
71                    .right("1/100 | 50%")
72                    .build(),
73            )
74            .child(View::spacer())
75            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
76            .child(
77                View::modal()
78                    .visible(show_help.get())
79                    .title("Example 19: Status Bar")
80                    .on_dismiss(with!(show_help => move || show_help.set(false)))
81                    .child(
82                        View::vstack()
83                            .child(View::styled_text("What you're seeing").bold().build())
84                            .child(View::text("• Status bars with left/center/right sections"))
85                            .child(View::text("• Custom foreground and background colors"))
86                            .child(View::text("• Editor-style status line example"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Key concepts").bold().build())
89                            .child(View::text("• View::status_bar() creates status lines"))
90                            .child(View::text("• .left(), .center(), .right() for sections"))
91                            .child(View::text("• .fg() and .bg() for custom colors"))
92                            .child(View::text("• Great for showing mode, file info, etc."))
93                            .child(View::gap(1))
94                            .child(View::styled_text("Try this").bold().build())
95                            .child(View::text("• Compare different status bar styles"))
96                            .child(View::text("• Notice how sections align"))
97                            .child(View::gap(1))
98                            .child(View::styled_text("Next up").bold().build())
99                            .child(View::text("→ 20_menu_bar: dropdown menus"))
100                            .child(View::gap(1))
101                            .child(View::styled_text("Press Escape to close").dim().build())
102                            .build(),
103                    )
104                    .build(),
105            )
106            .build()
107    }
examples/09_syntax_comparison.rs (line 45)
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    }
120}
121
122// =============================================================================
123// Builder Pattern
124// =============================================================================
125// Rust-native, explicit, IDE-friendly. Each method call is clear.
126// Good for: Complex conditional logic, learning the API, debugging.
127
128fn counter_builder(cx: Scope) -> View {
129    let count = state!(cx, || 0i32);
130
131    // with! macro clones the handle for you - much cleaner!
132    let increment = with!(count => move || count.update(|n| *n += 1));
133    let decrement = with!(count => move || count.update(|n| *n -= 1));
134
135    View::vstack()
136        .child(
137            View::styled_text("// Built with builder pattern")
138                .color(Color::DarkGrey)
139                .build(),
140        )
141        .child(View::gap(1))
142        .child(
143            View::styled_text(format!("Count: {}", count.get()))
144                .bold()
145                .build(),
146        )
147        .child(View::gap(1))
148        .child(
149            View::hstack()
150                .child(View::button().label(" - ").on_press(decrement).build())
151                .child(View::text(" "))
152                .child(View::button().label(" + ").on_press(increment).build())
153                .build(),
154        )
155        .build()
156}
Source

pub fn vstack() -> VStackBuilder

Create a vertical stack builder.

Examples found in repository?
examples/33_terminal.rs (line 15)
5fn app(cx: Scope) -> View {
6    let terminal = terminal!(cx);
7
8    // Spawn bash on first render
9    if !terminal.is_started() {
10        if let Err(e) = terminal.spawn("bash", &[], 80, 24) {
11            eprintln!("Failed to spawn terminal: {}", e);
12        }
13    }
14
15    View::vstack()
16        .child(View::text("Telex Terminal Demo"))
17        .child(View::text(
18            "Press Ctrl+Shift+[ to escape terminal focus, Tab to navigate",
19        ))
20        .child(View::terminal().handle(terminal).build())
21        .build()
22}
More examples
Hide additional examples
examples/01_hello_world.rs (line 28)
19    fn render(&self, cx: Scope) -> View {
20        let show_help = state!(cx, || false);
21
22        // F1 toggles help
23        cx.use_command(
24            KeyBinding::key(KeyCode::F(1)),
25            with!(show_help => move || show_help.update(|v| *v = !*v)),
26        );
27
28        View::vstack()
29            .child(View::styled_text("Hello World").bold().build())
30            .child(View::gap(1))
31            .child(View::text("Welcome to Telex!"))
32            .child(View::gap(1))
33            .child(
34                View::styled_text("F1 for help • Ctrl+Q to quit")
35                    .dim()
36                    .build(),
37            )
38            .child(
39                View::modal()
40                    .visible(show_help.get())
41                    .title("Example 01: Hello World")
42                    .on_dismiss(with!(show_help => move || show_help.set(false)))
43                    .child(
44                        View::vstack()
45                            .child(View::styled_text("What you're seeing").bold().build())
46                            .child(View::text(
47                                "• Basic app structure with struct + Component trait",
48                            ))
49                            .child(View::text(
50                                "• View::text() and View::styled_text() for display",
51                            ))
52                            .child(View::text("• View::vstack() for vertical layout"))
53                            .child(View::gap(1))
54                            .child(View::styled_text("Key concepts").bold().build())
55                            .child(View::text("• Every Telex app implements Component"))
56                            .child(View::text("• render() returns a View tree"))
57                            .child(View::text("• No state yet - this is purely static"))
58                            .child(View::gap(1))
59                            .child(View::styled_text("Next up").bold().build())
60                            .child(View::text("→ 02_counter: add state and interactivity"))
61                            .child(View::gap(1))
62                            .child(View::styled_text("Press Escape to close").dim().build())
63                            .build(),
64                    )
65                    .build(),
66            )
67            .build()
68    }
examples/30_image.rs (line 38)
30    fn render(&self, cx: Scope) -> View {
31        let show_help = state!(cx, || false);
32
33        // F1 toggles help
34        cx.use_command(
35            KeyBinding::key(KeyCode::F(1)),
36            with!(show_help => move || show_help.update(|v| *v = !*v)),
37        );
38        View::vstack()
39            .spacing(1)
40            .child(
41                View::styled_text("Image Widget Demo (Kitty Graphics)")
42                    .bold()
43                    .build(),
44            )
45            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
46            .child(View::text(""))
47            // Load image from file path
48            .child(View::text("Logo (from file path):"))
49            .child(View::image().file("assets/telex-tui.png").build())
50            .child(View::text(""))
51            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
52            .child(
53                View::modal()
54                    .visible(show_help.get())
55                    .title("Example 30: Image")
56                    .on_dismiss(with!(show_help => move || show_help.set(false)))
57                    .child(
58                        View::vstack()
59                            .child(View::styled_text("What you're seeing").bold().build())
60                            .child(View::text("• Image display via Kitty protocol"))
61                            .child(View::text("• PNG/JPEG/GIF support"))
62                            .child(View::text("• Loaded from file path"))
63                            .child(View::gap(1))
64                            .child(View::styled_text("Key concepts").bold().build())
65                            .child(View::text("• View::image() displays images"))
66                            .child(View::text("• .file(\"path\") loads from disk"))
67                            .child(View::text("• .bytes(data) for embedded images"))
68                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
69                            .child(View::gap(1))
70                            .child(View::styled_text("Try this").bold().build())
71                            .child(View::text("• Run in compatible terminal"))
72                            .child(View::text("• See the Telex logo rendered"))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Next up").bold().build())
75                            .child(View::text("→ 31_animated_canvas: animations"))
76                            .child(View::gap(1))
77                            .child(View::styled_text("Press Escape to close").dim().build())
78                            .build(),
79                    )
80                    .build(),
81            )
82            .build()
83    }
examples/04_timer.rs (line 48)
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        // Stream that yields elapsed seconds
32        let elapsed = stream!(cx, || {
33            (0u64..).inspect(|&s| {
34                if s > 0 {
35                    std::thread::sleep(Duration::from_secs(1));
36                }
37            })
38        });
39
40        let seconds = elapsed.get();
41        let is_running = elapsed.is_loading();
42
43        // Format as MM:SS
44        let minutes = seconds / 60;
45        let secs = seconds % 60;
46        let time_display = format!("{:02}:{:02}", minutes, secs);
47
48        View::vstack()
49            .child(View::styled_text("Timer").color(Color::Cyan).bold().build())
50            .child(View::gap(1))
51            .child(
52                View::hstack()
53                    .child(View::styled_text(&time_display).bold().build())
54                    .child(if is_running {
55                        View::styled_text(" ●").color(Color::Green).build()
56                    } else {
57                        View::styled_text(" ○").dim().build()
58                    })
59                    .build(),
60            )
61            .child(View::gap(1))
62            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
63            .child(
64                View::modal()
65                    .visible(show_help.get())
66                    .title("Example 04: Timer")
67                    .on_dismiss(with!(show_help => move || show_help.set(false)))
68                    .child(
69                        View::vstack()
70                            .child(View::styled_text("What you're seeing").bold().build())
71                            .child(View::text("• stream!() macro for background data"))
72                            .child(View::text("• Auto-updating UI without user input"))
73                            .child(View::text("• Green dot = stream is running"))
74                            .child(View::gap(1))
75                            .child(View::styled_text("Key concepts").bold().build())
76                            .child(View::text("• Streams run in background threads"))
77                            .child(View::text("• Each yielded value triggers a re-render"))
78                            .child(View::text("• is_loading() tells you if stream is active"))
79                            .child(View::gap(1))
80                            .child(View::styled_text("Try this").bold().build())
81                            .child(View::text("• Just watch - the timer ticks automatically"))
82                            .child(View::text("• No button presses needed for updates"))
83                            .child(View::gap(1))
84                            .child(View::styled_text("Next up").bold().build())
85                            .child(View::text("→ 05_todo_list: text input and list management"))
86                            .child(View::gap(1))
87                            .child(View::styled_text("Press Escape to close").dim().build())
88                            .build(),
89                    )
90                    .build(),
91            )
92            .build()
93    }
examples/02_counter.rs (line 32)
19    fn render(&self, cx: Scope) -> View {
20        let count = state!(cx, || 0i32);
21        let show_help = state!(cx, || false);
22
23        let increment = with!(count => move || count.update(|n| *n += 1));
24        let decrement = with!(count => move || count.update(|n| *n -= 1));
25
26        // F1 toggles help
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        View::vstack()
33            .child(View::styled_text("Counter").bold().build())
34            .child(View::gap(1))
35            .child(View::text(format!("Count: {}", count.get())))
36            .child(View::gap(1))
37            .child(
38                View::hstack()
39                    .child(
40                        View::button()
41                            .label("Decrement")
42                            .on_press(decrement)
43                            .build(),
44                    )
45                    .child(View::text(" "))
46                    .child(
47                        View::button()
48                            .label("Increment")
49                            .on_press(increment)
50                            .build(),
51                    )
52                    .build(),
53            )
54            .child(View::gap(1))
55            .child(
56                View::styled_text("Tab to switch • Enter to press • F1 for help • Ctrl+Q to quit")
57                    .dim()
58                    .build(),
59            )
60            .child(
61                View::modal()
62                    .visible(show_help.get())
63                    .title("Example 02: Counter")
64                    .on_dismiss(with!(show_help => move || show_help.set(false)))
65                    .child(
66                        View::vstack()
67                            .child(View::styled_text("What you're seeing").bold().build())
68                            .child(View::text("• state!() macro for reactive state"))
69                            .child(View::text("• View::button() with on_press callbacks"))
70                            .child(View::text(
71                                "• The with!() macro for capturing state in closures",
72                            ))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Key concepts").bold().build())
75                            .child(View::text("• State persists across renders"))
76                            .child(View::text("• Updating state triggers a re-render"))
77                            .child(View::text("• Tab navigates between focusable elements"))
78                            .child(View::gap(1))
79                            .child(View::styled_text("Try this").bold().build())
80                            .child(View::text("• Press +/- rapidly - notice instant updates"))
81                            .child(View::text(
82                                "• The UI stays in sync with state automatically",
83                            ))
84                            .child(View::gap(1))
85                            .child(View::styled_text("Next up").bold().build())
86                            .child(View::text("→ 03_theme_switcher: styling and colors"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Press Escape to close").dim().build())
89                            .build(),
90                    )
91                    .build(),
92            )
93            .build()
94    }
examples/19_status_bar.rs (line 32)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        // F1 toggles help
28        cx.use_command(
29            KeyBinding::key(KeyCode::F(1)),
30            with!(show_help => move || show_help.update(|v| *v = !*v)),
31        );
32        View::vstack()
33            .child(View::styled_text("Status Bar Examples").bold().build())
34            .child(View::text(""))
35            .child(View::text("Basic status bar (left only):"))
36            .child(View::status_bar().left("NORMAL").build())
37            .child(View::text(""))
38            .child(View::text("Left and right sections:"))
39            .child(
40                View::status_bar()
41                    .left("INSERT")
42                    .right("Ln 42, Col 8")
43                    .build(),
44            )
45            .child(View::text(""))
46            .child(View::text("All three sections:"))
47            .child(
48                View::status_bar()
49                    .left("VISUAL")
50                    .center("main.rs")
51                    .right("UTF-8 | LF | Rust")
52                    .build(),
53            )
54            .child(View::text(""))
55            .child(View::text("Custom colors (green on dark):"))
56            .child(
57                View::status_bar()
58                    .left("SUCCESS")
59                    .center("All tests passed")
60                    .right("100%")
61                    .fg(Color::Green)
62                    .bg(Color::DarkGreen)
63                    .build(),
64            )
65            .child(View::text(""))
66            .child(View::text("Editor-style status bar:"))
67            .child(
68                View::status_bar()
69                    .left("-- INSERT --")
70                    .center("~/projects/myapp/src/main.rs [+]")
71                    .right("1/100 | 50%")
72                    .build(),
73            )
74            .child(View::spacer())
75            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
76            .child(
77                View::modal()
78                    .visible(show_help.get())
79                    .title("Example 19: Status Bar")
80                    .on_dismiss(with!(show_help => move || show_help.set(false)))
81                    .child(
82                        View::vstack()
83                            .child(View::styled_text("What you're seeing").bold().build())
84                            .child(View::text("• Status bars with left/center/right sections"))
85                            .child(View::text("• Custom foreground and background colors"))
86                            .child(View::text("• Editor-style status line example"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Key concepts").bold().build())
89                            .child(View::text("• View::status_bar() creates status lines"))
90                            .child(View::text("• .left(), .center(), .right() for sections"))
91                            .child(View::text("• .fg() and .bg() for custom colors"))
92                            .child(View::text("• Great for showing mode, file info, etc."))
93                            .child(View::gap(1))
94                            .child(View::styled_text("Try this").bold().build())
95                            .child(View::text("• Compare different status bar styles"))
96                            .child(View::text("• Notice how sections align"))
97                            .child(View::gap(1))
98                            .child(View::styled_text("Next up").bold().build())
99                            .child(View::text("→ 20_menu_bar: dropdown menus"))
100                            .child(View::gap(1))
101                            .child(View::styled_text("Press Escape to close").dim().build())
102                            .build(),
103                    )
104                    .build(),
105            )
106            .build()
107    }
Source

pub fn hstack() -> HStackBuilder

Create a horizontal stack builder.

Examples found in repository?
examples/04_timer.rs (line 52)
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        // Stream that yields elapsed seconds
32        let elapsed = stream!(cx, || {
33            (0u64..).inspect(|&s| {
34                if s > 0 {
35                    std::thread::sleep(Duration::from_secs(1));
36                }
37            })
38        });
39
40        let seconds = elapsed.get();
41        let is_running = elapsed.is_loading();
42
43        // Format as MM:SS
44        let minutes = seconds / 60;
45        let secs = seconds % 60;
46        let time_display = format!("{:02}:{:02}", minutes, secs);
47
48        View::vstack()
49            .child(View::styled_text("Timer").color(Color::Cyan).bold().build())
50            .child(View::gap(1))
51            .child(
52                View::hstack()
53                    .child(View::styled_text(&time_display).bold().build())
54                    .child(if is_running {
55                        View::styled_text(" ●").color(Color::Green).build()
56                    } else {
57                        View::styled_text(" ○").dim().build()
58                    })
59                    .build(),
60            )
61            .child(View::gap(1))
62            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
63            .child(
64                View::modal()
65                    .visible(show_help.get())
66                    .title("Example 04: Timer")
67                    .on_dismiss(with!(show_help => move || show_help.set(false)))
68                    .child(
69                        View::vstack()
70                            .child(View::styled_text("What you're seeing").bold().build())
71                            .child(View::text("• stream!() macro for background data"))
72                            .child(View::text("• Auto-updating UI without user input"))
73                            .child(View::text("• Green dot = stream is running"))
74                            .child(View::gap(1))
75                            .child(View::styled_text("Key concepts").bold().build())
76                            .child(View::text("• Streams run in background threads"))
77                            .child(View::text("• Each yielded value triggers a re-render"))
78                            .child(View::text("• is_loading() tells you if stream is active"))
79                            .child(View::gap(1))
80                            .child(View::styled_text("Try this").bold().build())
81                            .child(View::text("• Just watch - the timer ticks automatically"))
82                            .child(View::text("• No button presses needed for updates"))
83                            .child(View::gap(1))
84                            .child(View::styled_text("Next up").bold().build())
85                            .child(View::text("→ 05_todo_list: text input and list management"))
86                            .child(View::gap(1))
87                            .child(View::styled_text("Press Escape to close").dim().build())
88                            .build(),
89                    )
90                    .build(),
91            )
92            .build()
93    }
More examples
Hide additional examples
examples/02_counter.rs (line 38)
19    fn render(&self, cx: Scope) -> View {
20        let count = state!(cx, || 0i32);
21        let show_help = state!(cx, || false);
22
23        let increment = with!(count => move || count.update(|n| *n += 1));
24        let decrement = with!(count => move || count.update(|n| *n -= 1));
25
26        // F1 toggles help
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        View::vstack()
33            .child(View::styled_text("Counter").bold().build())
34            .child(View::gap(1))
35            .child(View::text(format!("Count: {}", count.get())))
36            .child(View::gap(1))
37            .child(
38                View::hstack()
39                    .child(
40                        View::button()
41                            .label("Decrement")
42                            .on_press(decrement)
43                            .build(),
44                    )
45                    .child(View::text(" "))
46                    .child(
47                        View::button()
48                            .label("Increment")
49                            .on_press(increment)
50                            .build(),
51                    )
52                    .build(),
53            )
54            .child(View::gap(1))
55            .child(
56                View::styled_text("Tab to switch • Enter to press • F1 for help • Ctrl+Q to quit")
57                    .dim()
58                    .build(),
59            )
60            .child(
61                View::modal()
62                    .visible(show_help.get())
63                    .title("Example 02: Counter")
64                    .on_dismiss(with!(show_help => move || show_help.set(false)))
65                    .child(
66                        View::vstack()
67                            .child(View::styled_text("What you're seeing").bold().build())
68                            .child(View::text("• state!() macro for reactive state"))
69                            .child(View::text("• View::button() with on_press callbacks"))
70                            .child(View::text(
71                                "• The with!() macro for capturing state in closures",
72                            ))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Key concepts").bold().build())
75                            .child(View::text("• State persists across renders"))
76                            .child(View::text("• Updating state triggers a re-render"))
77                            .child(View::text("• Tab navigates between focusable elements"))
78                            .child(View::gap(1))
79                            .child(View::styled_text("Try this").bold().build())
80                            .child(View::text("• Press +/- rapidly - notice instant updates"))
81                            .child(View::text(
82                                "• The UI stays in sync with state automatically",
83                            ))
84                            .child(View::gap(1))
85                            .child(View::styled_text("Next up").bold().build())
86                            .child(View::text("→ 03_theme_switcher: styling and colors"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Press Escape to close").dim().build())
89                            .build(),
90                    )
91                    .build(),
92            )
93            .build()
94    }
examples/09_syntax_comparison.rs (line 57)
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    }
120}
121
122// =============================================================================
123// Builder Pattern
124// =============================================================================
125// Rust-native, explicit, IDE-friendly. Each method call is clear.
126// Good for: Complex conditional logic, learning the API, debugging.
127
128fn counter_builder(cx: Scope) -> View {
129    let count = state!(cx, || 0i32);
130
131    // with! macro clones the handle for you - much cleaner!
132    let increment = with!(count => move || count.update(|n| *n += 1));
133    let decrement = with!(count => move || count.update(|n| *n -= 1));
134
135    View::vstack()
136        .child(
137            View::styled_text("// Built with builder pattern")
138                .color(Color::DarkGrey)
139                .build(),
140        )
141        .child(View::gap(1))
142        .child(
143            View::styled_text(format!("Count: {}", count.get()))
144                .bold()
145                .build(),
146        )
147        .child(View::gap(1))
148        .child(
149            View::hstack()
150                .child(View::button().label(" - ").on_press(decrement).build())
151                .child(View::text(" "))
152                .child(View::button().label(" + ").on_press(increment).build())
153                .build(),
154        )
155        .build()
156}
examples/05_todo_list.rs (line 100)
20    fn render(&self, cx: Scope) -> View {
21        let items = state!(cx, || {
22            vec![
23                "Learn Telex".to_string(),
24                "Build something cool".to_string(),
25            ]
26        });
27        let input_value = state!(cx, String::new);
28        let selected = state!(cx, || 0usize);
29        let show_help = state!(cx, || false);
30
31        // F1 toggles help
32        cx.use_command(
33            KeyBinding::key(KeyCode::F(1)),
34            with!(show_help => move || show_help.update(|v| *v = !*v)),
35        );
36
37        // Add new item on submit
38        let on_submit = with!(items, input_value => move || {
39            let text = input_value.get();
40            if !text.is_empty() {
41                items.update(|v| v.push(text));
42                input_value.set(String::new());
43            }
44        });
45
46        // Handle input changes
47        let on_change = with!(input_value => move |text: String| {
48            input_value.set(text);
49        });
50
51        // Delete selected item
52        let on_delete = with!(items, selected => move || {
53            let idx = selected.get();
54            items.update(|v| {
55                if idx < v.len() {
56                    v.remove(idx);
57                    // Adjust selection if needed
58                    if idx > 0 && idx >= v.len() {
59                        selected.set(idx - 1);
60                    }
61                }
62            });
63        });
64
65        // Track selection
66        let on_select = with!(selected => move |idx: usize| {
67            selected.set(idx);
68        });
69
70        let item_count = items.get().len();
71
72        View::vstack()
73            .child(
74                View::styled_text("Todo List")
75                    .color(Color::Cyan)
76                    .bold()
77                    .build(),
78            )
79            .child(View::gap(1))
80            .child(
81                View::text_input()
82                    .value(input_value.get())
83                    .placeholder("Type something to add...")
84                    .on_change(on_change)
85                    .on_submit(on_submit)
86                    .build(),
87            )
88            .child(View::gap(1))
89            .child(if item_count > 0 {
90                View::list()
91                    .items(items.get())
92                    .selected(selected.get())
93                    .on_select(on_select)
94                    .build()
95            } else {
96                View::styled_text("No items yet").dim().build()
97            })
98            .child(View::gap(1))
99            .child(
100                View::hstack()
101                    .child(View::button().label("Delete").on_press(on_delete).build())
102                    .build(),
103            )
104            .child(View::gap(1))
105            .child(
106                View::styled_text("Tab navigate • Enter add/select • F1 help • Ctrl+Q quit")
107                    .dim()
108                    .build(),
109            )
110            .child(
111                View::modal()
112                    .visible(show_help.get())
113                    .title("Example 05: Todo List")
114                    .on_dismiss(with!(show_help => move || show_help.set(false)))
115                    .child(
116                        View::vstack()
117                            .child(View::styled_text("What you're seeing").bold().build())
118                            .child(View::text("• View::text_input() for text entry"))
119                            .child(View::text("• View::list() for displaying items"))
120                            .child(View::text("• on_submit callback for Enter key"))
121                            .child(View::gap(1))
122                            .child(View::styled_text("Key concepts").bold().build())
123                            .child(View::text("• Controlled input: value + on_change"))
124                            .child(View::text("• Vec<String> state for list items"))
125                            .child(View::text("• Conditional rendering (empty state)"))
126                            .child(View::gap(1))
127                            .child(View::styled_text("Try this").bold().build())
128                            .child(View::text("• Type something and press Enter to add"))
129                            .child(View::text("• Use ↑/↓ to select, then Delete button"))
130                            .child(View::text("• Delete all items to see empty state"))
131                            .child(View::gap(1))
132                            .child(View::styled_text("Next up").bold().build())
133                            .child(View::text("→ 06_log_viewer: streaming text content"))
134                            .child(View::gap(1))
135                            .child(View::styled_text("Press Escape to close").dim().build())
136                            .build(),
137                    )
138                    .build(),
139            )
140            .build()
141    }
examples/37_error_boundary.rs (line 63)
38    fn render(&self, cx: Scope) -> View {
39        let show_help = state!(cx, || false);
40
41        cx.use_command(
42            KeyBinding::key(KeyCode::F(1)),
43            with!(show_help => move || show_help.update(|v| *v = !*v)),
44        );
45
46        let count = state!(cx, || 0i32);
47
48        // The panic must happen at render time (inside render_view) so the
49        // error boundary's catch_unwind can catch it. A custom widget defers
50        // execution to the render pass.
51        let risky_view = View::custom(std::rc::Rc::new(std::cell::RefCell::new(RiskyCounter(count.get()))));
52
53        let fallback = View::vstack()
54            .child(View::styled_text("CAUGHT PANIC").color(Color::Red).bold().build())
55            .child(View::text("The child view panicked."))
56            .child(View::text("But the app is still running!"))
57            .build();
58
59        View::vstack()
60            .spacing(1)
61            .child(View::styled_text("Error Boundary Demo").bold().build())
62            .child(
63                View::hstack()
64                    .spacing(2)
65                    .child(
66                        View::vstack()
67                            .spacing(1)
68                            .child(View::styled_text("Protected Panel").bold().build())
69                            .child(
70                                View::error_boundary()
71                                    .child(risky_view)
72                                    .fallback(fallback)
73                                    .build(),
74                            )
75                            .build(),
76                    )
77                    .child(
78                        View::vstack()
79                            .spacing(1)
80                            .child(View::styled_text("How It Works").bold().build())
81                            .child(View::text("The left panel asserts"))
82                            .child(View::text("count < 5. When it hits"))
83                            .child(View::text("5, the error boundary"))
84                            .child(View::text("catches the panic and"))
85                            .child(View::text("renders the fallback."))
86                            .build(),
87                    )
88                    .build(),
89            )
90            .child(
91                View::hstack()
92                    .spacing(1)
93                    .child(
94                        View::button()
95                            .label("[ + Increment ]")
96                            .on_press(with!(count => move || count.update(|n| *n += 1)))
97                            .build(),
98                    )
99                    .child(
100                        View::button()
101                            .label("[ Reset to 0 ]")
102                            .on_press(with!(count => move || count.set(0)))
103                            .build(),
104                    )
105                    .child(View::styled_text(format!("count = {}", count.get())).dim().build())
106                    .build(),
107            )
108            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
109            .child(
110                View::modal()
111                    .visible(show_help.get())
112                    .title("Example 37: Error Boundary")
113                    .on_dismiss(with!(show_help => move || show_help.set(false)))
114                    .child(
115                        View::vstack()
116                            .child(View::styled_text("What you're seeing").bold().build())
117                            .child(View::text("• A counter that panics at 5"))
118                            .child(View::text("• Error boundary catches the panic"))
119                            .child(View::text("• Red fallback replaces the crash"))
120                            .child(View::gap(1))
121                            .child(View::styled_text("Key concepts").bold().build())
122                            .child(View::text("• View::error_boundary()"))
123                            .child(View::text("• .child(risky) .fallback(safe)"))
124                            .child(View::text("• Panics are caught, not propagated"))
125                            .child(View::text("• App keeps running after panic"))
126                            .child(View::gap(1))
127                            .child(View::styled_text("Try this").bold().build())
128                            .child(View::text("• Increment to 5 to trigger panic"))
129                            .child(View::text("• Reset to 0 to recover"))
130                            .child(View::text("• Keep incrementing past 5"))
131                            .child(View::gap(1))
132                            .child(View::styled_text("Next up").bold().build())
133                            .child(View::text("-> 38_custom_widget: Game of Life"))
134                            .child(View::gap(1))
135                            .child(View::styled_text("Press Escape to close").dim().build())
136                            .build(),
137                    )
138                    .build(),
139            )
140            .build()
141    }
examples/38_custom_widget.rs (line 149)
119    fn render(&self, cx: Scope) -> View {
120        let show_help = state!(cx, || false);
121
122        cx.use_command(
123            KeyBinding::key(KeyCode::F(1)),
124            with!(show_help => move || show_help.update(|v| *v = !*v)),
125        );
126
127        let game: State<GameOfLife> = state!(cx, || {
128            let mut g = GameOfLife::new();
129            g.randomize();
130            g
131        });
132        let generation = state!(cx, || 0u64);
133        let playing = state!(cx, || false);
134
135        // Auto-step when playing
136        interval!(cx, Duration::from_millis(150), with!(playing, game, generation => move || {
137            if playing.get() {
138                game.update(|g| g.step());
139                generation.update(|n| *n += 1);
140            }
141        }));
142
143        let widget = Rc::new(RefCell::new(game.get()));
144
145        View::vstack()
146            .spacing(1)
147            .child(View::styled_text("Game of Life").bold().build())
148            .child(
149                View::hstack()
150                    .spacing(1)
151                    .child(View::styled_text(format!("Gen: {}", generation.get())).dim().build())
152                    .child(View::styled_text(format!("Alive: {}", game.get().alive_count())).dim().build())
153                    .child(if playing.get() {
154                        View::styled_text("PLAYING").color(Color::Green).bold().build()
155                    } else {
156                        View::styled_text("PAUSED").color(Color::Yellow).build()
157                    })
158                    .build(),
159            )
160            .child(View::custom(widget))
161            .child(
162                View::hstack()
163                    .spacing(1)
164                    .child(
165                        View::button()
166                            .label("[ Step ]")
167                            .on_press(with!(game, generation => move || {
168                                game.update(|g| g.step());
169                                generation.update(|n| *n += 1);
170                            }))
171                            .build(),
172                    )
173                    .child(
174                        View::button()
175                            .label(if playing.get() { "[ Pause ]" } else { "[ Play ]" })
176                            .on_press(with!(playing => move || playing.update(|p| *p = !*p)))
177                            .build(),
178                    )
179                    .child(
180                        View::button()
181                            .label("[ Randomize ]")
182                            .on_press(with!(game, generation => move || {
183                                game.update(|g| g.randomize());
184                                generation.set(0);
185                            }))
186                            .build(),
187                    )
188                    .child(
189                        View::button()
190                            .label("[ Clear ]")
191                            .on_press(with!(game, generation => move || {
192                                game.update(|g| g.clear());
193                                generation.set(0);
194                            }))
195                            .build(),
196                    )
197                    .build(),
198            )
199            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
200            .child(
201                View::modal()
202                    .visible(show_help.get())
203                    .title("Example 38: Custom Widget")
204                    .on_dismiss(with!(show_help => move || show_help.set(false)))
205                    .child(
206                        View::vstack()
207                            .child(View::styled_text("What you're seeing").bold().build())
208                            .child(View::text("• Conway's Game of Life"))
209                            .child(View::text("• Custom Widget renders the grid"))
210                            .child(View::text("• interval! drives auto-play"))
211                            .child(View::gap(1))
212                            .child(View::styled_text("Key concepts").bold().build())
213                            .child(View::text("• impl Widget for YourStruct"))
214                            .child(View::text("• render(area, buf) draws cells"))
215                            .child(View::text("• height_hint / width_hint for sizing"))
216                            .child(View::text("• View::custom(Rc<RefCell<W>>)"))
217                            .child(View::gap(1))
218                            .child(View::styled_text("Try this").bold().build())
219                            .child(View::text("• Press Play to auto-step"))
220                            .child(View::text("• Step manually one at a time"))
221                            .child(View::text("• Randomize for a new pattern"))
222                            .child(View::text("• Clear then Step to watch"))
223                            .child(View::gap(1))
224                            .child(View::styled_text("Next up").bold().build())
225                            .child(View::text("-> 39_port: bidirectional comms"))
226                            .child(View::gap(1))
227                            .child(View::styled_text("Press Escape to close").dim().build())
228                            .build(),
229                    )
230                    .build(),
231            )
232            .build()
233    }
Source

pub fn button() -> ButtonBuilder

Create a button builder.

Examples found in repository?
examples/02_counter.rs (line 40)
19    fn render(&self, cx: Scope) -> View {
20        let count = state!(cx, || 0i32);
21        let show_help = state!(cx, || false);
22
23        let increment = with!(count => move || count.update(|n| *n += 1));
24        let decrement = with!(count => move || count.update(|n| *n -= 1));
25
26        // F1 toggles help
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        View::vstack()
33            .child(View::styled_text("Counter").bold().build())
34            .child(View::gap(1))
35            .child(View::text(format!("Count: {}", count.get())))
36            .child(View::gap(1))
37            .child(
38                View::hstack()
39                    .child(
40                        View::button()
41                            .label("Decrement")
42                            .on_press(decrement)
43                            .build(),
44                    )
45                    .child(View::text(" "))
46                    .child(
47                        View::button()
48                            .label("Increment")
49                            .on_press(increment)
50                            .build(),
51                    )
52                    .build(),
53            )
54            .child(View::gap(1))
55            .child(
56                View::styled_text("Tab to switch • Enter to press • F1 for help • Ctrl+Q to quit")
57                    .dim()
58                    .build(),
59            )
60            .child(
61                View::modal()
62                    .visible(show_help.get())
63                    .title("Example 02: Counter")
64                    .on_dismiss(with!(show_help => move || show_help.set(false)))
65                    .child(
66                        View::vstack()
67                            .child(View::styled_text("What you're seeing").bold().build())
68                            .child(View::text("• state!() macro for reactive state"))
69                            .child(View::text("• View::button() with on_press callbacks"))
70                            .child(View::text(
71                                "• The with!() macro for capturing state in closures",
72                            ))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Key concepts").bold().build())
75                            .child(View::text("• State persists across renders"))
76                            .child(View::text("• Updating state triggers a re-render"))
77                            .child(View::text("• Tab navigates between focusable elements"))
78                            .child(View::gap(1))
79                            .child(View::styled_text("Try this").bold().build())
80                            .child(View::text("• Press +/- rapidly - notice instant updates"))
81                            .child(View::text(
82                                "• The UI stays in sync with state automatically",
83                            ))
84                            .child(View::gap(1))
85                            .child(View::styled_text("Next up").bold().build())
86                            .child(View::text("→ 03_theme_switcher: styling and colors"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Press Escape to close").dim().build())
89                            .build(),
90                    )
91                    .build(),
92            )
93            .build()
94    }
More examples
Hide additional examples
examples/09_syntax_comparison.rs (line 81)
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    }
120}
121
122// =============================================================================
123// Builder Pattern
124// =============================================================================
125// Rust-native, explicit, IDE-friendly. Each method call is clear.
126// Good for: Complex conditional logic, learning the API, debugging.
127
128fn counter_builder(cx: Scope) -> View {
129    let count = state!(cx, || 0i32);
130
131    // with! macro clones the handle for you - much cleaner!
132    let increment = with!(count => move || count.update(|n| *n += 1));
133    let decrement = with!(count => move || count.update(|n| *n -= 1));
134
135    View::vstack()
136        .child(
137            View::styled_text("// Built with builder pattern")
138                .color(Color::DarkGrey)
139                .build(),
140        )
141        .child(View::gap(1))
142        .child(
143            View::styled_text(format!("Count: {}", count.get()))
144                .bold()
145                .build(),
146        )
147        .child(View::gap(1))
148        .child(
149            View::hstack()
150                .child(View::button().label(" - ").on_press(decrement).build())
151                .child(View::text(" "))
152                .child(View::button().label(" + ").on_press(increment).build())
153                .build(),
154        )
155        .build()
156}
examples/05_todo_list.rs (line 101)
20    fn render(&self, cx: Scope) -> View {
21        let items = state!(cx, || {
22            vec![
23                "Learn Telex".to_string(),
24                "Build something cool".to_string(),
25            ]
26        });
27        let input_value = state!(cx, String::new);
28        let selected = state!(cx, || 0usize);
29        let show_help = state!(cx, || false);
30
31        // F1 toggles help
32        cx.use_command(
33            KeyBinding::key(KeyCode::F(1)),
34            with!(show_help => move || show_help.update(|v| *v = !*v)),
35        );
36
37        // Add new item on submit
38        let on_submit = with!(items, input_value => move || {
39            let text = input_value.get();
40            if !text.is_empty() {
41                items.update(|v| v.push(text));
42                input_value.set(String::new());
43            }
44        });
45
46        // Handle input changes
47        let on_change = with!(input_value => move |text: String| {
48            input_value.set(text);
49        });
50
51        // Delete selected item
52        let on_delete = with!(items, selected => move || {
53            let idx = selected.get();
54            items.update(|v| {
55                if idx < v.len() {
56                    v.remove(idx);
57                    // Adjust selection if needed
58                    if idx > 0 && idx >= v.len() {
59                        selected.set(idx - 1);
60                    }
61                }
62            });
63        });
64
65        // Track selection
66        let on_select = with!(selected => move |idx: usize| {
67            selected.set(idx);
68        });
69
70        let item_count = items.get().len();
71
72        View::vstack()
73            .child(
74                View::styled_text("Todo List")
75                    .color(Color::Cyan)
76                    .bold()
77                    .build(),
78            )
79            .child(View::gap(1))
80            .child(
81                View::text_input()
82                    .value(input_value.get())
83                    .placeholder("Type something to add...")
84                    .on_change(on_change)
85                    .on_submit(on_submit)
86                    .build(),
87            )
88            .child(View::gap(1))
89            .child(if item_count > 0 {
90                View::list()
91                    .items(items.get())
92                    .selected(selected.get())
93                    .on_select(on_select)
94                    .build()
95            } else {
96                View::styled_text("No items yet").dim().build()
97            })
98            .child(View::gap(1))
99            .child(
100                View::hstack()
101                    .child(View::button().label("Delete").on_press(on_delete).build())
102                    .build(),
103            )
104            .child(View::gap(1))
105            .child(
106                View::styled_text("Tab navigate • Enter add/select • F1 help • Ctrl+Q quit")
107                    .dim()
108                    .build(),
109            )
110            .child(
111                View::modal()
112                    .visible(show_help.get())
113                    .title("Example 05: Todo List")
114                    .on_dismiss(with!(show_help => move || show_help.set(false)))
115                    .child(
116                        View::vstack()
117                            .child(View::styled_text("What you're seeing").bold().build())
118                            .child(View::text("• View::text_input() for text entry"))
119                            .child(View::text("• View::list() for displaying items"))
120                            .child(View::text("• on_submit callback for Enter key"))
121                            .child(View::gap(1))
122                            .child(View::styled_text("Key concepts").bold().build())
123                            .child(View::text("• Controlled input: value + on_change"))
124                            .child(View::text("• Vec<String> state for list items"))
125                            .child(View::text("• Conditional rendering (empty state)"))
126                            .child(View::gap(1))
127                            .child(View::styled_text("Try this").bold().build())
128                            .child(View::text("• Type something and press Enter to add"))
129                            .child(View::text("• Use ↑/↓ to select, then Delete button"))
130                            .child(View::text("• Delete all items to see empty state"))
131                            .child(View::gap(1))
132                            .child(View::styled_text("Next up").bold().build())
133                            .child(View::text("→ 06_log_viewer: streaming text content"))
134                            .child(View::gap(1))
135                            .child(View::styled_text("Press Escape to close").dim().build())
136                            .build(),
137                    )
138                    .build(),
139            )
140            .build()
141    }
examples/37_error_boundary.rs (line 94)
38    fn render(&self, cx: Scope) -> View {
39        let show_help = state!(cx, || false);
40
41        cx.use_command(
42            KeyBinding::key(KeyCode::F(1)),
43            with!(show_help => move || show_help.update(|v| *v = !*v)),
44        );
45
46        let count = state!(cx, || 0i32);
47
48        // The panic must happen at render time (inside render_view) so the
49        // error boundary's catch_unwind can catch it. A custom widget defers
50        // execution to the render pass.
51        let risky_view = View::custom(std::rc::Rc::new(std::cell::RefCell::new(RiskyCounter(count.get()))));
52
53        let fallback = View::vstack()
54            .child(View::styled_text("CAUGHT PANIC").color(Color::Red).bold().build())
55            .child(View::text("The child view panicked."))
56            .child(View::text("But the app is still running!"))
57            .build();
58
59        View::vstack()
60            .spacing(1)
61            .child(View::styled_text("Error Boundary Demo").bold().build())
62            .child(
63                View::hstack()
64                    .spacing(2)
65                    .child(
66                        View::vstack()
67                            .spacing(1)
68                            .child(View::styled_text("Protected Panel").bold().build())
69                            .child(
70                                View::error_boundary()
71                                    .child(risky_view)
72                                    .fallback(fallback)
73                                    .build(),
74                            )
75                            .build(),
76                    )
77                    .child(
78                        View::vstack()
79                            .spacing(1)
80                            .child(View::styled_text("How It Works").bold().build())
81                            .child(View::text("The left panel asserts"))
82                            .child(View::text("count < 5. When it hits"))
83                            .child(View::text("5, the error boundary"))
84                            .child(View::text("catches the panic and"))
85                            .child(View::text("renders the fallback."))
86                            .build(),
87                    )
88                    .build(),
89            )
90            .child(
91                View::hstack()
92                    .spacing(1)
93                    .child(
94                        View::button()
95                            .label("[ + Increment ]")
96                            .on_press(with!(count => move || count.update(|n| *n += 1)))
97                            .build(),
98                    )
99                    .child(
100                        View::button()
101                            .label("[ Reset to 0 ]")
102                            .on_press(with!(count => move || count.set(0)))
103                            .build(),
104                    )
105                    .child(View::styled_text(format!("count = {}", count.get())).dim().build())
106                    .build(),
107            )
108            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
109            .child(
110                View::modal()
111                    .visible(show_help.get())
112                    .title("Example 37: Error Boundary")
113                    .on_dismiss(with!(show_help => move || show_help.set(false)))
114                    .child(
115                        View::vstack()
116                            .child(View::styled_text("What you're seeing").bold().build())
117                            .child(View::text("• A counter that panics at 5"))
118                            .child(View::text("• Error boundary catches the panic"))
119                            .child(View::text("• Red fallback replaces the crash"))
120                            .child(View::gap(1))
121                            .child(View::styled_text("Key concepts").bold().build())
122                            .child(View::text("• View::error_boundary()"))
123                            .child(View::text("• .child(risky) .fallback(safe)"))
124                            .child(View::text("• Panics are caught, not propagated"))
125                            .child(View::text("• App keeps running after panic"))
126                            .child(View::gap(1))
127                            .child(View::styled_text("Try this").bold().build())
128                            .child(View::text("• Increment to 5 to trigger panic"))
129                            .child(View::text("• Reset to 0 to recover"))
130                            .child(View::text("• Keep incrementing past 5"))
131                            .child(View::gap(1))
132                            .child(View::styled_text("Next up").bold().build())
133                            .child(View::text("-> 38_custom_widget: Game of Life"))
134                            .child(View::gap(1))
135                            .child(View::styled_text("Press Escape to close").dim().build())
136                            .build(),
137                    )
138                    .build(),
139            )
140            .build()
141    }
examples/38_custom_widget.rs (line 165)
119    fn render(&self, cx: Scope) -> View {
120        let show_help = state!(cx, || false);
121
122        cx.use_command(
123            KeyBinding::key(KeyCode::F(1)),
124            with!(show_help => move || show_help.update(|v| *v = !*v)),
125        );
126
127        let game: State<GameOfLife> = state!(cx, || {
128            let mut g = GameOfLife::new();
129            g.randomize();
130            g
131        });
132        let generation = state!(cx, || 0u64);
133        let playing = state!(cx, || false);
134
135        // Auto-step when playing
136        interval!(cx, Duration::from_millis(150), with!(playing, game, generation => move || {
137            if playing.get() {
138                game.update(|g| g.step());
139                generation.update(|n| *n += 1);
140            }
141        }));
142
143        let widget = Rc::new(RefCell::new(game.get()));
144
145        View::vstack()
146            .spacing(1)
147            .child(View::styled_text("Game of Life").bold().build())
148            .child(
149                View::hstack()
150                    .spacing(1)
151                    .child(View::styled_text(format!("Gen: {}", generation.get())).dim().build())
152                    .child(View::styled_text(format!("Alive: {}", game.get().alive_count())).dim().build())
153                    .child(if playing.get() {
154                        View::styled_text("PLAYING").color(Color::Green).bold().build()
155                    } else {
156                        View::styled_text("PAUSED").color(Color::Yellow).build()
157                    })
158                    .build(),
159            )
160            .child(View::custom(widget))
161            .child(
162                View::hstack()
163                    .spacing(1)
164                    .child(
165                        View::button()
166                            .label("[ Step ]")
167                            .on_press(with!(game, generation => move || {
168                                game.update(|g| g.step());
169                                generation.update(|n| *n += 1);
170                            }))
171                            .build(),
172                    )
173                    .child(
174                        View::button()
175                            .label(if playing.get() { "[ Pause ]" } else { "[ Play ]" })
176                            .on_press(with!(playing => move || playing.update(|p| *p = !*p)))
177                            .build(),
178                    )
179                    .child(
180                        View::button()
181                            .label("[ Randomize ]")
182                            .on_press(with!(game, generation => move || {
183                                game.update(|g| g.randomize());
184                                generation.set(0);
185                            }))
186                            .build(),
187                    )
188                    .child(
189                        View::button()
190                            .label("[ Clear ]")
191                            .on_press(with!(game, generation => move || {
192                                game.update(|g| g.clear());
193                                generation.set(0);
194                            }))
195                            .build(),
196                    )
197                    .build(),
198            )
199            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
200            .child(
201                View::modal()
202                    .visible(show_help.get())
203                    .title("Example 38: Custom Widget")
204                    .on_dismiss(with!(show_help => move || show_help.set(false)))
205                    .child(
206                        View::vstack()
207                            .child(View::styled_text("What you're seeing").bold().build())
208                            .child(View::text("• Conway's Game of Life"))
209                            .child(View::text("• Custom Widget renders the grid"))
210                            .child(View::text("• interval! drives auto-play"))
211                            .child(View::gap(1))
212                            .child(View::styled_text("Key concepts").bold().build())
213                            .child(View::text("• impl Widget for YourStruct"))
214                            .child(View::text("• render(area, buf) draws cells"))
215                            .child(View::text("• height_hint / width_hint for sizing"))
216                            .child(View::text("• View::custom(Rc<RefCell<W>>)"))
217                            .child(View::gap(1))
218                            .child(View::styled_text("Try this").bold().build())
219                            .child(View::text("• Press Play to auto-step"))
220                            .child(View::text("• Step manually one at a time"))
221                            .child(View::text("• Randomize for a new pattern"))
222                            .child(View::text("• Clear then Step to watch"))
223                            .child(View::gap(1))
224                            .child(View::styled_text("Next up").bold().build())
225                            .child(View::text("-> 39_port: bidirectional comms"))
226                            .child(View::gap(1))
227                            .child(View::styled_text("Press Escape to close").dim().build())
228                            .build(),
229                    )
230                    .build(),
231            )
232            .build()
233    }
examples/10_state_explained.rs (line 98)
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    }
Source

pub fn boxed() -> BoxBuilder

Create a box builder.

Examples found in repository?
examples/09_syntax_comparison.rs (line 69)
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
Hide additional examples
examples/15_markdown.rs (line 73)
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 77)
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 65)
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 57)
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 109)
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    }
Source

pub fn spacer() -> Self

Create a spacer with flex factor 1 (expands to fill available space).

Examples found in repository?
examples/19_status_bar.rs (line 74)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        // F1 toggles help
28        cx.use_command(
29            KeyBinding::key(KeyCode::F(1)),
30            with!(show_help => move || show_help.update(|v| *v = !*v)),
31        );
32        View::vstack()
33            .child(View::styled_text("Status Bar Examples").bold().build())
34            .child(View::text(""))
35            .child(View::text("Basic status bar (left only):"))
36            .child(View::status_bar().left("NORMAL").build())
37            .child(View::text(""))
38            .child(View::text("Left and right sections:"))
39            .child(
40                View::status_bar()
41                    .left("INSERT")
42                    .right("Ln 42, Col 8")
43                    .build(),
44            )
45            .child(View::text(""))
46            .child(View::text("All three sections:"))
47            .child(
48                View::status_bar()
49                    .left("VISUAL")
50                    .center("main.rs")
51                    .right("UTF-8 | LF | Rust")
52                    .build(),
53            )
54            .child(View::text(""))
55            .child(View::text("Custom colors (green on dark):"))
56            .child(
57                View::status_bar()
58                    .left("SUCCESS")
59                    .center("All tests passed")
60                    .right("100%")
61                    .fg(Color::Green)
62                    .bg(Color::DarkGreen)
63                    .build(),
64            )
65            .child(View::text(""))
66            .child(View::text("Editor-style status bar:"))
67            .child(
68                View::status_bar()
69                    .left("-- INSERT --")
70                    .center("~/projects/myapp/src/main.rs [+]")
71                    .right("1/100 | 50%")
72                    .build(),
73            )
74            .child(View::spacer())
75            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
76            .child(
77                View::modal()
78                    .visible(show_help.get())
79                    .title("Example 19: Status Bar")
80                    .on_dismiss(with!(show_help => move || show_help.set(false)))
81                    .child(
82                        View::vstack()
83                            .child(View::styled_text("What you're seeing").bold().build())
84                            .child(View::text("• Status bars with left/center/right sections"))
85                            .child(View::text("• Custom foreground and background colors"))
86                            .child(View::text("• Editor-style status line example"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Key concepts").bold().build())
89                            .child(View::text("• View::status_bar() creates status lines"))
90                            .child(View::text("• .left(), .center(), .right() for sections"))
91                            .child(View::text("• .fg() and .bg() for custom colors"))
92                            .child(View::text("• Great for showing mode, file info, etc."))
93                            .child(View::gap(1))
94                            .child(View::styled_text("Try this").bold().build())
95                            .child(View::text("• Compare different status bar styles"))
96                            .child(View::text("• Notice how sections align"))
97                            .child(View::gap(1))
98                            .child(View::styled_text("Next up").bold().build())
99                            .child(View::text("→ 20_menu_bar: dropdown menus"))
100                            .child(View::gap(1))
101                            .child(View::styled_text("Press Escape to close").dim().build())
102                            .build(),
103                    )
104                    .build(),
105            )
106            .build()
107    }
More examples
Hide additional examples
examples/12_text_area.rs (line 87)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23        let theme_idx = state!(cx, || 0usize);
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        // F2 cycles through themes
32        cx.use_command(
33            KeyBinding::key(KeyCode::F(2)),
34            with!(theme_idx => move || {
35                let next = (theme_idx.get() + 1) % 6;
36                theme_idx.set(next);
37                let theme = match next {
38                    0 => Theme::nord(),
39                    1 => Theme::dark(),
40                    2 => Theme::light(),
41                    3 => Theme::dracula(),
42                    4 => Theme::gruvbox_dark(),
43                    _ => Theme::catppuccin_mocha(),
44                };
45                set_theme(theme);
46            }),
47        );
48
49        let content = state!(cx, String::new);
50        let cursor_line = state!(cx, || 0usize);
51        let cursor_col = state!(cx, || 0usize);
52
53        // Track changes and cursor position
54        let on_change = with!(content => move |text: String| {
55            content.set(text);
56        });
57
58        let on_cursor_change = with!(cursor_line, cursor_col => move |line: usize, col: usize| {
59            cursor_line.set(line);
60            cursor_col.set(col);
61        });
62
63        // Calculate stats
64        let text = content.get();
65        let line_count = if text.is_empty() {
66            0
67        } else {
68            text.lines().count()
69        };
70        let char_count = text.chars().count();
71        let word_count = text.split_whitespace().count();
72
73        let theme_name = match theme_idx.get() {
74            0 => "Nord",
75            1 => "Dark",
76            2 => "Light",
77            3 => "Dracula",
78            4 => "Gruvbox Dark",
79            _ => "Catppuccin Mocha",
80        };
81
82        View::vstack()
83            .spacing(1)
84            .child(
85                View::hstack()
86                    .child(View::styled_text("Notes").color(Color::Cyan).bold().build())
87                    .child(View::spacer())
88                    .child(View::styled_text(format!("Theme: {}", theme_name)).dim().build())
89                    .build(),
90            )
91            .child(
92                View::styled_text("A simple multi-line text editor")
93                    .dim()
94                    .build(),
95            )
96            .child(View::gap(1))
97            .child(
98                View::text_area()
99                    .value(content.get())
100                    .placeholder("Start typing your notes here...")
101                    .rows(12)
102                    .cursor_line(cursor_line.get())
103                    .cursor_col(cursor_col.get())
104                    .on_change(on_change)
105                    .on_cursor_change(on_cursor_change)
106                    .build(),
107            )
108            .child(View::gap(1))
109            .child(
110                View::hstack()
111                    .spacing(3)
112                    .child(
113                        View::styled_text(format!("Lines: {}", line_count))
114                            .color(Color::DarkGrey)
115                            .build(),
116                    )
117                    .child(
118                        View::styled_text(format!("Words: {}", word_count))
119                            .color(Color::DarkGrey)
120                            .build(),
121                    )
122                    .child(
123                        View::styled_text(format!("Chars: {}", char_count))
124                            .color(Color::DarkGrey)
125                            .build(),
126                    )
127                    .build(),
128            )
129            .child(View::gap(1))
130            .child(View::styled_text("F1 help • F2 theme • Ctrl+Q quit").dim().build())
131            .child(
132                View::modal()
133                    .visible(show_help.get())
134                    .title("Example 12: TextArea")
135                    .on_dismiss(with!(show_help => move || show_help.set(false)))
136                    .child(
137                        View::vstack()
138                            .child(View::styled_text("What you're seeing").bold().build())
139                            .child(View::text("• Multi-line text editing with TextArea"))
140                            .child(View::text("• Real-time line/word/char counts"))
141                            .child(View::text("• Cursor position tracking"))
142                            .child(View::gap(1))
143                            .child(View::styled_text("Key concepts").bold().build())
144                            .child(View::text("• View::text_area() for multi-line input"))
145                            .child(View::text("• on_change callback for text updates"))
146                            .child(View::text("• on_cursor_change for cursor tracking"))
147                            .child(View::text("• placeholder text when empty"))
148                            .child(View::gap(1))
149                            .child(View::styled_text("Try this").bold().build())
150                            .child(View::text("• Type multiple lines of text"))
151                            .child(View::text("• Watch the stats update in real-time"))
152                            .child(View::text("• Use arrow keys to navigate"))
153                            .child(View::gap(1))
154                            .child(View::styled_text("Next up").bold().build())
155                            .child(View::text("→ 13_split_panes: resizable panel layouts"))
156                            .child(View::gap(1))
157                            .child(View::styled_text("Press Escape to close").dim().build())
158                            .build(),
159                    )
160                    .build(),
161            )
162            .build()
163    }
examples/25_context.rs (line 178)
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}
285
286// Helper function for status bar
287fn render_status_bar(cx: &Scope) -> View {
288    let theme = cx
289        .use_context::<ColorTheme>()
290        .unwrap_or(ColorTheme::Default);
291
292    View::boxed()
293        .border(true)
294        .child(
295            View::hstack()
296                .child(
297                    View::styled_text(format!(" Theme: {} ", theme.name()))
298                        .color(theme.accent())
299                        .build(),
300                )
301                .child(View::spacer())
302                .child(
303                    View::styled_text(" Context values propagate automatically ")
304                        .dim()
305                        .build(),
306                )
307                .build(),
308        )
309        .build()
310}
examples/20_menu_bar.rs (line 155)
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    }
examples/21_toasts.rs (line 155)
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        // Create a toast queue with 3 second default duration
32        let toasts = state!(cx, || ToastQueue::with_duration(Duration::from_secs(3)));
33        let position = state!(cx, || ToastPosition::BottomRight);
34
35        // Buttons to trigger different toast types
36        let show_info = with!(toasts => move || {
37            toasts.get().info("This is an informational message");
38        });
39
40        let show_success = with!(toasts => move || {
41            toasts.get().success("Operation completed successfully!");
42        });
43
44        let show_warning = with!(toasts => move || {
45            toasts.get().warning("Warning: This action cannot be undone");
46        });
47
48        let show_error = with!(toasts => move || {
49            toasts.get().error("Error: Connection failed");
50        });
51
52        let show_long_error = with!(toasts => move || {
53            toasts.get().error_long("Critical Error: Server not responding. Check your network.");
54        });
55
56        let clear_all = with!(toasts => move || {
57            toasts.get().clear();
58        });
59
60        // Position cycling
61        let cycle_position = with!(position => move || {
62            let next = match position.get() {
63                ToastPosition::TopRight => ToastPosition::TopLeft,
64                ToastPosition::TopLeft => ToastPosition::BottomLeft,
65                ToastPosition::BottomLeft => ToastPosition::BottomRight,
66                ToastPosition::BottomRight => ToastPosition::TopRight,
67            };
68            position.set(next);
69        });
70
71        let position_name = match position.get() {
72            ToastPosition::TopRight => "Top Right",
73            ToastPosition::TopLeft => "Top Left",
74            ToastPosition::BottomLeft => "Bottom Left",
75            ToastPosition::BottomRight => "Bottom Right",
76        };
77
78        View::vstack()
79            .spacing(1)
80            .child(
81                // Header
82                View::boxed()
83                    .border(true)
84                    .padding(1)
85                    .child(
86                        View::vstack()
87                            .child(View::styled_text("Toast Notifications Demo").bold().build())
88                            .child(
89                                View::styled_text("Click buttons to show different toast types")
90                                    .dim()
91                                    .build(),
92                            )
93                            .build(),
94                    )
95                    .build(),
96            )
97            .child(
98                // Main content
99                View::boxed()
100                    .flex(1)
101                    .border(true)
102                    .padding(1)
103                    .child(
104                        View::vstack()
105                            .spacing(1)
106                            .child(View::text("Toast Types:"))
107                            .child(
108                                View::hstack()
109                                    .spacing(2)
110                                    .child(View::button().label("Info").on_press(show_info).build())
111                                    .child(
112                                        View::button()
113                                            .label("Success")
114                                            .on_press(show_success)
115                                            .build(),
116                                    )
117                                    .child(
118                                        View::button()
119                                            .label("Warning")
120                                            .on_press(show_warning)
121                                            .build(),
122                                    )
123                                    .child(
124                                        View::button().label("Error").on_press(show_error).build(),
125                                    )
126                                    .build(),
127                            )
128                            .child(View::gap(1))
129                            .child(View::text("Other Actions:"))
130                            .child(
131                                View::hstack()
132                                    .spacing(2)
133                                    .child(
134                                        View::button()
135                                            .label("Long Error")
136                                            .on_press(show_long_error)
137                                            .build(),
138                                    )
139                                    .child(
140                                        View::button()
141                                            .label("Clear All")
142                                            .on_press(clear_all)
143                                            .build(),
144                                    )
145                                    .build(),
146                            )
147                            .child(View::gap(1))
148                            .child(View::text(format!("Position: {}", position_name)))
149                            .child(
150                                View::button()
151                                    .label("Change Position")
152                                    .on_press(cycle_position)
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(
157                                View::styled_text(format!("Active toasts: {}", toasts.get().len()))
158                                    .color(Color::Yellow)
159                                    .build(),
160                            )
161                            .child(
162                                View::styled_text("Toasts auto-dismiss after 3 seconds")
163                                    .dim()
164                                    .build(),
165                            )
166                            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
167                            .build(),
168                    )
169                    .build(),
170            )
171            .child(
172                // Toast container - renders the toast stack
173                View::toast_container()
174                    .from_queue(&toasts.get())
175                    .position(position.get())
176                    .max_visible(5)
177                    .width(40)
178                    .build(),
179            )
180            .child(
181                View::modal()
182                    .visible(show_help.get())
183                    .title("Example 21: Toasts")
184                    .on_dismiss(with!(show_help => move || show_help.set(false)))
185                    .child(
186                        View::vstack()
187                            .child(View::styled_text("What you're seeing").bold().build())
188                            .child(View::text("• Toast notifications in corner"))
189                            .child(View::text("• Auto-dismiss after 3 seconds"))
190                            .child(View::text(
191                                "• Multiple toast types (info/success/warn/error)",
192                            ))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Key concepts").bold().build())
195                            .child(View::text("• ToastQueue manages notifications"))
196                            .child(View::text("• View::toast_container() renders them"))
197                            .child(View::text("• .position() controls corner placement"))
198                            .child(View::text("• .max_visible() limits shown toasts"))
199                            .child(View::gap(1))
200                            .child(View::styled_text("Try this").bold().build())
201                            .child(View::text("• Click different toast type buttons"))
202                            .child(View::text("• Change position to see toasts move"))
203                            .child(View::text("• Spam buttons to stack toasts"))
204                            .child(View::gap(1))
205                            .child(View::styled_text("Next up").bold().build())
206                            .child(View::text("→ 22_forms: form validation"))
207                            .child(View::gap(1))
208                            .child(View::styled_text("Press Escape to close").dim().build())
209                            .build(),
210                    )
211                    .build(),
212            )
213            .build()
214    }
examples/23_modal.rs (line 134)
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        // Modal visibility states
31        let show_confirm = state!(cx, || false);
32        let show_alert = state!(cx, || false);
33        let show_custom = state!(cx, || false);
34
35        // App state that modals can modify
36        let deleted_count = state!(cx, || 0);
37        let custom_input = state!(cx, String::new);
38        let last_action = state!(cx, || "No action yet".to_string());
39
40        // Handlers
41        let open_confirm = with!(show_confirm => move || {
42            show_confirm.set(true);
43        });
44
45        let open_alert = with!(show_alert => move || {
46            show_alert.set(true);
47        });
48
49        let open_custom = with!(show_custom => move || {
50            show_custom.set(true);
51        });
52
53        let on_confirm_yes = with!(show_confirm, deleted_count, last_action => move || {
54            deleted_count.set(deleted_count.get() + 1);
55            last_action.set(format!("Deleted item #{}", deleted_count.get()));
56            show_confirm.set(false);
57        });
58
59        let on_confirm_no = with!(show_confirm, last_action => move || {
60            last_action.set("Cancelled delete".to_string());
61            show_confirm.set(false);
62        });
63
64        let on_alert_dismiss = with!(show_alert, last_action => move || {
65            last_action.set("Dismissed alert".to_string());
66            show_alert.set(false);
67        });
68
69        let on_custom_save = with!(show_custom, custom_input, last_action => move || {
70            let value = custom_input.get();
71            if !value.is_empty() {
72                last_action.set(format!("Saved: {}", value));
73                custom_input.set(String::new());
74            }
75            show_custom.set(false);
76        });
77
78        let on_custom_dismiss = with!(show_custom => move || {
79            show_custom.set(false);
80        });
81
82        View::vstack()
83            .spacing(1)
84            .child(
85                // Header
86                View::boxed()
87                    .border(true)
88                    .padding(1)
89                    .child(
90                        View::vstack()
91                            .child(View::styled_text("Modal Dialogs Demo").bold().build())
92                            .child(
93                                View::styled_text("Click buttons to open different modal types")
94                                    .dim()
95                                    .build(),
96                            )
97                            .build(),
98                    )
99                    .build(),
100            )
101            .child(
102                // Main content
103                View::boxed()
104                    .flex(1)
105                    .border(true)
106                    .padding(1)
107                    .child(
108                        View::vstack()
109                            .spacing(1)
110                            .child(View::text("Modal Types:"))
111                            .child(
112                                View::hstack()
113                                    .spacing(2)
114                                    .child(
115                                        View::button()
116                                            .label("Confirm Dialog")
117                                            .on_press(open_confirm)
118                                            .build(),
119                                    )
120                                    .child(
121                                        View::button()
122                                            .label("Alert Dialog")
123                                            .on_press(open_alert)
124                                            .build(),
125                                    )
126                                    .child(
127                                        View::button()
128                                            .label("Custom Modal")
129                                            .on_press(open_custom)
130                                            .build(),
131                                    )
132                                    .build(),
133                            )
134                            .child(View::spacer())
135                            .child(
136                                View::styled_text(format!(
137                                    "Deleted items: {}",
138                                    deleted_count.get()
139                                ))
140                                .color(Color::Yellow)
141                                .build(),
142                            )
143                            .child(
144                                View::styled_text(format!("Last action: {}", last_action.get()))
145                                    .dim()
146                                    .build(),
147                            )
148                            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
149                            .build(),
150                    )
151                    .build(),
152            )
153            // Help modal
154            .child(
155                View::modal()
156                    .visible(show_help.get())
157                    .title("Example 23: Modal")
158                    .on_dismiss(with!(show_help => move || show_help.set(false)))
159                    .child(
160                        View::vstack()
161                            .child(View::styled_text("What you're seeing").bold().build())
162                            .child(View::text("• Confirm, alert, and custom modals"))
163                            .child(View::text("• Modal focus containment"))
164                            .child(View::text("• Escape to dismiss"))
165                            .child(View::gap(1))
166                            .child(View::styled_text("Key concepts").bold().build())
167                            .child(View::text("• View::modal() creates overlay"))
168                            .child(View::text("• .visible() controls show/hide"))
169                            .child(View::text("• .on_dismiss() handles Escape"))
170                            .child(View::text("• Focus trapped in open modal"))
171                            .child(View::gap(1))
172                            .child(View::styled_text("Try this").bold().build())
173                            .child(View::text("• Open confirm, click Yes/No"))
174                            .child(View::text("• Custom modal has text input"))
175                            .child(View::text("• Press Escape to close modals"))
176                            .child(View::gap(1))
177                            .child(View::styled_text("Next up").bold().build())
178                            .child(View::text("→ 24_async_data: async loading"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Press Escape to close").dim().build())
181                            .build(),
182                    )
183                    .build(),
184            )
185            // Confirm dialog modal
186            .child(
187                View::modal()
188                    .visible(show_confirm.get())
189                    .title("Confirm Delete")
190                    .on_dismiss(on_confirm_no.clone())
191                    .width(40)
192                    .height(30)
193                    .child(
194                        View::vstack()
195                            .spacing(1)
196                            .child(View::text("Are you sure you want to delete this item?"))
197                            .child(View::text("This action cannot be undone."))
198                            .child(View::spacer())
199                            .child(
200                                View::hstack()
201                                    .spacing(2)
202                                    .child(
203                                        View::button()
204                                            .label("Yes, Delete")
205                                            .on_press(on_confirm_yes)
206                                            .build(),
207                                    )
208                                    .child(
209                                        View::button()
210                                            .label("Cancel")
211                                            .on_press(on_confirm_no)
212                                            .build(),
213                                    )
214                                    .build(),
215                            )
216                            .build(),
217                    )
218                    .build(),
219            )
220            // Alert dialog modal
221            .child(
222                View::modal()
223                    .visible(show_alert.get())
224                    .title("Alert")
225                    .on_dismiss(on_alert_dismiss.clone())
226                    .width(50)
227                    .height(25)
228                    .child(
229                        View::vstack()
230                            .spacing(1)
231                            .child(
232                                View::styled_text("Operation completed successfully!")
233                                    .color(Color::Green)
234                                    .build(),
235                            )
236                            .child(View::text("Your changes have been saved."))
237                            .child(View::spacer())
238                            .child(
239                                View::button()
240                                    .label("OK")
241                                    .on_press(on_alert_dismiss)
242                                    .build(),
243                            )
244                            .build(),
245                    )
246                    .build(),
247            )
248            // Custom modal with input
249            .child(
250                View::modal()
251                    .visible(show_custom.get())
252                    .title("Enter Details")
253                    .on_dismiss(on_custom_dismiss.clone())
254                    .width(50)
255                    .height(40)
256                    .child(
257                        View::vstack()
258                            .spacing(1)
259                            .child(View::text("Enter a value:"))
260                            .child(
261                                View::text_input()
262                                    .value(custom_input.get())
263                                    .placeholder("Type something...")
264                                    .on_change(with!(custom_input => move |v: String| {
265                                        custom_input.set(v);
266                                    }))
267                                    .build(),
268                            )
269                            .child(View::spacer())
270                            .child(
271                                View::hstack()
272                                    .spacing(2)
273                                    .child(
274                                        View::button()
275                                            .label("Save")
276                                            .on_press(on_custom_save)
277                                            .build(),
278                                    )
279                                    .child(
280                                        View::button()
281                                            .label("Cancel")
282                                            .on_press(on_custom_dismiss)
283                                            .build(),
284                                    )
285                                    .build(),
286                            )
287                            .build(),
288                    )
289                    .build(),
290            )
291            .build()
292    }
Source

pub fn spacer_flex(flex: u16) -> Self

Create a spacer with a specific flex factor.

Source

pub fn gap(height: u16) -> Self

Create a fixed-height gap (blank lines).

Unlike spacer() which expands to fill space, gap() is a fixed height.

§Example
View::vstack()
    .child(View::text("Header"))
    .child(View::gap(1))  // One blank line
    .child(View::text("Content"))
    .build()
Examples found in repository?
examples/01_hello_world.rs (line 30)
19    fn render(&self, cx: Scope) -> View {
20        let show_help = state!(cx, || false);
21
22        // F1 toggles help
23        cx.use_command(
24            KeyBinding::key(KeyCode::F(1)),
25            with!(show_help => move || show_help.update(|v| *v = !*v)),
26        );
27
28        View::vstack()
29            .child(View::styled_text("Hello World").bold().build())
30            .child(View::gap(1))
31            .child(View::text("Welcome to Telex!"))
32            .child(View::gap(1))
33            .child(
34                View::styled_text("F1 for help • Ctrl+Q to quit")
35                    .dim()
36                    .build(),
37            )
38            .child(
39                View::modal()
40                    .visible(show_help.get())
41                    .title("Example 01: Hello World")
42                    .on_dismiss(with!(show_help => move || show_help.set(false)))
43                    .child(
44                        View::vstack()
45                            .child(View::styled_text("What you're seeing").bold().build())
46                            .child(View::text(
47                                "• Basic app structure with struct + Component trait",
48                            ))
49                            .child(View::text(
50                                "• View::text() and View::styled_text() for display",
51                            ))
52                            .child(View::text("• View::vstack() for vertical layout"))
53                            .child(View::gap(1))
54                            .child(View::styled_text("Key concepts").bold().build())
55                            .child(View::text("• Every Telex app implements Component"))
56                            .child(View::text("• render() returns a View tree"))
57                            .child(View::text("• No state yet - this is purely static"))
58                            .child(View::gap(1))
59                            .child(View::styled_text("Next up").bold().build())
60                            .child(View::text("→ 02_counter: add state and interactivity"))
61                            .child(View::gap(1))
62                            .child(View::styled_text("Press Escape to close").dim().build())
63                            .build(),
64                    )
65                    .build(),
66            )
67            .build()
68    }
More examples
Hide additional examples
examples/30_image.rs (line 63)
30    fn render(&self, cx: Scope) -> View {
31        let show_help = state!(cx, || false);
32
33        // F1 toggles help
34        cx.use_command(
35            KeyBinding::key(KeyCode::F(1)),
36            with!(show_help => move || show_help.update(|v| *v = !*v)),
37        );
38        View::vstack()
39            .spacing(1)
40            .child(
41                View::styled_text("Image Widget Demo (Kitty Graphics)")
42                    .bold()
43                    .build(),
44            )
45            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
46            .child(View::text(""))
47            // Load image from file path
48            .child(View::text("Logo (from file path):"))
49            .child(View::image().file("assets/telex-tui.png").build())
50            .child(View::text(""))
51            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
52            .child(
53                View::modal()
54                    .visible(show_help.get())
55                    .title("Example 30: Image")
56                    .on_dismiss(with!(show_help => move || show_help.set(false)))
57                    .child(
58                        View::vstack()
59                            .child(View::styled_text("What you're seeing").bold().build())
60                            .child(View::text("• Image display via Kitty protocol"))
61                            .child(View::text("• PNG/JPEG/GIF support"))
62                            .child(View::text("• Loaded from file path"))
63                            .child(View::gap(1))
64                            .child(View::styled_text("Key concepts").bold().build())
65                            .child(View::text("• View::image() displays images"))
66                            .child(View::text("• .file(\"path\") loads from disk"))
67                            .child(View::text("• .bytes(data) for embedded images"))
68                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
69                            .child(View::gap(1))
70                            .child(View::styled_text("Try this").bold().build())
71                            .child(View::text("• Run in compatible terminal"))
72                            .child(View::text("• See the Telex logo rendered"))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Next up").bold().build())
75                            .child(View::text("→ 31_animated_canvas: animations"))
76                            .child(View::gap(1))
77                            .child(View::styled_text("Press Escape to close").dim().build())
78                            .build(),
79                    )
80                    .build(),
81            )
82            .build()
83    }
examples/04_timer.rs (line 50)
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        // Stream that yields elapsed seconds
32        let elapsed = stream!(cx, || {
33            (0u64..).inspect(|&s| {
34                if s > 0 {
35                    std::thread::sleep(Duration::from_secs(1));
36                }
37            })
38        });
39
40        let seconds = elapsed.get();
41        let is_running = elapsed.is_loading();
42
43        // Format as MM:SS
44        let minutes = seconds / 60;
45        let secs = seconds % 60;
46        let time_display = format!("{:02}:{:02}", minutes, secs);
47
48        View::vstack()
49            .child(View::styled_text("Timer").color(Color::Cyan).bold().build())
50            .child(View::gap(1))
51            .child(
52                View::hstack()
53                    .child(View::styled_text(&time_display).bold().build())
54                    .child(if is_running {
55                        View::styled_text(" ●").color(Color::Green).build()
56                    } else {
57                        View::styled_text(" ○").dim().build()
58                    })
59                    .build(),
60            )
61            .child(View::gap(1))
62            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
63            .child(
64                View::modal()
65                    .visible(show_help.get())
66                    .title("Example 04: Timer")
67                    .on_dismiss(with!(show_help => move || show_help.set(false)))
68                    .child(
69                        View::vstack()
70                            .child(View::styled_text("What you're seeing").bold().build())
71                            .child(View::text("• stream!() macro for background data"))
72                            .child(View::text("• Auto-updating UI without user input"))
73                            .child(View::text("• Green dot = stream is running"))
74                            .child(View::gap(1))
75                            .child(View::styled_text("Key concepts").bold().build())
76                            .child(View::text("• Streams run in background threads"))
77                            .child(View::text("• Each yielded value triggers a re-render"))
78                            .child(View::text("• is_loading() tells you if stream is active"))
79                            .child(View::gap(1))
80                            .child(View::styled_text("Try this").bold().build())
81                            .child(View::text("• Just watch - the timer ticks automatically"))
82                            .child(View::text("• No button presses needed for updates"))
83                            .child(View::gap(1))
84                            .child(View::styled_text("Next up").bold().build())
85                            .child(View::text("→ 05_todo_list: text input and list management"))
86                            .child(View::gap(1))
87                            .child(View::styled_text("Press Escape to close").dim().build())
88                            .build(),
89                    )
90                    .build(),
91            )
92            .build()
93    }
examples/02_counter.rs (line 34)
19    fn render(&self, cx: Scope) -> View {
20        let count = state!(cx, || 0i32);
21        let show_help = state!(cx, || false);
22
23        let increment = with!(count => move || count.update(|n| *n += 1));
24        let decrement = with!(count => move || count.update(|n| *n -= 1));
25
26        // F1 toggles help
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        View::vstack()
33            .child(View::styled_text("Counter").bold().build())
34            .child(View::gap(1))
35            .child(View::text(format!("Count: {}", count.get())))
36            .child(View::gap(1))
37            .child(
38                View::hstack()
39                    .child(
40                        View::button()
41                            .label("Decrement")
42                            .on_press(decrement)
43                            .build(),
44                    )
45                    .child(View::text(" "))
46                    .child(
47                        View::button()
48                            .label("Increment")
49                            .on_press(increment)
50                            .build(),
51                    )
52                    .build(),
53            )
54            .child(View::gap(1))
55            .child(
56                View::styled_text("Tab to switch • Enter to press • F1 for help • Ctrl+Q to quit")
57                    .dim()
58                    .build(),
59            )
60            .child(
61                View::modal()
62                    .visible(show_help.get())
63                    .title("Example 02: Counter")
64                    .on_dismiss(with!(show_help => move || show_help.set(false)))
65                    .child(
66                        View::vstack()
67                            .child(View::styled_text("What you're seeing").bold().build())
68                            .child(View::text("• state!() macro for reactive state"))
69                            .child(View::text("• View::button() with on_press callbacks"))
70                            .child(View::text(
71                                "• The with!() macro for capturing state in closures",
72                            ))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Key concepts").bold().build())
75                            .child(View::text("• State persists across renders"))
76                            .child(View::text("• Updating state triggers a re-render"))
77                            .child(View::text("• Tab navigates between focusable elements"))
78                            .child(View::gap(1))
79                            .child(View::styled_text("Try this").bold().build())
80                            .child(View::text("• Press +/- rapidly - notice instant updates"))
81                            .child(View::text(
82                                "• The UI stays in sync with state automatically",
83                            ))
84                            .child(View::gap(1))
85                            .child(View::styled_text("Next up").bold().build())
86                            .child(View::text("→ 03_theme_switcher: styling and colors"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Press Escape to close").dim().build())
89                            .build(),
90                    )
91                    .build(),
92            )
93            .build()
94    }
examples/19_status_bar.rs (line 87)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        // F1 toggles help
28        cx.use_command(
29            KeyBinding::key(KeyCode::F(1)),
30            with!(show_help => move || show_help.update(|v| *v = !*v)),
31        );
32        View::vstack()
33            .child(View::styled_text("Status Bar Examples").bold().build())
34            .child(View::text(""))
35            .child(View::text("Basic status bar (left only):"))
36            .child(View::status_bar().left("NORMAL").build())
37            .child(View::text(""))
38            .child(View::text("Left and right sections:"))
39            .child(
40                View::status_bar()
41                    .left("INSERT")
42                    .right("Ln 42, Col 8")
43                    .build(),
44            )
45            .child(View::text(""))
46            .child(View::text("All three sections:"))
47            .child(
48                View::status_bar()
49                    .left("VISUAL")
50                    .center("main.rs")
51                    .right("UTF-8 | LF | Rust")
52                    .build(),
53            )
54            .child(View::text(""))
55            .child(View::text("Custom colors (green on dark):"))
56            .child(
57                View::status_bar()
58                    .left("SUCCESS")
59                    .center("All tests passed")
60                    .right("100%")
61                    .fg(Color::Green)
62                    .bg(Color::DarkGreen)
63                    .build(),
64            )
65            .child(View::text(""))
66            .child(View::text("Editor-style status bar:"))
67            .child(
68                View::status_bar()
69                    .left("-- INSERT --")
70                    .center("~/projects/myapp/src/main.rs [+]")
71                    .right("1/100 | 50%")
72                    .build(),
73            )
74            .child(View::spacer())
75            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
76            .child(
77                View::modal()
78                    .visible(show_help.get())
79                    .title("Example 19: Status Bar")
80                    .on_dismiss(with!(show_help => move || show_help.set(false)))
81                    .child(
82                        View::vstack()
83                            .child(View::styled_text("What you're seeing").bold().build())
84                            .child(View::text("• Status bars with left/center/right sections"))
85                            .child(View::text("• Custom foreground and background colors"))
86                            .child(View::text("• Editor-style status line example"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Key concepts").bold().build())
89                            .child(View::text("• View::status_bar() creates status lines"))
90                            .child(View::text("• .left(), .center(), .right() for sections"))
91                            .child(View::text("• .fg() and .bg() for custom colors"))
92                            .child(View::text("• Great for showing mode, file info, etc."))
93                            .child(View::gap(1))
94                            .child(View::styled_text("Try this").bold().build())
95                            .child(View::text("• Compare different status bar styles"))
96                            .child(View::text("• Notice how sections align"))
97                            .child(View::gap(1))
98                            .child(View::styled_text("Next up").bold().build())
99                            .child(View::text("→ 20_menu_bar: dropdown menus"))
100                            .child(View::gap(1))
101                            .child(View::styled_text("Press Escape to close").dim().build())
102                            .build(),
103                    )
104                    .build(),
105            )
106            .build()
107    }
examples/09_syntax_comparison.rs (line 55)
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    }
120}
121
122// =============================================================================
123// Builder Pattern
124// =============================================================================
125// Rust-native, explicit, IDE-friendly. Each method call is clear.
126// Good for: Complex conditional logic, learning the API, debugging.
127
128fn counter_builder(cx: Scope) -> View {
129    let count = state!(cx, || 0i32);
130
131    // with! macro clones the handle for you - much cleaner!
132    let increment = with!(count => move || count.update(|n| *n += 1));
133    let decrement = with!(count => move || count.update(|n| *n -= 1));
134
135    View::vstack()
136        .child(
137            View::styled_text("// Built with builder pattern")
138                .color(Color::DarkGrey)
139                .build(),
140        )
141        .child(View::gap(1))
142        .child(
143            View::styled_text(format!("Count: {}", count.get()))
144                .bold()
145                .build(),
146        )
147        .child(View::gap(1))
148        .child(
149            View::hstack()
150                .child(View::button().label(" - ").on_press(decrement).build())
151                .child(View::text(" "))
152                .child(View::button().label(" + ").on_press(increment).build())
153                .build(),
154        )
155        .build()
156}
Source

pub fn list() -> ListBuilder

Create a list builder.

Examples found in repository?
examples/05_todo_list.rs (line 90)
20    fn render(&self, cx: Scope) -> View {
21        let items = state!(cx, || {
22            vec![
23                "Learn Telex".to_string(),
24                "Build something cool".to_string(),
25            ]
26        });
27        let input_value = state!(cx, String::new);
28        let selected = state!(cx, || 0usize);
29        let show_help = state!(cx, || false);
30
31        // F1 toggles help
32        cx.use_command(
33            KeyBinding::key(KeyCode::F(1)),
34            with!(show_help => move || show_help.update(|v| *v = !*v)),
35        );
36
37        // Add new item on submit
38        let on_submit = with!(items, input_value => move || {
39            let text = input_value.get();
40            if !text.is_empty() {
41                items.update(|v| v.push(text));
42                input_value.set(String::new());
43            }
44        });
45
46        // Handle input changes
47        let on_change = with!(input_value => move |text: String| {
48            input_value.set(text);
49        });
50
51        // Delete selected item
52        let on_delete = with!(items, selected => move || {
53            let idx = selected.get();
54            items.update(|v| {
55                if idx < v.len() {
56                    v.remove(idx);
57                    // Adjust selection if needed
58                    if idx > 0 && idx >= v.len() {
59                        selected.set(idx - 1);
60                    }
61                }
62            });
63        });
64
65        // Track selection
66        let on_select = with!(selected => move |idx: usize| {
67            selected.set(idx);
68        });
69
70        let item_count = items.get().len();
71
72        View::vstack()
73            .child(
74                View::styled_text("Todo List")
75                    .color(Color::Cyan)
76                    .bold()
77                    .build(),
78            )
79            .child(View::gap(1))
80            .child(
81                View::text_input()
82                    .value(input_value.get())
83                    .placeholder("Type something to add...")
84                    .on_change(on_change)
85                    .on_submit(on_submit)
86                    .build(),
87            )
88            .child(View::gap(1))
89            .child(if item_count > 0 {
90                View::list()
91                    .items(items.get())
92                    .selected(selected.get())
93                    .on_select(on_select)
94                    .build()
95            } else {
96                View::styled_text("No items yet").dim().build()
97            })
98            .child(View::gap(1))
99            .child(
100                View::hstack()
101                    .child(View::button().label("Delete").on_press(on_delete).build())
102                    .build(),
103            )
104            .child(View::gap(1))
105            .child(
106                View::styled_text("Tab navigate • Enter add/select • F1 help • Ctrl+Q quit")
107                    .dim()
108                    .build(),
109            )
110            .child(
111                View::modal()
112                    .visible(show_help.get())
113                    .title("Example 05: Todo List")
114                    .on_dismiss(with!(show_help => move || show_help.set(false)))
115                    .child(
116                        View::vstack()
117                            .child(View::styled_text("What you're seeing").bold().build())
118                            .child(View::text("• View::text_input() for text entry"))
119                            .child(View::text("• View::list() for displaying items"))
120                            .child(View::text("• on_submit callback for Enter key"))
121                            .child(View::gap(1))
122                            .child(View::styled_text("Key concepts").bold().build())
123                            .child(View::text("• Controlled input: value + on_change"))
124                            .child(View::text("• Vec<String> state for list items"))
125                            .child(View::text("• Conditional rendering (empty state)"))
126                            .child(View::gap(1))
127                            .child(View::styled_text("Try this").bold().build())
128                            .child(View::text("• Type something and press Enter to add"))
129                            .child(View::text("• Use ↑/↓ to select, then Delete button"))
130                            .child(View::text("• Delete all items to see empty state"))
131                            .child(View::gap(1))
132                            .child(View::styled_text("Next up").bold().build())
133                            .child(View::text("→ 06_log_viewer: streaming text content"))
134                            .child(View::gap(1))
135                            .child(View::styled_text("Press Escape to close").dim().build())
136                            .build(),
137                    )
138                    .build(),
139            )
140            .build()
141    }
More examples
Hide additional examples
examples/13_split_panes.rs (line 73)
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/07_file_browser.rs (line 119)
49    fn render(&self, cx: Scope) -> View {
50        let current_path =
51            state!(cx, || std::env::current_dir().unwrap_or_else(|_| PathBuf::from("/")));
52        let selected = state!(cx, || 0usize);
53        let show_file_info = state!(cx, || false);
54        let selected_file_path = state!(cx, String::new);
55        let show_help = state!(cx, || false);
56        let entries = list_directory(&current_path.get());
57
58        // F1 toggles help
59        cx.use_command(
60            KeyBinding::key(KeyCode::F(1)),
61            with!(show_help => move || show_help.update(|v| *v = !*v)),
62        );
63
64        // Track selection (just updates index, doesn't navigate)
65        let on_select = with!(selected => move |idx: usize| {
66            selected.set(idx);
67        });
68
69        // Dismiss modal
70        let on_dismiss = with!(show_file_info => move || show_file_info.set(false));
71
72        // Open directory or show file info on Enter
73        let entries_for_cmd = entries.clone();
74        cx.use_command(
75            KeyBinding::key(KeyCode::Enter),
76            with!(current_path, selected, show_file_info, selected_file_path => move || {
77                let idx = selected.get();
78                if idx < entries_for_cmd.len() {
79                    let entry = &entries_for_cmd[idx];
80                    let path = current_path.get();
81
82                    if entry == ".." {
83                        if let Some(parent) = path.parent() {
84                            current_path.set(parent.to_path_buf());
85                            selected.set(0);
86                        }
87                    } else if entry.ends_with('/') {
88                        let dir_name = entry.trim_end_matches('/');
89                        let new_path = path.join(dir_name);
90                        current_path.set(new_path);
91                        selected.set(0);
92                    } else {
93                        // It's a file - show info modal
94                        let full_path = path.join(entry);
95                        selected_file_path.set(full_path.to_string_lossy().to_string());
96                        show_file_info.set(true);
97                    }
98                }
99            }),
100        );
101
102        let path_display = current_path.get().to_string_lossy().to_string();
103
104        View::vstack()
105            .child(
106                View::styled_text("File Browser")
107                    .color(Color::Cyan)
108                    .bold()
109                    .build(),
110            )
111            .child(View::gap(1))
112            .child(
113                View::styled_text(&path_display)
114                    .color(Color::Yellow)
115                    .build(),
116            )
117            .child(View::gap(1))
118            .child(
119                View::list()
120                    .items(entries)
121                    .selected(selected.get())
122                    .on_select(on_select)
123                    .build(),
124            )
125            .child(View::gap(1))
126            .child(
127                View::styled_text("↑/↓ navigate • Enter open • F1 help • Ctrl+Q quit")
128                    .dim()
129                    .build(),
130            )
131            .child(
132                View::modal()
133                    .visible(show_file_info.get())
134                    .title("File")
135                    .width(60)
136                    .height(20)
137                    .on_dismiss(on_dismiss)
138                    .child(
139                        View::vstack()
140                            .child(View::text(selected_file_path.get()))
141                            .child(View::gap(1))
142                            .child(View::styled_text("Press Escape to close").dim().build())
143                            .build(),
144                    )
145                    .build(),
146            )
147            .child(
148                View::modal()
149                    .visible(show_help.get())
150                    .title("Example 07: File Browser")
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("• Real filesystem navigation"))
156                            .child(View::text("• cx.use_command() for keyboard shortcuts"))
157                            .child(View::text("• Modal for file details"))
158                            .child(View::gap(1))
159                            .child(View::styled_text("Key concepts").bold().build())
160                            .child(View::text("• std::fs::read_dir for directory listing"))
161                            .child(View::text(
162                                "• KeyBinding::key(KeyCode::Enter) for Enter handling",
163                            ))
164                            .child(View::text("• Directories shown with trailing /"))
165                            .child(View::gap(1))
166                            .child(View::styled_text("Try this").bold().build())
167                            .child(View::text("• Navigate into directories with Enter"))
168                            .child(View::text("• Go up with '..' entry"))
169                            .child(View::text("• Select a file to see its path"))
170                            .child(View::gap(1))
171                            .child(View::styled_text("Next up").bold().build())
172                            .child(View::text(
173                                "→ 08_system_monitor: multiple concurrent streams",
174                            ))
175                            .child(View::gap(1))
176                            .child(View::styled_text("Press Escape to close").dim().build())
177                            .build(),
178                    )
179                    .build(),
180            )
181            .build()
182    }
examples/03_theme_switcher.rs (line 114)
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    }
Source

pub fn text_input() -> TextInputBuilder

Create a text input builder.

Examples found in repository?
examples/05_todo_list.rs (line 81)
20    fn render(&self, cx: Scope) -> View {
21        let items = state!(cx, || {
22            vec![
23                "Learn Telex".to_string(),
24                "Build something cool".to_string(),
25            ]
26        });
27        let input_value = state!(cx, String::new);
28        let selected = state!(cx, || 0usize);
29        let show_help = state!(cx, || false);
30
31        // F1 toggles help
32        cx.use_command(
33            KeyBinding::key(KeyCode::F(1)),
34            with!(show_help => move || show_help.update(|v| *v = !*v)),
35        );
36
37        // Add new item on submit
38        let on_submit = with!(items, input_value => move || {
39            let text = input_value.get();
40            if !text.is_empty() {
41                items.update(|v| v.push(text));
42                input_value.set(String::new());
43            }
44        });
45
46        // Handle input changes
47        let on_change = with!(input_value => move |text: String| {
48            input_value.set(text);
49        });
50
51        // Delete selected item
52        let on_delete = with!(items, selected => move || {
53            let idx = selected.get();
54            items.update(|v| {
55                if idx < v.len() {
56                    v.remove(idx);
57                    // Adjust selection if needed
58                    if idx > 0 && idx >= v.len() {
59                        selected.set(idx - 1);
60                    }
61                }
62            });
63        });
64
65        // Track selection
66        let on_select = with!(selected => move |idx: usize| {
67            selected.set(idx);
68        });
69
70        let item_count = items.get().len();
71
72        View::vstack()
73            .child(
74                View::styled_text("Todo List")
75                    .color(Color::Cyan)
76                    .bold()
77                    .build(),
78            )
79            .child(View::gap(1))
80            .child(
81                View::text_input()
82                    .value(input_value.get())
83                    .placeholder("Type something to add...")
84                    .on_change(on_change)
85                    .on_submit(on_submit)
86                    .build(),
87            )
88            .child(View::gap(1))
89            .child(if item_count > 0 {
90                View::list()
91                    .items(items.get())
92                    .selected(selected.get())
93                    .on_select(on_select)
94                    .build()
95            } else {
96                View::styled_text("No items yet").dim().build()
97            })
98            .child(View::gap(1))
99            .child(
100                View::hstack()
101                    .child(View::button().label("Delete").on_press(on_delete).build())
102                    .build(),
103            )
104            .child(View::gap(1))
105            .child(
106                View::styled_text("Tab navigate • Enter add/select • F1 help • Ctrl+Q quit")
107                    .dim()
108                    .build(),
109            )
110            .child(
111                View::modal()
112                    .visible(show_help.get())
113                    .title("Example 05: Todo List")
114                    .on_dismiss(with!(show_help => move || show_help.set(false)))
115                    .child(
116                        View::vstack()
117                            .child(View::styled_text("What you're seeing").bold().build())
118                            .child(View::text("• View::text_input() for text entry"))
119                            .child(View::text("• View::list() for displaying items"))
120                            .child(View::text("• on_submit callback for Enter key"))
121                            .child(View::gap(1))
122                            .child(View::styled_text("Key concepts").bold().build())
123                            .child(View::text("• Controlled input: value + on_change"))
124                            .child(View::text("• Vec<String> state for list items"))
125                            .child(View::text("• Conditional rendering (empty state)"))
126                            .child(View::gap(1))
127                            .child(View::styled_text("Try this").bold().build())
128                            .child(View::text("• Type something and press Enter to add"))
129                            .child(View::text("• Use ↑/↓ to select, then Delete button"))
130                            .child(View::text("• Delete all items to see empty state"))
131                            .child(View::gap(1))
132                            .child(View::styled_text("Next up").bold().build())
133                            .child(View::text("→ 06_log_viewer: streaming text content"))
134                            .child(View::gap(1))
135                            .child(View::styled_text("Press Escape to close").dim().build())
136                            .build(),
137                    )
138                    .build(),
139            )
140            .build()
141    }
More examples
Hide additional examples
examples/32_effects.rs (line 91)
23    fn render(&self, cx: Scope) -> View {
24        let show_help = state!(cx, || false);
25
26        // F1 toggles help
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        let count = state!(cx, || 0);
33        let name = state!(cx, String::new);
34        let last_effect = state!(cx, || String::from("(none yet)"));
35        let init_done = state!(cx, || false);
36
37        // Effect that runs only once - initialization
38        // Using effect_once! macro (order-independent)
39        effect_once!(cx, with!(init_done, last_effect => move || {
40            init_done.set(true);
41            last_effect.set("effect_once!: initialized!".to_string());
42            || {}
43        }));
44
45        // Effect that runs when count changes
46        // Using effect! macro (order-independent)
47        effect!(cx, count.get(), with!(last_effect => move |&val| {
48            last_effect.set(format!("effect!: count → {}", val));
49            || {}
50        }));
51
52        // Effect that runs when name changes
53        effect!(cx, name.get(), with!(last_effect => move |n: &String| {
54            if !n.is_empty() {
55                last_effect.set(format!("effect!: name → \"{}\"", n));
56            }
57            || {}
58        }));
59
60        View::vstack()
61            .spacing(1)
62            .child(View::styled_text("effect! Demo").bold().build())
63            .child(View::text(""))
64            .child(View::text(format!("Counter: {}", count.get())))
65            .child(
66                View::hstack()
67                    .spacing(1)
68                    .child(
69                        View::button()
70                            .label("[ - ]")
71                            .on_press(with!(count => move || count.update(|n| *n -= 1)))
72                            .build(),
73                    )
74                    .child(
75                        View::button()
76                            .label("[ + ]")
77                            .on_press(with!(count => move || count.update(|n| *n += 1)))
78                            .build(),
79                    )
80                    .build(),
81            )
82            .child(View::text(""))
83            .child({
84                let n = name.get();
85                View::text(format!(
86                    "Name: {}",
87                    if n.is_empty() { "(empty)" } else { &n }
88                ))
89            })
90            .child(
91                View::text_input()
92                    .value(name.get())
93                    .placeholder("Type your name...")
94                    .on_change(with!(name => move |s| name.set(s)))
95                    .build(),
96            )
97            .child(View::text(""))
98            .child(View::styled_text("─── Effect Status ───").dim().build())
99            .child(View::text(""))
100            .child(View::text(format!(
101                "Initialized: {}",
102                if init_done.get() { "✓ yes" } else { "no" }
103            )))
104            .child(View::text(format!("Last effect: {}", last_effect.get())))
105            .child(View::text(""))
106            .child(View::styled_text("─── How it works ───").dim().build())
107            .child(View::text(""))
108            .child(View::text("effect_once!  → Ran once at startup"))
109            .child(View::text("effect!       → Runs when deps change"))
110            .child(View::text(""))
111            .child(
112                View::styled_text("Press +/- or type to see effects trigger")
113                    .dim()
114                    .build(),
115            )
116            .child(View::text(""))
117            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
118            .child(
119                View::modal()
120                    .visible(show_help.get())
121                    .title("Example 32: Effects")
122                    .on_dismiss(with!(show_help => move || show_help.set(false)))
123                    .child(
124                        View::vstack()
125                            .child(View::styled_text("What you're seeing").bold().build())
126                            .child(View::text("• effect_once! runs at startup"))
127                            .child(View::text("• effect! runs when deps change"))
128                            .child(View::text("• Last effect shows what triggered"))
129                            .child(View::gap(1))
130                            .child(View::styled_text("Key concepts").bold().build())
131                            .child(View::text("• effect_once!(cx, || { cleanup })"))
132                            .child(View::text("• effect!(cx, deps, |&d| { cleanup })"))
133                            .child(View::text("• Return || {} for cleanup"))
134                            .child(View::text("• Effects run AFTER render"))
135                            .child(View::text("• Safe in conditionals!"))
136                            .child(View::gap(1))
137                            .child(View::styled_text("Try this").bold().build())
138                            .child(View::text("• Click +/- to change counter"))
139                            .child(View::text("• Type in the name field"))
140                            .child(View::text("• Watch 'Last effect' update"))
141                            .child(View::gap(1))
142                            .child(View::styled_text("Press Escape to close").dim().build())
143                            .build(),
144                    )
145                    .build(),
146            )
147            .build()
148    }
examples/36_reducer.rs (line 104)
38    fn render(&self, cx: Scope) -> View {
39        let show_help = state!(cx, || false);
40
41        cx.use_command(
42            KeyBinding::key(KeyCode::F(1)),
43            with!(show_help => move || show_help.update(|v| *v = !*v)),
44        );
45
46        let (wizard, dispatch) = reducer!(cx, WizardState::Welcome, |state: WizardState, action: WizardAction| {
47            match (state, action) {
48                (_, WizardAction::Reset) => WizardState::Welcome,
49                (WizardState::Welcome, WizardAction::Next) => WizardState::Name(String::new()),
50                (WizardState::Name(_), WizardAction::SetName(n)) => WizardState::Name(n),
51                (WizardState::Name(name), WizardAction::Next) => {
52                    let name = if name.is_empty() { "Anonymous".to_string() } else { name };
53                    WizardState::Color(name, String::new())
54                }
55                (WizardState::Name(_), WizardAction::Back) => WizardState::Welcome,
56                (WizardState::Color(name, _), WizardAction::SetColor(c)) => WizardState::Color(name, c),
57                (WizardState::Color(name, color), WizardAction::Next) => {
58                    let color = if color.is_empty() { "Blue".to_string() } else { color };
59                    WizardState::Done(name, color)
60                }
61                (WizardState::Color(_, _), WizardAction::Back) => WizardState::Name(String::new()),
62                (WizardState::Done(_, _), WizardAction::Back) => WizardState::Welcome,
63                (s, _) => s,
64            }
65        });
66
67        let step = match &wizard.get() {
68            WizardState::Welcome => 1,
69            WizardState::Name(_) => 2,
70            WizardState::Color(_, _) => 3,
71            WizardState::Done(_, _) => 4,
72        };
73
74        let progress = format!("Step {} of 4", step);
75        let dots: String = (1..=4)
76            .map(|i| if i <= step { "●" } else { "○" })
77            .collect::<Vec<_>>()
78            .join(" ");
79
80        let content = match &wizard.get() {
81            WizardState::Welcome => {
82                let d = dispatch.clone();
83                View::vstack()
84                    .spacing(1)
85                    .child(View::styled_text("Welcome to the Wizard!").color(Color::Cyan).bold().build())
86                    .child(View::text("This example shows centralized state"))
87                    .child(View::text("management with reducer!"))
88                    .child(
89                        View::button()
90                            .label("[ Start -> ]")
91                            .on_press(move || d(WizardAction::Next))
92                            .build(),
93                    )
94                    .build()
95            }
96            WizardState::Name(name) => {
97                let d1 = dispatch.clone();
98                let d2 = dispatch.clone();
99                let d3 = dispatch.clone();
100                View::vstack()
101                    .spacing(1)
102                    .child(View::styled_text("What's your name?").color(Color::Cyan).bold().build())
103                    .child(
104                        View::text_input()
105                            .value(name.clone())
106                            .placeholder("Enter your name...")
107                            .on_change(move |s: String| d1(WizardAction::SetName(s)))
108                            .build(),
109                    )
110                    .child(
111                        View::hstack()
112                            .spacing(1)
113                            .child(
114                                View::button()
115                                    .label("[ <- Back ]")
116                                    .on_press(move || d2(WizardAction::Back))
117                                    .build(),
118                            )
119                            .child(
120                                View::button()
121                                    .label("[ Next -> ]")
122                                    .on_press(move || d3(WizardAction::Next))
123                                    .build(),
124                            )
125                            .build(),
126                    )
127                    .build()
128            }
129            WizardState::Color(name, color) => {
130                let d1 = dispatch.clone();
131                let d2 = dispatch.clone();
132                let d3 = dispatch.clone();
133                View::vstack()
134                    .spacing(1)
135                    .child(View::styled_text(format!("Hi, {}! Pick a color:", name)).color(Color::Cyan).bold().build())
136                    .child(
137                        View::text_input()
138                            .value(color.clone())
139                            .placeholder("Enter a color (e.g. Blue)...")
140                            .on_change(move |s: String| d1(WizardAction::SetColor(s)))
141                            .build(),
142                    )
143                    .child(
144                        View::hstack()
145                            .spacing(1)
146                            .child(
147                                View::button()
148                                    .label("[ <- Back ]")
149                                    .on_press(move || d2(WizardAction::Back))
150                                    .build(),
151                            )
152                            .child(
153                                View::button()
154                                    .label("[ Finish -> ]")
155                                    .on_press(move || d3(WizardAction::Next))
156                                    .build(),
157                            )
158                            .build(),
159                    )
160                    .build()
161            }
162            WizardState::Done(name, color) => {
163                let d = dispatch.clone();
164                View::vstack()
165                    .spacing(1)
166                    .child(View::styled_text("All done!").color(Color::Green).bold().build())
167                    .child(View::text(format!("Name:  {}", name)))
168                    .child(View::text(format!("Color: {}", color)))
169                    .child(
170                        View::button()
171                            .label("[ Start Over ]")
172                            .on_press(move || d(WizardAction::Reset))
173                            .build(),
174                    )
175                    .build()
176            }
177        };
178
179        View::vstack()
180            .spacing(1)
181            .child(View::styled_text("Reducer Wizard").bold().build())
182            .child(
183                View::hstack()
184                    .spacing(1)
185                    .child(View::styled_text(&progress).dim().build())
186                    .child(View::styled_text(&dots).color(Color::Cyan).build())
187                    .build(),
188            )
189            .child(View::styled_text("────────────────────────").dim().build())
190            .child(content)
191            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
192            .child(
193                View::modal()
194                    .visible(show_help.get())
195                    .title("Example 36: Reducer")
196                    .on_dismiss(with!(show_help => move || show_help.set(false)))
197                    .child(
198                        View::vstack()
199                            .child(View::styled_text("What you're seeing").bold().build())
200                            .child(View::text("• Multi-step wizard state machine"))
201                            .child(View::text("• All transitions in one reducer fn"))
202                            .child(View::text("• No scattered booleans"))
203                            .child(View::gap(1))
204                            .child(View::styled_text("Key concepts").bold().build())
205                            .child(View::text("• reducer!(cx, init, |state, action| ...)"))
206                            .child(View::text("• Returns (State<S>, Rc<dyn Fn(A)>)"))
207                            .child(View::text("• dispatch(action) to transition"))
208                            .child(View::text("• Pattern match (state, action) pairs"))
209                            .child(View::gap(1))
210                            .child(View::styled_text("Try this").bold().build())
211                            .child(View::text("• Walk through all 4 steps"))
212                            .child(View::text("• Go back and change answers"))
213                            .child(View::text("• Watch the progress dots"))
214                            .child(View::gap(1))
215                            .child(View::styled_text("Next up").bold().build())
216                            .child(View::text("-> 37_error_boundary: crash protection"))
217                            .child(View::gap(1))
218                            .child(View::styled_text("Press Escape to close").dim().build())
219                            .build(),
220                    )
221                    .build(),
222            )
223            .build()
224    }
examples/23_modal.rs (line 261)
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        // Modal visibility states
31        let show_confirm = state!(cx, || false);
32        let show_alert = state!(cx, || false);
33        let show_custom = state!(cx, || false);
34
35        // App state that modals can modify
36        let deleted_count = state!(cx, || 0);
37        let custom_input = state!(cx, String::new);
38        let last_action = state!(cx, || "No action yet".to_string());
39
40        // Handlers
41        let open_confirm = with!(show_confirm => move || {
42            show_confirm.set(true);
43        });
44
45        let open_alert = with!(show_alert => move || {
46            show_alert.set(true);
47        });
48
49        let open_custom = with!(show_custom => move || {
50            show_custom.set(true);
51        });
52
53        let on_confirm_yes = with!(show_confirm, deleted_count, last_action => move || {
54            deleted_count.set(deleted_count.get() + 1);
55            last_action.set(format!("Deleted item #{}", deleted_count.get()));
56            show_confirm.set(false);
57        });
58
59        let on_confirm_no = with!(show_confirm, last_action => move || {
60            last_action.set("Cancelled delete".to_string());
61            show_confirm.set(false);
62        });
63
64        let on_alert_dismiss = with!(show_alert, last_action => move || {
65            last_action.set("Dismissed alert".to_string());
66            show_alert.set(false);
67        });
68
69        let on_custom_save = with!(show_custom, custom_input, last_action => move || {
70            let value = custom_input.get();
71            if !value.is_empty() {
72                last_action.set(format!("Saved: {}", value));
73                custom_input.set(String::new());
74            }
75            show_custom.set(false);
76        });
77
78        let on_custom_dismiss = with!(show_custom => move || {
79            show_custom.set(false);
80        });
81
82        View::vstack()
83            .spacing(1)
84            .child(
85                // Header
86                View::boxed()
87                    .border(true)
88                    .padding(1)
89                    .child(
90                        View::vstack()
91                            .child(View::styled_text("Modal Dialogs Demo").bold().build())
92                            .child(
93                                View::styled_text("Click buttons to open different modal types")
94                                    .dim()
95                                    .build(),
96                            )
97                            .build(),
98                    )
99                    .build(),
100            )
101            .child(
102                // Main content
103                View::boxed()
104                    .flex(1)
105                    .border(true)
106                    .padding(1)
107                    .child(
108                        View::vstack()
109                            .spacing(1)
110                            .child(View::text("Modal Types:"))
111                            .child(
112                                View::hstack()
113                                    .spacing(2)
114                                    .child(
115                                        View::button()
116                                            .label("Confirm Dialog")
117                                            .on_press(open_confirm)
118                                            .build(),
119                                    )
120                                    .child(
121                                        View::button()
122                                            .label("Alert Dialog")
123                                            .on_press(open_alert)
124                                            .build(),
125                                    )
126                                    .child(
127                                        View::button()
128                                            .label("Custom Modal")
129                                            .on_press(open_custom)
130                                            .build(),
131                                    )
132                                    .build(),
133                            )
134                            .child(View::spacer())
135                            .child(
136                                View::styled_text(format!(
137                                    "Deleted items: {}",
138                                    deleted_count.get()
139                                ))
140                                .color(Color::Yellow)
141                                .build(),
142                            )
143                            .child(
144                                View::styled_text(format!("Last action: {}", last_action.get()))
145                                    .dim()
146                                    .build(),
147                            )
148                            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
149                            .build(),
150                    )
151                    .build(),
152            )
153            // Help modal
154            .child(
155                View::modal()
156                    .visible(show_help.get())
157                    .title("Example 23: Modal")
158                    .on_dismiss(with!(show_help => move || show_help.set(false)))
159                    .child(
160                        View::vstack()
161                            .child(View::styled_text("What you're seeing").bold().build())
162                            .child(View::text("• Confirm, alert, and custom modals"))
163                            .child(View::text("• Modal focus containment"))
164                            .child(View::text("• Escape to dismiss"))
165                            .child(View::gap(1))
166                            .child(View::styled_text("Key concepts").bold().build())
167                            .child(View::text("• View::modal() creates overlay"))
168                            .child(View::text("• .visible() controls show/hide"))
169                            .child(View::text("• .on_dismiss() handles Escape"))
170                            .child(View::text("• Focus trapped in open modal"))
171                            .child(View::gap(1))
172                            .child(View::styled_text("Try this").bold().build())
173                            .child(View::text("• Open confirm, click Yes/No"))
174                            .child(View::text("• Custom modal has text input"))
175                            .child(View::text("• Press Escape to close modals"))
176                            .child(View::gap(1))
177                            .child(View::styled_text("Next up").bold().build())
178                            .child(View::text("→ 24_async_data: async loading"))
179                            .child(View::gap(1))
180                            .child(View::styled_text("Press Escape to close").dim().build())
181                            .build(),
182                    )
183                    .build(),
184            )
185            // Confirm dialog modal
186            .child(
187                View::modal()
188                    .visible(show_confirm.get())
189                    .title("Confirm Delete")
190                    .on_dismiss(on_confirm_no.clone())
191                    .width(40)
192                    .height(30)
193                    .child(
194                        View::vstack()
195                            .spacing(1)
196                            .child(View::text("Are you sure you want to delete this item?"))
197                            .child(View::text("This action cannot be undone."))
198                            .child(View::spacer())
199                            .child(
200                                View::hstack()
201                                    .spacing(2)
202                                    .child(
203                                        View::button()
204                                            .label("Yes, Delete")
205                                            .on_press(on_confirm_yes)
206                                            .build(),
207                                    )
208                                    .child(
209                                        View::button()
210                                            .label("Cancel")
211                                            .on_press(on_confirm_no)
212                                            .build(),
213                                    )
214                                    .build(),
215                            )
216                            .build(),
217                    )
218                    .build(),
219            )
220            // Alert dialog modal
221            .child(
222                View::modal()
223                    .visible(show_alert.get())
224                    .title("Alert")
225                    .on_dismiss(on_alert_dismiss.clone())
226                    .width(50)
227                    .height(25)
228                    .child(
229                        View::vstack()
230                            .spacing(1)
231                            .child(
232                                View::styled_text("Operation completed successfully!")
233                                    .color(Color::Green)
234                                    .build(),
235                            )
236                            .child(View::text("Your changes have been saved."))
237                            .child(View::spacer())
238                            .child(
239                                View::button()
240                                    .label("OK")
241                                    .on_press(on_alert_dismiss)
242                                    .build(),
243                            )
244                            .build(),
245                    )
246                    .build(),
247            )
248            // Custom modal with input
249            .child(
250                View::modal()
251                    .visible(show_custom.get())
252                    .title("Enter Details")
253                    .on_dismiss(on_custom_dismiss.clone())
254                    .width(50)
255                    .height(40)
256                    .child(
257                        View::vstack()
258                            .spacing(1)
259                            .child(View::text("Enter a value:"))
260                            .child(
261                                View::text_input()
262                                    .value(custom_input.get())
263                                    .placeholder("Type something...")
264                                    .on_change(with!(custom_input => move |v: String| {
265                                        custom_input.set(v);
266                                    }))
267                                    .build(),
268                            )
269                            .child(View::spacer())
270                            .child(
271                                View::hstack()
272                                    .spacing(2)
273                                    .child(
274                                        View::button()
275                                            .label("Save")
276                                            .on_press(on_custom_save)
277                                            .build(),
278                                    )
279                                    .child(
280                                        View::button()
281                                            .label("Cancel")
282                                            .on_press(on_custom_dismiss)
283                                            .build(),
284                                    )
285                                    .build(),
286                            )
287                            .build(),
288                    )
289                    .build(),
290            )
291            .build()
292    }
Source

pub fn checkbox() -> CheckboxBuilder

Create a checkbox builder.

Examples found in repository?
examples/14_tabs.rs (line 91)
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    }
More examples
Hide additional examples
examples/11_checkbox.rs (line 59)
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/27_keyed_state.rs (line 130)
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    }
Source

pub fn radio_group() -> RadioGroupBuilder

Create a radio group builder.

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

pub fn text_area() -> TextAreaBuilder

Create a text area builder.

Examples found in repository?
examples/12_text_area.rs (line 98)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23        let theme_idx = state!(cx, || 0usize);
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        // F2 cycles through themes
32        cx.use_command(
33            KeyBinding::key(KeyCode::F(2)),
34            with!(theme_idx => move || {
35                let next = (theme_idx.get() + 1) % 6;
36                theme_idx.set(next);
37                let theme = match next {
38                    0 => Theme::nord(),
39                    1 => Theme::dark(),
40                    2 => Theme::light(),
41                    3 => Theme::dracula(),
42                    4 => Theme::gruvbox_dark(),
43                    _ => Theme::catppuccin_mocha(),
44                };
45                set_theme(theme);
46            }),
47        );
48
49        let content = state!(cx, String::new);
50        let cursor_line = state!(cx, || 0usize);
51        let cursor_col = state!(cx, || 0usize);
52
53        // Track changes and cursor position
54        let on_change = with!(content => move |text: String| {
55            content.set(text);
56        });
57
58        let on_cursor_change = with!(cursor_line, cursor_col => move |line: usize, col: usize| {
59            cursor_line.set(line);
60            cursor_col.set(col);
61        });
62
63        // Calculate stats
64        let text = content.get();
65        let line_count = if text.is_empty() {
66            0
67        } else {
68            text.lines().count()
69        };
70        let char_count = text.chars().count();
71        let word_count = text.split_whitespace().count();
72
73        let theme_name = match theme_idx.get() {
74            0 => "Nord",
75            1 => "Dark",
76            2 => "Light",
77            3 => "Dracula",
78            4 => "Gruvbox Dark",
79            _ => "Catppuccin Mocha",
80        };
81
82        View::vstack()
83            .spacing(1)
84            .child(
85                View::hstack()
86                    .child(View::styled_text("Notes").color(Color::Cyan).bold().build())
87                    .child(View::spacer())
88                    .child(View::styled_text(format!("Theme: {}", theme_name)).dim().build())
89                    .build(),
90            )
91            .child(
92                View::styled_text("A simple multi-line text editor")
93                    .dim()
94                    .build(),
95            )
96            .child(View::gap(1))
97            .child(
98                View::text_area()
99                    .value(content.get())
100                    .placeholder("Start typing your notes here...")
101                    .rows(12)
102                    .cursor_line(cursor_line.get())
103                    .cursor_col(cursor_col.get())
104                    .on_change(on_change)
105                    .on_cursor_change(on_cursor_change)
106                    .build(),
107            )
108            .child(View::gap(1))
109            .child(
110                View::hstack()
111                    .spacing(3)
112                    .child(
113                        View::styled_text(format!("Lines: {}", line_count))
114                            .color(Color::DarkGrey)
115                            .build(),
116                    )
117                    .child(
118                        View::styled_text(format!("Words: {}", word_count))
119                            .color(Color::DarkGrey)
120                            .build(),
121                    )
122                    .child(
123                        View::styled_text(format!("Chars: {}", char_count))
124                            .color(Color::DarkGrey)
125                            .build(),
126                    )
127                    .build(),
128            )
129            .child(View::gap(1))
130            .child(View::styled_text("F1 help • F2 theme • Ctrl+Q quit").dim().build())
131            .child(
132                View::modal()
133                    .visible(show_help.get())
134                    .title("Example 12: TextArea")
135                    .on_dismiss(with!(show_help => move || show_help.set(false)))
136                    .child(
137                        View::vstack()
138                            .child(View::styled_text("What you're seeing").bold().build())
139                            .child(View::text("• Multi-line text editing with TextArea"))
140                            .child(View::text("• Real-time line/word/char counts"))
141                            .child(View::text("• Cursor position tracking"))
142                            .child(View::gap(1))
143                            .child(View::styled_text("Key concepts").bold().build())
144                            .child(View::text("• View::text_area() for multi-line input"))
145                            .child(View::text("• on_change callback for text updates"))
146                            .child(View::text("• on_cursor_change for cursor tracking"))
147                            .child(View::text("• placeholder text when empty"))
148                            .child(View::gap(1))
149                            .child(View::styled_text("Try this").bold().build())
150                            .child(View::text("• Type multiple lines of text"))
151                            .child(View::text("• Watch the stats update in real-time"))
152                            .child(View::text("• Use arrow keys to navigate"))
153                            .child(View::gap(1))
154                            .child(View::styled_text("Next up").bold().build())
155                            .child(View::text("→ 13_split_panes: resizable panel layouts"))
156                            .child(View::gap(1))
157                            .child(View::styled_text("Press Escape to close").dim().build())
158                            .build(),
159                    )
160                    .build(),
161            )
162            .build()
163    }
Source

pub fn modal() -> ModalBuilder

Create a modal dialog builder.

Examples found in repository?
examples/01_hello_world.rs (line 39)
19    fn render(&self, cx: Scope) -> View {
20        let show_help = state!(cx, || false);
21
22        // F1 toggles help
23        cx.use_command(
24            KeyBinding::key(KeyCode::F(1)),
25            with!(show_help => move || show_help.update(|v| *v = !*v)),
26        );
27
28        View::vstack()
29            .child(View::styled_text("Hello World").bold().build())
30            .child(View::gap(1))
31            .child(View::text("Welcome to Telex!"))
32            .child(View::gap(1))
33            .child(
34                View::styled_text("F1 for help • Ctrl+Q to quit")
35                    .dim()
36                    .build(),
37            )
38            .child(
39                View::modal()
40                    .visible(show_help.get())
41                    .title("Example 01: Hello World")
42                    .on_dismiss(with!(show_help => move || show_help.set(false)))
43                    .child(
44                        View::vstack()
45                            .child(View::styled_text("What you're seeing").bold().build())
46                            .child(View::text(
47                                "• Basic app structure with struct + Component trait",
48                            ))
49                            .child(View::text(
50                                "• View::text() and View::styled_text() for display",
51                            ))
52                            .child(View::text("• View::vstack() for vertical layout"))
53                            .child(View::gap(1))
54                            .child(View::styled_text("Key concepts").bold().build())
55                            .child(View::text("• Every Telex app implements Component"))
56                            .child(View::text("• render() returns a View tree"))
57                            .child(View::text("• No state yet - this is purely static"))
58                            .child(View::gap(1))
59                            .child(View::styled_text("Next up").bold().build())
60                            .child(View::text("→ 02_counter: add state and interactivity"))
61                            .child(View::gap(1))
62                            .child(View::styled_text("Press Escape to close").dim().build())
63                            .build(),
64                    )
65                    .build(),
66            )
67            .build()
68    }
More examples
Hide additional examples
examples/30_image.rs (line 53)
30    fn render(&self, cx: Scope) -> View {
31        let show_help = state!(cx, || false);
32
33        // F1 toggles help
34        cx.use_command(
35            KeyBinding::key(KeyCode::F(1)),
36            with!(show_help => move || show_help.update(|v| *v = !*v)),
37        );
38        View::vstack()
39            .spacing(1)
40            .child(
41                View::styled_text("Image Widget Demo (Kitty Graphics)")
42                    .bold()
43                    .build(),
44            )
45            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
46            .child(View::text(""))
47            // Load image from file path
48            .child(View::text("Logo (from file path):"))
49            .child(View::image().file("assets/telex-tui.png").build())
50            .child(View::text(""))
51            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
52            .child(
53                View::modal()
54                    .visible(show_help.get())
55                    .title("Example 30: Image")
56                    .on_dismiss(with!(show_help => move || show_help.set(false)))
57                    .child(
58                        View::vstack()
59                            .child(View::styled_text("What you're seeing").bold().build())
60                            .child(View::text("• Image display via Kitty protocol"))
61                            .child(View::text("• PNG/JPEG/GIF support"))
62                            .child(View::text("• Loaded from file path"))
63                            .child(View::gap(1))
64                            .child(View::styled_text("Key concepts").bold().build())
65                            .child(View::text("• View::image() displays images"))
66                            .child(View::text("• .file(\"path\") loads from disk"))
67                            .child(View::text("• .bytes(data) for embedded images"))
68                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
69                            .child(View::gap(1))
70                            .child(View::styled_text("Try this").bold().build())
71                            .child(View::text("• Run in compatible terminal"))
72                            .child(View::text("• See the Telex logo rendered"))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Next up").bold().build())
75                            .child(View::text("→ 31_animated_canvas: animations"))
76                            .child(View::gap(1))
77                            .child(View::styled_text("Press Escape to close").dim().build())
78                            .build(),
79                    )
80                    .build(),
81            )
82            .build()
83    }
examples/04_timer.rs (line 64)
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        // Stream that yields elapsed seconds
32        let elapsed = stream!(cx, || {
33            (0u64..).inspect(|&s| {
34                if s > 0 {
35                    std::thread::sleep(Duration::from_secs(1));
36                }
37            })
38        });
39
40        let seconds = elapsed.get();
41        let is_running = elapsed.is_loading();
42
43        // Format as MM:SS
44        let minutes = seconds / 60;
45        let secs = seconds % 60;
46        let time_display = format!("{:02}:{:02}", minutes, secs);
47
48        View::vstack()
49            .child(View::styled_text("Timer").color(Color::Cyan).bold().build())
50            .child(View::gap(1))
51            .child(
52                View::hstack()
53                    .child(View::styled_text(&time_display).bold().build())
54                    .child(if is_running {
55                        View::styled_text(" ●").color(Color::Green).build()
56                    } else {
57                        View::styled_text(" ○").dim().build()
58                    })
59                    .build(),
60            )
61            .child(View::gap(1))
62            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
63            .child(
64                View::modal()
65                    .visible(show_help.get())
66                    .title("Example 04: Timer")
67                    .on_dismiss(with!(show_help => move || show_help.set(false)))
68                    .child(
69                        View::vstack()
70                            .child(View::styled_text("What you're seeing").bold().build())
71                            .child(View::text("• stream!() macro for background data"))
72                            .child(View::text("• Auto-updating UI without user input"))
73                            .child(View::text("• Green dot = stream is running"))
74                            .child(View::gap(1))
75                            .child(View::styled_text("Key concepts").bold().build())
76                            .child(View::text("• Streams run in background threads"))
77                            .child(View::text("• Each yielded value triggers a re-render"))
78                            .child(View::text("• is_loading() tells you if stream is active"))
79                            .child(View::gap(1))
80                            .child(View::styled_text("Try this").bold().build())
81                            .child(View::text("• Just watch - the timer ticks automatically"))
82                            .child(View::text("• No button presses needed for updates"))
83                            .child(View::gap(1))
84                            .child(View::styled_text("Next up").bold().build())
85                            .child(View::text("→ 05_todo_list: text input and list management"))
86                            .child(View::gap(1))
87                            .child(View::styled_text("Press Escape to close").dim().build())
88                            .build(),
89                    )
90                    .build(),
91            )
92            .build()
93    }
examples/02_counter.rs (line 61)
19    fn render(&self, cx: Scope) -> View {
20        let count = state!(cx, || 0i32);
21        let show_help = state!(cx, || false);
22
23        let increment = with!(count => move || count.update(|n| *n += 1));
24        let decrement = with!(count => move || count.update(|n| *n -= 1));
25
26        // F1 toggles help
27        cx.use_command(
28            KeyBinding::key(KeyCode::F(1)),
29            with!(show_help => move || show_help.update(|v| *v = !*v)),
30        );
31
32        View::vstack()
33            .child(View::styled_text("Counter").bold().build())
34            .child(View::gap(1))
35            .child(View::text(format!("Count: {}", count.get())))
36            .child(View::gap(1))
37            .child(
38                View::hstack()
39                    .child(
40                        View::button()
41                            .label("Decrement")
42                            .on_press(decrement)
43                            .build(),
44                    )
45                    .child(View::text(" "))
46                    .child(
47                        View::button()
48                            .label("Increment")
49                            .on_press(increment)
50                            .build(),
51                    )
52                    .build(),
53            )
54            .child(View::gap(1))
55            .child(
56                View::styled_text("Tab to switch • Enter to press • F1 for help • Ctrl+Q to quit")
57                    .dim()
58                    .build(),
59            )
60            .child(
61                View::modal()
62                    .visible(show_help.get())
63                    .title("Example 02: Counter")
64                    .on_dismiss(with!(show_help => move || show_help.set(false)))
65                    .child(
66                        View::vstack()
67                            .child(View::styled_text("What you're seeing").bold().build())
68                            .child(View::text("• state!() macro for reactive state"))
69                            .child(View::text("• View::button() with on_press callbacks"))
70                            .child(View::text(
71                                "• The with!() macro for capturing state in closures",
72                            ))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Key concepts").bold().build())
75                            .child(View::text("• State persists across renders"))
76                            .child(View::text("• Updating state triggers a re-render"))
77                            .child(View::text("• Tab navigates between focusable elements"))
78                            .child(View::gap(1))
79                            .child(View::styled_text("Try this").bold().build())
80                            .child(View::text("• Press +/- rapidly - notice instant updates"))
81                            .child(View::text(
82                                "• The UI stays in sync with state automatically",
83                            ))
84                            .child(View::gap(1))
85                            .child(View::styled_text("Next up").bold().build())
86                            .child(View::text("→ 03_theme_switcher: styling and colors"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Press Escape to close").dim().build())
89                            .build(),
90                    )
91                    .build(),
92            )
93            .build()
94    }
examples/19_status_bar.rs (line 77)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        // F1 toggles help
28        cx.use_command(
29            KeyBinding::key(KeyCode::F(1)),
30            with!(show_help => move || show_help.update(|v| *v = !*v)),
31        );
32        View::vstack()
33            .child(View::styled_text("Status Bar Examples").bold().build())
34            .child(View::text(""))
35            .child(View::text("Basic status bar (left only):"))
36            .child(View::status_bar().left("NORMAL").build())
37            .child(View::text(""))
38            .child(View::text("Left and right sections:"))
39            .child(
40                View::status_bar()
41                    .left("INSERT")
42                    .right("Ln 42, Col 8")
43                    .build(),
44            )
45            .child(View::text(""))
46            .child(View::text("All three sections:"))
47            .child(
48                View::status_bar()
49                    .left("VISUAL")
50                    .center("main.rs")
51                    .right("UTF-8 | LF | Rust")
52                    .build(),
53            )
54            .child(View::text(""))
55            .child(View::text("Custom colors (green on dark):"))
56            .child(
57                View::status_bar()
58                    .left("SUCCESS")
59                    .center("All tests passed")
60                    .right("100%")
61                    .fg(Color::Green)
62                    .bg(Color::DarkGreen)
63                    .build(),
64            )
65            .child(View::text(""))
66            .child(View::text("Editor-style status bar:"))
67            .child(
68                View::status_bar()
69                    .left("-- INSERT --")
70                    .center("~/projects/myapp/src/main.rs [+]")
71                    .right("1/100 | 50%")
72                    .build(),
73            )
74            .child(View::spacer())
75            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
76            .child(
77                View::modal()
78                    .visible(show_help.get())
79                    .title("Example 19: Status Bar")
80                    .on_dismiss(with!(show_help => move || show_help.set(false)))
81                    .child(
82                        View::vstack()
83                            .child(View::styled_text("What you're seeing").bold().build())
84                            .child(View::text("• Status bars with left/center/right sections"))
85                            .child(View::text("• Custom foreground and background colors"))
86                            .child(View::text("• Editor-style status line example"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Key concepts").bold().build())
89                            .child(View::text("• View::status_bar() creates status lines"))
90                            .child(View::text("• .left(), .center(), .right() for sections"))
91                            .child(View::text("• .fg() and .bg() for custom colors"))
92                            .child(View::text("• Great for showing mode, file info, etc."))
93                            .child(View::gap(1))
94                            .child(View::styled_text("Try this").bold().build())
95                            .child(View::text("• Compare different status bar styles"))
96                            .child(View::text("• Notice how sections align"))
97                            .child(View::gap(1))
98                            .child(View::styled_text("Next up").bold().build())
99                            .child(View::text("→ 20_menu_bar: dropdown menus"))
100                            .child(View::gap(1))
101                            .child(View::styled_text("Press Escape to close").dim().build())
102                            .build(),
103                    )
104                    .build(),
105            )
106            .build()
107    }
examples/09_syntax_comparison.rs (line 89)
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    }
Source

pub fn split() -> SplitBuilder

Create a split pane builder.

Examples found in repository?
examples/15_markdown.rs (line 76)
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
Hide additional examples
examples/13_split_panes.rs (line 60)
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    }
Source

pub fn tabs() -> TabsBuilder

Create a tabs builder.

Examples found in repository?
examples/14_tabs.rs (line 71)
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    }
Source

pub fn tree() -> TreeBuilder

Create a tree builder.

Examples found in repository?
examples/16_tree.rs (line 81)
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    }
Source

pub fn table() -> TableBuilder

Create a table builder.

Examples found in repository?
examples/17_table.rs (line 165)
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        // Track selected row
31        let selected = state!(cx, || 0usize);
32
33        // Track sort state (column index, ascending)
34        let sort_state = state!(cx, || None::<(usize, bool)>);
35
36        // Sample pod data (like k9s)
37        let base_data = vec![
38            vec![
39                "nginx-pod".to_string(),
40                "Running".to_string(),
41                "12%".to_string(),
42                "256Mi".to_string(),
43                "2h".to_string(),
44            ],
45            vec![
46                "redis-cache".to_string(),
47                "Running".to_string(),
48                "8%".to_string(),
49                "128Mi".to_string(),
50                "5d".to_string(),
51            ],
52            vec![
53                "api-server".to_string(),
54                "Running".to_string(),
55                "45%".to_string(),
56                "512Mi".to_string(),
57                "1h".to_string(),
58            ],
59            vec![
60                "db-postgres".to_string(),
61                "Running".to_string(),
62                "23%".to_string(),
63                "1Gi".to_string(),
64                "3d".to_string(),
65            ],
66            vec![
67                "worker-1".to_string(),
68                "Pending".to_string(),
69                "0%".to_string(),
70                "0Mi".to_string(),
71                "5m".to_string(),
72            ],
73            vec![
74                "worker-2".to_string(),
75                "Running".to_string(),
76                "67%".to_string(),
77                "384Mi".to_string(),
78                "45m".to_string(),
79            ],
80            vec![
81                "frontend".to_string(),
82                "Running".to_string(),
83                "5%".to_string(),
84                "64Mi".to_string(),
85                "12h".to_string(),
86            ],
87            vec![
88                "metrics".to_string(),
89                "CrashLoop".to_string(),
90                "0%".to_string(),
91                "32Mi".to_string(),
92                "2m".to_string(),
93            ],
94        ];
95
96        // Sort data based on current sort state
97        let mut rows = base_data.clone();
98        if let Some((col, ascending)) = sort_state.get() {
99            rows.sort_by(|a, b| {
100                let a_val = a.get(col).map(|s| s.as_str()).unwrap_or("");
101                let b_val = b.get(col).map(|s| s.as_str()).unwrap_or("");
102                if ascending {
103                    a_val.cmp(b_val)
104                } else {
105                    b_val.cmp(a_val)
106                }
107            });
108        }
109
110        let on_select = with!(selected => move |idx: usize| {
111            selected.set(idx);
112        });
113
114        let on_sort = with!(sort_state => move |col: usize, asc: bool| {
115            sort_state.set(Some((col, asc)));
116        });
117
118        let on_activate = with!(selected => move |idx: usize| {
119            // In a real app, this might open a details view
120            selected.set(idx);
121        });
122
123        let selected_name = rows
124            .get(selected.get())
125            .and_then(|r| r.first())
126            .map(|s| s.as_str())
127            .unwrap_or("None");
128
129        let sort_info = match sort_state.get() {
130            Some((col, asc)) => {
131                let col_name = match col {
132                    0 => "NAME",
133                    1 => "STATUS",
134                    2 => "CPU",
135                    3 => "MEMORY",
136                    4 => "AGE",
137                    _ => "?",
138                };
139                format!(
140                    "Sorted by {} ({})",
141                    col_name,
142                    if asc { "asc" } else { "desc" }
143                )
144            }
145            None => "Unsorted".to_string(),
146        };
147
148        View::vstack()
149            .child(
150                View::styled_text("Pod Dashboard")
151                    .color(Color::Cyan)
152                    .bold()
153                    .build(),
154            )
155            .child(
156                View::styled_text(format!("Selected: {} | {}", selected_name, sort_info))
157                    .dim()
158                    .build(),
159            )
160            .child(
161                View::boxed()
162                    .flex(1)
163                    .border(true)
164                    .child(
165                        View::table()
166                            .column("NAME")
167                            .column_with(TableColumn::new("STATUS").width(ColumnWidth::Fixed(12)))
168                            .column_with(
169                                TableColumn::new("CPU")
170                                    .width(ColumnWidth::Fixed(8))
171                                    .align(TextAlign::Right),
172                            )
173                            .column_with(
174                                TableColumn::new("MEMORY")
175                                    .width(ColumnWidth::Fixed(10))
176                                    .align(TextAlign::Right),
177                            )
178                            .column_with(
179                                TableColumn::new("AGE")
180                                    .width(ColumnWidth::Fixed(8))
181                                    .align(TextAlign::Right),
182                            )
183                            .rows(rows)
184                            .selected(selected.get())
185                            .sort(sort_state.get())
186                            .on_select(on_select)
187                            .on_sort(on_sort)
188                            .on_activate(on_activate)
189                            .build(),
190                    )
191                    .build(),
192            )
193            .child(
194                View::styled_text("↑↓/jk: navigate | Enter: activate | F1 help | Ctrl+Q: quit")
195                    .dim()
196                    .build(),
197            )
198            .child(
199                View::modal()
200                    .visible(show_help.get())
201                    .title("Example 17: Table")
202                    .on_dismiss(with!(show_help => move || show_help.set(false)))
203                    .child(
204                        View::vstack()
205                            .child(View::styled_text("What you're seeing").bold().build())
206                            .child(View::text("• Data table with sortable columns"))
207                            .child(View::text("• Row selection and activation"))
208                            .child(View::text("• Fixed and flexible column widths"))
209                            .child(View::gap(1))
210                            .child(View::styled_text("Key concepts").bold().build())
211                            .child(View::text("• View::table() for tabular data"))
212                            .child(View::text("• TableColumn for column config"))
213                            .child(View::text("• ColumnWidth::Fixed or ColumnWidth::Flex"))
214                            .child(View::text("• on_sort callback for sorting"))
215                            .child(View::gap(1))
216                            .child(View::styled_text("Try this").bold().build())
217                            .child(View::text("• Navigate rows with arrow keys"))
218                            .child(View::text("• Press Enter to activate a row"))
219                            .child(View::text("• Sorting is managed via on_sort"))
220                            .child(View::gap(1))
221                            .child(View::styled_text("Next up").bold().build())
222                            .child(View::text("→ 18_progress_bar: progress indicators"))
223                            .child(View::gap(1))
224                            .child(View::styled_text("Press Escape to close").dim().build())
225                            .build(),
226                    )
227                    .build(),
228            )
229            .build()
230    }
Source

pub fn progress_bar() -> ProgressBarBuilder

Create a progress bar builder.

Examples found in repository?
examples/18_progress_bar.rs (line 53)
25    fn render(&self, cx: Scope) -> View {
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        // Animated progress value using stream
35        let progress = stream!(cx, || {
36            (0u64..).map(|i| {
37                if i > 0 {
38                    std::thread::sleep(Duration::from_millis(50));
39                }
40                // Progress cycles from 0.0 to 1.0
41                (i % 100) as f32 / 100.0
42            })
43        });
44
45        let current_progress = progress.get();
46
47        View::vstack()
48            .spacing(1)
49            .child(View::styled_text("Progress Bar Examples").bold().build())
50            .child(View::text(""))
51            // Basic progress bar
52            .child(View::text("Basic (75%):"))
53            .child(View::progress_bar().value(0.75).build())
54            // With label
55            .child(View::text("With label (50%):"))
56            .child(View::progress_bar().value(0.5).label("Loading").build())
57            // Without percentage
58            .child(View::text("No percentage (33%):"))
59            .child(
60                View::progress_bar()
61                    .value(0.33)
62                    .show_percentage(false)
63                    .build(),
64            )
65            // Fixed width
66            .child(View::text("Fixed width (20 chars, 60%):"))
67            .child(View::progress_bar().value(0.6).width(20).build())
68            // Custom characters
69            .child(View::text("Custom characters (80%):"))
70            .child(
71                View::progress_bar()
72                    .value(0.8)
73                    .filled_char('=')
74                    .empty_char('-')
75                    .width(20)
76                    .build(),
77            )
78            // Another style
79            .child(View::text("Block style (65%):"))
80            .child(
81                View::progress_bar()
82                    .value(0.65)
83                    .filled_char('#')
84                    .empty_char('.')
85                    .width(25)
86                    .build(),
87            )
88            // Animated progress
89            .child(View::text("Animated (loops 0-100%):"))
90            .child(
91                View::progress_bar()
92                    .value(current_progress)
93                    .label("Progress")
94                    .build(),
95            )
96            .child(View::text(""))
97            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
98            .child(
99                View::modal()
100                    .visible(show_help.get())
101                    .title("Example 18: Progress Bar")
102                    .on_dismiss(with!(show_help => move || show_help.set(false)))
103                    .child(
104                        View::vstack()
105                            .child(View::styled_text("What you're seeing").bold().build())
106                            .child(View::text("• Progress bars with various styles"))
107                            .child(View::text("• Animated progress using stream!() macro"))
108                            .child(View::text("• Custom fill and empty characters"))
109                            .child(View::gap(1))
110                            .child(View::styled_text("Key concepts").bold().build())
111                            .child(View::text("• View::progress_bar() creates bars"))
112                            .child(View::text("• .value(0.0 to 1.0) sets progress"))
113                            .child(View::text("• .label() adds text label"))
114                            .child(View::text("• .filled_char() / .empty_char() customize"))
115                            .child(View::gap(1))
116                            .child(View::styled_text("Try this").bold().build())
117                            .child(View::text("• Watch the animated bar loop"))
118                            .child(View::text("• Compare different bar styles"))
119                            .child(View::gap(1))
120                            .child(View::styled_text("Next up").bold().build())
121                            .child(View::text("→ 19_status_bar: status bar widget"))
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    }
Source

pub fn status_bar() -> StatusBarBuilder

Create a status bar builder.

Examples found in repository?
examples/19_status_bar.rs (line 36)
24    fn render(&self, cx: Scope) -> View {
25        let show_help = state!(cx, || false);
26
27        // F1 toggles help
28        cx.use_command(
29            KeyBinding::key(KeyCode::F(1)),
30            with!(show_help => move || show_help.update(|v| *v = !*v)),
31        );
32        View::vstack()
33            .child(View::styled_text("Status Bar Examples").bold().build())
34            .child(View::text(""))
35            .child(View::text("Basic status bar (left only):"))
36            .child(View::status_bar().left("NORMAL").build())
37            .child(View::text(""))
38            .child(View::text("Left and right sections:"))
39            .child(
40                View::status_bar()
41                    .left("INSERT")
42                    .right("Ln 42, Col 8")
43                    .build(),
44            )
45            .child(View::text(""))
46            .child(View::text("All three sections:"))
47            .child(
48                View::status_bar()
49                    .left("VISUAL")
50                    .center("main.rs")
51                    .right("UTF-8 | LF | Rust")
52                    .build(),
53            )
54            .child(View::text(""))
55            .child(View::text("Custom colors (green on dark):"))
56            .child(
57                View::status_bar()
58                    .left("SUCCESS")
59                    .center("All tests passed")
60                    .right("100%")
61                    .fg(Color::Green)
62                    .bg(Color::DarkGreen)
63                    .build(),
64            )
65            .child(View::text(""))
66            .child(View::text("Editor-style status bar:"))
67            .child(
68                View::status_bar()
69                    .left("-- INSERT --")
70                    .center("~/projects/myapp/src/main.rs [+]")
71                    .right("1/100 | 50%")
72                    .build(),
73            )
74            .child(View::spacer())
75            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
76            .child(
77                View::modal()
78                    .visible(show_help.get())
79                    .title("Example 19: Status Bar")
80                    .on_dismiss(with!(show_help => move || show_help.set(false)))
81                    .child(
82                        View::vstack()
83                            .child(View::styled_text("What you're seeing").bold().build())
84                            .child(View::text("• Status bars with left/center/right sections"))
85                            .child(View::text("• Custom foreground and background colors"))
86                            .child(View::text("• Editor-style status line example"))
87                            .child(View::gap(1))
88                            .child(View::styled_text("Key concepts").bold().build())
89                            .child(View::text("• View::status_bar() creates status lines"))
90                            .child(View::text("• .left(), .center(), .right() for sections"))
91                            .child(View::text("• .fg() and .bg() for custom colors"))
92                            .child(View::text("• Great for showing mode, file info, etc."))
93                            .child(View::gap(1))
94                            .child(View::styled_text("Try this").bold().build())
95                            .child(View::text("• Compare different status bar styles"))
96                            .child(View::text("• Notice how sections align"))
97                            .child(View::gap(1))
98                            .child(View::styled_text("Next up").bold().build())
99                            .child(View::text("→ 20_menu_bar: dropdown menus"))
100                            .child(View::gap(1))
101                            .child(View::styled_text("Press Escape to close").dim().build())
102                            .build(),
103                    )
104                    .build(),
105            )
106            .build()
107    }
Source

pub fn command_palette() -> CommandPaletteBuilder

Create a command palette builder.

Source

pub fn menu_bar() -> MenuBarBuilder

Create a menu bar builder.

Examples found in repository?
examples/20_menu_bar.rs (line 122)
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    }
Source

pub fn toast_container() -> ToastContainerBuilder

Create a toast container builder.

Examples found in repository?
examples/21_toasts.rs (line 173)
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        // Create a toast queue with 3 second default duration
32        let toasts = state!(cx, || ToastQueue::with_duration(Duration::from_secs(3)));
33        let position = state!(cx, || ToastPosition::BottomRight);
34
35        // Buttons to trigger different toast types
36        let show_info = with!(toasts => move || {
37            toasts.get().info("This is an informational message");
38        });
39
40        let show_success = with!(toasts => move || {
41            toasts.get().success("Operation completed successfully!");
42        });
43
44        let show_warning = with!(toasts => move || {
45            toasts.get().warning("Warning: This action cannot be undone");
46        });
47
48        let show_error = with!(toasts => move || {
49            toasts.get().error("Error: Connection failed");
50        });
51
52        let show_long_error = with!(toasts => move || {
53            toasts.get().error_long("Critical Error: Server not responding. Check your network.");
54        });
55
56        let clear_all = with!(toasts => move || {
57            toasts.get().clear();
58        });
59
60        // Position cycling
61        let cycle_position = with!(position => move || {
62            let next = match position.get() {
63                ToastPosition::TopRight => ToastPosition::TopLeft,
64                ToastPosition::TopLeft => ToastPosition::BottomLeft,
65                ToastPosition::BottomLeft => ToastPosition::BottomRight,
66                ToastPosition::BottomRight => ToastPosition::TopRight,
67            };
68            position.set(next);
69        });
70
71        let position_name = match position.get() {
72            ToastPosition::TopRight => "Top Right",
73            ToastPosition::TopLeft => "Top Left",
74            ToastPosition::BottomLeft => "Bottom Left",
75            ToastPosition::BottomRight => "Bottom Right",
76        };
77
78        View::vstack()
79            .spacing(1)
80            .child(
81                // Header
82                View::boxed()
83                    .border(true)
84                    .padding(1)
85                    .child(
86                        View::vstack()
87                            .child(View::styled_text("Toast Notifications Demo").bold().build())
88                            .child(
89                                View::styled_text("Click buttons to show different toast types")
90                                    .dim()
91                                    .build(),
92                            )
93                            .build(),
94                    )
95                    .build(),
96            )
97            .child(
98                // Main content
99                View::boxed()
100                    .flex(1)
101                    .border(true)
102                    .padding(1)
103                    .child(
104                        View::vstack()
105                            .spacing(1)
106                            .child(View::text("Toast Types:"))
107                            .child(
108                                View::hstack()
109                                    .spacing(2)
110                                    .child(View::button().label("Info").on_press(show_info).build())
111                                    .child(
112                                        View::button()
113                                            .label("Success")
114                                            .on_press(show_success)
115                                            .build(),
116                                    )
117                                    .child(
118                                        View::button()
119                                            .label("Warning")
120                                            .on_press(show_warning)
121                                            .build(),
122                                    )
123                                    .child(
124                                        View::button().label("Error").on_press(show_error).build(),
125                                    )
126                                    .build(),
127                            )
128                            .child(View::gap(1))
129                            .child(View::text("Other Actions:"))
130                            .child(
131                                View::hstack()
132                                    .spacing(2)
133                                    .child(
134                                        View::button()
135                                            .label("Long Error")
136                                            .on_press(show_long_error)
137                                            .build(),
138                                    )
139                                    .child(
140                                        View::button()
141                                            .label("Clear All")
142                                            .on_press(clear_all)
143                                            .build(),
144                                    )
145                                    .build(),
146                            )
147                            .child(View::gap(1))
148                            .child(View::text(format!("Position: {}", position_name)))
149                            .child(
150                                View::button()
151                                    .label("Change Position")
152                                    .on_press(cycle_position)
153                                    .build(),
154                            )
155                            .child(View::spacer())
156                            .child(
157                                View::styled_text(format!("Active toasts: {}", toasts.get().len()))
158                                    .color(Color::Yellow)
159                                    .build(),
160                            )
161                            .child(
162                                View::styled_text("Toasts auto-dismiss after 3 seconds")
163                                    .dim()
164                                    .build(),
165                            )
166                            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
167                            .build(),
168                    )
169                    .build(),
170            )
171            .child(
172                // Toast container - renders the toast stack
173                View::toast_container()
174                    .from_queue(&toasts.get())
175                    .position(position.get())
176                    .max_visible(5)
177                    .width(40)
178                    .build(),
179            )
180            .child(
181                View::modal()
182                    .visible(show_help.get())
183                    .title("Example 21: Toasts")
184                    .on_dismiss(with!(show_help => move || show_help.set(false)))
185                    .child(
186                        View::vstack()
187                            .child(View::styled_text("What you're seeing").bold().build())
188                            .child(View::text("• Toast notifications in corner"))
189                            .child(View::text("• Auto-dismiss after 3 seconds"))
190                            .child(View::text(
191                                "• Multiple toast types (info/success/warn/error)",
192                            ))
193                            .child(View::gap(1))
194                            .child(View::styled_text("Key concepts").bold().build())
195                            .child(View::text("• ToastQueue manages notifications"))
196                            .child(View::text("• View::toast_container() renders them"))
197                            .child(View::text("• .position() controls corner placement"))
198                            .child(View::text("• .max_visible() limits shown toasts"))
199                            .child(View::gap(1))
200                            .child(View::styled_text("Try this").bold().build())
201                            .child(View::text("• Click different toast type buttons"))
202                            .child(View::text("• Change position to see toasts move"))
203                            .child(View::text("• Spam buttons to stack toasts"))
204                            .child(View::gap(1))
205                            .child(View::styled_text("Next up").bold().build())
206                            .child(View::text("→ 22_forms: form validation"))
207                            .child(View::gap(1))
208                            .child(View::styled_text("Press Escape to close").dim().build())
209                            .build(),
210                    )
211                    .build(),
212            )
213            .build()
214    }
Source

pub fn form() -> FormBuilder

Create a form builder.

Examples found in repository?
examples/22_forms.rs (line 144)
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        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn form_field(name: impl Into<String>) -> FormFieldBuilder

Create a form field builder.

Examples found in repository?
examples/22_forms.rs (line 147)
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        // Create form state with validated fields
31        let form = state!(cx, || {
32            FormState::new()
33                .field(
34                    FieldBuilder::new("email")
35                        .required()
36                        .email()
37                        .error_message("Please enter a valid email address")
38                        .build(),
39                )
40                .field(
41                    FieldBuilder::new("password")
42                        .required()
43                        .min_length(8)
44                        .error_message("Password must be at least 8 characters")
45                        .build(),
46                )
47                .field(
48                    FieldBuilder::new("username")
49                        .required()
50                        .min_length(3)
51                        .max_length(20)
52                        .custom(|v| {
53                            if v.contains(' ') {
54                                Some("Username cannot contain spaces".to_string())
55                            } else if !v.chars().all(|c| c.is_alphanumeric() || c == '_') {
56                                Some(
57                                    "Username can only contain letters, numbers, and underscores"
58                                        .to_string(),
59                                )
60                            } else {
61                                None
62                            }
63                        })
64                        .build(),
65                )
66                .field(
67                    FieldBuilder::new("age")
68                        .integer()
69                        .custom(|v| {
70                            if v.is_empty() {
71                                return None; // Optional field
72                            }
73                            match v.parse::<i32>() {
74                                Ok(age) if age < 0 => Some("Age cannot be negative".to_string()),
75                                Ok(age) if age > 150 => {
76                                    Some("Please enter a valid age".to_string())
77                                }
78                                Ok(_) => None,
79                                Err(_) => Some("Please enter a valid number".to_string()),
80                            }
81                        })
82                        .build(),
83                )
84        });
85
86        let submit_message = state!(cx, String::new);
87
88        // Get current field values and errors
89        let email = form.get().get_value("email");
90        let password = form.get().get_value("password");
91        let username = form.get().get_value("username");
92        let age = form.get().get_value("age");
93
94        let email_error = form.get().get_error("email");
95        let password_error = form.get().get_error("password");
96        let username_error = form.get().get_error("username");
97        let age_error = form.get().get_error("age");
98
99        // Handlers
100        let on_submit = with!(form, submit_message => move || {
101            if form.get().validate() {
102                let values = form.get().values();
103                submit_message.set(format!(
104                    "Form submitted! Email: {}, Username: {}",
105                    values.get("email").unwrap_or(&String::new()),
106                    values.get("username").unwrap_or(&String::new())
107                ));
108            } else {
109                submit_message.set("Please fix the errors above".to_string());
110            }
111        });
112
113        let on_reset = with!(form, submit_message => move || {
114            form.get().reset();
115            submit_message.set(String::new());
116        });
117
118        View::vstack()
119            .spacing(1)
120            .child(
121                // Header
122                View::boxed()
123                    .border(true)
124                    .padding(1)
125                    .child(
126                        View::vstack()
127                            .child(View::styled_text("Form Validation Demo").bold().build())
128                            .child(
129                                View::styled_text("Tab between fields, type to enter values")
130                                    .dim()
131                                    .build(),
132                            )
133                            .build(),
134                    )
135                    .build(),
136            )
137            .child(
138                // Form
139                View::boxed()
140                    .flex(1)
141                    .border(true)
142                    .padding(1)
143                    .child(
144                        View::form()
145                            .spacing(1)
146                            .child(
147                                View::form_field("email")
148                                    .label("Email Address *")
149                                    .value(email.clone())
150                                    .placeholder("you@example.com")
151                                    .error(email_error)
152                                    .on_change(with!(form => move |v: String| {
153                                        form.get().set_value("email", v);
154                                    }))
155                                    .on_blur(with!(form => move || {
156                                        form.get().touch("email");
157                                    }))
158                                    .build(),
159                            )
160                            .child(
161                                View::form_field("username")
162                                    .label("Username *")
163                                    .value(username.clone())
164                                    .placeholder("johndoe")
165                                    .error(username_error)
166                                    .on_change(with!(form => move |v: String| {
167                                        form.get().set_value("username", v);
168                                    }))
169                                    .on_blur(with!(form => move || {
170                                        form.get().touch("username");
171                                    }))
172                                    .build(),
173                            )
174                            .child(
175                                View::form_field("password")
176                                    .label("Password * (min 8 chars)")
177                                    .value(password.clone())
178                                    .placeholder("Enter password")
179                                    .password(true)
180                                    .error(password_error)
181                                    .on_change(with!(form => move |v: String| {
182                                        form.get().set_value("password", v);
183                                    }))
184                                    .on_blur(with!(form => move || {
185                                        form.get().touch("password");
186                                    }))
187                                    .build(),
188                            )
189                            .child(
190                                View::form_field("age")
191                                    .label("Age (optional)")
192                                    .value(age.clone())
193                                    .placeholder("25")
194                                    .error(age_error)
195                                    .on_change(with!(form => move |v: String| {
196                                        form.get().set_value("age", v);
197                                    }))
198                                    .on_blur(with!(form => move || {
199                                        form.get().touch("age");
200                                    }))
201                                    .build(),
202                            )
203                            .build(),
204                    )
205                    .build(),
206            )
207            .child(
208                // Actions
209                View::hstack()
210                    .spacing(2)
211                    .child(View::button().label("Submit").on_press(on_submit).build())
212                    .child(View::button().label("Reset").on_press(on_reset).build())
213                    .build(),
214            )
215            .child(
216                // Status
217                View::boxed()
218                    .border(true)
219                    .padding(1)
220                    .child(
221                        View::vstack()
222                            .child(View::text(if submit_message.get().is_empty() {
223                                "Fill in the form and click Submit".to_string()
224                            } else {
225                                submit_message.get()
226                            }))
227                            .child(
228                                View::styled_text(format!(
229                                    "Form valid: {}",
230                                    if form.get().is_valid() { "Yes" } else { "No" }
231                                ))
232                                .color(if form.get().is_valid() {
233                                    Color::Green
234                                } else {
235                                    Color::Red
236                                })
237                                .build(),
238                            )
239                            .build(),
240                    )
241                    .build(),
242            )
243            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
244            .child(
245                View::modal()
246                    .visible(show_help.get())
247                    .title("Example 22: Forms")
248                    .on_dismiss(with!(show_help => move || show_help.set(false)))
249                    .child(
250                        View::vstack()
251                            .child(View::styled_text("What you're seeing").bold().build())
252                            .child(View::text("• Declarative form validation"))
253                            .child(View::text("• Required, email, length validators"))
254                            .child(View::text("• Custom validation functions"))
255                            .child(View::gap(1))
256                            .child(View::styled_text("Key concepts").bold().build())
257                            .child(View::text("• FormState manages all fields"))
258                            .child(View::text("• FieldBuilder defines validation"))
259                            .child(View::text("• View::form_field() renders inputs"))
260                            .child(View::text("• on_blur triggers validation"))
261                            .child(View::gap(1))
262                            .child(View::styled_text("Try this").bold().build())
263                            .child(View::text("• Enter invalid email, see error"))
264                            .child(View::text("• Try short password (<8 chars)"))
265                            .child(View::text("• Username with spaces shows error"))
266                            .child(View::gap(1))
267                            .child(View::styled_text("Next up").bold().build())
268                            .child(View::text("→ 23_modal: modal dialogs"))
269                            .child(View::gap(1))
270                            .child(View::styled_text("Press Escape to close").dim().build())
271                            .build(),
272                    )
273                    .build(),
274            )
275            .build()
276    }
Source

pub fn canvas() -> CanvasBuilder

Create a canvas builder for pixel-level drawing.

Experimental Feature

Canvas uses the Kitty graphics protocol for actual pixel rendering. Requires a compatible terminal (Kitty, Ghostty, WezTerm). Other terminals will show a placeholder message.

Examples found in repository?
examples/29_canvas.rs (line 63)
31    fn render(&self, cx: Scope) -> View {
32        let show_help = state!(cx, || false);
33
34        // F1 toggles help
35        cx.use_command(
36            KeyBinding::key(KeyCode::F(1)),
37            with!(show_help => move || show_help.update(|v| *v = !*v)),
38        );
39
40        // Animated value for the bar chart using stream macro
41        let frame_stream = stream!(cx, || {
42            (0u32..).inspect(|&i| {
43                if i > 0 {
44                    std::thread::sleep(std::time::Duration::from_millis(100));
45                }
46            })
47        });
48
49        let current_frame = frame_stream.get();
50
51        View::vstack()
52            .spacing(1)
53            .child(
54                View::styled_text("Canvas Examples (Kitty Graphics)")
55                    .bold()
56                    .build(),
57            )
58            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59            .child(View::text(""))
60            // Basic shapes demo
61            .child(View::text("Basic Shapes:"))
62            .child(
63                View::canvas()
64                    .width(200)
65                    .height(80)
66                    .on_draw(|ctx| {
67                        // Clear to dark background
68                        ctx.clear(Color::Rgb {
69                            r: 30,
70                            g: 30,
71                            b: 40,
72                        });
73
74                        // Draw some lines
75                        ctx.line(10, 10, 190, 10, Color::Red);
76                        ctx.line(10, 10, 10, 70, Color::Green);
77                        ctx.line(10, 70, 190, 70, Color::Blue);
78                        ctx.line(190, 10, 190, 70, Color::Yellow);
79
80                        // Draw diagonal lines
81                        ctx.line(10, 10, 190, 70, Color::Cyan);
82                        ctx.line(10, 70, 190, 10, Color::Magenta);
83
84                        // Draw filled rectangles
85                        ctx.fill_rect(30, 25, 30, 20, Color::Red);
86                        ctx.fill_rect(80, 25, 30, 20, Color::Green);
87                        ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89                        // Draw stroked rectangles
90                        ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91                        ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92                        ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93                    })
94                    .build(),
95            )
96            .child(View::text(""))
97            // Circles demo
98            .child(View::text("Circles:"))
99            .child(
100                View::canvas()
101                    .width(200)
102                    .height(60)
103                    .on_draw(|ctx| {
104                        ctx.clear(Color::Rgb {
105                            r: 20,
106                            g: 25,
107                            b: 35,
108                        });
109
110                        // Filled circles
111                        ctx.fill_circle(30, 30, 20, Color::Red);
112                        ctx.fill_circle(80, 30, 15, Color::Green);
113                        ctx.fill_circle(120, 30, 10, Color::Blue);
114
115                        // Stroked circles
116                        ctx.circle(160, 30, 20, Color::Yellow);
117                        ctx.circle(160, 30, 15, Color::Cyan);
118                        ctx.circle(160, 30, 10, Color::Magenta);
119                    })
120                    .build(),
121            )
122            .child(View::text(""))
123            // Animated bar chart
124            .child(View::text("Animated Bar Chart:"))
125            .child(
126                View::canvas()
127                    .width(200)
128                    .height(80)
129                    .on_draw({
130                        move |ctx| {
131                            ctx.clear(Color::Rgb {
132                                r: 25,
133                                g: 25,
134                                b: 30,
135                            });
136
137                            // Generate animated data
138                            let data: Vec<f32> = (0..8)
139                                .map(|i| {
140                                    let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141                                    0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142                                })
143                                .collect();
144
145                            let bar_width = 20u16;
146                            let gap = 5u16;
147                            let max_height = 60u16;
148                            let start_x = 10u16;
149                            let baseline = 75u16;
150
151                            // Draw baseline
152                            ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154                            // Draw bars
155                            let colors = [
156                                Color::Red,
157                                Color::Green,
158                                Color::Blue,
159                                Color::Yellow,
160                                Color::Cyan,
161                                Color::Magenta,
162                                Color::Rgb {
163                                    r: 255,
164                                    g: 128,
165                                    b: 0,
166                                },
167                                Color::Rgb {
168                                    r: 128,
169                                    g: 255,
170                                    b: 128,
171                                },
172                            ];
173
174                            for (i, &value) in data.iter().enumerate() {
175                                let x = start_x + (i as u16) * (bar_width + gap);
176                                let height = (value * max_height as f32) as u16;
177                                let y = baseline - height;
178                                ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179                            }
180                        }
181                    })
182                    .build(),
183            )
184            .child(View::text(""))
185            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186            .child(
187                View::modal()
188                    .visible(show_help.get())
189                    .title("Example 29: Canvas")
190                    .on_dismiss(with!(show_help => move || show_help.set(false)))
191                    .child(
192                        View::vstack()
193                            .child(View::styled_text("What you're seeing").bold().build())
194                            .child(View::text("• Pixel graphics via Kitty protocol"))
195                            .child(View::text("• Lines, rectangles, circles"))
196                            .child(View::text("• Animated bar chart"))
197                            .child(View::gap(1))
198                            .child(View::styled_text("Key concepts").bold().build())
199                            .child(View::text("• View::canvas() creates drawing area"))
200                            .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201                            .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203                            .child(View::gap(1))
204                            .child(View::styled_text("Try this").bold().build())
205                            .child(View::text("• Watch the animated bar chart"))
206                            .child(View::text("• Run in compatible terminal"))
207                            .child(View::gap(1))
208                            .child(View::styled_text("Next up").bold().build())
209                            .child(View::text("→ 30_image: image display"))
210                            .child(View::gap(1))
211                            .child(View::styled_text("Press Escape to close").dim().build())
212                            .build(),
213                    )
214                    .build(),
215            )
216            .build()
217    }
Source

pub fn image() -> ImageBuilder

Create an image builder for displaying images.

Experimental Feature

Displays PNG, JPEG, or GIF images using the Kitty graphics protocol. GIF animations are handled natively by Kitty. Requires a compatible terminal (Kitty, Ghostty, WezTerm). Other terminals will show alt text or a placeholder message.

Examples found in repository?
examples/30_image.rs (line 49)
30    fn render(&self, cx: Scope) -> View {
31        let show_help = state!(cx, || false);
32
33        // F1 toggles help
34        cx.use_command(
35            KeyBinding::key(KeyCode::F(1)),
36            with!(show_help => move || show_help.update(|v| *v = !*v)),
37        );
38        View::vstack()
39            .spacing(1)
40            .child(
41                View::styled_text("Image Widget Demo (Kitty Graphics)")
42                    .bold()
43                    .build(),
44            )
45            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
46            .child(View::text(""))
47            // Load image from file path
48            .child(View::text("Logo (from file path):"))
49            .child(View::image().file("assets/telex-tui.png").build())
50            .child(View::text(""))
51            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
52            .child(
53                View::modal()
54                    .visible(show_help.get())
55                    .title("Example 30: Image")
56                    .on_dismiss(with!(show_help => move || show_help.set(false)))
57                    .child(
58                        View::vstack()
59                            .child(View::styled_text("What you're seeing").bold().build())
60                            .child(View::text("• Image display via Kitty protocol"))
61                            .child(View::text("• PNG/JPEG/GIF support"))
62                            .child(View::text("• Loaded from file path"))
63                            .child(View::gap(1))
64                            .child(View::styled_text("Key concepts").bold().build())
65                            .child(View::text("• View::image() displays images"))
66                            .child(View::text("• .file(\"path\") loads from disk"))
67                            .child(View::text("• .bytes(data) for embedded images"))
68                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
69                            .child(View::gap(1))
70                            .child(View::styled_text("Try this").bold().build())
71                            .child(View::text("• Run in compatible terminal"))
72                            .child(View::text("• See the Telex logo rendered"))
73                            .child(View::gap(1))
74                            .child(View::styled_text("Next up").bold().build())
75                            .child(View::text("→ 31_animated_canvas: animations"))
76                            .child(View::gap(1))
77                            .child(View::styled_text("Press Escape to close").dim().build())
78                            .build(),
79                    )
80                    .build(),
81            )
82            .build()
83    }
Source

pub fn terminal() -> TerminalBuilder

Create a terminal builder for interactive PTY terminal emulation.

Status: Experimental Preview

Supports running shell commands (bash, vim, htop, etc.) with full keyboard input and ANSI color/style rendering.

§Known Limitations
  • No scrollback buffer
  • No terminal resize support
  • No copy/paste
  • No mouse input

Use for prototyping and experimentation. Breaking changes likely.

§Example
let terminal = cx.use_terminal();
if !terminal.is_started() {
    terminal.spawn("bash", &[], 80, 24);
}
View::terminal().handle(terminal).build()
Examples found in repository?
examples/33_terminal.rs (line 20)
5fn app(cx: Scope) -> View {
6    let terminal = terminal!(cx);
7
8    // Spawn bash on first render
9    if !terminal.is_started() {
10        if let Err(e) = terminal.spawn("bash", &[], 80, 24) {
11            eprintln!("Failed to spawn terminal: {}", e);
12        }
13    }
14
15    View::vstack()
16        .child(View::text("Telex Terminal Demo"))
17        .child(View::text(
18            "Press Ctrl+Shift+[ to escape terminal focus, Tab to navigate",
19        ))
20        .child(View::terminal().handle(terminal).build())
21        .build()
22}
Source

pub fn custom(widget: Rc<RefCell<dyn Widget>>) -> Self

Create a custom widget view.

Wraps a user-defined Widget implementation in a View. Use this for custom character-cell rendering that can’t be composed from built-in widgets.

§Example
let my_widget = Rc::new(RefCell::new(MyWidget::new()));
View::custom(my_widget)
Examples found in repository?
examples/37_error_boundary.rs (line 51)
38    fn render(&self, cx: Scope) -> View {
39        let show_help = state!(cx, || false);
40
41        cx.use_command(
42            KeyBinding::key(KeyCode::F(1)),
43            with!(show_help => move || show_help.update(|v| *v = !*v)),
44        );
45
46        let count = state!(cx, || 0i32);
47
48        // The panic must happen at render time (inside render_view) so the
49        // error boundary's catch_unwind can catch it. A custom widget defers
50        // execution to the render pass.
51        let risky_view = View::custom(std::rc::Rc::new(std::cell::RefCell::new(RiskyCounter(count.get()))));
52
53        let fallback = View::vstack()
54            .child(View::styled_text("CAUGHT PANIC").color(Color::Red).bold().build())
55            .child(View::text("The child view panicked."))
56            .child(View::text("But the app is still running!"))
57            .build();
58
59        View::vstack()
60            .spacing(1)
61            .child(View::styled_text("Error Boundary Demo").bold().build())
62            .child(
63                View::hstack()
64                    .spacing(2)
65                    .child(
66                        View::vstack()
67                            .spacing(1)
68                            .child(View::styled_text("Protected Panel").bold().build())
69                            .child(
70                                View::error_boundary()
71                                    .child(risky_view)
72                                    .fallback(fallback)
73                                    .build(),
74                            )
75                            .build(),
76                    )
77                    .child(
78                        View::vstack()
79                            .spacing(1)
80                            .child(View::styled_text("How It Works").bold().build())
81                            .child(View::text("The left panel asserts"))
82                            .child(View::text("count < 5. When it hits"))
83                            .child(View::text("5, the error boundary"))
84                            .child(View::text("catches the panic and"))
85                            .child(View::text("renders the fallback."))
86                            .build(),
87                    )
88                    .build(),
89            )
90            .child(
91                View::hstack()
92                    .spacing(1)
93                    .child(
94                        View::button()
95                            .label("[ + Increment ]")
96                            .on_press(with!(count => move || count.update(|n| *n += 1)))
97                            .build(),
98                    )
99                    .child(
100                        View::button()
101                            .label("[ Reset to 0 ]")
102                            .on_press(with!(count => move || count.set(0)))
103                            .build(),
104                    )
105                    .child(View::styled_text(format!("count = {}", count.get())).dim().build())
106                    .build(),
107            )
108            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
109            .child(
110                View::modal()
111                    .visible(show_help.get())
112                    .title("Example 37: Error Boundary")
113                    .on_dismiss(with!(show_help => move || show_help.set(false)))
114                    .child(
115                        View::vstack()
116                            .child(View::styled_text("What you're seeing").bold().build())
117                            .child(View::text("• A counter that panics at 5"))
118                            .child(View::text("• Error boundary catches the panic"))
119                            .child(View::text("• Red fallback replaces the crash"))
120                            .child(View::gap(1))
121                            .child(View::styled_text("Key concepts").bold().build())
122                            .child(View::text("• View::error_boundary()"))
123                            .child(View::text("• .child(risky) .fallback(safe)"))
124                            .child(View::text("• Panics are caught, not propagated"))
125                            .child(View::text("• App keeps running after panic"))
126                            .child(View::gap(1))
127                            .child(View::styled_text("Try this").bold().build())
128                            .child(View::text("• Increment to 5 to trigger panic"))
129                            .child(View::text("• Reset to 0 to recover"))
130                            .child(View::text("• Keep incrementing past 5"))
131                            .child(View::gap(1))
132                            .child(View::styled_text("Next up").bold().build())
133                            .child(View::text("-> 38_custom_widget: Game of Life"))
134                            .child(View::gap(1))
135                            .child(View::styled_text("Press Escape to close").dim().build())
136                            .build(),
137                    )
138                    .build(),
139            )
140            .build()
141    }
More examples
Hide additional examples
examples/38_custom_widget.rs (line 160)
119    fn render(&self, cx: Scope) -> View {
120        let show_help = state!(cx, || false);
121
122        cx.use_command(
123            KeyBinding::key(KeyCode::F(1)),
124            with!(show_help => move || show_help.update(|v| *v = !*v)),
125        );
126
127        let game: State<GameOfLife> = state!(cx, || {
128            let mut g = GameOfLife::new();
129            g.randomize();
130            g
131        });
132        let generation = state!(cx, || 0u64);
133        let playing = state!(cx, || false);
134
135        // Auto-step when playing
136        interval!(cx, Duration::from_millis(150), with!(playing, game, generation => move || {
137            if playing.get() {
138                game.update(|g| g.step());
139                generation.update(|n| *n += 1);
140            }
141        }));
142
143        let widget = Rc::new(RefCell::new(game.get()));
144
145        View::vstack()
146            .spacing(1)
147            .child(View::styled_text("Game of Life").bold().build())
148            .child(
149                View::hstack()
150                    .spacing(1)
151                    .child(View::styled_text(format!("Gen: {}", generation.get())).dim().build())
152                    .child(View::styled_text(format!("Alive: {}", game.get().alive_count())).dim().build())
153                    .child(if playing.get() {
154                        View::styled_text("PLAYING").color(Color::Green).bold().build()
155                    } else {
156                        View::styled_text("PAUSED").color(Color::Yellow).build()
157                    })
158                    .build(),
159            )
160            .child(View::custom(widget))
161            .child(
162                View::hstack()
163                    .spacing(1)
164                    .child(
165                        View::button()
166                            .label("[ Step ]")
167                            .on_press(with!(game, generation => move || {
168                                game.update(|g| g.step());
169                                generation.update(|n| *n += 1);
170                            }))
171                            .build(),
172                    )
173                    .child(
174                        View::button()
175                            .label(if playing.get() { "[ Pause ]" } else { "[ Play ]" })
176                            .on_press(with!(playing => move || playing.update(|p| *p = !*p)))
177                            .build(),
178                    )
179                    .child(
180                        View::button()
181                            .label("[ Randomize ]")
182                            .on_press(with!(game, generation => move || {
183                                game.update(|g| g.randomize());
184                                generation.set(0);
185                            }))
186                            .build(),
187                    )
188                    .child(
189                        View::button()
190                            .label("[ Clear ]")
191                            .on_press(with!(game, generation => move || {
192                                game.update(|g| g.clear());
193                                generation.set(0);
194                            }))
195                            .build(),
196                    )
197                    .build(),
198            )
199            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
200            .child(
201                View::modal()
202                    .visible(show_help.get())
203                    .title("Example 38: Custom Widget")
204                    .on_dismiss(with!(show_help => move || show_help.set(false)))
205                    .child(
206                        View::vstack()
207                            .child(View::styled_text("What you're seeing").bold().build())
208                            .child(View::text("• Conway's Game of Life"))
209                            .child(View::text("• Custom Widget renders the grid"))
210                            .child(View::text("• interval! drives auto-play"))
211                            .child(View::gap(1))
212                            .child(View::styled_text("Key concepts").bold().build())
213                            .child(View::text("• impl Widget for YourStruct"))
214                            .child(View::text("• render(area, buf) draws cells"))
215                            .child(View::text("• height_hint / width_hint for sizing"))
216                            .child(View::text("• View::custom(Rc<RefCell<W>>)"))
217                            .child(View::gap(1))
218                            .child(View::styled_text("Try this").bold().build())
219                            .child(View::text("• Press Play to auto-step"))
220                            .child(View::text("• Step manually one at a time"))
221                            .child(View::text("• Randomize for a new pattern"))
222                            .child(View::text("• Clear then Step to watch"))
223                            .child(View::gap(1))
224                            .child(View::styled_text("Next up").bold().build())
225                            .child(View::text("-> 39_port: bidirectional comms"))
226                            .child(View::gap(1))
227                            .child(View::styled_text("Press Escape to close").dim().build())
228                            .build(),
229                    )
230                    .build(),
231            )
232            .build()
233    }
Source

pub fn slider() -> SliderBuilder

Create a slider builder for bounded numeric values.

§Example
View::slider()
    .min(0.0)
    .max(127.0)
    .value(64.0)
    .step(1.0)
    .label("Volume")
    .on_change(move |v| vol.set(v))
    .build()
Examples found in repository?
examples/35_slider.rs (line 41)
21    fn render(&self, cx: Scope) -> View {
22        let show_help = state!(cx, || false);
23
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 r = state!(cx, || 128.0);
30        let g = state!(cx, || 0.0);
31        let b = state!(cx, || 255.0);
32
33        let rv = r.get() as u8;
34        let gv = g.get() as u8;
35        let bv = b.get() as u8;
36
37        View::vstack()
38            .spacing(1)
39            .child(View::styled_text("RGB Color Mixer").bold().build())
40            .child(
41                View::slider()
42                    .min(0.0)
43                    .max(255.0)
44                    .step(1.0)
45                    .value(r.get())
46                    .label(&format!("Red:   {:>3}", rv))
47                    .color(Color::Rgb { r: 255, g: 80, b: 80 })
48                    .on_change(with!(r => move |v: f64| r.set(v)))
49                    .build(),
50            )
51            .child(
52                View::slider()
53                    .min(0.0)
54                    .max(255.0)
55                    .step(1.0)
56                    .value(g.get())
57                    .label(&format!("Green: {:>3}", gv))
58                    .color(Color::Rgb { r: 80, g: 255, b: 80 })
59                    .on_change(with!(g => move |v: f64| g.set(v)))
60                    .build(),
61            )
62            .child(
63                View::slider()
64                    .min(0.0)
65                    .max(255.0)
66                    .step(1.0)
67                    .value(b.get())
68                    .label(&format!("Blue:  {:>3}", bv))
69                    .color(Color::Rgb { r: 80, g: 80, b: 255 })
70                    .on_change(with!(b => move |v: f64| b.set(v)))
71                    .build(),
72            )
73            .child(
74                View::styled_text("████████████████")
75                    .color(Color::Rgb { r: rv, g: gv, b: bv })
76                    .bold()
77                    .build(),
78            )
79            .child(View::styled_text(format!("#{:02X}{:02X}{:02X}", rv, gv, bv)).bold().build())
80            .child(View::styled_text("Tab: switch slider • Left/Right: adjust • F1: help • Ctrl+Q: quit").dim().build())
81            .child(
82                View::modal()
83                    .visible(show_help.get())
84                    .title("Example 35: Slider")
85                    .on_dismiss(with!(show_help => move || show_help.set(false)))
86                    .child(
87                        View::vstack()
88                            .child(View::styled_text("What you're seeing").bold().build())
89                            .child(View::text("• Three sliders for R, G, B"))
90                            .child(View::text("• Color preview swatch"))
91                            .child(View::text("• Live hex code"))
92                            .child(View::gap(1))
93                            .child(View::styled_text("Key concepts").bold().build())
94                            .child(View::text("• View::slider() with min/max/step"))
95                            .child(View::text("• on_change callback with f64"))
96                            .child(View::text("• Color::Rgb for true color"))
97                            .child(View::gap(1))
98                            .child(View::styled_text("Try this").bold().build())
99                            .child(View::text("• Tab between sliders"))
100                            .child(View::text("• Left/Right arrows to adjust"))
101                            .child(View::text("• Watch the preview change"))
102                            .child(View::gap(1))
103                            .child(View::styled_text("Next up").bold().build())
104                            .child(View::text("-> 36_reducer: state machine wizard"))
105                            .child(View::gap(1))
106                            .child(View::styled_text("Press Escape to close").dim().build())
107                            .build(),
108                    )
109                    .build(),
110            )
111            .build()
112    }
Source

pub fn error_boundary() -> ErrorBoundaryBuilder

Create an error boundary builder.

An error boundary catches panics in its child view and displays a fallback view instead of crashing the application.

§Example
View::error_boundary()
    .child(risky_component_view)
    .fallback(View::text("Something went wrong"))
    .build()
Examples found in repository?
examples/37_error_boundary.rs (line 70)
38    fn render(&self, cx: Scope) -> View {
39        let show_help = state!(cx, || false);
40
41        cx.use_command(
42            KeyBinding::key(KeyCode::F(1)),
43            with!(show_help => move || show_help.update(|v| *v = !*v)),
44        );
45
46        let count = state!(cx, || 0i32);
47
48        // The panic must happen at render time (inside render_view) so the
49        // error boundary's catch_unwind can catch it. A custom widget defers
50        // execution to the render pass.
51        let risky_view = View::custom(std::rc::Rc::new(std::cell::RefCell::new(RiskyCounter(count.get()))));
52
53        let fallback = View::vstack()
54            .child(View::styled_text("CAUGHT PANIC").color(Color::Red).bold().build())
55            .child(View::text("The child view panicked."))
56            .child(View::text("But the app is still running!"))
57            .build();
58
59        View::vstack()
60            .spacing(1)
61            .child(View::styled_text("Error Boundary Demo").bold().build())
62            .child(
63                View::hstack()
64                    .spacing(2)
65                    .child(
66                        View::vstack()
67                            .spacing(1)
68                            .child(View::styled_text("Protected Panel").bold().build())
69                            .child(
70                                View::error_boundary()
71                                    .child(risky_view)
72                                    .fallback(fallback)
73                                    .build(),
74                            )
75                            .build(),
76                    )
77                    .child(
78                        View::vstack()
79                            .spacing(1)
80                            .child(View::styled_text("How It Works").bold().build())
81                            .child(View::text("The left panel asserts"))
82                            .child(View::text("count < 5. When it hits"))
83                            .child(View::text("5, the error boundary"))
84                            .child(View::text("catches the panic and"))
85                            .child(View::text("renders the fallback."))
86                            .build(),
87                    )
88                    .build(),
89            )
90            .child(
91                View::hstack()
92                    .spacing(1)
93                    .child(
94                        View::button()
95                            .label("[ + Increment ]")
96                            .on_press(with!(count => move || count.update(|n| *n += 1)))
97                            .build(),
98                    )
99                    .child(
100                        View::button()
101                            .label("[ Reset to 0 ]")
102                            .on_press(with!(count => move || count.set(0)))
103                            .build(),
104                    )
105                    .child(View::styled_text(format!("count = {}", count.get())).dim().build())
106                    .build(),
107            )
108            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
109            .child(
110                View::modal()
111                    .visible(show_help.get())
112                    .title("Example 37: Error Boundary")
113                    .on_dismiss(with!(show_help => move || show_help.set(false)))
114                    .child(
115                        View::vstack()
116                            .child(View::styled_text("What you're seeing").bold().build())
117                            .child(View::text("• A counter that panics at 5"))
118                            .child(View::text("• Error boundary catches the panic"))
119                            .child(View::text("• Red fallback replaces the crash"))
120                            .child(View::gap(1))
121                            .child(View::styled_text("Key concepts").bold().build())
122                            .child(View::text("• View::error_boundary()"))
123                            .child(View::text("• .child(risky) .fallback(safe)"))
124                            .child(View::text("• Panics are caught, not propagated"))
125                            .child(View::text("• App keeps running after panic"))
126                            .child(View::gap(1))
127                            .child(View::styled_text("Try this").bold().build())
128                            .child(View::text("• Increment to 5 to trigger panic"))
129                            .child(View::text("• Reset to 0 to recover"))
130                            .child(View::text("• Keep incrementing past 5"))
131                            .child(View::gap(1))
132                            .child(View::styled_text("Next up").bold().build())
133                            .child(View::text("-> 38_custom_widget: Game of Life"))
134                            .child(View::gap(1))
135                            .child(View::styled_text("Press Escape to close").dim().build())
136                            .build(),
137                    )
138                    .build(),
139            )
140            .build()
141    }
Source

pub fn empty() -> Self

Create an empty view.

Examples found in repository?
examples/39_port.rs (line 142)
36    fn render(&self, cx: Scope) -> View {
37        let show_help = state!(cx, || false);
38
39        cx.use_command(
40            KeyBinding::key(KeyCode::F(1)),
41            with!(show_help => move || show_help.update(|v| *v = !*v)),
42        );
43
44        let status = state!(cx, || "Idle".to_string());
45        let progress = state!(cx, || 0u8);
46        let result: State<Option<String>> = state!(cx, || None);
47        let running = state!(cx, || false);
48
49        // Bidirectional port: inbound TaskProgress, outbound TaskCommand
50        let port = port!(cx, TaskProgress, TaskCommand);
51
52        // Process incoming progress messages
53        for msg in port.rx.get() {
54            match msg {
55                TaskProgress::Started => {
56                    status.set("Working...".to_string());
57                    progress.set(0);
58                    result.set(None);
59                    running.set(true);
60                }
61                TaskProgress::Progress(pct) => {
62                    status.set(format!("Progress: {}%", pct));
63                    progress.set(pct);
64                }
65                TaskProgress::Done(data) => {
66                    status.set("Done!".to_string());
67                    progress.set(100);
68                    result.set(Some(data));
69                    running.set(false);
70                }
71                TaskProgress::Cancelled => {
72                    status.set("Cancelled".to_string());
73                    running.set(false);
74                }
75            }
76        }
77
78        // Spawn the worker thread on first render
79        let worker_started = state!(cx, || false);
80        if !worker_started.get() {
81            worker_started.set(true);
82            let tx_progress = port.rx.tx();
83            if let Some(rx_commands) = port.take_outbound_rx() {
84                std::thread::spawn(move || {
85                    worker_loop(tx_progress, rx_commands);
86                });
87            }
88        }
89
90        let start_task = {
91            let tx = port.tx();
92            with!(running => move || {
93                if !running.get() {
94                    let _ = tx.send(TaskCommand::Start);
95                }
96            })
97        };
98
99        let cancel_task = {
100            let tx = port.tx();
101            with!(running => move || {
102                if running.get() {
103                    let _ = tx.send(TaskCommand::Cancel);
104                }
105            })
106        };
107
108        let pct = progress.get();
109        let bar_width = 30usize;
110        let filled = (pct as usize * bar_width) / 100;
111        let bar = format!(
112            "[{}{}] {}%",
113            "█".repeat(filled),
114            "░".repeat(bar_width - filled),
115            pct
116        );
117
118        View::vstack()
119            .spacing(1)
120            .child(View::styled_text("Port: Background Task Runner").bold().build())
121            .child(
122                View::hstack()
123                    .spacing(1)
124                    .child(View::styled_text("Status:").dim().build())
125                    .child(View::styled_text(status.get())
126                        .color(if running.get() { Color::Yellow } else if pct == 100 { Color::Green } else { Color::Reset })
127                        .bold()
128                        .build())
129                    .build(),
130            )
131            .child(View::styled_text(&bar).color(
132                if pct == 100 { Color::Green }
133                else if pct > 50 { Color::Yellow }
134                else { Color::Cyan }
135            ).build())
136            .child(if let Some(data) = result.get() {
137                View::vstack()
138                    .child(View::styled_text("Result:").dim().build())
139                    .child(View::styled_text(format!("  {}", data)).color(Color::Green).build())
140                    .build()
141            } else {
142                View::empty()
143            })
144            .child(
145                View::hstack()
146                    .spacing(1)
147                    .child(
148                        View::button()
149                            .label(if running.get() { "[ Running... ]" } else { "[ Start Task ]" })
150                            .on_press(start_task)
151                            .build(),
152                    )
153                    .child(
154                        View::button()
155                            .label("[ Cancel ]")
156                            .on_press(cancel_task)
157                            .build(),
158                    )
159                    .build(),
160            )
161            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
162            .child(
163                View::modal()
164                    .visible(show_help.get())
165                    .title("Example 39: Port")
166                    .on_dismiss(with!(show_help => move || show_help.set(false)))
167                    .child(
168                        View::vstack()
169                            .child(View::styled_text("What you're seeing").bold().build())
170                            .child(View::text("• Background task with progress"))
171                            .child(View::text("• Bidirectional communication"))
172                            .child(View::text("• Start and cancel controls"))
173                            .child(View::gap(1))
174                            .child(View::styled_text("Key concepts").bold().build())
175                            .child(View::text("• port!(cx, InType, OutType)"))
176                            .child(View::text("• port.rx.tx() sends to UI"))
177                            .child(View::text("• port.tx() sends to worker"))
178                            .child(View::text("• port.take_outbound_rx() for worker"))
179                            .child(View::text("• port.rx.get() reads this frame"))
180                            .child(View::gap(1))
181                            .child(View::styled_text("Try this").bold().build())
182                            .child(View::text("• Start a task, watch progress"))
183                            .child(View::text("• Cancel mid-way"))
184                            .child(View::text("• Start another after completion"))
185                            .child(View::gap(1))
186                            .child(View::styled_text("Press Escape to close").dim().build())
187                            .build(),
188                    )
189                    .build(),
190            )
191            .build()
192    }
Source

pub fn is_focusable(&self) -> bool

Check if this view is focusable.

Source

pub fn flex(&self) -> u16

Get the flex factor of this view (for layout).

Source

pub fn min_height(&self) -> Option<u16>

Get the minimum height constraint, if any.

Source

pub fn max_height(&self) -> Option<u16>

Get the maximum height constraint, if any.

Source

pub fn min_width(&self) -> Option<u16>

Get the minimum width constraint, if any.

Source

pub fn max_width(&self) -> Option<u16>

Get the maximum width constraint, if any.

Source

pub fn intrinsic_height(&self) -> Option<u16>

Calculate the intrinsic (natural) height of this view based on its content. Returns None for views that have no intrinsic height (flexible).

Source

pub fn intrinsic_width(&self) -> Option<u16>

Calculate the intrinsic (natural) width of this view based on its content. Returns None for views that have no intrinsic width (flexible).

Trait Implementations§

Source§

impl Clone for View

Source§

fn clone(&self) -> View

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for View

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for View

§

impl !RefUnwindSafe for View

§

impl !Send for View

§

impl !Sync for View

§

impl Unpin for View

§

impl !UnwindSafe for View

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

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>

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)

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)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.