use crate::core::animation::AnimationClock;
use crate::core::async_context::{AsyncContext, LocalExecutor};
use crate::core::clipboard::{ClipboardProvider, MemoryClipboard};
use crate::core::entity_events::{EventQueue, Subscription};
use crate::core::focus::FocusState;
use crate::core::task::{Cancellable, Task};
use crate::core::tasks::{BackgroundExecutor, ForegroundSender, Inbox};
use crate::core::timers::{TimerId, TimerQueue};
use slotmap::{SlotMap, new_key_type};
use std::any::Any;
use std::future::Future;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
new_key_type! {
pub struct EntityId;
}
pub use crate::core::handle::Handle;
pub struct EntityMap {
slots: SlotMap<EntityId, Option<Box<dyn Any>>>,
dirty: Vec<EntityId>,
quiet: Vec<EntityId>,
pub(crate) events: EventQueue,
depth: usize,
pub timers: TimerQueue,
pub background: BackgroundExecutor,
pub inbox: Inbox,
pub focus: FocusState,
pub animations: AnimationClock,
pub clipboard: Box<dyn ClipboardProvider>,
pub pending_actions: Vec<crate::core::action::Action>,
pub local: LocalExecutor,
pub drag_type: Option<std::any::TypeId>,
pub key_hint: Option<crate::core::key_hint::KeyHint>,
}
impl Default for EntityMap {
fn default() -> Self {
Self {
slots: SlotMap::default(),
dirty: Vec::new(),
quiet: Vec::new(),
events: EventQueue::default(),
depth: 0,
timers: TimerQueue::default(),
background: BackgroundExecutor::default(),
inbox: Inbox::default(),
focus: FocusState::default(),
animations: AnimationClock::default(),
clipboard: Box::new(MemoryClipboard::default()),
pending_actions: Vec::new(),
local: LocalExecutor::default(),
drag_type: None,
key_hint: None,
}
}
}
impl EntityMap {
pub fn new() -> Self {
Self::default()
}
pub fn insert<T: Any>(&mut self, value: T) -> Handle<T> {
let id = self.slots.insert(Some(Box::new(value)));
Handle { id, marker: PhantomData }
}
pub fn read<T: Any>(&self, handle: Handle<T>) -> Option<&T> {
self.slots.get(handle.id)?.as_ref()?.downcast_ref::<T>()
}
pub fn update<T: Any, R>(
&mut self,
handle: Handle<T>,
mutate: impl FnOnce(&mut T, &mut EntityContext<T>) -> R,
) -> Option<R> {
let mut boxed = self.slots.get_mut(handle.id)?.take()?;
self.depth += 1;
let result = match boxed.downcast_mut::<T>() {
Some(value) => {
let mut cx = EntityContext { entities: self, handle };
Some(mutate(value, &mut cx))
}
None => None,
};
self.depth -= 1;
if let Some(slot) = self.slots.get_mut(handle.id) {
*slot = Some(boxed);
}
self.mark_dirty(handle.id);
if self.depth == 0 {
self.flush_events();
}
result
}
pub fn mark_dirty(&mut self, id: EntityId) {
if !self.dirty.contains(&id) && !self.quiet.contains(&id) {
self.dirty.push(id);
}
}
pub fn set_quiet(&mut self, id: EntityId, quiet: bool) {
self.quiet.retain(|other| *other != id);
if quiet {
self.quiet.push(id);
} else {
self.mark_dirty(id);
}
}
pub fn remove(&mut self, id: EntityId) -> bool {
self.quiet.retain(|other| *other != id);
self.events.forget(id);
self.slots.remove(id).is_some()
}
pub fn contains(&self, id: EntityId) -> bool {
self.slots.contains_key(id)
}
pub fn take_dirty(&mut self) -> Vec<EntityId> {
std::mem::take(&mut self.dirty)
}
pub fn is_dirty(&self) -> bool {
!self.dirty.is_empty()
}
pub fn foreground(&self) -> ForegroundSender {
self.inbox.sender()
}
pub fn schedule(&mut self, delay: std::time::Duration, job: impl FnOnce(&mut EntityMap) + 'static) -> TimerId {
self.timers.schedule(delay, job)
}
pub fn run_pending(&mut self, now: std::time::Instant) -> bool {
let mut ran = false;
for job in self.timers.take_due(now) {
job(self);
ran = true;
}
loop {
let sender = self.inbox.sender();
let mut progressed = self.local.poll_ready(&sender);
while let Some(job) = self.inbox.try_next() {
job(self);
progressed = true;
}
if !progressed {
return ran;
}
ran = true;
}
}
pub fn spawn_local(&mut self, future: impl Future<Output = ()> + 'static) {
let sender = self.inbox.sender();
self.local.spawn(future, &sender);
}
pub fn async_context(&self) -> AsyncContext {
AsyncContext { sender: self.inbox.sender(), background: self.background.clone() }
}
pub fn subscribe<T: Any, S: Any, E: Any>(
&mut self,
emitter: Handle<T>,
subscriber: Handle<S>,
callback: impl Fn(&mut S, &E, &mut EntityContext<S>) + 'static,
) -> Subscription {
self.events.subscribe(emitter.id, subscriber.id, move |entities, event| {
let Some(event) = event.downcast_ref::<E>() else { return };
entities.update(subscriber, |state, cx| callback(state, event, cx));
})
}
pub fn unsubscribe(&mut self, subscription: Subscription) {
self.events.unsubscribe(subscription);
}
fn flush_events(&mut self) {
while let Some((emitter, event)) = self.events.pop() {
for callback in self.events.listeners(emitter) {
callback(self, event.as_ref());
}
}
}
}
pub struct EntityContext<'a, T> {
entities: &'a mut EntityMap,
handle: Handle<T>,
}
impl<T: Any> EntityContext<'_, T> {
pub fn spawn<R: 'static, F: Future<Output = R> + 'static>(
&mut self,
body: impl FnOnce(Handle<T>, AsyncContext) -> F,
) -> Task<R> {
let (task, completer) = Task::pending();
let cancel = task.cancel_token();
let future = body(self.handle(), self.entities.async_context());
self.entities.spawn_local(async move {
if let Some(value) = (Cancellable { future: Box::pin(future), cancel }).await {
completer.complete(value);
}
});
task
}
pub fn handle(&self) -> Handle<T> {
self.handle
}
pub fn emit<E: Any>(&mut self, event: E) {
self.entities.events.push(self.handle.id, Box::new(event));
}
pub fn notify(&mut self) {
self.entities.mark_dirty(self.handle.id);
}
pub fn dispatch_action(&mut self, action: crate::core::action::Action) {
self.entities.pending_actions.push(action);
}
}
impl<T> Deref for EntityContext<'_, T> {
type Target = EntityMap;
fn deref(&self) -> &EntityMap {
self.entities
}
}
impl<T> DerefMut for EntityContext<'_, T> {
fn deref_mut(&mut self) -> &mut EntityMap {
self.entities
}
}
#[cfg(test)]
#[path = "entity_tests.rs"]
mod tests;