diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs
index 638e5c6ca3..84843c9876 100644
--- a/crates/gpui2/src/action.rs
+++ b/crates/gpui2/src/action.rs
@@ -4,7 +4,7 @@ use collections::{HashMap, HashSet};
use serde::Deserialize;
use std::any::{type_name, Any};
-pub trait Action: Any + Send {
+pub trait Action: 'static {
fn qualified_name() -> SharedString
where
Self: Sized;
@@ -19,7 +19,7 @@ pub trait Action: Any + Send {
impl Action for A
where
- A: for<'a> Deserialize<'a> + PartialEq + Any + Send + Clone + Default,
+ A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + 'static,
{
fn qualified_name() -> SharedString {
type_name::().into()
diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs
index 265ce59a02..48a9324b05 100644
--- a/crates/gpui2/src/app.rs
+++ b/crates/gpui2/src/app.rs
@@ -13,11 +13,12 @@ use smallvec::SmallVec;
pub use test_context::*;
use crate::{
- current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AppMetadata, AssetSource,
- BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, FocusEvent, FocusHandle,
- FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId, Pixels, Platform, Point, Render,
- SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
- TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
+ current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle,
+ AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
+ Entity, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId,
+ Pixels, Platform, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task,
+ TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle,
+ WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@@ -114,8 +115,8 @@ type ActionBuilder = fn(json: Option) -> anyhow::Result;
type Handler = Box bool + 'static>;
type Listener = Box bool + 'static>;
-type QuitHandler = Box LocalBoxFuture<'static, ()> + 'static>;
-type ReleaseListener = Box;
+type QuitHandler = Box LocalBoxFuture<'static, ()> + 'static>;
+type ReleaseListener = Box;
pub struct AppContext {
this: Weak>,
@@ -214,10 +215,9 @@ impl AppContext {
pub fn quit(&mut self) {
let mut futures = Vec::new();
- self.quit_observers.clone().retain(&(), |observer| {
+ for observer in self.quit_observers.remove(&()) {
futures.push(observer(self));
- true
- });
+ }
self.windows.clear();
self.flush_effects();
@@ -255,37 +255,31 @@ impl AppContext {
result
}
- pub(crate) fn read_window(
- &self,
- id: WindowId,
- read: impl FnOnce(&WindowContext) -> R,
- ) -> Result {
- let window = self
- .windows
- .get(id)
- .ok_or_else(|| anyhow!("window not found"))?
- .as_ref()
- .unwrap();
- Ok(read(&WindowContext::immutable(self, &window)))
+ pub fn windows(&self) -> Vec {
+ self.windows
+ .values()
+ .filter_map(|window| Some(window.as_ref()?.handle.clone()))
+ .collect()
}
pub(crate) fn update_window(
&mut self,
- id: WindowId,
- update: impl FnOnce(&mut WindowContext) -> R,
+ handle: AnyWindowHandle,
+ update: impl FnOnce(AnyView, &mut WindowContext) -> R,
) -> Result {
self.update(|cx| {
let mut window = cx
.windows
- .get_mut(id)
+ .get_mut(handle.id)
.ok_or_else(|| anyhow!("window not found"))?
.take()
.unwrap();
- let result = update(&mut WindowContext::mutable(cx, &mut window));
+ let root_view = window.root_view.clone().unwrap();
+ let result = update(root_view, &mut WindowContext::new(cx, &mut window));
cx.windows
- .get_mut(id)
+ .get_mut(handle.id)
.ok_or_else(|| anyhow!("window not found"))?
.replace(window);
@@ -305,7 +299,7 @@ impl AppContext {
let id = cx.windows.insert(None);
let handle = WindowHandle::new(id);
let mut window = Window::new(handle.into(), options, cx);
- let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
+ let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
window.root_view.replace(root_view.into());
cx.windows.get_mut(id).unwrap().replace(window);
handle
@@ -386,8 +380,11 @@ impl AppContext {
self.apply_notify_effect(emitter);
}
Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
- Effect::FocusChanged { window_id, focused } => {
- self.apply_focus_changed_effect(window_id, focused);
+ Effect::FocusChanged {
+ window_handle,
+ focused,
+ } => {
+ self.apply_focus_changed_effect(window_handle, focused);
}
Effect::Refresh => {
self.apply_refresh_effect();
@@ -407,18 +404,18 @@ impl AppContext {
let dirty_window_ids = self
.windows
.iter()
- .filter_map(|(window_id, window)| {
+ .filter_map(|(_, window)| {
let window = window.as_ref().unwrap();
if window.dirty {
- Some(window_id)
+ Some(window.handle.clone())
} else {
None
}
})
.collect::>();
- for dirty_window_id in dirty_window_ids {
- self.update_window(dirty_window_id, |cx| cx.draw()).unwrap();
+ for dirty_window_handle in dirty_window_ids {
+ dirty_window_handle.update(self, |_, cx| cx.draw()).unwrap();
}
}
@@ -435,7 +432,7 @@ impl AppContext {
for (entity_id, mut entity) in dropped {
self.observers.remove(&entity_id);
self.event_listeners.remove(&entity_id);
- for mut release_callback in self.release_listeners.remove(&entity_id) {
+ for release_callback in self.release_listeners.remove(&entity_id) {
release_callback(entity.as_mut(), self);
}
}
@@ -446,27 +443,27 @@ impl AppContext {
/// For now, we simply blur the window if this happens, but we may want to support invoking
/// a window blur handler to restore focus to some logical element.
fn release_dropped_focus_handles(&mut self) {
- let window_ids = self.windows.keys().collect::>();
- for window_id in window_ids {
- self.update_window(window_id, |cx| {
- let mut blur_window = false;
- let focus = cx.window.focus;
- cx.window.focus_handles.write().retain(|handle_id, count| {
- if count.load(SeqCst) == 0 {
- if focus == Some(handle_id) {
- blur_window = true;
+ for window_handle in self.windows() {
+ window_handle
+ .update(self, |_, cx| {
+ let mut blur_window = false;
+ let focus = cx.window.focus;
+ cx.window.focus_handles.write().retain(|handle_id, count| {
+ if count.load(SeqCst) == 0 {
+ if focus == Some(handle_id) {
+ blur_window = true;
+ }
+ false
+ } else {
+ true
}
- false
- } else {
- true
- }
- });
+ });
- if blur_window {
- cx.blur();
- }
- })
- .unwrap();
+ if blur_window {
+ cx.blur();
+ }
+ })
+ .unwrap();
}
}
@@ -483,30 +480,35 @@ impl AppContext {
.retain(&emitter, |handler| handler(event.as_ref(), self));
}
- fn apply_focus_changed_effect(&mut self, window_id: WindowId, focused: Option) {
- self.update_window(window_id, |cx| {
- if cx.window.focus == focused {
- let mut listeners = mem::take(&mut cx.window.focus_listeners);
- let focused =
- focused.map(|id| FocusHandle::for_id(id, &cx.window.focus_handles).unwrap());
- let blurred = cx
- .window
- .last_blur
- .take()
- .unwrap()
- .and_then(|id| FocusHandle::for_id(id, &cx.window.focus_handles));
- if focused.is_some() || blurred.is_some() {
- let event = FocusEvent { focused, blurred };
- for listener in &listeners {
- listener(&event, cx);
+ fn apply_focus_changed_effect(
+ &mut self,
+ window_handle: AnyWindowHandle,
+ focused: Option,
+ ) {
+ window_handle
+ .update(self, |_, cx| {
+ if cx.window.focus == focused {
+ let mut listeners = mem::take(&mut cx.window.focus_listeners);
+ let focused = focused
+ .map(|id| FocusHandle::for_id(id, &cx.window.focus_handles).unwrap());
+ let blurred = cx
+ .window
+ .last_blur
+ .take()
+ .unwrap()
+ .and_then(|id| FocusHandle::for_id(id, &cx.window.focus_handles));
+ if focused.is_some() || blurred.is_some() {
+ let event = FocusEvent { focused, blurred };
+ for listener in &listeners {
+ listener(&event, cx);
+ }
}
- }
- listeners.extend(cx.window.focus_listeners.drain(..));
- cx.window.focus_listeners = listeners;
- }
- })
- .ok();
+ listeners.extend(cx.window.focus_listeners.drain(..));
+ cx.window.focus_listeners = listeners;
+ }
+ })
+ .ok();
}
fn apply_refresh_effect(&mut self) {
@@ -680,6 +682,24 @@ impl AppContext {
self.globals_by_type.insert(global_type, lease.global);
}
+ pub fn observe_release(
+ &mut self,
+ handle: &E,
+ on_release: impl FnOnce(&mut T, &mut AppContext) + 'static,
+ ) -> Subscription
+ where
+ E: Entity,
+ T: 'static,
+ {
+ self.release_listeners.insert(
+ handle.entity_id(),
+ Box::new(move |entity, cx| {
+ let entity = entity.downcast_mut().expect("invalid entity type");
+ on_release(entity, cx)
+ }),
+ )
+ }
+
pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
self.text_style_stack.push(text_style);
}
@@ -733,7 +753,6 @@ impl AppContext {
}
impl Context for AppContext {
- type ModelContext<'a, T> = ModelContext<'a, T>;
type Result = T;
/// Build an entity that is owned by the application. The given function will be invoked with
@@ -741,11 +760,11 @@ impl Context for AppContext {
/// which can be used to access the entity in a context.
fn build_model(
&mut self,
- build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T,
+ build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Model {
self.update(|cx| {
let slot = cx.entities.reserve();
- let entity = build_model(&mut ModelContext::mutable(cx, slot.downgrade()));
+ let entity = build_model(&mut ModelContext::new(cx, slot.downgrade()));
cx.entities.insert(slot, entity)
})
}
@@ -755,18 +774,38 @@ impl Context for AppContext {
fn update_model(
&mut self,
model: &Model,
- update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
+ update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
) -> R {
self.update(|cx| {
let mut entity = cx.entities.lease(model);
- let result = update(
- &mut entity,
- &mut ModelContext::mutable(cx, model.downgrade()),
- );
+ let result = update(&mut entity, &mut ModelContext::new(cx, model.downgrade()));
cx.entities.end_lease(entity);
result
})
}
+
+ fn update_window(&mut self, handle: AnyWindowHandle, update: F) -> Result
+ where
+ F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
+ {
+ self.update(|cx| {
+ let mut window = cx
+ .windows
+ .get_mut(handle.id)
+ .ok_or_else(|| anyhow!("window not found"))?
+ .take()
+ .unwrap();
+
+ let root_view = window.root_view.clone().unwrap();
+ let result = update(root_view, &mut WindowContext::new(cx, &mut window));
+ cx.windows
+ .get_mut(handle.id)
+ .ok_or_else(|| anyhow!("window not found"))?
+ .replace(window);
+
+ Ok(result)
+ })
+ }
}
/// These effects are processed at the end of each application update cycle.
@@ -779,7 +818,7 @@ pub(crate) enum Effect {
event: Box,
},
FocusChanged {
- window_id: WindowId,
+ window_handle: AnyWindowHandle,
focused: Option,
},
Refresh,
diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs
index fb941b91b8..01af7ae194 100644
--- a/crates/gpui2/src/app/async_context.rs
+++ b/crates/gpui2/src/app/async_context.rs
@@ -1,8 +1,8 @@
use crate::{
- AnyWindowHandle, AppContext, BackgroundExecutor, Context, ForegroundExecutor, Model,
- ModelContext, Result, Task, WindowContext,
+ AnyView, AnyWindowHandle, AppContext, BackgroundExecutor, Context, ForegroundExecutor, Model,
+ ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext,
};
-use anyhow::anyhow;
+use anyhow::{anyhow, Context as _};
use derive_more::{Deref, DerefMut};
use std::{cell::RefCell, future::Future, rc::Weak};
@@ -14,12 +14,11 @@ pub struct AsyncAppContext {
}
impl Context for AsyncAppContext {
- type ModelContext<'a, T> = ModelContext<'a, T>;
type Result = Result;
fn build_model(
&mut self,
- build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T,
+ build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result>
where
T: 'static,
@@ -35,7 +34,7 @@ impl Context for AsyncAppContext {
fn update_model(
&mut self,
handle: &Model,
- update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
+ update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
) -> Self::Result {
let app = self
.app
@@ -44,6 +43,15 @@ impl Context for AsyncAppContext {
let mut app = app.borrow_mut();
Ok(app.update_model(handle, update))
}
+
+ fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result
+ where
+ F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
+ {
+ let app = self.app.upgrade().context("app was released")?;
+ let mut lock = app.borrow_mut();
+ lock.update_window(window, f)
+ }
}
impl AsyncAppContext {
@@ -74,30 +82,17 @@ impl AsyncAppContext {
Ok(f(&mut *lock))
}
- pub fn read_window(
- &self,
- handle: AnyWindowHandle,
- update: impl FnOnce(&WindowContext) -> R,
- ) -> Result {
- let app = self
- .app
- .upgrade()
- .ok_or_else(|| anyhow!("app was released"))?;
- let app_context = app.borrow();
- app_context.read_window(handle.id, update)
- }
-
pub fn update_window(
&self,
handle: AnyWindowHandle,
- update: impl FnOnce(&mut WindowContext) -> R,
+ update: impl FnOnce(AnyView, &mut WindowContext) -> R,
) -> Result {
let app = self
.app
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut app_context = app.borrow_mut();
- app_context.update_window(handle.id, update)
+ app_context.update_window(handle, update)
}
pub fn spawn(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task
@@ -161,22 +156,22 @@ impl AsyncWindowContext {
Self { app, window }
}
- pub fn update(&self, update: impl FnOnce(&mut WindowContext) -> R) -> Result {
+ pub fn update(
+ &mut self,
+ update: impl FnOnce(AnyView, &mut WindowContext) -> R,
+ ) -> Result {
self.app.update_window(self.window, update)
}
- pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + Send + 'static) {
- self.app
- .update_window(self.window, |cx| cx.on_next_frame(f))
- .ok();
+ pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
+ self.window.update(self, |_, cx| cx.on_next_frame(f)).ok();
}
pub fn read_global(
- &self,
+ &mut self,
read: impl FnOnce(&G, &WindowContext) -> R,
) -> Result {
- self.app
- .read_window(self.window, |cx| read(cx.global(), cx))
+ self.window.update(self, |_, cx| read(cx.global(), cx))
}
pub fn update_global(
@@ -186,32 +181,79 @@ impl AsyncWindowContext {
where
G: 'static,
{
- self.app
- .update_window(self.window, |cx| cx.update_global(update))
+ self.window.update(self, |_, cx| cx.update_global(update))
+ }
+
+ pub fn spawn(&self, f: impl FnOnce(AsyncWindowContext) -> Fut + 'static) -> Task
+ where
+ Fut: Future