Skip to main content

Scope

Struct Scope 

Source
pub struct Scope { /* private fields */ }
Expand description

Context passed to components during rendering.

Provides access to hooks like state!, effect!, stream!, etc.

Implementations§

Source§

impl Scope

Source

pub fn new() -> Self

Create a new scope with fresh state storage.

Source

pub fn with_storage(storage: Rc<StateStorage>) -> Self

Create a scope with existing storage (for re-renders).

Source

pub fn with_storage_and_commands( storage: Rc<StateStorage>, commands: Rc<CommandRegistry>, ) -> Self

Create a scope with existing storage and command registry.

Source

pub fn with_all( storage: Rc<StateStorage>, commands: Rc<CommandRegistry>, context: Rc<ContextStorage>, ) -> Self

Create a scope with all dependencies.

Source

pub fn component_id(&self) -> Option<TypeId>

Get the component identity (if set by the view! macro).

Source

pub fn with_component_id(self, id: TypeId) -> Self

Set the component identity (used by the view! macro).

Source

pub fn storage(&self) -> Rc<StateStorage>

Get the underlying storage for persistence.

Source

pub fn use_state_keyed<K: 'static, T: 'static>( &self, init: impl FnOnce() -> T, ) -> State<T>

Create keyed state that persists across re-renders (order-independent).

The type K acts as the key - same K always returns the same state. Prefer the state! macro which auto-generates the key.

§Example
let count = state!(cx, || 0);
Examples found in repository?
examples/28_shared_state.rs (line 42)
29    fn render(&self, cx: Scope) -> View {
30        let show_help = state!(cx, || false);
31
32        // F1 toggles help
33        cx.use_command(
34            KeyBinding::key(KeyCode::F(1)),
35            with!(show_help => move || show_help.update(|v| *v = !*v)),
36        );
37
38        // Both panes will use the SAME key type = SAME state
39        // Note: using use_state_keyed directly with an explicit key type
40
41        // PANE A - gets the shared counter
42        let count_a = cx.use_state_keyed::<SharedCounterKey, _>(|| 0);
43        let inc_a = with!(count_a => move || count_a.update(|n| *n += 1));
44
45        // PANE B - uses the SAME key, so gets the SAME state!
46        let count_b = cx.use_state_keyed::<SharedCounterKey, _>(|| 0);
47        let inc_b = with!(count_b => move || count_b.update(|n| *n += 1));
48
49        View::vstack()
50            .spacing(1)
51            .child(
52                View::styled_text("Shared State Demo")
53                    .color(Color::Cyan)
54                    .bold()
55                    .build(),
56            )
57            .child(View::gap(1))
58            .child(
59                View::hstack()
60                    .spacing(2)
61                    // Pane A
62                    .child(
63                        View::boxed()
64                            .border(true)
65                            .padding(1)
66                            .max_width(25)
67                            .child(
68                                View::vstack()
69                                    .child(View::styled_text("Pane A").bold().build())
70                                    .child(View::gap(1))
71                                    .child(
72                                        View::hstack()
73                                            .spacing(1)
74                                            .child(View::text("Value:"))
75                                            .child(
76                                                View::styled_text(format!("{}", count_a.get()))
77                                                    .color(Color::Yellow)
78                                                    .bold()
79                                                    .build(),
80                                            )
81                                            .child(View::button().label("+").on_press(inc_a).build())
82                                            .build(),
83                                    )
84                                    .build(),
85                            )
86                            .build(),
87                    )
88                    // Pane B
89                    .child(
90                        View::boxed()
91                            .border(true)
92                            .padding(1)
93                            .max_width(25)
94                            .child(
95                                View::vstack()
96                                    .child(View::styled_text("Pane B").bold().build())
97                                    .child(View::gap(1))
98                                    .child(
99                                        View::hstack()
100                                            .spacing(1)
101                                            .child(View::text("Value:"))
102                                            .child(
103                                                View::styled_text(format!("{}", count_b.get()))
104                                                    .color(Color::Yellow)
105                                                    .bold()
106                                                    .build(),
107                                            )
108                                            .child(View::button().label("+").on_press(inc_b).build())
109                                            .build(),
110                                    )
111                                    .build(),
112                            )
113                            .build(),
114                    )
115                    .build(),
116            )
117            .child(View::gap(1))
118            .child(View::styled_text("Try this:").bold().build())
119            .child(View::text("  1. Click + on Pane A"))
120            .child(View::text("  2. Watch Pane B update too!"))
121            .child(View::text("  3. Click + on Pane B - same thing"))
122            .child(View::gap(1))
123            .child(
124                View::styled_text("Both panes share the SAME state.")
125                    .color(Color::Green)
126                    .build(),
127            )
128            .child(View::gap(1))
129            .child(
130                View::boxed()
131                    .border(true)
132                    .padding(1)
133                    .child(
134                        View::vstack()
135                            .child(View::styled_text("The code:").bold().build())
136                            .child(View::gap(1))
137                            .child(
138                                View::styled_text("struct SharedCounterKey;  // Named key type")
139                                    .color(Color::Green)
140                                    .build(),
141                            )
142                            .child(View::gap(1))
143                            .child(View::styled_text("// Pane A").color(Color::DarkGrey).build())
144                            .child(
145                                View::styled_text("let count_a = cx.use_state_keyed::<SharedCounterKey, _>(|| 0);")
146                                    .color(Color::Yellow)
147                                    .build(),
148                            )
149                            .child(View::gap(1))
150                            .child(View::styled_text("// Pane B - SAME key type!").color(Color::DarkGrey).build())
151                            .child(
152                                View::styled_text("let count_b = cx.use_state_keyed::<SharedCounterKey, _>(|| 0);")
153                                    .color(Color::Yellow)
154                                    .build(),
155                            )
156                            .child(View::gap(1))
157                            .child(View::text("Same key = same state. Both variables"))
158                            .child(View::text("point to the same underlying value."))
159                            .build(),
160                    )
161                    .build(),
162            )
163            .child(View::gap(1))
164            .child(
165                View::boxed()
166                    .border(true)
167                    .padding(1)
168                    .child(
169                        View::vstack()
170                            .child(View::styled_text("Compare with example 27:").bold().build())
171                            .child(View::gap(1))
172                            .child(View::styled_text("state!(cx, || 0)  // Anonymous key").color(Color::Magenta).build())
173                            .child(View::text("  Each call = unique key = independent state"))
174                            .child(View::gap(1))
175                            .child(View::styled_text("cx.use_state_keyed::<MyKey, _>(|| 0)  // Named key").color(Color::Yellow).build())
176                            .child(View::text("  Same key = same state = shared everywhere"))
177                            .build(),
178                    )
179                    .build(),
180            )
181            .child(View::gap(1))
182            .child(View::styled_text("Tab: navigate | F1 help | Ctrl+Q: quit").dim().build())
183            .child(
184                View::modal()
185                    .visible(show_help.get())
186                    .title("Example 28: Shared State")
187                    .on_dismiss(with!(show_help => move || show_help.set(false)))
188                    .child(
189                        View::vstack()
190                            .child(View::styled_text("What you're seeing").bold().build())
191                            .child(View::text("• Two panes sharing ONE counter"))
192                            .child(View::text("• Both + buttons increment same value"))
193                            .child(View::text("• Named key type = shared state"))
194                            .child(View::gap(1))
195                            .child(View::styled_text("Key concepts").bold().build())
196                            .child(View::text("• struct SharedCounterKey; defines key"))
197                            .child(View::text("• use_state_keyed::<Key, _>() uses it"))
198                            .child(View::text("• Same key = same underlying value"))
199                            .child(View::text("• Opposite of state! (unique keys)"))
200                            .child(View::gap(1))
201                            .child(View::styled_text("Try this").bold().build())
202                            .child(View::text("• Click + on Pane A"))
203                            .child(View::text("• Watch Pane B update too!"))
204                            .child(View::text("• Both buttons modify same state"))
205                            .child(View::gap(1))
206                            .child(View::styled_text("Next up").bold().build())
207                            .child(View::text("→ 29_canvas: pixel graphics"))
208                            .child(View::gap(1))
209                            .child(View::styled_text("Press Escape to close").dim().build())
210                            .build()
211                    )
212                    .build()
213            )
214            .build()
215    }
Source

pub fn use_async_keyed<K: 'static, T, F>(&self, f: F) -> Async<T>
where T: Clone + Send + 'static, F: FnOnce() -> Result<T, String> + Send + 'static,

Load keyed async data (order-independent). Prefer the async_data! macro which auto-generates the key.

§Example
let data = async_data!(cx, || {
    Ok(fetch_data())
});
Source

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

Stream data with keyed state (order-independent). Prefer the stream! macro which auto-generates the key.

§Example
let elapsed = stream!(cx, || {
    (0..).inspect(|_| std::thread::sleep(Duration::from_secs(1)))
});
Source

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

Stream text with keyed state (order-independent). Prefer the text_stream! macro which auto-generates the key.

§Example
let logs = text_stream!(cx, || {
    generate_log_entries()
});
Source

pub fn use_text_stream_with_restart_keyed<K: 'static, F, I>( &self, restart: bool, stream_fn: F, ) -> TextStreamHandle
where F: FnOnce() -> I + Send + 'static, I: Iterator<Item = String> + Send + 'static,

Stream text with restart support, keyed state (order-independent). Prefer the text_stream_with_restart! macro which auto-generates the key.

Source

pub fn use_terminal_keyed<K: 'static>(&self) -> TerminalHandle

Create or get a keyed terminal handle (order-independent). Prefer the terminal! macro which auto-generates the key.

§Example
let terminal = terminal!(cx);
Source

pub fn use_reducer_keyed<K: 'static, S: Clone + 'static, A: 'static>( &self, initial: S, reducer: impl Fn(S, A) -> S + 'static, ) -> (State<S>, Rc<dyn Fn(A)>)

Create a reducer (order-independent). Prefer the reducer! macro which auto-generates the key.

Returns (state, dispatch) where dispatch sends actions through the reducer function to produce new state.

§Example
let (state, dispatch) = reducer!(cx, AppState::Idle, |state, action| {
    match (state, action) {
        (_, Action::Reset) => AppState::Idle,
        (s, _) => s,
    }
});
Source

pub fn use_channel_keyed<K: 'static, T: 'static>(&self) -> ChannelHandle<T>

Create a typed inbound channel (order-independent). Prefer the channel! macro which auto-generates the key.

§Example
let ch = channel!(cx, String);
let tx = ch.tx();
// hand tx to an external thread
for msg in ch.get() { /* ... */ }
Source

pub fn use_port_keyed<K: 'static, In: 'static, Out: 'static>( &self, ) -> PortHandle<In, Out>

Create a bidirectional port (order-independent). Prefer the port! macro which auto-generates the key.

§Example
let midi = port!(cx, MidiIn, MidiOut);
let inbound_tx = midi.rx.tx();  // external sends MidiIn here
let outbound_tx = midi.tx();    // component sends MidiOut here
for msg in midi.rx.get() { /* ... */ }
Source

pub fn use_interval_keyed<K: 'static>( &self, duration: Duration, callback: impl Fn() + 'static, )

Create a periodic interval (order-independent). Prefer the interval! macro which auto-generates the key.

The callback runs on the main thread each frame that the timer fired. Internally uses a channel + timer thread.

§Example
let count = state!(cx, || 0u64);
let c = count.clone();
interval!(cx, Duration::from_secs(1), move || {
    c.update(|n| *n += 1);
});
Source

pub fn use_command<F>(&self, binding: KeyBinding, callback: F)
where F: Fn() + 'static,

Register a keyboard command/shortcut.

The callback will be invoked when the key combination is pressed. Commands registered later in the render tree take precedence.

§Example
fn App(cx: Scope) -> View {
    let count = state!(cx, || 0);
    let c = count.clone();

    // Ctrl+R to reset counter
    cx.use_command(KeyBinding::ctrl('r'), move || {
        c.set(0);
    });

    view! { <Text>{format!("Count: {}", count.get())}</Text> }
}
Examples found in repository?
examples/01_hello_world.rs (lines 23-26)
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 (lines 34-37)
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 (lines 26-29)
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 (lines 27-30)
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 (lines 28-31)
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 (lines 29-32)
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 provide_context<T: Clone + 'static>(&self, value: T)

Provide a value in the context for child components to access.

Values are stored by type, so each type can only have one value. Providing a value of a type that already exists will replace it.

§Example
#[derive(Clone)]
struct UserState {
    name: String,
    logged_in: bool,
}

fn App(cx: Scope) -> View {
    cx.provide_context(UserState {
        name: "Alice".to_string(),
        logged_in: true,
    });

    view! { <Header /> }
}
Examples found in repository?
examples/25_context.rs (lines 88-91)
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    }
Source

pub fn use_context<T: Clone + 'static>(&self) -> Option<T>

Get a value from the context.

Returns None if no value of this type has been provided by a parent.

§Example
fn Header(cx: Scope) -> View {
    let user = cx.use_context::<UserState>();

    match user {
        Some(u) => view! { <Text>{format!("Hello, {}", u.name)}</Text> },
        None => view! { <Text>"Not logged in"</Text> },
    }
}
Examples found in repository?
examples/25_context.rs (line 226)
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}
Source

pub fn context(&self) -> Rc<ContextStorage>

Get the context storage (for passing to child scopes).

Source

pub fn use_effect_once_keyed<K: 'static, F, C>(&self, effect_fn: F)
where F: FnOnce() -> C + 'static, C: FnOnce() + 'static,

Run a keyed side effect only once (on first render). Order-independent - safe to use in conditionals.

Prefer the effect_once! macro which auto-generates the key:

effect_once!(cx, || {
    println!("initialized");
    || { println!("cleanup"); }
});
Source

pub fn use_effect_keyed<K: 'static, D, F, C>(&self, deps: D, effect_fn: F)
where D: PartialEq + Clone + 'static, F: FnOnce(&D) -> C + 'static, C: FnOnce() + 'static,

Run a keyed side effect when dependencies change. Order-independent - safe to use in conditionals.

Prefer the effect! macro which auto-generates the key:

effect!(cx, count.get(), |&c| {
    println!("count changed to {}", c);
    || {}  // cleanup
});

Trait Implementations§

Source§

impl Clone for Scope

Source§

fn clone(&self) -> Scope

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 Default for Scope

Source§

fn default() -> Self

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

Auto Trait Implementations§

§

impl Freeze for Scope

§

impl !RefUnwindSafe for Scope

§

impl !Send for Scope

§

impl !Sync for Scope

§

impl Unpin for Scope

§

impl !UnwindSafe for Scope

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.