Skip to main content

DrawContext

Struct DrawContext 

Source
pub struct DrawContext<'a> { /* private fields */ }
Expand description

Drawing context passed to the canvas on_draw callback.

Provides primitives for drawing pixels, lines, rectangles, and circles.

Implementations§

Source§

impl<'a> DrawContext<'a>

Source

pub fn new(buffer: &'a mut PixelBuffer) -> Self

Create a new draw context wrapping a pixel buffer.

Source

pub fn dimensions(&self) -> (u16, u16)

Get canvas dimensions (width, height) in pixels.

Source

pub fn clear(&mut self, color: Color)

Clear the canvas to a solid color.

Examples found in repository?
examples/29_canvas.rs (lines 68-72)
31    fn render(&self, cx: Scope) -> View {
32        let show_help = state!(cx, || false);
33
34        // F1 toggles help
35        cx.use_command(
36            KeyBinding::key(KeyCode::F(1)),
37            with!(show_help => move || show_help.update(|v| *v = !*v)),
38        );
39
40        // Animated value for the bar chart using stream macro
41        let frame_stream = stream!(cx, || {
42            (0u32..).inspect(|&i| {
43                if i > 0 {
44                    std::thread::sleep(std::time::Duration::from_millis(100));
45                }
46            })
47        });
48
49        let current_frame = frame_stream.get();
50
51        View::vstack()
52            .spacing(1)
53            .child(
54                View::styled_text("Canvas Examples (Kitty Graphics)")
55                    .bold()
56                    .build(),
57            )
58            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59            .child(View::text(""))
60            // Basic shapes demo
61            .child(View::text("Basic Shapes:"))
62            .child(
63                View::canvas()
64                    .width(200)
65                    .height(80)
66                    .on_draw(|ctx| {
67                        // Clear to dark background
68                        ctx.clear(Color::Rgb {
69                            r: 30,
70                            g: 30,
71                            b: 40,
72                        });
73
74                        // Draw some lines
75                        ctx.line(10, 10, 190, 10, Color::Red);
76                        ctx.line(10, 10, 10, 70, Color::Green);
77                        ctx.line(10, 70, 190, 70, Color::Blue);
78                        ctx.line(190, 10, 190, 70, Color::Yellow);
79
80                        // Draw diagonal lines
81                        ctx.line(10, 10, 190, 70, Color::Cyan);
82                        ctx.line(10, 70, 190, 10, Color::Magenta);
83
84                        // Draw filled rectangles
85                        ctx.fill_rect(30, 25, 30, 20, Color::Red);
86                        ctx.fill_rect(80, 25, 30, 20, Color::Green);
87                        ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89                        // Draw stroked rectangles
90                        ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91                        ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92                        ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93                    })
94                    .build(),
95            )
96            .child(View::text(""))
97            // Circles demo
98            .child(View::text("Circles:"))
99            .child(
100                View::canvas()
101                    .width(200)
102                    .height(60)
103                    .on_draw(|ctx| {
104                        ctx.clear(Color::Rgb {
105                            r: 20,
106                            g: 25,
107                            b: 35,
108                        });
109
110                        // Filled circles
111                        ctx.fill_circle(30, 30, 20, Color::Red);
112                        ctx.fill_circle(80, 30, 15, Color::Green);
113                        ctx.fill_circle(120, 30, 10, Color::Blue);
114
115                        // Stroked circles
116                        ctx.circle(160, 30, 20, Color::Yellow);
117                        ctx.circle(160, 30, 15, Color::Cyan);
118                        ctx.circle(160, 30, 10, Color::Magenta);
119                    })
120                    .build(),
121            )
122            .child(View::text(""))
123            // Animated bar chart
124            .child(View::text("Animated Bar Chart:"))
125            .child(
126                View::canvas()
127                    .width(200)
128                    .height(80)
129                    .on_draw({
130                        move |ctx| {
131                            ctx.clear(Color::Rgb {
132                                r: 25,
133                                g: 25,
134                                b: 30,
135                            });
136
137                            // Generate animated data
138                            let data: Vec<f32> = (0..8)
139                                .map(|i| {
140                                    let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141                                    0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142                                })
143                                .collect();
144
145                            let bar_width = 20u16;
146                            let gap = 5u16;
147                            let max_height = 60u16;
148                            let start_x = 10u16;
149                            let baseline = 75u16;
150
151                            // Draw baseline
152                            ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154                            // Draw bars
155                            let colors = [
156                                Color::Red,
157                                Color::Green,
158                                Color::Blue,
159                                Color::Yellow,
160                                Color::Cyan,
161                                Color::Magenta,
162                                Color::Rgb {
163                                    r: 255,
164                                    g: 128,
165                                    b: 0,
166                                },
167                                Color::Rgb {
168                                    r: 128,
169                                    g: 255,
170                                    b: 128,
171                                },
172                            ];
173
174                            for (i, &value) in data.iter().enumerate() {
175                                let x = start_x + (i as u16) * (bar_width + gap);
176                                let height = (value * max_height as f32) as u16;
177                                let y = baseline - height;
178                                ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179                            }
180                        }
181                    })
182                    .build(),
183            )
184            .child(View::text(""))
185            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186            .child(
187                View::modal()
188                    .visible(show_help.get())
189                    .title("Example 29: Canvas")
190                    .on_dismiss(with!(show_help => move || show_help.set(false)))
191                    .child(
192                        View::vstack()
193                            .child(View::styled_text("What you're seeing").bold().build())
194                            .child(View::text("• Pixel graphics via Kitty protocol"))
195                            .child(View::text("• Lines, rectangles, circles"))
196                            .child(View::text("• Animated bar chart"))
197                            .child(View::gap(1))
198                            .child(View::styled_text("Key concepts").bold().build())
199                            .child(View::text("• View::canvas() creates drawing area"))
200                            .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201                            .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203                            .child(View::gap(1))
204                            .child(View::styled_text("Try this").bold().build())
205                            .child(View::text("• Watch the animated bar chart"))
206                            .child(View::text("• Run in compatible terminal"))
207                            .child(View::gap(1))
208                            .child(View::styled_text("Next up").bold().build())
209                            .child(View::text("→ 30_image: image display"))
210                            .child(View::gap(1))
211                            .child(View::styled_text("Press Escape to close").dim().build())
212                            .build(),
213                    )
214                    .build(),
215            )
216            .build()
217    }
More examples
Hide additional examples
examples/31_animated_canvas.rs (lines 58-62)
33    fn render(&self, cx: Scope) -> View {
34        let show_help = state!(cx, || false);
35
36        // F1 toggles help
37        cx.use_command(
38            KeyBinding::key(KeyCode::F(1)),
39            with!(show_help => move || show_help.update(|v| *v = !*v)),
40        );
41        View::vstack()
42            .spacing(1)
43            .child(
44                View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45                    .bold()
46                    .build(),
47            )
48            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49            .child(View::text(""))
50            // Bouncing ball animation
51            .child(View::text("Bouncing Ball (60 FPS):"))
52            .child(
53                animated_canvas(cx.clone())
54                    .width(200)
55                    .height(60)
56                    .fps(60)
57                    .on_frame(|ctx, frame| {
58                        ctx.clear(Color::Rgb {
59                            r: 20,
60                            g: 20,
61                            b: 30,
62                        });
63
64                        // Ball physics
65                        let t = frame as f32 * 0.05;
66                        let x = 100.0 + 80.0 * t.cos();
67                        let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69                        // Shadow
70                        ctx.fill_circle(
71                            x as u16,
72                            55,
73                            8,
74                            Color::Rgb {
75                                r: 40,
76                                g: 40,
77                                b: 50,
78                            },
79                        );
80
81                        // Ball with gradient effect (outer to inner)
82                        ctx.fill_circle(
83                            x as u16,
84                            y as u16,
85                            12,
86                            Color::Rgb {
87                                r: 200,
88                                g: 50,
89                                b: 50,
90                            },
91                        );
92                        ctx.fill_circle(
93                            x as u16,
94                            y as u16,
95                            8,
96                            Color::Rgb {
97                                r: 255,
98                                g: 80,
99                                b: 80,
100                            },
101                        );
102                        ctx.fill_circle(
103                            x as u16 - 2,
104                            y as u16 - 2,
105                            3,
106                            Color::Rgb {
107                                r: 255,
108                                g: 200,
109                                b: 200,
110                            },
111                        );
112
113                        // Ground line
114                        ctx.line(10, 58, 190, 58, Color::Grey);
115                    })
116                    .build(),
117            )
118            .child(View::text(""))
119            // Sine wave animation
120            .child(View::text("Sine Waves (30 FPS):"))
121            .child(
122                animated_canvas(cx.clone())
123                    .width(200)
124                    .height(50)
125                    .fps(30)
126                    .on_frame(|ctx, frame| {
127                        ctx.clear(Color::Rgb {
128                            r: 15,
129                            g: 25,
130                            b: 35,
131                        });
132
133                        let phase = frame as f32 * 0.1;
134
135                        // Draw multiple sine waves
136                        let colors = [
137                            Color::Rgb {
138                                r: 255,
139                                g: 100,
140                                b: 100,
141                            },
142                            Color::Rgb {
143                                r: 100,
144                                g: 255,
145                                b: 100,
146                            },
147                            Color::Rgb {
148                                r: 100,
149                                g: 100,
150                                b: 255,
151                            },
152                        ];
153
154                        for (wave_idx, color) in colors.iter().enumerate() {
155                            let offset = wave_idx as f32 * 0.5;
156                            let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158                            for x in 0..200 {
159                                let t = x as f32 * 0.05 + phase + offset;
160                                let y = 25.0 + amplitude * t.sin();
161                                ctx.pixel(x, y as u16, *color);
162
163                                // Make line thicker
164                                if y as u16 > 0 {
165                                    ctx.pixel(x, y as u16 - 1, *color);
166                                }
167                            }
168                        }
169
170                        // Center line
171                        ctx.line(0, 25, 199, 25, Color::DarkGrey);
172                    })
173                    .build(),
174            )
175            .child(View::text(""))
176            // Particle effect
177            .child(View::text("Particle Fountain (45 FPS):"))
178            .child(
179                animated_canvas(cx.clone())
180                    .width(200)
181                    .height(60)
182                    .fps(45)
183                    .on_frame(|ctx, frame| {
184                        ctx.clear(Color::Rgb {
185                            r: 10,
186                            g: 10,
187                            b: 20,
188                        });
189
190                        // Simple particle system using deterministic "random" based on frame
191                        let num_particles = 30;
192                        for i in 0..num_particles {
193                            // Each particle has a lifecycle based on frame offset
194                            let particle_frame = (frame + i * 7) % 60;
195                            let life = particle_frame as f32 / 60.0;
196
197                            // Starting position (center bottom)
198                            let start_x = 100.0;
199                            let start_y = 55.0;
200
201                            // Velocity varies per particle (deterministic "random")
202                            let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203                            let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205                            // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206                            let vx = angle.cos() * speed;
207                            let vy = angle.sin() * speed;
208                            let gravity = 80.0;
209
210                            let x = start_x + vx * life;
211                            let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213                            // Fade out as particle ages
214                            let alpha = (1.0 - life) * 255.0;
215
216                            // Color based on age (yellow -> orange -> red)
217                            let r = 255;
218                            let g = ((1.0 - life * 0.7) * 200.0) as u8;
219                            let b = ((1.0 - life) * 100.0) as u8;
220
221                            if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222                                let color = Color::Rgb { r, g, b };
223                                ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224                                // Make particles slightly larger
225                                if x > 0.0 {
226                                    ctx.pixel_alpha(
227                                        x as u16 - 1,
228                                        y as u16,
229                                        color,
230                                        (alpha * 0.5) as u8,
231                                    );
232                                }
233                                if y > 0.0 {
234                                    ctx.pixel_alpha(
235                                        x as u16,
236                                        y as u16 - 1,
237                                        color,
238                                        (alpha * 0.5) as u8,
239                                    );
240                                }
241                            }
242                        }
243
244                        // Emitter glow
245                        ctx.fill_circle(100, 55, 3, Color::Yellow);
246                    })
247                    .build(),
248            )
249            .child(View::text(""))
250            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251            .child(
252                View::modal()
253                    .visible(show_help.get())
254                    .title("Example 31: Animated Canvas")
255                    .on_dismiss(with!(show_help => move || show_help.set(false)))
256                    .child(
257                        View::vstack()
258                            .child(View::styled_text("What you're seeing").bold().build())
259                            .child(View::text("• Bouncing ball animation (60 FPS)"))
260                            .child(View::text("• Sine wave visualization (30 FPS)"))
261                            .child(View::text("• Particle fountain (45 FPS)"))
262                            .child(View::gap(1))
263                            .child(View::styled_text("Key concepts").bold().build())
264                            .child(View::text("• animated_canvas(cx) helper"))
265                            .child(View::text("• .fps() sets frame rate"))
266                            .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267                            .child(View::text("• Frame counter enables animation"))
268                            .child(View::gap(1))
269                            .child(View::styled_text("Try this").bold().build())
270                            .child(View::text("• Watch the smooth animations"))
271                            .child(View::text("• Notice different FPS rates"))
272                            .child(View::gap(1))
273                            .child(View::styled_text("Next up").bold().build())
274                            .child(View::text("→ 32_effects: side effects"))
275                            .child(View::gap(1))
276                            .child(View::styled_text("Press Escape to close").dim().build())
277                            .build(),
278                    )
279                    .build(),
280            )
281            .build()
282    }
Source

pub fn pixel(&mut self, x: u16, y: u16, color: Color)

Set a single pixel.

Examples found in repository?
examples/31_animated_canvas.rs (line 161)
33    fn render(&self, cx: Scope) -> View {
34        let show_help = state!(cx, || false);
35
36        // F1 toggles help
37        cx.use_command(
38            KeyBinding::key(KeyCode::F(1)),
39            with!(show_help => move || show_help.update(|v| *v = !*v)),
40        );
41        View::vstack()
42            .spacing(1)
43            .child(
44                View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45                    .bold()
46                    .build(),
47            )
48            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49            .child(View::text(""))
50            // Bouncing ball animation
51            .child(View::text("Bouncing Ball (60 FPS):"))
52            .child(
53                animated_canvas(cx.clone())
54                    .width(200)
55                    .height(60)
56                    .fps(60)
57                    .on_frame(|ctx, frame| {
58                        ctx.clear(Color::Rgb {
59                            r: 20,
60                            g: 20,
61                            b: 30,
62                        });
63
64                        // Ball physics
65                        let t = frame as f32 * 0.05;
66                        let x = 100.0 + 80.0 * t.cos();
67                        let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69                        // Shadow
70                        ctx.fill_circle(
71                            x as u16,
72                            55,
73                            8,
74                            Color::Rgb {
75                                r: 40,
76                                g: 40,
77                                b: 50,
78                            },
79                        );
80
81                        // Ball with gradient effect (outer to inner)
82                        ctx.fill_circle(
83                            x as u16,
84                            y as u16,
85                            12,
86                            Color::Rgb {
87                                r: 200,
88                                g: 50,
89                                b: 50,
90                            },
91                        );
92                        ctx.fill_circle(
93                            x as u16,
94                            y as u16,
95                            8,
96                            Color::Rgb {
97                                r: 255,
98                                g: 80,
99                                b: 80,
100                            },
101                        );
102                        ctx.fill_circle(
103                            x as u16 - 2,
104                            y as u16 - 2,
105                            3,
106                            Color::Rgb {
107                                r: 255,
108                                g: 200,
109                                b: 200,
110                            },
111                        );
112
113                        // Ground line
114                        ctx.line(10, 58, 190, 58, Color::Grey);
115                    })
116                    .build(),
117            )
118            .child(View::text(""))
119            // Sine wave animation
120            .child(View::text("Sine Waves (30 FPS):"))
121            .child(
122                animated_canvas(cx.clone())
123                    .width(200)
124                    .height(50)
125                    .fps(30)
126                    .on_frame(|ctx, frame| {
127                        ctx.clear(Color::Rgb {
128                            r: 15,
129                            g: 25,
130                            b: 35,
131                        });
132
133                        let phase = frame as f32 * 0.1;
134
135                        // Draw multiple sine waves
136                        let colors = [
137                            Color::Rgb {
138                                r: 255,
139                                g: 100,
140                                b: 100,
141                            },
142                            Color::Rgb {
143                                r: 100,
144                                g: 255,
145                                b: 100,
146                            },
147                            Color::Rgb {
148                                r: 100,
149                                g: 100,
150                                b: 255,
151                            },
152                        ];
153
154                        for (wave_idx, color) in colors.iter().enumerate() {
155                            let offset = wave_idx as f32 * 0.5;
156                            let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158                            for x in 0..200 {
159                                let t = x as f32 * 0.05 + phase + offset;
160                                let y = 25.0 + amplitude * t.sin();
161                                ctx.pixel(x, y as u16, *color);
162
163                                // Make line thicker
164                                if y as u16 > 0 {
165                                    ctx.pixel(x, y as u16 - 1, *color);
166                                }
167                            }
168                        }
169
170                        // Center line
171                        ctx.line(0, 25, 199, 25, Color::DarkGrey);
172                    })
173                    .build(),
174            )
175            .child(View::text(""))
176            // Particle effect
177            .child(View::text("Particle Fountain (45 FPS):"))
178            .child(
179                animated_canvas(cx.clone())
180                    .width(200)
181                    .height(60)
182                    .fps(45)
183                    .on_frame(|ctx, frame| {
184                        ctx.clear(Color::Rgb {
185                            r: 10,
186                            g: 10,
187                            b: 20,
188                        });
189
190                        // Simple particle system using deterministic "random" based on frame
191                        let num_particles = 30;
192                        for i in 0..num_particles {
193                            // Each particle has a lifecycle based on frame offset
194                            let particle_frame = (frame + i * 7) % 60;
195                            let life = particle_frame as f32 / 60.0;
196
197                            // Starting position (center bottom)
198                            let start_x = 100.0;
199                            let start_y = 55.0;
200
201                            // Velocity varies per particle (deterministic "random")
202                            let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203                            let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205                            // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206                            let vx = angle.cos() * speed;
207                            let vy = angle.sin() * speed;
208                            let gravity = 80.0;
209
210                            let x = start_x + vx * life;
211                            let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213                            // Fade out as particle ages
214                            let alpha = (1.0 - life) * 255.0;
215
216                            // Color based on age (yellow -> orange -> red)
217                            let r = 255;
218                            let g = ((1.0 - life * 0.7) * 200.0) as u8;
219                            let b = ((1.0 - life) * 100.0) as u8;
220
221                            if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222                                let color = Color::Rgb { r, g, b };
223                                ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224                                // Make particles slightly larger
225                                if x > 0.0 {
226                                    ctx.pixel_alpha(
227                                        x as u16 - 1,
228                                        y as u16,
229                                        color,
230                                        (alpha * 0.5) as u8,
231                                    );
232                                }
233                                if y > 0.0 {
234                                    ctx.pixel_alpha(
235                                        x as u16,
236                                        y as u16 - 1,
237                                        color,
238                                        (alpha * 0.5) as u8,
239                                    );
240                                }
241                            }
242                        }
243
244                        // Emitter glow
245                        ctx.fill_circle(100, 55, 3, Color::Yellow);
246                    })
247                    .build(),
248            )
249            .child(View::text(""))
250            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251            .child(
252                View::modal()
253                    .visible(show_help.get())
254                    .title("Example 31: Animated Canvas")
255                    .on_dismiss(with!(show_help => move || show_help.set(false)))
256                    .child(
257                        View::vstack()
258                            .child(View::styled_text("What you're seeing").bold().build())
259                            .child(View::text("• Bouncing ball animation (60 FPS)"))
260                            .child(View::text("• Sine wave visualization (30 FPS)"))
261                            .child(View::text("• Particle fountain (45 FPS)"))
262                            .child(View::gap(1))
263                            .child(View::styled_text("Key concepts").bold().build())
264                            .child(View::text("• animated_canvas(cx) helper"))
265                            .child(View::text("• .fps() sets frame rate"))
266                            .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267                            .child(View::text("• Frame counter enables animation"))
268                            .child(View::gap(1))
269                            .child(View::styled_text("Try this").bold().build())
270                            .child(View::text("• Watch the smooth animations"))
271                            .child(View::text("• Notice different FPS rates"))
272                            .child(View::gap(1))
273                            .child(View::styled_text("Next up").bold().build())
274                            .child(View::text("→ 32_effects: side effects"))
275                            .child(View::gap(1))
276                            .child(View::styled_text("Press Escape to close").dim().build())
277                            .build(),
278                    )
279                    .build(),
280            )
281            .build()
282    }
Source

pub fn pixel_alpha(&mut self, x: u16, y: u16, color: Color, alpha: u8)

Set a pixel with alpha.

Examples found in repository?
examples/31_animated_canvas.rs (line 223)
33    fn render(&self, cx: Scope) -> View {
34        let show_help = state!(cx, || false);
35
36        // F1 toggles help
37        cx.use_command(
38            KeyBinding::key(KeyCode::F(1)),
39            with!(show_help => move || show_help.update(|v| *v = !*v)),
40        );
41        View::vstack()
42            .spacing(1)
43            .child(
44                View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45                    .bold()
46                    .build(),
47            )
48            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49            .child(View::text(""))
50            // Bouncing ball animation
51            .child(View::text("Bouncing Ball (60 FPS):"))
52            .child(
53                animated_canvas(cx.clone())
54                    .width(200)
55                    .height(60)
56                    .fps(60)
57                    .on_frame(|ctx, frame| {
58                        ctx.clear(Color::Rgb {
59                            r: 20,
60                            g: 20,
61                            b: 30,
62                        });
63
64                        // Ball physics
65                        let t = frame as f32 * 0.05;
66                        let x = 100.0 + 80.0 * t.cos();
67                        let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69                        // Shadow
70                        ctx.fill_circle(
71                            x as u16,
72                            55,
73                            8,
74                            Color::Rgb {
75                                r: 40,
76                                g: 40,
77                                b: 50,
78                            },
79                        );
80
81                        // Ball with gradient effect (outer to inner)
82                        ctx.fill_circle(
83                            x as u16,
84                            y as u16,
85                            12,
86                            Color::Rgb {
87                                r: 200,
88                                g: 50,
89                                b: 50,
90                            },
91                        );
92                        ctx.fill_circle(
93                            x as u16,
94                            y as u16,
95                            8,
96                            Color::Rgb {
97                                r: 255,
98                                g: 80,
99                                b: 80,
100                            },
101                        );
102                        ctx.fill_circle(
103                            x as u16 - 2,
104                            y as u16 - 2,
105                            3,
106                            Color::Rgb {
107                                r: 255,
108                                g: 200,
109                                b: 200,
110                            },
111                        );
112
113                        // Ground line
114                        ctx.line(10, 58, 190, 58, Color::Grey);
115                    })
116                    .build(),
117            )
118            .child(View::text(""))
119            // Sine wave animation
120            .child(View::text("Sine Waves (30 FPS):"))
121            .child(
122                animated_canvas(cx.clone())
123                    .width(200)
124                    .height(50)
125                    .fps(30)
126                    .on_frame(|ctx, frame| {
127                        ctx.clear(Color::Rgb {
128                            r: 15,
129                            g: 25,
130                            b: 35,
131                        });
132
133                        let phase = frame as f32 * 0.1;
134
135                        // Draw multiple sine waves
136                        let colors = [
137                            Color::Rgb {
138                                r: 255,
139                                g: 100,
140                                b: 100,
141                            },
142                            Color::Rgb {
143                                r: 100,
144                                g: 255,
145                                b: 100,
146                            },
147                            Color::Rgb {
148                                r: 100,
149                                g: 100,
150                                b: 255,
151                            },
152                        ];
153
154                        for (wave_idx, color) in colors.iter().enumerate() {
155                            let offset = wave_idx as f32 * 0.5;
156                            let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158                            for x in 0..200 {
159                                let t = x as f32 * 0.05 + phase + offset;
160                                let y = 25.0 + amplitude * t.sin();
161                                ctx.pixel(x, y as u16, *color);
162
163                                // Make line thicker
164                                if y as u16 > 0 {
165                                    ctx.pixel(x, y as u16 - 1, *color);
166                                }
167                            }
168                        }
169
170                        // Center line
171                        ctx.line(0, 25, 199, 25, Color::DarkGrey);
172                    })
173                    .build(),
174            )
175            .child(View::text(""))
176            // Particle effect
177            .child(View::text("Particle Fountain (45 FPS):"))
178            .child(
179                animated_canvas(cx.clone())
180                    .width(200)
181                    .height(60)
182                    .fps(45)
183                    .on_frame(|ctx, frame| {
184                        ctx.clear(Color::Rgb {
185                            r: 10,
186                            g: 10,
187                            b: 20,
188                        });
189
190                        // Simple particle system using deterministic "random" based on frame
191                        let num_particles = 30;
192                        for i in 0..num_particles {
193                            // Each particle has a lifecycle based on frame offset
194                            let particle_frame = (frame + i * 7) % 60;
195                            let life = particle_frame as f32 / 60.0;
196
197                            // Starting position (center bottom)
198                            let start_x = 100.0;
199                            let start_y = 55.0;
200
201                            // Velocity varies per particle (deterministic "random")
202                            let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203                            let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205                            // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206                            let vx = angle.cos() * speed;
207                            let vy = angle.sin() * speed;
208                            let gravity = 80.0;
209
210                            let x = start_x + vx * life;
211                            let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213                            // Fade out as particle ages
214                            let alpha = (1.0 - life) * 255.0;
215
216                            // Color based on age (yellow -> orange -> red)
217                            let r = 255;
218                            let g = ((1.0 - life * 0.7) * 200.0) as u8;
219                            let b = ((1.0 - life) * 100.0) as u8;
220
221                            if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222                                let color = Color::Rgb { r, g, b };
223                                ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224                                // Make particles slightly larger
225                                if x > 0.0 {
226                                    ctx.pixel_alpha(
227                                        x as u16 - 1,
228                                        y as u16,
229                                        color,
230                                        (alpha * 0.5) as u8,
231                                    );
232                                }
233                                if y > 0.0 {
234                                    ctx.pixel_alpha(
235                                        x as u16,
236                                        y as u16 - 1,
237                                        color,
238                                        (alpha * 0.5) as u8,
239                                    );
240                                }
241                            }
242                        }
243
244                        // Emitter glow
245                        ctx.fill_circle(100, 55, 3, Color::Yellow);
246                    })
247                    .build(),
248            )
249            .child(View::text(""))
250            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251            .child(
252                View::modal()
253                    .visible(show_help.get())
254                    .title("Example 31: Animated Canvas")
255                    .on_dismiss(with!(show_help => move || show_help.set(false)))
256                    .child(
257                        View::vstack()
258                            .child(View::styled_text("What you're seeing").bold().build())
259                            .child(View::text("• Bouncing ball animation (60 FPS)"))
260                            .child(View::text("• Sine wave visualization (30 FPS)"))
261                            .child(View::text("• Particle fountain (45 FPS)"))
262                            .child(View::gap(1))
263                            .child(View::styled_text("Key concepts").bold().build())
264                            .child(View::text("• animated_canvas(cx) helper"))
265                            .child(View::text("• .fps() sets frame rate"))
266                            .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267                            .child(View::text("• Frame counter enables animation"))
268                            .child(View::gap(1))
269                            .child(View::styled_text("Try this").bold().build())
270                            .child(View::text("• Watch the smooth animations"))
271                            .child(View::text("• Notice different FPS rates"))
272                            .child(View::gap(1))
273                            .child(View::styled_text("Next up").bold().build())
274                            .child(View::text("→ 32_effects: side effects"))
275                            .child(View::gap(1))
276                            .child(View::styled_text("Press Escape to close").dim().build())
277                            .build(),
278                    )
279                    .build(),
280            )
281            .build()
282    }
Source

pub fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Color)

Draw a line using Bresenham’s algorithm.

Examples found in repository?
examples/29_canvas.rs (line 75)
31    fn render(&self, cx: Scope) -> View {
32        let show_help = state!(cx, || false);
33
34        // F1 toggles help
35        cx.use_command(
36            KeyBinding::key(KeyCode::F(1)),
37            with!(show_help => move || show_help.update(|v| *v = !*v)),
38        );
39
40        // Animated value for the bar chart using stream macro
41        let frame_stream = stream!(cx, || {
42            (0u32..).inspect(|&i| {
43                if i > 0 {
44                    std::thread::sleep(std::time::Duration::from_millis(100));
45                }
46            })
47        });
48
49        let current_frame = frame_stream.get();
50
51        View::vstack()
52            .spacing(1)
53            .child(
54                View::styled_text("Canvas Examples (Kitty Graphics)")
55                    .bold()
56                    .build(),
57            )
58            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59            .child(View::text(""))
60            // Basic shapes demo
61            .child(View::text("Basic Shapes:"))
62            .child(
63                View::canvas()
64                    .width(200)
65                    .height(80)
66                    .on_draw(|ctx| {
67                        // Clear to dark background
68                        ctx.clear(Color::Rgb {
69                            r: 30,
70                            g: 30,
71                            b: 40,
72                        });
73
74                        // Draw some lines
75                        ctx.line(10, 10, 190, 10, Color::Red);
76                        ctx.line(10, 10, 10, 70, Color::Green);
77                        ctx.line(10, 70, 190, 70, Color::Blue);
78                        ctx.line(190, 10, 190, 70, Color::Yellow);
79
80                        // Draw diagonal lines
81                        ctx.line(10, 10, 190, 70, Color::Cyan);
82                        ctx.line(10, 70, 190, 10, Color::Magenta);
83
84                        // Draw filled rectangles
85                        ctx.fill_rect(30, 25, 30, 20, Color::Red);
86                        ctx.fill_rect(80, 25, 30, 20, Color::Green);
87                        ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89                        // Draw stroked rectangles
90                        ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91                        ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92                        ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93                    })
94                    .build(),
95            )
96            .child(View::text(""))
97            // Circles demo
98            .child(View::text("Circles:"))
99            .child(
100                View::canvas()
101                    .width(200)
102                    .height(60)
103                    .on_draw(|ctx| {
104                        ctx.clear(Color::Rgb {
105                            r: 20,
106                            g: 25,
107                            b: 35,
108                        });
109
110                        // Filled circles
111                        ctx.fill_circle(30, 30, 20, Color::Red);
112                        ctx.fill_circle(80, 30, 15, Color::Green);
113                        ctx.fill_circle(120, 30, 10, Color::Blue);
114
115                        // Stroked circles
116                        ctx.circle(160, 30, 20, Color::Yellow);
117                        ctx.circle(160, 30, 15, Color::Cyan);
118                        ctx.circle(160, 30, 10, Color::Magenta);
119                    })
120                    .build(),
121            )
122            .child(View::text(""))
123            // Animated bar chart
124            .child(View::text("Animated Bar Chart:"))
125            .child(
126                View::canvas()
127                    .width(200)
128                    .height(80)
129                    .on_draw({
130                        move |ctx| {
131                            ctx.clear(Color::Rgb {
132                                r: 25,
133                                g: 25,
134                                b: 30,
135                            });
136
137                            // Generate animated data
138                            let data: Vec<f32> = (0..8)
139                                .map(|i| {
140                                    let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141                                    0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142                                })
143                                .collect();
144
145                            let bar_width = 20u16;
146                            let gap = 5u16;
147                            let max_height = 60u16;
148                            let start_x = 10u16;
149                            let baseline = 75u16;
150
151                            // Draw baseline
152                            ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154                            // Draw bars
155                            let colors = [
156                                Color::Red,
157                                Color::Green,
158                                Color::Blue,
159                                Color::Yellow,
160                                Color::Cyan,
161                                Color::Magenta,
162                                Color::Rgb {
163                                    r: 255,
164                                    g: 128,
165                                    b: 0,
166                                },
167                                Color::Rgb {
168                                    r: 128,
169                                    g: 255,
170                                    b: 128,
171                                },
172                            ];
173
174                            for (i, &value) in data.iter().enumerate() {
175                                let x = start_x + (i as u16) * (bar_width + gap);
176                                let height = (value * max_height as f32) as u16;
177                                let y = baseline - height;
178                                ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179                            }
180                        }
181                    })
182                    .build(),
183            )
184            .child(View::text(""))
185            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186            .child(
187                View::modal()
188                    .visible(show_help.get())
189                    .title("Example 29: Canvas")
190                    .on_dismiss(with!(show_help => move || show_help.set(false)))
191                    .child(
192                        View::vstack()
193                            .child(View::styled_text("What you're seeing").bold().build())
194                            .child(View::text("• Pixel graphics via Kitty protocol"))
195                            .child(View::text("• Lines, rectangles, circles"))
196                            .child(View::text("• Animated bar chart"))
197                            .child(View::gap(1))
198                            .child(View::styled_text("Key concepts").bold().build())
199                            .child(View::text("• View::canvas() creates drawing area"))
200                            .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201                            .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203                            .child(View::gap(1))
204                            .child(View::styled_text("Try this").bold().build())
205                            .child(View::text("• Watch the animated bar chart"))
206                            .child(View::text("• Run in compatible terminal"))
207                            .child(View::gap(1))
208                            .child(View::styled_text("Next up").bold().build())
209                            .child(View::text("→ 30_image: image display"))
210                            .child(View::gap(1))
211                            .child(View::styled_text("Press Escape to close").dim().build())
212                            .build(),
213                    )
214                    .build(),
215            )
216            .build()
217    }
More examples
Hide additional examples
examples/31_animated_canvas.rs (line 114)
33    fn render(&self, cx: Scope) -> View {
34        let show_help = state!(cx, || false);
35
36        // F1 toggles help
37        cx.use_command(
38            KeyBinding::key(KeyCode::F(1)),
39            with!(show_help => move || show_help.update(|v| *v = !*v)),
40        );
41        View::vstack()
42            .spacing(1)
43            .child(
44                View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45                    .bold()
46                    .build(),
47            )
48            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49            .child(View::text(""))
50            // Bouncing ball animation
51            .child(View::text("Bouncing Ball (60 FPS):"))
52            .child(
53                animated_canvas(cx.clone())
54                    .width(200)
55                    .height(60)
56                    .fps(60)
57                    .on_frame(|ctx, frame| {
58                        ctx.clear(Color::Rgb {
59                            r: 20,
60                            g: 20,
61                            b: 30,
62                        });
63
64                        // Ball physics
65                        let t = frame as f32 * 0.05;
66                        let x = 100.0 + 80.0 * t.cos();
67                        let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69                        // Shadow
70                        ctx.fill_circle(
71                            x as u16,
72                            55,
73                            8,
74                            Color::Rgb {
75                                r: 40,
76                                g: 40,
77                                b: 50,
78                            },
79                        );
80
81                        // Ball with gradient effect (outer to inner)
82                        ctx.fill_circle(
83                            x as u16,
84                            y as u16,
85                            12,
86                            Color::Rgb {
87                                r: 200,
88                                g: 50,
89                                b: 50,
90                            },
91                        );
92                        ctx.fill_circle(
93                            x as u16,
94                            y as u16,
95                            8,
96                            Color::Rgb {
97                                r: 255,
98                                g: 80,
99                                b: 80,
100                            },
101                        );
102                        ctx.fill_circle(
103                            x as u16 - 2,
104                            y as u16 - 2,
105                            3,
106                            Color::Rgb {
107                                r: 255,
108                                g: 200,
109                                b: 200,
110                            },
111                        );
112
113                        // Ground line
114                        ctx.line(10, 58, 190, 58, Color::Grey);
115                    })
116                    .build(),
117            )
118            .child(View::text(""))
119            // Sine wave animation
120            .child(View::text("Sine Waves (30 FPS):"))
121            .child(
122                animated_canvas(cx.clone())
123                    .width(200)
124                    .height(50)
125                    .fps(30)
126                    .on_frame(|ctx, frame| {
127                        ctx.clear(Color::Rgb {
128                            r: 15,
129                            g: 25,
130                            b: 35,
131                        });
132
133                        let phase = frame as f32 * 0.1;
134
135                        // Draw multiple sine waves
136                        let colors = [
137                            Color::Rgb {
138                                r: 255,
139                                g: 100,
140                                b: 100,
141                            },
142                            Color::Rgb {
143                                r: 100,
144                                g: 255,
145                                b: 100,
146                            },
147                            Color::Rgb {
148                                r: 100,
149                                g: 100,
150                                b: 255,
151                            },
152                        ];
153
154                        for (wave_idx, color) in colors.iter().enumerate() {
155                            let offset = wave_idx as f32 * 0.5;
156                            let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158                            for x in 0..200 {
159                                let t = x as f32 * 0.05 + phase + offset;
160                                let y = 25.0 + amplitude * t.sin();
161                                ctx.pixel(x, y as u16, *color);
162
163                                // Make line thicker
164                                if y as u16 > 0 {
165                                    ctx.pixel(x, y as u16 - 1, *color);
166                                }
167                            }
168                        }
169
170                        // Center line
171                        ctx.line(0, 25, 199, 25, Color::DarkGrey);
172                    })
173                    .build(),
174            )
175            .child(View::text(""))
176            // Particle effect
177            .child(View::text("Particle Fountain (45 FPS):"))
178            .child(
179                animated_canvas(cx.clone())
180                    .width(200)
181                    .height(60)
182                    .fps(45)
183                    .on_frame(|ctx, frame| {
184                        ctx.clear(Color::Rgb {
185                            r: 10,
186                            g: 10,
187                            b: 20,
188                        });
189
190                        // Simple particle system using deterministic "random" based on frame
191                        let num_particles = 30;
192                        for i in 0..num_particles {
193                            // Each particle has a lifecycle based on frame offset
194                            let particle_frame = (frame + i * 7) % 60;
195                            let life = particle_frame as f32 / 60.0;
196
197                            // Starting position (center bottom)
198                            let start_x = 100.0;
199                            let start_y = 55.0;
200
201                            // Velocity varies per particle (deterministic "random")
202                            let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203                            let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205                            // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206                            let vx = angle.cos() * speed;
207                            let vy = angle.sin() * speed;
208                            let gravity = 80.0;
209
210                            let x = start_x + vx * life;
211                            let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213                            // Fade out as particle ages
214                            let alpha = (1.0 - life) * 255.0;
215
216                            // Color based on age (yellow -> orange -> red)
217                            let r = 255;
218                            let g = ((1.0 - life * 0.7) * 200.0) as u8;
219                            let b = ((1.0 - life) * 100.0) as u8;
220
221                            if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222                                let color = Color::Rgb { r, g, b };
223                                ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224                                // Make particles slightly larger
225                                if x > 0.0 {
226                                    ctx.pixel_alpha(
227                                        x as u16 - 1,
228                                        y as u16,
229                                        color,
230                                        (alpha * 0.5) as u8,
231                                    );
232                                }
233                                if y > 0.0 {
234                                    ctx.pixel_alpha(
235                                        x as u16,
236                                        y as u16 - 1,
237                                        color,
238                                        (alpha * 0.5) as u8,
239                                    );
240                                }
241                            }
242                        }
243
244                        // Emitter glow
245                        ctx.fill_circle(100, 55, 3, Color::Yellow);
246                    })
247                    .build(),
248            )
249            .child(View::text(""))
250            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251            .child(
252                View::modal()
253                    .visible(show_help.get())
254                    .title("Example 31: Animated Canvas")
255                    .on_dismiss(with!(show_help => move || show_help.set(false)))
256                    .child(
257                        View::vstack()
258                            .child(View::styled_text("What you're seeing").bold().build())
259                            .child(View::text("• Bouncing ball animation (60 FPS)"))
260                            .child(View::text("• Sine wave visualization (30 FPS)"))
261                            .child(View::text("• Particle fountain (45 FPS)"))
262                            .child(View::gap(1))
263                            .child(View::styled_text("Key concepts").bold().build())
264                            .child(View::text("• animated_canvas(cx) helper"))
265                            .child(View::text("• .fps() sets frame rate"))
266                            .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267                            .child(View::text("• Frame counter enables animation"))
268                            .child(View::gap(1))
269                            .child(View::styled_text("Try this").bold().build())
270                            .child(View::text("• Watch the smooth animations"))
271                            .child(View::text("• Notice different FPS rates"))
272                            .child(View::gap(1))
273                            .child(View::styled_text("Next up").bold().build())
274                            .child(View::text("→ 32_effects: side effects"))
275                            .child(View::gap(1))
276                            .child(View::styled_text("Press Escape to close").dim().build())
277                            .build(),
278                    )
279                    .build(),
280            )
281            .build()
282    }
Source

pub fn stroke_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: Color)

Draw a stroked rectangle (outline only).

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

pub fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: Color)

Draw a filled rectangle.

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

pub fn circle(&mut self, cx: u16, cy: u16, radius: u16, color: Color)

Draw a stroked circle (outline only) using midpoint algorithm.

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

pub fn fill_circle(&mut self, cx: u16, cy: u16, radius: u16, color: Color)

Draw a filled circle.

Examples found in repository?
examples/29_canvas.rs (line 111)
31    fn render(&self, cx: Scope) -> View {
32        let show_help = state!(cx, || false);
33
34        // F1 toggles help
35        cx.use_command(
36            KeyBinding::key(KeyCode::F(1)),
37            with!(show_help => move || show_help.update(|v| *v = !*v)),
38        );
39
40        // Animated value for the bar chart using stream macro
41        let frame_stream = stream!(cx, || {
42            (0u32..).inspect(|&i| {
43                if i > 0 {
44                    std::thread::sleep(std::time::Duration::from_millis(100));
45                }
46            })
47        });
48
49        let current_frame = frame_stream.get();
50
51        View::vstack()
52            .spacing(1)
53            .child(
54                View::styled_text("Canvas Examples (Kitty Graphics)")
55                    .bold()
56                    .build(),
57            )
58            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
59            .child(View::text(""))
60            // Basic shapes demo
61            .child(View::text("Basic Shapes:"))
62            .child(
63                View::canvas()
64                    .width(200)
65                    .height(80)
66                    .on_draw(|ctx| {
67                        // Clear to dark background
68                        ctx.clear(Color::Rgb {
69                            r: 30,
70                            g: 30,
71                            b: 40,
72                        });
73
74                        // Draw some lines
75                        ctx.line(10, 10, 190, 10, Color::Red);
76                        ctx.line(10, 10, 10, 70, Color::Green);
77                        ctx.line(10, 70, 190, 70, Color::Blue);
78                        ctx.line(190, 10, 190, 70, Color::Yellow);
79
80                        // Draw diagonal lines
81                        ctx.line(10, 10, 190, 70, Color::Cyan);
82                        ctx.line(10, 70, 190, 10, Color::Magenta);
83
84                        // Draw filled rectangles
85                        ctx.fill_rect(30, 25, 30, 20, Color::Red);
86                        ctx.fill_rect(80, 25, 30, 20, Color::Green);
87                        ctx.fill_rect(130, 25, 30, 20, Color::Blue);
88
89                        // Draw stroked rectangles
90                        ctx.stroke_rect(30, 50, 30, 15, Color::Yellow);
91                        ctx.stroke_rect(80, 50, 30, 15, Color::Cyan);
92                        ctx.stroke_rect(130, 50, 30, 15, Color::Magenta);
93                    })
94                    .build(),
95            )
96            .child(View::text(""))
97            // Circles demo
98            .child(View::text("Circles:"))
99            .child(
100                View::canvas()
101                    .width(200)
102                    .height(60)
103                    .on_draw(|ctx| {
104                        ctx.clear(Color::Rgb {
105                            r: 20,
106                            g: 25,
107                            b: 35,
108                        });
109
110                        // Filled circles
111                        ctx.fill_circle(30, 30, 20, Color::Red);
112                        ctx.fill_circle(80, 30, 15, Color::Green);
113                        ctx.fill_circle(120, 30, 10, Color::Blue);
114
115                        // Stroked circles
116                        ctx.circle(160, 30, 20, Color::Yellow);
117                        ctx.circle(160, 30, 15, Color::Cyan);
118                        ctx.circle(160, 30, 10, Color::Magenta);
119                    })
120                    .build(),
121            )
122            .child(View::text(""))
123            // Animated bar chart
124            .child(View::text("Animated Bar Chart:"))
125            .child(
126                View::canvas()
127                    .width(200)
128                    .height(80)
129                    .on_draw({
130                        move |ctx| {
131                            ctx.clear(Color::Rgb {
132                                r: 25,
133                                g: 25,
134                                b: 30,
135                            });
136
137                            // Generate animated data
138                            let data: Vec<f32> = (0..8)
139                                .map(|i| {
140                                    let phase = (current_frame as f32 * 0.1) + (i as f32 * 0.5);
141                                    0.3 + 0.7 * ((phase.sin() + 1.0) / 2.0)
142                                })
143                                .collect();
144
145                            let bar_width = 20u16;
146                            let gap = 5u16;
147                            let max_height = 60u16;
148                            let start_x = 10u16;
149                            let baseline = 75u16;
150
151                            // Draw baseline
152                            ctx.line(5, baseline as i32, 195, baseline as i32, Color::Grey);
153
154                            // Draw bars
155                            let colors = [
156                                Color::Red,
157                                Color::Green,
158                                Color::Blue,
159                                Color::Yellow,
160                                Color::Cyan,
161                                Color::Magenta,
162                                Color::Rgb {
163                                    r: 255,
164                                    g: 128,
165                                    b: 0,
166                                },
167                                Color::Rgb {
168                                    r: 128,
169                                    g: 255,
170                                    b: 128,
171                                },
172                            ];
173
174                            for (i, &value) in data.iter().enumerate() {
175                                let x = start_x + (i as u16) * (bar_width + gap);
176                                let height = (value * max_height as f32) as u16;
177                                let y = baseline - height;
178                                ctx.fill_rect(x, y, bar_width, height, colors[i % colors.len()]);
179                            }
180                        }
181                    })
182                    .build(),
183            )
184            .child(View::text(""))
185            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
186            .child(
187                View::modal()
188                    .visible(show_help.get())
189                    .title("Example 29: Canvas")
190                    .on_dismiss(with!(show_help => move || show_help.set(false)))
191                    .child(
192                        View::vstack()
193                            .child(View::styled_text("What you're seeing").bold().build())
194                            .child(View::text("• Pixel graphics via Kitty protocol"))
195                            .child(View::text("• Lines, rectangles, circles"))
196                            .child(View::text("• Animated bar chart"))
197                            .child(View::gap(1))
198                            .child(View::styled_text("Key concepts").bold().build())
199                            .child(View::text("• View::canvas() creates drawing area"))
200                            .child(View::text("• .on_draw(|ctx| { ... }) draws pixels"))
201                            .child(View::text("• ctx.line(), ctx.fill_rect(), etc."))
202                            .child(View::text("• Works in Kitty/Ghostty/WezTerm"))
203                            .child(View::gap(1))
204                            .child(View::styled_text("Try this").bold().build())
205                            .child(View::text("• Watch the animated bar chart"))
206                            .child(View::text("• Run in compatible terminal"))
207                            .child(View::gap(1))
208                            .child(View::styled_text("Next up").bold().build())
209                            .child(View::text("→ 30_image: image display"))
210                            .child(View::gap(1))
211                            .child(View::styled_text("Press Escape to close").dim().build())
212                            .build(),
213                    )
214                    .build(),
215            )
216            .build()
217    }
More examples
Hide additional examples
examples/31_animated_canvas.rs (lines 70-79)
33    fn render(&self, cx: Scope) -> View {
34        let show_help = state!(cx, || false);
35
36        // F1 toggles help
37        cx.use_command(
38            KeyBinding::key(KeyCode::F(1)),
39            with!(show_help => move || show_help.update(|v| *v = !*v)),
40        );
41        View::vstack()
42            .spacing(1)
43            .child(
44                View::styled_text("Animated Canvas Demo (Kitty Graphics)")
45                    .bold()
46                    .build(),
47            )
48            .child(View::text("Requires Kitty, Ghostty, or WezTerm terminal"))
49            .child(View::text(""))
50            // Bouncing ball animation
51            .child(View::text("Bouncing Ball (60 FPS):"))
52            .child(
53                animated_canvas(cx.clone())
54                    .width(200)
55                    .height(60)
56                    .fps(60)
57                    .on_frame(|ctx, frame| {
58                        ctx.clear(Color::Rgb {
59                            r: 20,
60                            g: 20,
61                            b: 30,
62                        });
63
64                        // Ball physics
65                        let t = frame as f32 * 0.05;
66                        let x = 100.0 + 80.0 * t.cos();
67                        let y = 30.0 + 20.0 * (t * 1.5).sin().abs();
68
69                        // Shadow
70                        ctx.fill_circle(
71                            x as u16,
72                            55,
73                            8,
74                            Color::Rgb {
75                                r: 40,
76                                g: 40,
77                                b: 50,
78                            },
79                        );
80
81                        // Ball with gradient effect (outer to inner)
82                        ctx.fill_circle(
83                            x as u16,
84                            y as u16,
85                            12,
86                            Color::Rgb {
87                                r: 200,
88                                g: 50,
89                                b: 50,
90                            },
91                        );
92                        ctx.fill_circle(
93                            x as u16,
94                            y as u16,
95                            8,
96                            Color::Rgb {
97                                r: 255,
98                                g: 80,
99                                b: 80,
100                            },
101                        );
102                        ctx.fill_circle(
103                            x as u16 - 2,
104                            y as u16 - 2,
105                            3,
106                            Color::Rgb {
107                                r: 255,
108                                g: 200,
109                                b: 200,
110                            },
111                        );
112
113                        // Ground line
114                        ctx.line(10, 58, 190, 58, Color::Grey);
115                    })
116                    .build(),
117            )
118            .child(View::text(""))
119            // Sine wave animation
120            .child(View::text("Sine Waves (30 FPS):"))
121            .child(
122                animated_canvas(cx.clone())
123                    .width(200)
124                    .height(50)
125                    .fps(30)
126                    .on_frame(|ctx, frame| {
127                        ctx.clear(Color::Rgb {
128                            r: 15,
129                            g: 25,
130                            b: 35,
131                        });
132
133                        let phase = frame as f32 * 0.1;
134
135                        // Draw multiple sine waves
136                        let colors = [
137                            Color::Rgb {
138                                r: 255,
139                                g: 100,
140                                b: 100,
141                            },
142                            Color::Rgb {
143                                r: 100,
144                                g: 255,
145                                b: 100,
146                            },
147                            Color::Rgb {
148                                r: 100,
149                                g: 100,
150                                b: 255,
151                            },
152                        ];
153
154                        for (wave_idx, color) in colors.iter().enumerate() {
155                            let offset = wave_idx as f32 * 0.5;
156                            let amplitude = 15.0 - wave_idx as f32 * 3.0;
157
158                            for x in 0..200 {
159                                let t = x as f32 * 0.05 + phase + offset;
160                                let y = 25.0 + amplitude * t.sin();
161                                ctx.pixel(x, y as u16, *color);
162
163                                // Make line thicker
164                                if y as u16 > 0 {
165                                    ctx.pixel(x, y as u16 - 1, *color);
166                                }
167                            }
168                        }
169
170                        // Center line
171                        ctx.line(0, 25, 199, 25, Color::DarkGrey);
172                    })
173                    .build(),
174            )
175            .child(View::text(""))
176            // Particle effect
177            .child(View::text("Particle Fountain (45 FPS):"))
178            .child(
179                animated_canvas(cx.clone())
180                    .width(200)
181                    .height(60)
182                    .fps(45)
183                    .on_frame(|ctx, frame| {
184                        ctx.clear(Color::Rgb {
185                            r: 10,
186                            g: 10,
187                            b: 20,
188                        });
189
190                        // Simple particle system using deterministic "random" based on frame
191                        let num_particles = 30;
192                        for i in 0..num_particles {
193                            // Each particle has a lifecycle based on frame offset
194                            let particle_frame = (frame + i * 7) % 60;
195                            let life = particle_frame as f32 / 60.0;
196
197                            // Starting position (center bottom)
198                            let start_x = 100.0;
199                            let start_y = 55.0;
200
201                            // Velocity varies per particle (deterministic "random")
202                            let angle = -1.57 + (i as f32 * 0.21).sin() * 0.8; // Around -90 degrees
203                            let speed = 40.0 + (i as f32 * 0.37).cos() * 20.0;
204
205                            // Physics: position = start + velocity * time + 0.5 * gravity * time^2
206                            let vx = angle.cos() * speed;
207                            let vy = angle.sin() * speed;
208                            let gravity = 80.0;
209
210                            let x = start_x + vx * life;
211                            let y = start_y + vy * life + 0.5 * gravity * life * life;
212
213                            // Fade out as particle ages
214                            let alpha = (1.0 - life) * 255.0;
215
216                            // Color based on age (yellow -> orange -> red)
217                            let r = 255;
218                            let g = ((1.0 - life * 0.7) * 200.0) as u8;
219                            let b = ((1.0 - life) * 100.0) as u8;
220
221                            if (0.0..200.0).contains(&x) && (0.0..60.0).contains(&y) {
222                                let color = Color::Rgb { r, g, b };
223                                ctx.pixel_alpha(x as u16, y as u16, color, alpha as u8);
224                                // Make particles slightly larger
225                                if x > 0.0 {
226                                    ctx.pixel_alpha(
227                                        x as u16 - 1,
228                                        y as u16,
229                                        color,
230                                        (alpha * 0.5) as u8,
231                                    );
232                                }
233                                if y > 0.0 {
234                                    ctx.pixel_alpha(
235                                        x as u16,
236                                        y as u16 - 1,
237                                        color,
238                                        (alpha * 0.5) as u8,
239                                    );
240                                }
241                            }
242                        }
243
244                        // Emitter glow
245                        ctx.fill_circle(100, 55, 3, Color::Yellow);
246                    })
247                    .build(),
248            )
249            .child(View::text(""))
250            .child(View::styled_text("F1 help • Ctrl+Q quit").dim().build())
251            .child(
252                View::modal()
253                    .visible(show_help.get())
254                    .title("Example 31: Animated Canvas")
255                    .on_dismiss(with!(show_help => move || show_help.set(false)))
256                    .child(
257                        View::vstack()
258                            .child(View::styled_text("What you're seeing").bold().build())
259                            .child(View::text("• Bouncing ball animation (60 FPS)"))
260                            .child(View::text("• Sine wave visualization (30 FPS)"))
261                            .child(View::text("• Particle fountain (45 FPS)"))
262                            .child(View::gap(1))
263                            .child(View::styled_text("Key concepts").bold().build())
264                            .child(View::text("• animated_canvas(cx) helper"))
265                            .child(View::text("• .fps() sets frame rate"))
266                            .child(View::text("• .on_frame(|ctx, frame| { }) draws"))
267                            .child(View::text("• Frame counter enables animation"))
268                            .child(View::gap(1))
269                            .child(View::styled_text("Try this").bold().build())
270                            .child(View::text("• Watch the smooth animations"))
271                            .child(View::text("• Notice different FPS rates"))
272                            .child(View::gap(1))
273                            .child(View::styled_text("Next up").bold().build())
274                            .child(View::text("→ 32_effects: side effects"))
275                            .child(View::gap(1))
276                            .child(View::styled_text("Press Escape to close").dim().build())
277                            .build(),
278                    )
279                    .build(),
280            )
281            .build()
282    }

Auto Trait Implementations§

§

impl<'a> Freeze for DrawContext<'a>

§

impl<'a> RefUnwindSafe for DrawContext<'a>

§

impl<'a> Send for DrawContext<'a>

§

impl<'a> Sync for DrawContext<'a>

§

impl<'a> Unpin for DrawContext<'a>

§

impl<'a> !UnwindSafe for DrawContext<'a>

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> 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> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
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, 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.