WIP: readable ids
This commit is contained in:
parent
05583b892b
commit
9daba42f00
5 changed files with 358 additions and 3 deletions
|
@ -32,10 +32,11 @@ pub struct Context {
|
|||
output: Mutex<Output>,
|
||||
/// Used to debug name clashes of e.g. windows
|
||||
used_ids: Mutex<AHashMap<Id, Pos2>>,
|
||||
|
||||
id_interner: Arc<Mutex<crate::id4::IdInterner>>,
|
||||
paint_stats: Mutex<PaintStats>,
|
||||
}
|
||||
|
||||
// We need to Clone a Context between each frame
|
||||
impl Clone for Context {
|
||||
fn clone(&self) -> Self {
|
||||
Context {
|
||||
|
@ -48,6 +49,7 @@ impl Clone for Context {
|
|||
graphics: Mutex::new(self.graphics.lock().clone()),
|
||||
output: Mutex::new(self.output.lock().clone()),
|
||||
used_ids: Mutex::new(self.used_ids.lock().clone()),
|
||||
id_interner: Arc::new(Mutex::new(self.id_interner.lock().clone()))
|
||||
paint_stats: Mutex::new(*self.paint_stats.lock()),
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +69,7 @@ impl Context {
|
|||
graphics: Default::default(),
|
||||
output: Default::default(),
|
||||
used_ids: Default::default(),
|
||||
id_interner: Default::default(),
|
||||
paint_stats: Default::default(),
|
||||
})
|
||||
}
|
||||
|
|
124
emigui/src/id2.rs
Normal file
124
emigui/src/id2.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use ahash::AHashMap;
|
||||
|
||||
// TODO: is stored Id string with own hash for faster lookups
|
||||
// i.e. pub struct Id(u64, Arc<String>);
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Id(Arc<String>);
|
||||
|
||||
impl Id {
|
||||
pub fn as_string(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
// #[derive(Eq, Hash, PartialEq)]
|
||||
// pub enum Component<'a> {
|
||||
// /// E.g. name of a window
|
||||
// String(Cow<'a, str>),
|
||||
|
||||
// /// For loop indices, hashes etc
|
||||
// Int(u64),
|
||||
// }
|
||||
|
||||
// impl<'a> Component<'a> {
|
||||
// fn to_owned(self) -> Component<'static> {
|
||||
// match self {
|
||||
// Component::String(s) => Component::String(s.into()),
|
||||
// Component::Int(int) => Component::Int(int),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a> std::fmt::Debug for Component<'a> {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// match self {
|
||||
// Component::String(v) => v.fmt(f),
|
||||
// Component::Int(v) => v.fmt(f),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a> std::fmt::Display for Component<'a> {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// match self {
|
||||
// Component::String(v) => v.fmt(f),
|
||||
// Component::Int(v) => v.fmt(f),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type Generation = u64;
|
||||
|
||||
// One per context
|
||||
#[derive(Default)]
|
||||
pub struct IdInterner {
|
||||
/// used to garbage-collect id:s which hasn't been used in a while
|
||||
generation: Generation,
|
||||
|
||||
/// Maps
|
||||
children: AHashMap<(Id, Cow<'static, str>), (Generation, Id)>,
|
||||
}
|
||||
|
||||
impl IdInterner {
|
||||
pub fn new_root(&self, root_id: &str) -> Id {
|
||||
Id(Arc::new(root_id.to_string()))
|
||||
}
|
||||
|
||||
/// Append `comp` to `parent_id`.
|
||||
/// This is pretty cheap if the same lookup was done last frame,
|
||||
/// else it will cost a memory allocation
|
||||
pub fn child<'a>(&mut self, parent_id: &'a Id, comp: &'a str) -> Id {
|
||||
if let Some(existing) = self.children.get_mut(&(parent_id.clone(), comp.into())) {
|
||||
existing.0 = self.generation;
|
||||
existing.1.clone()
|
||||
} else {
|
||||
let child_id = Id(Arc::new(format!("{}/{}", parent_id, comp)));
|
||||
self.children.insert(
|
||||
(parent_id.clone(), comp.into()),
|
||||
(self.generation, child_id.clone()),
|
||||
);
|
||||
child_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by the context once per frame
|
||||
pub fn gc(&mut self) {
|
||||
let current_gen = self.generation;
|
||||
self.children.retain(|_k, v| v.0 == current_gen);
|
||||
self.generation += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_id() {
|
||||
let interner = parking_lot::Mutex::new(IdInterner::default());
|
||||
let root: Id = interner.lock().new_root("root");
|
||||
let child_a: Id = interner.lock().child(&root, Component::Int(42));
|
||||
let child_b: Id = interner.lock().child(&root, Component::Int(42));
|
||||
|
||||
assert!(root != child_a);
|
||||
assert_eq!(child_a, child_b);
|
||||
}
|
||||
}
|
92
emigui/src/id3.rs
Normal file
92
emigui/src/id3.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
use ahash::AHashMap;
|
||||
// use serde_derive::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Id(Arc<String>);
|
||||
|
||||
impl Id {
|
||||
pub fn as_string(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type Generation = u64;
|
||||
|
||||
// One per context
|
||||
#[derive(Default)]
|
||||
pub struct IdInterner {
|
||||
/// used to garbage-collect id:s which hasn't been used in a while
|
||||
generation: Generation,
|
||||
|
||||
/// Maps
|
||||
children: AHashMap<Id, AHashMap<String, (Generation, Id)>>,
|
||||
}
|
||||
|
||||
impl IdInterner {
|
||||
pub fn new_root(&self, root_id: &str) -> Id {
|
||||
Id(Arc::new(root_id.to_string()))
|
||||
}
|
||||
|
||||
/// Append `comp` to `parent_id`.
|
||||
/// This is pretty cheap if the same lookup was done last frame,
|
||||
/// else it will cost a memory allocation
|
||||
pub fn child(&mut self, parent_id: &Id, comp: &str) -> Id {
|
||||
if let Some(map) = self.children.get_mut(parent_id) {
|
||||
if let Some((gen, child_id)) = map.get_mut(comp) {
|
||||
*gen = self.generation;
|
||||
child_id.clone()
|
||||
} else {
|
||||
let child_id = Id(Arc::new(format!("{}/{}", parent_id, comp)));
|
||||
map.insert(comp.to_owned(), (self.generation, child_id.clone()));
|
||||
child_id
|
||||
}
|
||||
} else {
|
||||
let child_id = Id(Arc::new(format!("{}/{}", parent_id, comp)));
|
||||
let mut map = AHashMap::new();
|
||||
map.insert(comp.to_owned(), (self.generation, child_id.clone()));
|
||||
self.children.insert(parent_id.clone(), map);
|
||||
child_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by the context once per frame
|
||||
pub fn gc(&mut self) {
|
||||
let current_gen = self.generation;
|
||||
for value in self.children.values_mut() {
|
||||
value.retain(|_comp, (gen, _id)| *gen == current_gen);
|
||||
}
|
||||
self.children.retain(|_k, v| !v.is_empty());
|
||||
self.generation += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_id() {
|
||||
let interner = parking_lot::Mutex::new(IdInterner::default());
|
||||
let root: Id = interner.lock().new_root("root");
|
||||
let child_a: Id = interner.lock().child(&root, "child");
|
||||
let child_b: Id = interner.lock().child(&root, "child");
|
||||
|
||||
assert!(root != child_a);
|
||||
assert_eq!(child_a, child_b);
|
||||
}
|
||||
}
|
135
emigui/src/id4.rs
Normal file
135
emigui/src/id4.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use ahash::AHashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Id {
|
||||
hash: u64, // precomputed as an optimization
|
||||
string: Arc<String>,
|
||||
}
|
||||
|
||||
impl Id {
|
||||
fn from_string(string: String) -> Id {
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut hasher = ahash::AHasher::default();
|
||||
string.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
Id {
|
||||
hash,
|
||||
string: Arc::new(string),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for Id {
|
||||
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
|
||||
hasher.write_u64(self.hash);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::cmp::Eq for Id {}
|
||||
|
||||
impl std::cmp::PartialEq for Id {
|
||||
fn eq(&self, other: &Id) -> bool {
|
||||
self.hash == other.hash
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.string.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Id {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.string.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Id {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
(*self.string).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Id {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Id, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(Id::from_string(serde::Deserialize::deserialize(
|
||||
deserializer,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type Generation = u64;
|
||||
|
||||
// One per context
|
||||
#[derive(Default)]
|
||||
pub struct IdInterner {
|
||||
/// used to garbage-collect id:s which hasn't been used in a while
|
||||
generation: Generation,
|
||||
|
||||
/// Maps
|
||||
children: AHashMap<Id, AHashMap<String, (Generation, Id)>>,
|
||||
}
|
||||
|
||||
impl IdInterner {
|
||||
pub fn new_root(&self, root_id: &str) -> Id {
|
||||
Id::from_string(root_id.to_string())
|
||||
}
|
||||
|
||||
/// Append `comp` to `parent_id`.
|
||||
/// This is pretty cheap if the same lookup was done last frame,
|
||||
/// else it will cost a memory allocation
|
||||
pub fn child(&mut self, parent_id: &Id, comp: &str) -> Id {
|
||||
if let Some(map) = self.children.get_mut(parent_id) {
|
||||
if let Some((gen, child_id)) = map.get_mut(comp) {
|
||||
*gen = self.generation;
|
||||
child_id.clone()
|
||||
} else {
|
||||
let child_id = Id::from_string(format!("{}/{}", parent_id, comp));
|
||||
map.insert(comp.to_owned(), (self.generation, child_id.clone()));
|
||||
child_id
|
||||
}
|
||||
} else {
|
||||
let child_id = Id::from_string(format!("{}/{}", parent_id, comp));
|
||||
let mut map = AHashMap::default();
|
||||
map.insert(comp.to_owned(), (self.generation, child_id.clone()));
|
||||
self.children.insert(parent_id.clone(), map);
|
||||
child_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by the context once per frame
|
||||
pub fn gc(&mut self) {
|
||||
let current_gen = self.generation;
|
||||
for value in self.children.values_mut() {
|
||||
value.retain(|_comp, (gen, _id)| *gen == current_gen);
|
||||
}
|
||||
self.children.retain(|_k, v| !v.is_empty());
|
||||
self.generation += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_id() {
|
||||
let interner = parking_lot::Mutex::new(IdInterner::default());
|
||||
let root: Id = interner.lock().new_root("root");
|
||||
let child_a: Id = interner.lock().child(&root, "child");
|
||||
let child_b: Id = interner.lock().child(&root, "child");
|
||||
|
||||
assert!(root != child_a);
|
||||
assert_eq!(child_a, child_b);
|
||||
assert_eq!(child_a.to_string(), "root/child");
|
||||
}
|
||||
}
|
|
@ -25,7 +25,8 @@
|
|||
pub mod containers;
|
||||
mod context;
|
||||
pub mod examples;
|
||||
mod id;
|
||||
// mod id;
|
||||
mod id4;
|
||||
mod input;
|
||||
mod introspection;
|
||||
mod layers;
|
||||
|
@ -41,7 +42,7 @@ pub mod widgets;
|
|||
|
||||
pub use {
|
||||
context::Context,
|
||||
id::Id,
|
||||
id4::Id,
|
||||
input::*,
|
||||
layers::*,
|
||||
layout::*,
|
||||
|
|
Loading…
Reference in a new issue