Skip to main content

lgui_core/core/layout/
dirty.rs

1use std::collections::HashSet;
2
3use super::{HostTree, UiEvent, UiId, UiRect};
4
5#[derive(Clone, Debug, Default)]
6pub struct DirtySet {
7    ids: HashSet<UiId>,
8    rects: Vec<UiRect>,
9}
10
11impl DirtySet {
12    pub fn new() -> Self {
13        Self {
14            ids: HashSet::new(),
15            rects: Vec::new(),
16        }
17    }
18
19    pub fn mark_id(&mut self, id: UiId) {
20        self.ids.insert(id);
21    }
22
23    pub fn mark_rect(&mut self, rect: UiRect) {
24        self.rects.push(rect);
25    }
26
27    pub fn is_empty(&self) -> bool {
28        self.ids.is_empty() && self.rects.is_empty()
29    }
30
31    pub fn bounds(&self, tree: &HostTree) -> Option<UiRect> {
32        let mut bounds = tree.paint_bounds(self.ids.iter().cloned());
33        for rect in &self.rects {
34            bounds = Some(bounds.map_or(*rect, |current| current.union(*rect)));
35        }
36        bounds
37    }
38}
39
40#[derive(Default)]
41pub struct DirtyTracker {
42    dirty: DirtySet,
43}
44
45impl DirtyTracker {
46    pub fn mark_id(&mut self, id: UiId) {
47        self.dirty.mark_id(id);
48    }
49
50    pub fn mark_rect(&mut self, rect: UiRect) {
51        self.dirty.mark_rect(rect);
52    }
53
54    pub fn mark_event(&mut self, event: UiEvent) {
55        match event {
56            UiEvent::HoverChanged { previous, current } => {
57                if let Some(id) = previous {
58                    self.mark_id(id);
59                }
60                if let Some(hit) = current {
61                    self.mark_id(hit.id);
62                }
63            }
64            UiEvent::PressedChanged { previous, current } => {
65                if let Some(id) = previous {
66                    self.mark_id(id);
67                }
68                if let Some(hit) = current {
69                    self.mark_id(hit.id);
70                }
71            }
72            UiEvent::Clicked(hit) => self.mark_id(hit.id),
73            UiEvent::Wheel { hit, .. } => self.mark_id(hit.id),
74            UiEvent::TextInput { target, .. }
75            | UiEvent::ImeStarted { target }
76            | UiEvent::ImeUpdated { target, .. }
77            | UiEvent::ImeEnded { target }
78            | UiEvent::Keyboard { target, .. }
79            | UiEvent::SemanticValue { target, .. }
80            | UiEvent::SemanticAction { target, .. } => self.mark_id(target),
81            UiEvent::PointerPressed { hit, .. } => self.mark_id(hit.id),
82            // Pointer coordinates are not paint state. Handlers that derive visuals from a move
83            // enqueue their own state update; moving inside an unchanged hover target must not
84            // schedule a frame by itself.
85            UiEvent::PointerMoved { .. } => {}
86            UiEvent::PointerDragged { hit, .. } => self.mark_id(hit.id),
87            UiEvent::PointerReleased { hit, .. } => self.mark_id(hit.id),
88            UiEvent::FocusChanged { previous, current } => {
89                if let Some(id) = previous {
90                    self.mark_id(id);
91                }
92                if let Some(hit) = current {
93                    self.mark_id(hit.id);
94                }
95            }
96            UiEvent::PointerLeft { previous } => {
97                if let Some(id) = previous {
98                    self.mark_id(id);
99                }
100            }
101        }
102    }
103
104    pub fn mark_animation_ids(&mut self, ids: impl IntoIterator<Item = UiId>) {
105        for id in ids {
106            self.mark_id(id);
107        }
108    }
109
110    pub fn take(&mut self) -> DirtySet {
111        std::mem::take(&mut self.dirty)
112    }
113}