Skip to main content

StreamHandle

Struct StreamHandle 

Source
pub struct StreamHandle<T> { /* private fields */ }
Expand description

Handle for stream state that can be stored and cloned.

Implementations§

Source§

impl<T: Clone + Default + 'static> StreamHandle<T>

Source

pub fn new() -> Self

Create a new stream handle with default accumulated value.

Source

pub fn with_initial(initial: T) -> Self

Create a new stream handle with a specific initial value.

Source

pub fn get(&self) -> T

Get the current accumulated value.

Examples found in repository?
examples/04_timer.rs (line 40)
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/18_progress_bar.rs (line 45)
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    }
examples/06_log_viewer.rs (line 82)
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    }
examples/08_system_monitor.rs (line 66)
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        // CPU usage stream (fluctuates between 10-90%)
31        let cpu = stream!(cx, || {
32            let mut rng_state = 42u64;
33            (0..).map(move |_| {
34                std::thread::sleep(Duration::from_millis(500));
35                // Simple LCG pseudo-random
36                rng_state = rng_state.wrapping_mul(1103515245).wrapping_add(12345);
37
38                ((rng_state >> 16) % 80) as u8 + 10
39            })
40        });
41
42        // Memory usage stream (slowly increases then drops)
43        let memory = stream!(cx, || {
44            (0..).map(|i| {
45                std::thread::sleep(Duration::from_millis(800));
46                let cycle = i % 20;
47                if cycle < 15 {
48                    40 + (cycle * 3) as u8
49                } else {
50                    40 + ((20 - cycle) * 8) as u8
51                }
52            })
53        });
54
55        // Network stream (random-ish traffic)
56        let network = stream!(cx, || {
57            let mut rng_state = 123u64;
58            (0..).map(move |_| {
59                std::thread::sleep(Duration::from_millis(300));
60                rng_state = rng_state.wrapping_mul(1103515245).wrapping_add(12345);
61
62                ((rng_state >> 16) % 1000) as u32 + 100
63            })
64        });
65
66        let cpu_val = cpu.get();
67        let mem_val = memory.get();
68        let net_val = network.get();
69
70        // Color based on value
71        let cpu_color = if cpu_val > 80 {
72            Color::Red
73        } else if cpu_val > 50 {
74            Color::Yellow
75        } else {
76            Color::Green
77        };
78
79        let mem_color = if mem_val > 80 {
80            Color::Red
81        } else if mem_val > 60 {
82            Color::Yellow
83        } else {
84            Color::Green
85        };
86
87        // Create progress bar (ASCII to avoid multi-byte char issues)
88        fn progress_bar(value: u8, width: usize) -> String {
89            let filled = (value as usize * width) / 100;
90            let empty = width - filled;
91            format!("[{}{}]", "#".repeat(filled), "-".repeat(empty))
92        }
93
94        View::vstack()
95            .child(
96                View::styled_text("System Monitor")
97                    .color(Color::Cyan)
98                    .bold()
99                    .build(),
100            )
101            .child(View::gap(1))
102            .child(
103                View::hstack()
104                    .child(View::text("CPU:    "))
105                    .child(
106                        View::styled_text(progress_bar(cpu_val, 20))
107                            .color(cpu_color)
108                            .build(),
109                    )
110                    .child(
111                        View::styled_text(format!(" {:>3}%", cpu_val))
112                            .bold()
113                            .build(),
114                    )
115                    .build(),
116            )
117            .child(
118                View::hstack()
119                    .child(View::text("Memory: "))
120                    .child(
121                        View::styled_text(progress_bar(mem_val, 20))
122                            .color(mem_color)
123                            .build(),
124                    )
125                    .child(
126                        View::styled_text(format!(" {:>3}%", mem_val))
127                            .bold()
128                            .build(),
129                    )
130                    .build(),
131            )
132            .child(View::gap(1))
133            .child(
134                View::hstack()
135                    .child(View::text("Network: "))
136                    .child(
137                        View::styled_text(format!("{:>6} KB/s", net_val))
138                            .color(Color::Magenta)
139                            .build(),
140                    )
141                    .build(),
142            )
143            .child(View::gap(1))
144            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
145            .child(
146                View::modal()
147                    .visible(show_help.get())
148                    .title("Example 08: System Monitor")
149                    .on_dismiss(with!(show_help => move || show_help.set(false)))
150                    .child(
151                        View::vstack()
152                            .child(View::styled_text("What you're seeing").bold().build())
153                            .child(View::text(
154                                "• Multiple independent streams running together",
155                            ))
156                            .child(View::text("• Color-coded thresholds (green/yellow/red)"))
157                            .child(View::text("• ASCII progress bars"))
158                            .child(View::gap(1))
159                            .child(View::styled_text("Key concepts").bold().build())
160                            .child(View::text("• Each stream!() runs in its own thread"))
161                            .child(View::text(
162                                "• Streams update independently at different rates",
163                            ))
164                            .child(View::text("• Conditional styling based on values"))
165                            .child(View::gap(1))
166                            .child(View::styled_text("Try this").bold().build())
167                            .child(View::text("• Watch CPU fluctuate randomly"))
168                            .child(View::text("• Memory climbs then drops cyclically"))
169                            .child(View::text("• Network updates fastest (300ms)"))
170                            .child(View::gap(1))
171                            .child(View::styled_text("Next up").bold().build())
172                            .child(View::text(
173                                "→ 09_syntax_comparison: builder vs macro syntax",
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/29_canvas.rs (line 49)
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 is_loading(&self) -> bool

Check if the stream is currently loading/streaming.

Examples found in repository?
examples/04_timer.rs (line 41)
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/06_log_viewer.rs (line 83)
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 is_streaming(&self) -> bool

Check if the stream is actively receiving data.

Source

pub fn is_done(&self) -> bool

Check if the stream has completed.

Source

pub fn is_error(&self) -> bool

Check if the stream encountered an error.

Source

pub fn error(&self) -> Option<String>

Get the error message if there was an error.

Source

pub fn state(&self) -> StreamState

Get the current stream state.

Source§

impl<T: Clone + Send + 'static> StreamHandle<T>

Source

pub fn start<F, I>(&self, stream_fn: F)
where F: FnOnce() -> I + Send + 'static, I: Iterator<Item = T> + Send + 'static,

Start the stream if not already started.

The stream_fn should be a function that returns an iterator. Each item from the iterator will be sent through the channel.

Source

pub fn start_with_result<F, I>(&self, stream_fn: F)
where F: FnOnce() -> Result<I, String> + Send + 'static, I: Iterator<Item = T> + Send + 'static,

Start the stream with error handling.

Source

pub fn poll(&self, accumulate: impl Fn(&mut T, T)) -> bool

Poll for new items and update accumulated value. Returns true if there were updates.

Source

pub fn reset(&self)
where T: Default,

Reset the stream to allow restarting.

Source

pub fn reset_with(&self, initial: T)

Reset the stream with a specific initial value.

Source§

impl StreamHandle<String>

Source

pub fn poll_text(&self) -> bool

Poll and accumulate text by concatenation.

Trait Implementations§

Source§

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

Source§

fn clone(&self) -> Self

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<T: Clone + Default + 'static> Default for StreamHandle<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<T> Freeze for StreamHandle<T>

§

impl<T> !RefUnwindSafe for StreamHandle<T>

§

impl<T> !Send for StreamHandle<T>

§

impl<T> !Sync for StreamHandle<T>

§

impl<T> Unpin for StreamHandle<T>

§

impl<T> !UnwindSafe for StreamHandle<T>

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.