Skip to main content

term_wm_layout_engine/
region_map.rs

1use crate::rect::{LayoutRect, rect_contains};
2
3#[cfg(not(feature = "std"))]
4use alloc::collections::BTreeMap;
5#[cfg(feature = "std")]
6use std::collections::BTreeMap;
7
8/// A map from ID to rectangle, with hit-testing support.
9///
10/// Regions are tested in insertion order (via BTreeMap iteration).
11/// Explicitly pass a slice of IDs to control the hit-test order
12/// (e.g., reverse for topmost-first).
13#[derive(Debug, Clone)]
14pub struct RegionMap<T: Ord> {
15    regions: BTreeMap<T, LayoutRect>,
16}
17
18impl<T: Ord> RegionMap<T> {
19    pub fn new() -> Self {
20        Self {
21            regions: BTreeMap::new(),
22        }
23    }
24
25    pub fn ids(&self) -> Vec<T>
26    where
27        T: Copy,
28    {
29        self.regions.keys().copied().collect()
30    }
31
32    pub fn set(&mut self, id: T, rect: LayoutRect) {
33        self.regions.insert(id, rect);
34    }
35
36    pub fn get(&self, id: &T) -> Option<LayoutRect> {
37        self.regions.get(id).copied()
38    }
39
40    pub fn remove(&mut self, id: &T) {
41        self.regions.remove(id);
42    }
43
44    pub fn is_empty(&self) -> bool {
45        self.regions.is_empty()
46    }
47
48    pub fn contains_key(&self, id: &T) -> bool {
49        self.regions.contains_key(id)
50    }
51
52    /// Find the first ID (in the given order) whose region contains (col, row).
53    pub fn hit_test(&self, col: u16, row: u16, ids: &[T]) -> Option<T>
54    where
55        T: Copy,
56    {
57        for id in ids {
58            if let Some(rect) = self.regions.get(id)
59                && rect_contains(rect, col, row)
60            {
61                return Some(*id);
62            }
63        }
64        None
65    }
66}
67
68impl<T: Ord> Default for RegionMap<T> {
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    fn rect(x: i32, y: i32, w: u16, h: u16) -> LayoutRect {
79        LayoutRect {
80            x,
81            y,
82            width: w,
83            height: h,
84        }
85    }
86
87    #[test]
88    fn region_map_set_get_remove() {
89        let mut map: RegionMap<u8> = RegionMap::new();
90        map.set(1, rect(0, 0, 10, 10));
91        assert_eq!(map.get(&1), Some(rect(0, 0, 10, 10)));
92        map.remove(&1);
93        assert_eq!(map.get(&1), None);
94    }
95
96    #[test]
97    fn region_map_hit_test() {
98        let mut map: RegionMap<u8> = RegionMap::new();
99        map.set(1, rect(0, 0, 10, 10));
100        map.set(2, rect(10, 0, 10, 10));
101        assert_eq!(map.hit_test(5, 5, &[1, 2]), Some(1));
102        assert_eq!(map.hit_test(15, 5, &[1, 2]), Some(2));
103        assert_eq!(map.hit_test(50, 50, &[1, 2]), None);
104    }
105
106    #[test]
107    fn region_map_ids() {
108        let mut map: RegionMap<u8> = RegionMap::new();
109        map.set(3, rect(0, 0, 1, 1));
110        map.set(1, rect(0, 0, 1, 1));
111        let mut ids = map.ids();
112        ids.sort();
113        assert_eq!(ids, vec![1, 3]);
114    }
115
116    #[test]
117    fn region_map_is_empty() {
118        let map: RegionMap<u8> = RegionMap::new();
119        assert!(map.is_empty());
120    }
121}