Previously, we mentioned that Program has the method draw that every time the corresponding Canvas needed to be re-drawn, the method is called. However, if the shapes of the Canvas remain unchanged, it is not performant to re-draw all these shapes. Instead, we store an image of these shapes in a Cache, and we draw this cache when the draw method of Program is called.
To do so, we declare a field of type Cache in our app
struct MyApp {
cache: Cache,
}
and initialize it.
fn new() -> Self {
Self {
cache: Cache::new(),
}
}
In the draw method, instead of creating a new Frame
let mut frame = Frame::new(renderer, bounds.size());
// ...
vec![frame.into_geometry()]
we use cache.draw to construct the Geometry.
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
// ...
});
vec![geometry]
The closure |frame| { /* ... */ }
is only called when the dimensions of the frame
change or the cache is explicitly cleared.
In addition, previously, we implement Program for the struct MyProgram
.
But because we need to access the cache
field of MyApp
, we have to implement Program for MyApp
.
The full code is as follows:
use iced::{
mouse,
widget::{
canvas::{Cache, Geometry, Path, Program, Stroke},
column, Canvas,
},
Alignment, Color, Length, Point, Rectangle, Renderer, Sandbox, Settings, Theme, Vector,
};
fn main() -> iced::Result {
MyApp::run(Settings::default())
}
struct MyApp {
cache: Cache,
}
impl Sandbox for MyApp {
type Message = ();
fn new() -> Self {
Self {
cache: Cache::new(),
}
}
fn title(&self) -> String {
String::from("My App")
}
fn update(&mut self, _message: Self::Message) {}
fn view(&self) -> iced::Element<Self::Message> {
column![
"A Canvas",
Canvas::new(self).width(Length::Fill).height(Length::Fill)
]
.align_items(Alignment::Center)
.into()
}
}
impl<Message> Program<Message> for MyApp {
type State = ();
fn draw(
&self,
_state: &Self::State,
renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
) -> Vec<Geometry> {
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
frame.fill_rectangle(Point::ORIGIN, bounds.size(), Color::from_rgb(0.0, 0.2, 0.4));
frame.fill(
&Path::circle(frame.center(), frame.width().min(frame.height()) / 4.0),
Color::from_rgb(0.6, 0.8, 1.0),
);
frame.stroke(
&Path::line(
frame.center() + Vector::new(-250.0, 100.0),
frame.center() + Vector::new(250.0, -100.0),
),
Stroke {
style: Color::WHITE.into(),
width: 50.0,
..Default::default()
},
);
});
vec![geometry]
}
}
➡️ Next: Drawing Widgets
📘 Back: Table of contents