zed/crates/gpui3/src/app.rs

462 lines
14 KiB
Rust
Raw Normal View History

2023-09-20 16:17:29 +00:00
use crate::{
2023-09-25 17:47:37 +00:00
current_platform, AnyWindowHandle, Context, LayoutId, MainThreadOnly, Platform, Reference,
RootView, TextSystem, Window, WindowContext, WindowHandle, WindowId,
2023-09-20 16:17:29 +00:00
};
2023-09-19 19:19:22 +00:00
use anyhow::{anyhow, Result};
2023-09-25 17:47:37 +00:00
use collections::{HashMap, VecDeque};
use futures::{future, Future};
use parking_lot::Mutex;
2023-09-19 19:19:22 +00:00
use slotmap::SlotMap;
2023-09-25 17:47:37 +00:00
use smallvec::SmallVec;
2023-09-22 14:33:51 +00:00
use std::{
any::Any,
marker::PhantomData,
sync::{Arc, Weak},
};
2023-09-27 21:35:46 +00:00
use util::ResultExt;
2023-09-19 19:19:22 +00:00
2023-09-20 16:17:29 +00:00
#[derive(Clone)]
pub struct App(Arc<Mutex<AppContext>>);
2023-09-19 19:19:22 +00:00
2023-09-20 16:17:29 +00:00
impl App {
2023-09-22 16:02:11 +00:00
pub fn production() -> Self {
Self::new(current_platform())
}
#[cfg(any(test, feature = "test"))]
pub fn test() -> Self {
Self::new(Arc::new(super::TestPlatform::new()))
}
fn new(platform: Arc<dyn Platform>) -> Self {
let dispatcher = platform.dispatcher();
2023-09-22 16:02:11 +00:00
let text_system = Arc::new(TextSystem::new(platform.text_system()));
let mut entities = SlotMap::with_key();
2023-09-26 17:29:44 +00:00
let unit_entity = Handle::new(entities.insert(Some(Box::new(()) as Box<dyn Any + Send>)));
2023-09-22 16:02:11 +00:00
Self(Arc::new_cyclic(|this| {
Mutex::new(AppContext {
2023-09-22 16:02:11 +00:00
this: this.clone(),
platform: MainThreadOnly::new(platform, dispatcher),
2023-09-22 16:02:11 +00:00
text_system,
2023-09-26 17:29:44 +00:00
unit_entity,
2023-09-22 16:02:11 +00:00
entities,
windows: SlotMap::with_key(),
2023-09-25 17:47:37 +00:00
pending_updates: 0,
pending_effects: Default::default(),
observers: Default::default(),
2023-09-22 16:02:11 +00:00
layout_id_buffer: Default::default(),
})
}))
2023-09-20 18:03:37 +00:00
}
pub fn run<F>(self, on_finish_launching: F)
where
F: 'static + FnOnce(&mut AppContext),
2023-09-20 18:03:37 +00:00
{
let this = self.clone();
let platform = self.0.lock().platform.clone();
platform.borrow_on_main_thread().run(Box::new(move || {
let cx = &mut *this.0.lock();
2023-09-22 16:02:11 +00:00
on_finish_launching(cx);
2023-09-20 18:03:37 +00:00
}));
2023-09-20 16:17:29 +00:00
}
}
2023-09-19 19:19:22 +00:00
2023-09-25 17:47:37 +00:00
type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
pub struct AppContext {
this: Weak<Mutex<AppContext>>,
platform: MainThreadOnly<dyn Platform>,
2023-09-20 18:03:37 +00:00
text_system: Arc<TextSystem>,
2023-09-26 17:29:44 +00:00
pub(crate) unit_entity: Handle<()>,
pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any + Send>>>,
2023-09-19 19:19:22 +00:00
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
2023-09-25 17:47:37 +00:00
pending_updates: usize,
pub(crate) pending_effects: VecDeque<Effect>,
pub(crate) observers: HashMap<EntityId, Handlers>,
2023-09-19 19:19:22 +00:00
// We recycle this memory across layout requests.
pub(crate) layout_id_buffer: Vec<LayoutId>,
}
impl AppContext {
2023-09-20 18:03:37 +00:00
pub fn text_system(&self) -> &Arc<TextSystem> {
&self.text_system
2023-09-19 19:19:22 +00:00
}
2023-09-26 17:29:44 +00:00
pub fn to_async(&self) -> AsyncContext {
AsyncContext(self.this.clone())
}
pub fn spawn_on_main<F, R>(
2023-09-26 17:29:44 +00:00
&self,
f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
) -> impl Future<Output = R>
where
F: Future<Output = R> + 'static,
R: Send + 'static,
{
let this = self.this.upgrade().unwrap();
self.platform.read(move |platform| {
let cx = &mut *this.lock();
2023-09-26 17:34:41 +00:00
cx.update(|cx| f(platform, cx))
})
}
2023-09-26 17:29:44 +00:00
pub fn open_window<S: 'static + Send + Sync>(
2023-09-19 19:19:22 +00:00
&mut self,
options: crate::WindowOptions,
build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
) -> impl Future<Output = WindowHandle<S>> {
let id = self.windows.insert(None);
let handle = WindowHandle::new(id);
self.spawn_on_main(move |platform, cx| {
2023-09-26 17:29:44 +00:00
let mut window = Window::new(handle.into(), options, platform, cx);
let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
2023-09-26 17:29:44 +00:00
window.root_view.replace(root_view.into_any());
cx.windows.get_mut(id).unwrap().replace(window);
future::ready(handle)
})
2023-09-19 19:19:22 +00:00
}
pub(crate) fn update_window<R>(
&mut self,
2023-09-25 17:55:05 +00:00
id: WindowId,
update: impl FnOnce(&mut WindowContext) -> R,
2023-09-19 19:19:22 +00:00
) -> Result<R> {
2023-09-26 17:34:41 +00:00
self.update(|cx| {
let mut window = cx
.windows
.get_mut(id)
.ok_or_else(|| anyhow!("window not found"))?
.take()
.unwrap();
let result = update(&mut WindowContext::mutable(cx, &mut window));
window.dirty = true;
cx.windows
.get_mut(id)
.ok_or_else(|| anyhow!("window not found"))?
.replace(window);
Ok(result)
})
2023-09-19 19:19:22 +00:00
}
2023-09-25 17:47:37 +00:00
fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
self.pending_updates += 1;
let result = update(self);
self.pending_updates -= 1;
if self.pending_updates == 0 {
self.flush_effects();
}
result
}
fn flush_effects(&mut self) {
while let Some(effect) = self.pending_effects.pop_front() {
match effect {
Effect::Notify(entity_id) => self.apply_notify_effect(entity_id),
}
}
2023-09-25 17:55:05 +00:00
let dirty_window_ids = self
.windows
.iter()
.filter_map(|(window_id, window)| {
let window = window.as_ref().unwrap();
if window.dirty {
Some(window_id)
} else {
None
}
})
.collect::<Vec<_>>();
for dirty_window_id in dirty_window_ids {
2023-09-27 21:35:46 +00:00
self.update_window(dirty_window_id, |cx| cx.draw())
.unwrap() // We know we have the window.
.log_err();
2023-09-25 17:55:05 +00:00
}
2023-09-25 17:47:37 +00:00
}
fn apply_notify_effect(&mut self, updated_entity: EntityId) {
if let Some(mut handlers) = self.observers.remove(&updated_entity) {
handlers.retain(|handler| handler(self));
if let Some(new_handlers) = self.observers.remove(&updated_entity) {
handlers.extend(new_handlers);
}
self.observers.insert(updated_entity, handlers);
}
}
2023-09-19 19:19:22 +00:00
}
impl Context for AppContext {
2023-09-25 17:47:37 +00:00
type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
2023-09-26 17:29:44 +00:00
type Result<T> = T;
2023-09-19 19:19:22 +00:00
2023-09-25 17:47:37 +00:00
fn entity<T: Send + Sync + 'static>(
2023-09-19 19:19:22 +00:00
&mut self,
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
) -> Handle<T> {
let id = self.entities.insert(None);
let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
self.entities.get_mut(id).unwrap().replace(entity);
Handle::new(id)
}
2023-09-25 17:47:37 +00:00
fn update_entity<T: Send + Sync + 'static, R>(
2023-09-19 19:19:22 +00:00
&mut self,
handle: &Handle<T>,
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> R {
let mut entity = self
.entities
.get_mut(handle.id)
.unwrap()
.take()
.unwrap()
.downcast::<T>()
.unwrap();
let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
self.entities.get_mut(handle.id).unwrap().replace(entity);
result
}
}
2023-09-26 17:29:44 +00:00
#[derive(Clone)]
pub struct AsyncContext(Weak<Mutex<AppContext>>);
impl Context for AsyncContext {
type EntityContext<'a, 'b, T: Send + Sync + 'static> = ModelContext<'a, T>;
type Result<T> = Result<T>;
fn entity<T: Send + Sync + 'static>(
&mut self,
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
) -> Result<Handle<T>> {
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock();
Ok(lock.entity(build_entity))
}
fn update_entity<T: Send + Sync + 'static, R>(
&mut self,
handle: &Handle<T>,
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> Result<R> {
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock();
Ok(lock.update_entity(handle, update))
}
}
impl AsyncContext {
pub fn update_window<T>(
&self,
handle: AnyWindowHandle,
update: impl FnOnce(&mut WindowContext) -> T + Send + Sync,
) -> Result<T> {
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut app_context = app.lock();
app_context.update_window(handle.id, update)
}
}
pub struct ModelContext<'a, T> {
app: Reference<'a, AppContext>,
2023-09-19 19:19:22 +00:00
entity_type: PhantomData<T>,
entity_id: EntityId,
}
2023-09-25 17:47:37 +00:00
impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
2023-09-19 19:19:22 +00:00
Self {
app: Reference::Mutable(app),
entity_type: PhantomData,
entity_id,
}
}
2023-09-27 23:25:04 +00:00
// todo!
// fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
// let mut entity = self
// .app
// .entities
// .get_mut(self.entity_id)
// .unwrap()
// .take()
// .unwrap();
// let result = update(entity.downcast_mut::<T>().unwrap(), self);
// self.app
// .entities
// .get_mut(self.entity_id)
// .unwrap()
// .replace(entity);
// result
// }
2023-09-25 17:47:37 +00:00
pub fn handle(&self) -> WeakHandle<T> {
WeakHandle {
id: self.entity_id,
entity_type: PhantomData,
}
}
pub fn observe<E: Send + Sync + 'static>(
&mut self,
handle: &Handle<E>,
on_notify: impl Fn(&mut T, Handle<E>, &mut ModelContext<'_, T>) + Send + Sync + 'static,
) {
let this = self.handle();
let handle = handle.downgrade();
self.app
.observers
.entry(handle.id)
.or_default()
.push(Arc::new(move |cx| {
if let Some((this, handle)) = this.upgrade(cx).zip(handle.upgrade(cx)) {
this.update(cx, |this, cx| on_notify(this, handle, cx));
true
} else {
false
}
}));
}
pub fn notify(&mut self) {
self.app
.pending_effects
.push_back(Effect::Notify(self.entity_id));
}
2023-09-19 19:19:22 +00:00
}
impl<'a, T: 'static> Context for ModelContext<'a, T> {
2023-09-25 17:47:37 +00:00
type EntityContext<'b, 'c, U: Send + Sync + 'static> = ModelContext<'b, U>;
2023-09-26 17:29:44 +00:00
type Result<U> = U;
2023-09-25 17:47:37 +00:00
fn entity<U: Send + Sync + 'static>(
2023-09-19 19:19:22 +00:00
&mut self,
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
) -> Handle<U> {
self.app.entity(build_entity)
}
2023-09-25 17:47:37 +00:00
fn update_entity<U: Send + Sync + 'static, R>(
2023-09-19 19:19:22 +00:00
&mut self,
handle: &Handle<U>,
update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
) -> R {
self.app.update_entity(handle, update)
}
}
2023-09-25 17:47:37 +00:00
slotmap::new_key_type! { pub struct EntityId; }
2023-09-19 19:19:22 +00:00
pub struct Handle<T> {
pub(crate) id: EntityId,
pub(crate) entity_type: PhantomData<T>,
}
2023-09-25 17:47:37 +00:00
impl<T: Send + Sync + 'static> Handle<T> {
2023-09-19 19:19:22 +00:00
fn new(id: EntityId) -> Self {
Self {
id,
entity_type: PhantomData,
}
}
2023-09-25 17:47:37 +00:00
pub fn downgrade(&self) -> WeakHandle<T> {
WeakHandle {
id: self.id,
entity_type: self.entity_type,
}
}
2023-09-19 19:19:22 +00:00
/// 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,
2023-09-26 17:29:44 +00:00
) -> C::Result<R> {
2023-09-19 19:19:22 +00:00
cx.update_entity(self, update)
}
}
impl<T> Clone for Handle<T> {
fn clone(&self) -> Self {
Self {
id: self.id,
entity_type: PhantomData,
}
}
}
2023-09-25 17:47:37 +00:00
pub struct WeakHandle<T> {
pub(crate) id: EntityId,
pub(crate) entity_type: PhantomData<T>,
}
impl<T: Send + Sync + 'static> WeakHandle<T> {
2023-09-27 23:25:04 +00:00
pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
2023-09-25 17:47:37 +00:00
// 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,
2023-09-26 17:29:44 +00:00
) -> 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)),
)
2023-09-25 17:47:37 +00:00
}
}
pub(crate) enum Effect {
Notify(EntityId),
}
#[cfg(test)]
mod tests {
use super::AppContext;
#[test]
fn test_app_context_send_sync() {
// This will not compile if `AppContext` does not implement `Send`
fn assert_send<T: Send>() {}
assert_send::<AppContext>();
}
}