invalidate/
invalidate.rs

1// Copyright 2020 The Druid Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::any::Any;
16
17use std::time::Instant;
18
19use druid_shell::kurbo::{Point, Rect, Size};
20use druid_shell::piet::{Color, Piet, RenderContext};
21
22use druid_shell::{Application, Region, WinHandler, WindowBuilder, WindowHandle};
23
24struct InvalidateTest {
25    handle: WindowHandle,
26    size: Size,
27    start_time: Instant,
28    color: Color,
29    rect: Rect,
30    cursor: Rect,
31}
32
33impl InvalidateTest {
34    fn update_color_and_rect(&mut self) {
35        let time_since_start = (Instant::now() - self.start_time).as_millis();
36        let (r, g, b, _) = self.color.as_rgba8();
37        self.color = match (time_since_start % 2, time_since_start % 3) {
38            (0, _) => Color::rgb8(r.wrapping_add(10), g, b),
39            (_, 0) => Color::rgb8(r, g.wrapping_add(10), b),
40            (_, _) => Color::rgb8(r, g, b.wrapping_add(10)),
41        };
42
43        self.rect.x0 = (self.rect.x0 + 5.0) % self.size.width;
44        self.rect.x1 = (self.rect.x1 + 5.5) % self.size.width;
45        self.rect.y0 = (self.rect.y0 + 3.0) % self.size.height;
46        self.rect.y1 = (self.rect.y1 + 3.5) % self.size.height;
47
48        // Invalidate the old and new cursor positions.
49        self.handle.invalidate_rect(self.cursor);
50        self.cursor.x0 += 4.0;
51        self.cursor.x1 += 4.0;
52        if self.cursor.x0 > self.size.width {
53            self.cursor.x1 = self.cursor.width();
54            self.cursor.x0 = 0.0;
55        }
56        self.handle.invalidate_rect(self.cursor);
57    }
58}
59
60impl WinHandler for InvalidateTest {
61    fn connect(&mut self, handle: &WindowHandle) {
62        self.handle = handle.clone();
63    }
64
65    fn prepare_paint(&mut self) {
66        self.update_color_and_rect();
67        self.handle.invalidate_rect(self.rect);
68    }
69
70    fn paint(&mut self, piet: &mut Piet, region: &Region) {
71        // We can ask to draw something bigger than our rect, but things outside
72        // the invalidation region won't draw. (So they'll draw if and only if
73        // they intersect the cursor's invalidated region or the rect that we
74        // invalidated.)
75        piet.fill(region.bounding_box(), &self.color);
76        piet.fill(self.cursor, &Color::WHITE);
77        self.handle.request_anim_frame();
78    }
79
80    fn size(&mut self, size: Size) {
81        self.size = size;
82    }
83
84    fn command(&mut self, id: u32) {
85        match id {
86            0x100 => self.handle.close(),
87            _ => println!("unexpected id {id}"),
88        }
89    }
90
91    fn request_close(&mut self) {
92        self.handle.close();
93    }
94
95    fn destroy(&mut self) {
96        Application::global().quit()
97    }
98
99    fn as_any(&mut self) -> &mut dyn Any {
100        self
101    }
102}
103
104fn main() {
105    tracing_subscriber::fmt().init();
106    let app = Application::new().unwrap();
107    let mut builder = WindowBuilder::new(app.clone());
108    let inv_test = InvalidateTest {
109        size: Size::ZERO,
110        handle: Default::default(),
111        start_time: Instant::now(),
112        rect: Rect::from_origin_size(Point::ZERO, (10.0, 20.0)),
113        cursor: Rect::from_origin_size(Point::ZERO, (2.0, 100.0)),
114        color: Color::WHITE,
115    };
116    builder.set_handler(Box::new(inv_test));
117    builder.set_title("Invalidate tester");
118
119    let window = builder.build().unwrap();
120    window.show();
121    app.run(None);
122}