Skip to main content

qframe/widget/
id.rs

1//! Stable widget identities.
2
3use std::collections::HashMap;
4use std::fmt;
5use std::hash::{BuildHasherDefault, Hasher};
6
7/// Identifies a widget across frames.
8///
9/// Derived from the parent's id and the widget's key: its explicit name when one was given
10/// with [`NodeMut::id`](crate::widget::NodeMut::id), otherwise its position among its siblings
11/// and its type. Hashing is deterministic (FNV-1a), so ids are the same on every run.
12#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct WidgetId(u64);
14
15impl WidgetId {
16    /// The id of the root of the view.
17    pub const ROOT: Self = Self(0xcbf2_9ce4_8422_2325);
18
19    pub(crate) fn child(self, key: &Key, type_name: &str) -> Self {
20        let mut hash = Fnv(self.0);
21        match key {
22            Key::Index(index) => {
23                hash.write(b"#");
24                hash.write(&index.to_le_bytes());
25                hash.write(type_name.as_bytes());
26            }
27            Key::Named(name) => {
28                hash.write(b"@");
29                hash.write(name.as_bytes());
30            }
31        }
32        Self(hash.0)
33    }
34}
35
36impl fmt::Debug for WidgetId {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        write!(f, "WidgetId({:016x})", self.0)
39    }
40}
41
42/// How a node is told apart from its siblings.
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub(crate) enum Key {
45    Index(usize),
46    Named(String),
47}
48
49/// A map keyed by widget ids, as painting fills several of them for every widget in every frame.
50pub(crate) type IdMap<K, V> = HashMap<K, V, BuildHasherDefault<IdHasher>>;
51
52/// Hashes keys made of widget ids and small numbers. A widget id is already a well mixed hash,
53/// so a multiply-and-rotate step per field is enough; the keys come from the application's own
54/// view, not from input an attacker controls, so the collision resistance of the standard hasher
55/// buys nothing and costs a noticeable share of each frame.
56#[derive(Debug, Default, Clone, Copy)]
57pub(crate) struct IdHasher(u64);
58
59impl IdHasher {
60    fn add(&mut self, value: u64) {
61        self.0 = (self.0.rotate_left(5) ^ value).wrapping_mul(0x517c_c1b7_2722_0a95);
62    }
63}
64
65impl Hasher for IdHasher {
66    fn finish(&self) -> u64 {
67        self.0
68    }
69
70    fn write(&mut self, bytes: &[u8]) {
71        for byte in bytes {
72            self.add(u64::from(*byte));
73        }
74    }
75
76    fn write_u16(&mut self, value: u16) {
77        self.add(u64::from(value));
78    }
79
80    fn write_u32(&mut self, value: u32) {
81        self.add(u64::from(value));
82    }
83
84    fn write_u64(&mut self, value: u64) {
85        self.add(value);
86    }
87
88    fn write_usize(&mut self, value: usize) {
89        self.add(value as u64);
90    }
91}
92
93struct Fnv(u64);
94
95impl Fnv {
96    fn write(&mut self, bytes: &[u8]) {
97        for byte in bytes {
98            self.0 ^= u64::from(*byte);
99            self.0 = self.0.wrapping_mul(0x0100_0000_01b3);
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn ids_depend_on_parent_key_and_type() {
110        let a = WidgetId::ROOT.child(&Key::Index(0), "Button");
111        assert_eq!(a, WidgetId::ROOT.child(&Key::Index(0), "Button"));
112        assert_ne!(a, WidgetId::ROOT.child(&Key::Index(1), "Button"));
113        assert_ne!(a, WidgetId::ROOT.child(&Key::Index(0), "Text"));
114        let named = WidgetId::ROOT.child(&Key::Named("save".into()), "Button");
115        assert_eq!(named, WidgetId::ROOT.child(&Key::Named("save".into()), "Text"));
116        assert_ne!(a.child(&Key::Index(0), "Text"), named.child(&Key::Index(0), "Text"));
117    }
118}