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