1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use bitflags::bitflags;
bitflags! {
/// What changed for one element.
///
/// Layout flags (`LAYOUT`, `CHILDREN`, `TEXT`) require a rebuild and a
/// full repaint. Paint flags (`PAINT`, `TRANSFORM`, `CANVAS`, `OVERLAY`,
/// `SURFACE`) only require painting; `PAINT` alone uses the element bounds
/// for a partial repaint. `HIT_TEST` and `DATA` are bookkeeping flags and
/// never request a frame by themselves.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Dirty: u16 {
const NONE = 0;
/// Pixel-only change (hover, focus, press, direct repaint request).
const PAINT = 1 << 0;
/// Measure/arrange may change; implies rebuild and full repaint.
const LAYOUT = 1 << 1;
/// Child set may change; implies rebuild and full repaint.
const CHILDREN = 1 << 2;
/// Transform or clip changed; repaint without rebuilding.
const TRANSFORM = 1 << 3;
/// Text content or shaping changed; implies rebuild and full repaint.
const TEXT = 1 << 4;
/// Pointer hit-test results may change; no repaint by itself.
const HIT_TEST = 1 << 5;
/// Application data changed; add `PAINT` if pixels depend on it.
const DATA = 1 << 6;
/// Canvas display list changed; repaint without rebuilding.
const CANVAS = 1 << 7;
/// Overlay stack changed; repaint without rebuilding.
const OVERLAY = 1 << 8;
/// Embedded surface content changed; repaint without rebuilding.
const SURFACE = 1 << 9;
}
}
impl Dirty {
/// The element must run `body`/children reconciliation before layout.
pub fn needs_layout(self) -> bool {
self.intersects(Self::LAYOUT | Self::CHILDREN | Self::TEXT)
}
/// A frame must paint pixels for this change.
///
/// Layout changes are included because they can move or remove children.
pub fn needs_repaint(self) -> bool {
self.intersects(
Self::PAINT
| Self::LAYOUT
| Self::CHILDREN
| Self::TRANSFORM
| Self::TEXT
| Self::CANVAS
| Self::OVERLAY
| Self::SURFACE,
)
}
/// Pointer hit testing must be recomputed (it is evaluated on demand).
pub fn needs_hit_test(self) -> bool {
self.intersects(Self::HIT_TEST | Self::CHILDREN | Self::LAYOUT | Self::TRANSFORM)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_is_clean() {
let d = Dirty::empty();
assert!(!d.needs_layout());
assert!(!d.needs_repaint());
assert!(!d.needs_hit_test());
}
#[test]
fn body_change_needs_layout() {
let d = Dirty::LAYOUT | Dirty::CHILDREN;
assert!(d.needs_layout());
assert!(d.needs_repaint());
}
#[test]
fn hover_needs_only_repaint() {
let d = Dirty::PAINT;
assert!(!d.needs_layout());
assert!(d.needs_repaint());
assert!(!d.needs_hit_test());
}
#[test]
fn flags_accumulate() {
let mut d = Dirty::empty();
d |= Dirty::PAINT;
d |= Dirty::TEXT;
assert!(d.needs_layout());
assert!(d.needs_repaint());
}
}