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>,
|
output: Mutex<Output>,
|
||||||
/// Used to debug name clashes of e.g. windows
|
/// Used to debug name clashes of e.g. windows
|
||||||
used_ids: Mutex<AHashMap<Id, Pos2>>,
|
used_ids: Mutex<AHashMap<Id, Pos2>>,
|
||||||
|
id_interner: Arc<Mutex<crate::id4::IdInterner>>,
|
||||||
paint_stats: Mutex<PaintStats>,
|
paint_stats: Mutex<PaintStats>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to Clone a Context between each frame
|
||||||
impl Clone for Context {
|
impl Clone for Context {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Context {
|
Context {
|
||||||
|
@ -48,6 +49,7 @@ impl Clone for Context {
|
||||||
graphics: Mutex::new(self.graphics.lock().clone()),
|
graphics: Mutex::new(self.graphics.lock().clone()),
|
||||||
output: Mutex::new(self.output.lock().clone()),
|
output: Mutex::new(self.output.lock().clone()),
|
||||||
used_ids: Mutex::new(self.used_ids.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()),
|
paint_stats: Mutex::new(*self.paint_stats.lock()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +69,7 @@ impl Context {
|
||||||
graphics: Default::default(),
|
graphics: Default::default(),
|
||||||
output: Default::default(),
|
output: Default::default(),
|
||||||
used_ids: Default::default(),
|
used_ids: Default::default(),
|
||||||
|
id_interner: Default::default(),
|
||||||
paint_stats: 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;
|
pub mod containers;
|
||||||
mod context;
|
mod context;
|
||||||
pub mod examples;
|
pub mod examples;
|
||||||
mod id;
|
// mod id;
|
||||||
|
mod id4;
|
||||||
mod input;
|
mod input;
|
||||||
mod introspection;
|
mod introspection;
|
||||||
mod layers;
|
mod layers;
|
||||||
|
@ -41,7 +42,7 @@ pub mod widgets;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
context::Context,
|
context::Context,
|
||||||
id::Id,
|
id4::Id,
|
||||||
input::*,
|
input::*,
|
||||||
layers::*,
|
layers::*,
|
||||||
layout::*,
|
layout::*,
|
||||||
|
|
Loading…
Reference in a new issue