Checkpoint
This commit is contained in:
parent
c5470d4050
commit
13ba450c4c
3 changed files with 161 additions and 132 deletions
|
@ -1,7 +1,9 @@
|
||||||
mod async_context;
|
mod async_context;
|
||||||
|
mod entities;
|
||||||
mod model_context;
|
mod model_context;
|
||||||
|
|
||||||
pub use async_context::*;
|
pub use async_context::*;
|
||||||
|
pub use entities::*;
|
||||||
pub use model_context::*;
|
pub use model_context::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -14,11 +16,7 @@ use futures::{future, Future};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use slotmap::SlotMap;
|
use slotmap::SlotMap;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::sync::{Arc, Weak};
|
||||||
any::Any,
|
|
||||||
marker::PhantomData,
|
|
||||||
sync::{Arc, Weak},
|
|
||||||
};
|
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -37,8 +35,8 @@ impl App {
|
||||||
fn new(platform: Arc<dyn Platform>) -> Self {
|
fn new(platform: Arc<dyn Platform>) -> Self {
|
||||||
let dispatcher = platform.dispatcher();
|
let dispatcher = platform.dispatcher();
|
||||||
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
||||||
let mut entities = SlotMap::with_key();
|
let entities = EntityMap::new();
|
||||||
let unit_entity = Handle::new(entities.insert(Some(Box::new(()) as Box<dyn Any + Send>)));
|
let unit_entity = entities.redeem(entities.reserve(), ());
|
||||||
Self(Arc::new_cyclic(|this| {
|
Self(Arc::new_cyclic(|this| {
|
||||||
Mutex::new(AppContext {
|
Mutex::new(AppContext {
|
||||||
this: this.clone(),
|
this: this.clone(),
|
||||||
|
@ -76,7 +74,7 @@ pub struct AppContext {
|
||||||
text_system: Arc<TextSystem>,
|
text_system: Arc<TextSystem>,
|
||||||
pending_updates: usize,
|
pending_updates: usize,
|
||||||
pub(crate) unit_entity: Handle<()>,
|
pub(crate) unit_entity: Handle<()>,
|
||||||
pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any + Send>>>,
|
pub(crate) entities: EntityMap,
|
||||||
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
|
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
|
||||||
pub(crate) pending_effects: VecDeque<Effect>,
|
pub(crate) pending_effects: VecDeque<Effect>,
|
||||||
pub(crate) observers: HashMap<EntityId, Handlers>,
|
pub(crate) observers: HashMap<EntityId, Handlers>,
|
||||||
|
@ -204,11 +202,9 @@ impl Context for AppContext {
|
||||||
&mut self,
|
&mut self,
|
||||||
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
||||||
) -> Handle<T> {
|
) -> Handle<T> {
|
||||||
let id = self.entities.insert(None);
|
let slot = self.entities.reserve();
|
||||||
let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
|
let entity = build_entity(&mut ModelContext::mutable(self, slot.id));
|
||||||
self.entities.get_mut(id).unwrap().replace(entity);
|
self.entities.redeem(slot, entity)
|
||||||
|
|
||||||
Handle::new(id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_entity<T: Send + Sync + 'static, R>(
|
fn update_entity<T: Send + Sync + 'static, R>(
|
||||||
|
@ -216,103 +212,13 @@ impl Context for AppContext {
|
||||||
handle: &Handle<T>,
|
handle: &Handle<T>,
|
||||||
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
||||||
) -> R {
|
) -> R {
|
||||||
let mut entity = self
|
let mut entity = self.entities.lease(handle);
|
||||||
.entities
|
|
||||||
.get_mut(handle.id)
|
|
||||||
.unwrap()
|
|
||||||
.take()
|
|
||||||
.unwrap()
|
|
||||||
.downcast::<T>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
|
let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
|
||||||
self.entities.get_mut(handle.id).unwrap().replace(entity);
|
self.entities.end_lease(entity);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slotmap::new_key_type! { pub struct EntityId; }
|
|
||||||
|
|
||||||
pub struct Handle<T> {
|
|
||||||
pub(crate) id: EntityId,
|
|
||||||
pub(crate) entity_type: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Send + Sync + 'static> Handle<T> {
|
|
||||||
fn new(id: EntityId) -> Self {
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
entity_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn downgrade(&self) -> WeakHandle<T> {
|
|
||||||
WeakHandle {
|
|
||||||
id: self.id,
|
|
||||||
entity_type: self.entity_type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the entity referenced by this handle with the given function.
|
|
||||||
///
|
|
||||||
/// The update function receives a context appropriate for its environment.
|
|
||||||
/// When updating in an `AppContext`, it receives a `ModelContext`.
|
|
||||||
/// When updating an a `WindowContext`, it receives a `ViewContext`.
|
|
||||||
pub fn update<C: Context, R>(
|
|
||||||
&self,
|
|
||||||
cx: &mut C,
|
|
||||||
update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
|
|
||||||
) -> C::Result<R> {
|
|
||||||
cx.update_entity(self, update)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Clone for Handle<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
id: self.id,
|
|
||||||
entity_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WeakHandle<T> {
|
|
||||||
pub(crate) id: EntityId,
|
|
||||||
pub(crate) entity_type: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Send + Sync + 'static> WeakHandle<T> {
|
|
||||||
pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
|
|
||||||
// todo!("Actually upgrade")
|
|
||||||
Some(Handle {
|
|
||||||
id: self.id,
|
|
||||||
entity_type: self.entity_type,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the entity referenced by this handle with the given function if
|
|
||||||
/// the referenced entity still exists. Returns an error if the entity has
|
|
||||||
/// been released.
|
|
||||||
///
|
|
||||||
/// The update function receives a context appropriate for its environment.
|
|
||||||
/// When updating in an `AppContext`, it receives a `ModelContext`.
|
|
||||||
/// When updating an a `WindowContext`, it receives a `ViewContext`.
|
|
||||||
pub fn update<C: Context, R>(
|
|
||||||
&self,
|
|
||||||
cx: &mut C,
|
|
||||||
update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
|
|
||||||
) -> Result<R>
|
|
||||||
where
|
|
||||||
Result<C::Result<R>>: crate::Flatten<R>,
|
|
||||||
{
|
|
||||||
crate::Flatten::flatten(
|
|
||||||
self.upgrade(cx)
|
|
||||||
.ok_or_else(|| anyhow!("entity release"))
|
|
||||||
.map(|this| cx.update_entity(&this, update)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) enum Effect {
|
pub(crate) enum Effect {
|
||||||
Notify(EntityId),
|
Notify(EntityId),
|
||||||
}
|
}
|
||||||
|
|
143
crates/gpui3/src/app/entities.rs
Normal file
143
crates/gpui3/src/app/entities.rs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
use crate::Context;
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use slotmap::{SecondaryMap, SlotMap};
|
||||||
|
use std::{any::Any, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
|
slotmap::new_key_type! { pub struct EntityId; }
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut)]
|
||||||
|
pub struct Lease<T> {
|
||||||
|
#[deref]
|
||||||
|
#[deref_mut]
|
||||||
|
entity: Box<T>,
|
||||||
|
pub id: EntityId,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct EntityMap {
|
||||||
|
ref_counts: Arc<Mutex<SlotMap<EntityId, usize>>>,
|
||||||
|
entities: Arc<Mutex<SecondaryMap<EntityId, Box<dyn Any + Send + Sync>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntityMap {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ref_counts: Arc::new(Mutex::new(SlotMap::with_key())),
|
||||||
|
entities: Arc::new(Mutex::new(SecondaryMap::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reserve<T>(&self) -> Slot<T> {
|
||||||
|
let id = self.ref_counts.lock().insert(1);
|
||||||
|
Slot(Handle {
|
||||||
|
id,
|
||||||
|
entity_type: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redeem<T: 'static + Any + Send + Sync>(&self, slot: Slot<T>, entity: T) -> Handle<T> {
|
||||||
|
let handle = slot.0;
|
||||||
|
self.entities.lock().insert(handle.id, Box::new(entity));
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lease<T: 'static>(&self, handle: &Handle<T>) -> Lease<T> {
|
||||||
|
let id = handle.id;
|
||||||
|
let entity = self
|
||||||
|
.entities
|
||||||
|
.lock()
|
||||||
|
.remove(id)
|
||||||
|
.expect("Circular entity lease. Is the entity already being updated?")
|
||||||
|
.downcast::<T>()
|
||||||
|
.unwrap();
|
||||||
|
Lease { id, entity }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_lease<T: 'static + Send + Sync>(&mut self, lease: Lease<T>) {
|
||||||
|
self.entities.lock().insert(lease.id, lease.entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deref, DerefMut)]
|
||||||
|
pub struct Slot<T>(Handle<T>);
|
||||||
|
|
||||||
|
pub struct Handle<T> {
|
||||||
|
pub(crate) id: EntityId,
|
||||||
|
pub(crate) entity_type: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync + 'static> Handle<T> {
|
||||||
|
pub fn new(id: EntityId) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
entity_type: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downgrade(&self) -> WeakHandle<T> {
|
||||||
|
WeakHandle {
|
||||||
|
id: self.id,
|
||||||
|
entity_type: self.entity_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the entity referenced by this handle with the given function.
|
||||||
|
///
|
||||||
|
/// The update function receives a context appropriate for its environment.
|
||||||
|
/// When updating in an `AppContext`, it receives a `ModelContext`.
|
||||||
|
/// When updating an a `WindowContext`, it receives a `ViewContext`.
|
||||||
|
pub fn update<C: Context, R>(
|
||||||
|
&self,
|
||||||
|
cx: &mut C,
|
||||||
|
update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
|
||||||
|
) -> C::Result<R> {
|
||||||
|
cx.update_entity(self, update)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for Handle<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
id: self.id,
|
||||||
|
entity_type: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WeakHandle<T> {
|
||||||
|
pub(crate) id: EntityId,
|
||||||
|
pub(crate) entity_type: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + Sync + 'static> WeakHandle<T> {
|
||||||
|
pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
|
||||||
|
// todo!("Actually upgrade")
|
||||||
|
Some(Handle {
|
||||||
|
id: self.id,
|
||||||
|
entity_type: self.entity_type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the entity referenced by this handle with the given function if
|
||||||
|
/// the referenced entity still exists. Returns an error if the entity has
|
||||||
|
/// been released.
|
||||||
|
///
|
||||||
|
/// The update function receives a context appropriate for its environment.
|
||||||
|
/// When updating in an `AppContext`, it receives a `ModelContext`.
|
||||||
|
/// When updating an a `WindowContext`, it receives a `ViewContext`.
|
||||||
|
pub fn update<C: Context, R>(
|
||||||
|
&self,
|
||||||
|
cx: &mut C,
|
||||||
|
update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
|
||||||
|
) -> Result<R>
|
||||||
|
where
|
||||||
|
Result<C::Result<R>>: crate::Flatten<R>,
|
||||||
|
{
|
||||||
|
crate::Flatten::flatten(
|
||||||
|
self.upgrade(cx)
|
||||||
|
.ok_or_else(|| anyhow!("entity release"))
|
||||||
|
.map(|this| cx.update_entity(&this, update)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -214,18 +214,13 @@ impl Context for WindowContext<'_, '_> {
|
||||||
&mut self,
|
&mut self,
|
||||||
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
|
||||||
) -> Handle<T> {
|
) -> Handle<T> {
|
||||||
let id = self.entities.insert(None);
|
let slot = self.entities.reserve();
|
||||||
let entity = Box::new(build_entity(&mut ViewContext::mutable(
|
let entity = build_entity(&mut ViewContext::mutable(
|
||||||
&mut *self.app,
|
&mut *self.app,
|
||||||
&mut self.window,
|
&mut self.window,
|
||||||
id,
|
slot.id,
|
||||||
)));
|
));
|
||||||
self.entities.get_mut(id).unwrap().replace(entity);
|
self.entities.redeem(slot, entity)
|
||||||
|
|
||||||
Handle {
|
|
||||||
id,
|
|
||||||
entity_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_entity<T: Send + Sync + 'static, R>(
|
fn update_entity<T: Send + Sync + 'static, R>(
|
||||||
|
@ -233,27 +228,12 @@ impl Context for WindowContext<'_, '_> {
|
||||||
handle: &Handle<T>,
|
handle: &Handle<T>,
|
||||||
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
|
||||||
) -> R {
|
) -> R {
|
||||||
let mut entity = self
|
let mut entity = self.entities.lease(handle);
|
||||||
.app
|
|
||||||
.entities
|
|
||||||
.get_mut(handle.id)
|
|
||||||
.unwrap()
|
|
||||||
.take()
|
|
||||||
.unwrap()
|
|
||||||
.downcast::<T>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let result = update(
|
let result = update(
|
||||||
&mut *entity,
|
&mut *entity,
|
||||||
&mut ViewContext::mutable(&mut *self.app, &mut *self.window, handle.id),
|
&mut ViewContext::mutable(&mut *self.app, &mut *self.window, handle.id),
|
||||||
);
|
);
|
||||||
|
self.entities.end_lease(entity);
|
||||||
self.app
|
|
||||||
.entities
|
|
||||||
.get_mut(handle.id)
|
|
||||||
.unwrap()
|
|
||||||
.replace(entity);
|
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue