tuix_core/state/
entity.rs

1use std::cmp::{Eq, PartialEq};
2use std::collections::VecDeque;
3use std::hash::Hash;
4
5const ENTITY_INDEX_BITS: u32 = 24;
6const ENTITY_INDEX_MASK: u32  = (1<<ENTITY_INDEX_BITS)-1;
7
8const ENTITY_GENERATION_BITS: u32 = 8;
9const ENTITY_GENERATION_MASK: u32 = (1<<ENTITY_GENERATION_BITS)-1;
10
11const ENTITY_MAX: u32 = std::u32::MAX>>8;
12
13const MINIMUM_FREE_INDICES: usize = 1024;
14
15
16// An entity is an id used to reference to get/set properties in State.
17// Rather than having widgets own their data, all state is stored in a single database and
18// is stored and loaded using entities.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub struct Entity(u32);
21
22impl Default for Entity {
23    fn default() -> Self {
24        Entity::null()
25    }
26}
27
28impl std::fmt::Display for Entity {
29    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
30        write!(f, "{}", self.index_unchecked())
31    }
32}
33
34impl Entity {
35    /// Creates a null entity
36    ///
37    /// A null entity can be used as a placeholder within a widget struct but cannot be used to get/set properties
38    pub fn null() -> Entity {
39        Entity(std::u32::MAX)
40    }
41
42    /// Creates a root entity
43    ///
44    /// The root entity represents the main window and is alwys valid. 
45    /// The root entity can be used to set properties on the window, such as background color, 
46    /// as well as sending events to the window such as Restyle and Redraw events.
47    pub fn root() -> Entity {
48        Entity(0)
49    }
50
51    /// Creates a new entity with a given index and generation
52    pub(crate) fn new(index: u32, generation: u32) -> Entity {
53        Entity(index | generation << ENTITY_INDEX_BITS)
54    }
55
56    /// Returns true if the entity is null
57    pub fn is_null(&self) -> bool {
58        self.0 == std::u32::MAX
59    }
60
61    /// Returns the index of the entity
62    pub fn index(&self) -> Option<usize> {
63        if self.0 < std::u32::MAX {
64            Some((self.0 & ENTITY_INDEX_MASK) as usize)
65        } else {
66            None
67        }
68    }
69
70    /// Returns the generation of the entity
71    pub fn generation(&self) -> Option<u8> {
72        if self.0 < std::u32::MAX {
73            Some(((self.0 >> ENTITY_INDEX_BITS) & ENTITY_GENERATION_MASK) as u8)
74        } else {
75            None
76        }
77    }
78
79    pub(crate) fn index_unchecked(&self) -> usize {
80        (self.0 & ENTITY_INDEX_MASK) as usize
81    }
82
83
84}
85
86/// The entity manager is responsibe for creating, destroying, and reusing entities as well as checking if an entity is 'alive'.
87pub(crate) struct EntityManager {
88    count: u32,
89    generation: Vec<u8>,
90    free_list: VecDeque<u32>,
91}
92
93impl EntityManager {
94    pub fn new() -> EntityManager {
95        EntityManager {
96            count: 0,
97            generation: Vec::new(),
98            free_list: VecDeque::with_capacity(MINIMUM_FREE_INDICES),
99        }
100    }
101
102    /// Creates a new entity, reusing a destroyed entity if the number of reusable entities is greater than MINIMUM_FREE_INDICES.
103    pub(crate) fn create_entity(&mut self) -> Option<Entity> {
104        let index = if self.free_list.len() > MINIMUM_FREE_INDICES {
105            self.free_list.pop_front()
106        } else {
107            self.generation.push(0);
108            let idx = (self.generation.len() - 1) as u32;
109            assert!((idx as u32) < ENTITY_MAX, "Entity index exceeds maximum allowed value");
110            Some(idx)
111        };
112
113        // Convert Option<u32> (index) to Option<Entity>
114        index.map(|idx| Entity::new(idx, self.generation[idx as usize] as u32))
115    }
116
117    /// Returns true is the entity is alive
118    pub fn is_alive(&self, entity: Entity) -> bool {
119        self.generation[entity.index_unchecked()] == entity.generation().unwrap()
120    }
121
122    /// Destroys an entity, adding it to the list of reusable entities
123    pub fn destroy_entity(&mut self, entity: Entity) {
124        let index = entity.index_unchecked() as u32;
125        assert!(self.generation[index as usize] <= std::u8::MAX, "Entity generation exceeds maximum allowed value");
126        self.generation[index as usize] += 1;
127        self.free_list.push_back(index);
128    }
129}
130
131pub trait AsEntity {
132    fn entity(&self) -> Entity;
133}
134
135impl AsEntity for Entity {
136    fn entity(&self) -> Entity {
137        *self
138    }
139}
140
141impl AsEntity for (Entity, Entity) {
142    fn entity(&self) -> Entity {
143        self.0
144    }
145}
146
147impl AsEntity for (Entity, Entity, Entity) {
148    fn entity(&self) -> Entity {
149        self.0
150    }
151}
152
153impl AsEntity for (Entity, Entity, Entity, Entity) {
154    fn entity(&self) -> Entity {
155        self.0
156    }
157}
158
159impl AsEntity for (Entity, Entity, Entity, Entity, Entity) {
160    fn entity(&self) -> Entity {
161        self.0
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    #[test]
169    fn create() {
170        let entity = Entity::new(42, 69);
171        assert_eq!(entity.index(), Some(42));
172        assert_eq!(entity.generation(), Some(69));
173    }
174}