1
1
// TODO(emilk): have separate types `PositionId` and `UniqueId`. ?
2
2
3
+ use std:: num:: NonZeroU64 ;
4
+
3
5
/// egui tracks widgets frame-to-frame using [`Id`]s.
4
6
///
5
7
/// For instance, if you start dragging a slider one frame, egui stores
25
27
///
26
28
/// Then there are widgets that need no identifiers at all, like labels,
27
29
/// because they have no state nor are interacted with.
30
+ ///
31
+ /// This is niche-optimized to that `Option<Id>` is the same size as `Id`.
28
32
#[ derive( Clone , Copy , Hash , Eq , PartialEq ) ]
29
33
#[ cfg_attr( feature = "serde" , derive( serde:: Deserialize , serde:: Serialize ) ) ]
30
- pub struct Id ( u64 ) ;
34
+ pub struct Id ( NonZeroU64 ) ;
31
35
32
36
impl Id {
33
37
/// A special [`Id`], in particular as a key to [`crate::Memory::data`]
34
38
/// for when there is no particular widget to attach the data.
35
39
///
36
40
/// The null [`Id`] is still a valid id to use in all circumstances,
37
41
/// though obviously it will lead to a lot of collisions if you do use it!
38
- pub const NULL : Self = Self ( 0 ) ;
42
+ pub const NULL : Self = Self ( NonZeroU64 :: MAX ) ;
39
43
40
- pub ( crate ) const fn background ( ) -> Self {
41
- Self ( 1 )
44
+ #[ inline]
45
+ const fn from_hash ( hash : u64 ) -> Self {
46
+ if let Some ( nonzero) = NonZeroU64 :: new ( hash) {
47
+ Self ( nonzero)
48
+ } else {
49
+ Self ( NonZeroU64 :: MIN ) // The hash was exactly zero (very bad luck)
50
+ }
42
51
}
43
52
44
53
/// Generate a new [`Id`] by hashing some source (e.g. a string or integer).
45
54
pub fn new ( source : impl std:: hash:: Hash ) -> Self {
46
- Self ( epaint:: ahash:: RandomState :: with_seeds ( 1 , 2 , 3 , 4 ) . hash_one ( source) )
55
+ Self :: from_hash ( epaint:: ahash:: RandomState :: with_seeds ( 1 , 2 , 3 , 4 ) . hash_one ( source) )
47
56
}
48
57
49
58
/// Generate a new [`Id`] by hashing the parent [`Id`] and the given argument.
50
59
pub fn with ( self , child : impl std:: hash:: Hash ) -> Self {
51
60
use std:: hash:: { BuildHasher , Hasher } ;
52
61
let mut hasher = epaint:: ahash:: RandomState :: with_seeds ( 1 , 2 , 3 , 4 ) . build_hasher ( ) ;
53
- hasher. write_u64 ( self . 0 ) ;
62
+ hasher. write_u64 ( self . 0 . get ( ) ) ;
54
63
child. hash ( & mut hasher) ;
55
- Self ( hasher. finish ( ) )
64
+ Self :: from_hash ( hasher. finish ( ) )
56
65
}
57
66
58
67
/// Short and readable summary
59
68
pub fn short_debug_format ( & self ) -> String {
60
- format ! ( "{:04X}" , self . 0 as u16 )
69
+ format ! ( "{:04X}" , self . value ( ) as u16 )
61
70
}
62
71
72
+ /// The inner value of the [`Id`].
73
+ ///
74
+ /// This is a high-entropy hash, or [`Self::NULL`].
63
75
#[ inline( always) ]
64
- pub ( crate ) fn value ( & self ) -> u64 {
65
- self . 0
76
+ pub fn value ( & self ) -> u64 {
77
+ self . 0 . get ( )
66
78
}
67
79
68
80
#[ cfg( feature = "accesskit" ) ]
69
81
pub ( crate ) fn accesskit_id ( & self ) -> accesskit:: NodeId {
70
- self . 0 . into ( )
82
+ self . value ( ) . into ( )
71
83
}
72
84
}
73
85
@@ -92,6 +104,12 @@ impl From<String> for Id {
92
104
}
93
105
}
94
106
107
+ #[ test]
108
+ fn id_size ( ) {
109
+ assert_eq ! ( std:: mem:: size_of:: <Id >( ) , 8 ) ;
110
+ assert_eq ! ( std:: mem:: size_of:: <Option <Id >>( ) , 8 ) ;
111
+ }
112
+
95
113
// ----------------------------------------------------------------------------
96
114
97
115
// Idea taken from the `nohash_hasher` crate.
0 commit comments