use gpui::{App, Global, SharedString};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct ElementManager {
next_id: Arc<AtomicUsize>,
}
impl ElementManager {
pub fn new() -> Self {
Self {
next_id: Arc::new(AtomicUsize::new(0)),
}
}
pub fn id(&self) -> usize {
self.next_id.fetch_add(1, Ordering::SeqCst)
}
#[allow(dead_code)]
fn current(&self) -> usize {
self.next_id.load(Ordering::SeqCst)
}
}
impl Default for ElementManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct GlobalElementManager(pub Arc<ElementManager>);
impl GlobalElementManager {
pub fn new() -> Self {
Self(Arc::new(ElementManager::new()))
}
}
impl Default for GlobalElementManager {
fn default() -> Self {
Self::new()
}
}
impl std::ops::Deref for GlobalElementManager {
type Target = Arc<ElementManager>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for GlobalElementManager {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Global for GlobalElementManager {}
pub trait ElementManagerExt {
fn element_manager(&self) -> &Arc<ElementManager>;
fn next_id(&self) -> usize;
fn next_id_named(&self, name: impl Into<SharedString>) -> gpui::ElementId;
}
impl ElementManagerExt for App {
fn element_manager(&self) -> &Arc<ElementManager> {
&self.global::<GlobalElementManager>().0
}
fn next_id(&self) -> usize {
self.element_manager().id()
}
fn next_id_named(&self, name: impl Into<SharedString>) -> gpui::ElementId {
let name = name.into();
let id = self.next_id();
gpui::ElementId::named_usize(name, id)
}
}
pub fn init(cx: &mut App) {
cx.set_global(GlobalElementManager::new());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_element_manager_next() {
let manager = ElementManager::new();
assert_eq!(manager.id(), 0);
assert_eq!(manager.id(), 1);
assert_eq!(manager.id(), 2);
}
#[test]
fn test_element_manager_thread_safe() {
let manager = Arc::new(ElementManager::new());
let mut handles = vec![];
for _ in 0..10 {
let manager_clone = Arc::clone(&manager);
let handle = std::thread::spawn(move || {
for _ in 0..100 {
manager_clone.id();
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(manager.current(), 1000);
}
}