Skip to main content

28_shared_state/
28_shared_state.rs

1//! Example 28: Shared State (Same Key = Same State)
2//!
3//! This example shows the OPPOSITE of example 27.
4//!
5//! In example 27, each `state!` call creates independent state
6//! because each macro invocation generates a unique anonymous key type.
7//!
8//! Here, we use an EXPLICIT key type, so multiple calls with the SAME key
9//! all access the SAME state.
10//!
11//! Run with: cargo run -p telex --example 28_shared_state
12
13use crossterm::event::KeyCode;
14use telex::prelude::*;
15use telex::Color;
16
17telex::require_api!(0, 2);
18
19// Define a NAMED key type - anywhere this is used, we get the SAME state
20struct SharedCounterKey;
21
22fn main() {
23    telex::run_with_theme(App, telex::theme::Theme::nord()).unwrap();
24}
25
26struct App;
27
28impl Component for App {
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    }
216}