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
impl Scope
Sourcepub fn with_storage(storage: Rc<StateStorage>) -> Self
pub fn with_storage(storage: Rc<StateStorage>) -> Self
Create a scope with existing storage (for re-renders).
Sourcepub fn with_storage_and_commands(
storage: Rc<StateStorage>,
commands: Rc<CommandRegistry>,
) -> Self
pub fn with_storage_and_commands( storage: Rc<StateStorage>, commands: Rc<CommandRegistry>, ) -> Self
Create a scope with existing storage and command registry.
Sourcepub fn with_all(
storage: Rc<StateStorage>,
commands: Rc<CommandRegistry>,
context: Rc<ContextStorage>,
) -> Self
pub fn with_all( storage: Rc<StateStorage>, commands: Rc<CommandRegistry>, context: Rc<ContextStorage>, ) -> Self
Create a scope with all dependencies.
Sourcepub fn component_id(&self) -> Option<TypeId>
pub fn component_id(&self) -> Option<TypeId>
Get the component identity (if set by the view! macro).
Sourcepub fn with_component_id(self, id: TypeId) -> Self
pub fn with_component_id(self, id: TypeId) -> Self
Set the component identity (used by the view! macro).
Sourcepub fn use_state_keyed<K: 'static, T: 'static>(
&self,
init: impl FnOnce() -> T,
) -> State<T>
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?
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 }Sourcepub fn use_async_keyed<K: 'static, T, F>(&self, f: F) -> Async<T>
pub fn use_async_keyed<K: 'static, T, F>(&self, f: F) -> Async<T>
Sourcepub fn use_stream_keyed<K: 'static, T, F, I>(
&self,
stream_fn: F,
) -> StreamHandle<T>
pub fn use_stream_keyed<K: 'static, T, F, I>( &self, stream_fn: F, ) -> StreamHandle<T>
Sourcepub fn use_text_stream_keyed<K: 'static, F, I>(
&self,
stream_fn: F,
) -> TextStreamHandle
pub fn use_text_stream_keyed<K: 'static, F, I>( &self, stream_fn: F, ) -> TextStreamHandle
Sourcepub fn use_text_stream_with_restart_keyed<K: 'static, F, I>(
&self,
restart: bool,
stream_fn: F,
) -> TextStreamHandle
pub fn use_text_stream_with_restart_keyed<K: 'static, F, I>( &self, restart: bool, stream_fn: F, ) -> TextStreamHandle
Stream text with restart support, keyed state (order-independent).
Prefer the text_stream_with_restart! macro which auto-generates the key.
Sourcepub fn use_terminal_keyed<K: 'static>(&self) -> TerminalHandle
pub fn use_terminal_keyed<K: 'static>(&self) -> TerminalHandle
Sourcepub 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)>)
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,
}
});Sourcepub fn use_channel_keyed<K: 'static, T: 'static>(&self) -> ChannelHandle<T>
pub fn use_channel_keyed<K: 'static, T: 'static>(&self) -> ChannelHandle<T>
Sourcepub fn use_port_keyed<K: 'static, In: 'static, Out: 'static>(
&self,
) -> PortHandle<In, Out>
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() { /* ... */ }Sourcepub fn use_interval_keyed<K: 'static>(
&self,
duration: Duration,
callback: impl Fn() + 'static,
)
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);
});Sourcepub fn use_command<F>(&self, binding: KeyBinding, callback: F)where
F: Fn() + 'static,
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?
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
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 }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 }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 }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 }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 }- examples/35_slider.rs
- examples/15_markdown.rs
- examples/18_progress_bar.rs
- examples/16_tree.rs
- examples/05_todo_list.rs
- examples/37_error_boundary.rs
- examples/38_custom_widget.rs
- examples/10_state_explained.rs
- examples/32_effects.rs
- examples/13_split_panes.rs
- examples/07_file_browser.rs
- examples/12_text_area.rs
- examples/06_log_viewer.rs
- examples/24_async_data.rs
- examples/34_channels_and_intervals.rs
- examples/08_system_monitor.rs
- examples/39_port.rs
- examples/25_context.rs
- examples/14_tabs.rs
- examples/11_checkbox.rs
- examples/17_table.rs
- examples/29_canvas.rs
- examples/20_menu_bar.rs
- examples/26_radio_buttons.rs
- examples/21_toasts.rs
- examples/36_reducer.rs
- examples/03_theme_switcher.rs
- examples/28_shared_state.rs
- examples/27_keyed_state.rs
- examples/31_animated_canvas.rs
- examples/22_forms.rs
- examples/23_modal.rs
Sourcepub fn provide_context<T: Clone + 'static>(&self, value: T)
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?
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 }Sourcepub fn use_context<T: Clone + 'static>(&self) -> Option<T>
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?
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}Sourcepub fn context(&self) -> Rc<ContextStorage>
pub fn context(&self) -> Rc<ContextStorage>
Get the context storage (for passing to child scopes).
Sourcepub fn use_effect_once_keyed<K: 'static, F, C>(&self, effect_fn: F)
pub fn use_effect_once_keyed<K: 'static, F, C>(&self, effect_fn: F)
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"); }
});Sourcepub fn use_effect_keyed<K: 'static, D, F, C>(&self, deps: D, effect_fn: F)
pub fn use_effect_keyed<K: 'static, D, F, C>(&self, deps: D, effect_fn: F)
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§
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.