Compare commits
10 Commits
acp-rewind
...
remove-res
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aac6168d74 | ||
|
|
91da475b64 | ||
|
|
fcc82019fb | ||
|
|
adce2dda14 | ||
|
|
51390ab00c | ||
|
|
d6f960b68c | ||
|
|
c5688fde73 | ||
|
|
66eb3d6dc4 | ||
|
|
5231937f3c | ||
|
|
d3ff40ef01 |
358
Cargo.lock
generated
358
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,8 @@ use std::{fs, path::PathBuf, time::Duration};
|
||||
use anyhow::Result;
|
||||
use gpui::{
|
||||
div, hsla, img, point, prelude::*, px, rgb, size, svg, App, Application, AssetSource, Bounds,
|
||||
BoxShadow, ClickEvent, Context, SharedString, Task, Timer, Window, WindowBounds, WindowOptions,
|
||||
BoxShadow, ClickEvent, Context, ForegroundTask, SharedString, Timer, Window, WindowBounds,
|
||||
WindowOptions,
|
||||
};
|
||||
|
||||
struct Assets {
|
||||
@@ -34,7 +35,7 @@ impl AssetSource for Assets {
|
||||
}
|
||||
|
||||
struct HelloWorld {
|
||||
_task: Option<Task<()>>,
|
||||
_task: Option<ForegroundTask<()>>,
|
||||
opacity: f32,
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ use util::ResultExt;
|
||||
use crate::{
|
||||
current_platform, hash, init_app_menus, Action, ActionBuildError, ActionRegistry, Any, AnyView,
|
||||
AnyWindowHandle, AppContext, Asset, AssetSource, BackgroundExecutor, Bounds, ClipboardItem,
|
||||
DispatchPhase, DisplayId, EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global,
|
||||
KeyBinding, Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels,
|
||||
Platform, PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
|
||||
RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet,
|
||||
Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance, WindowHandle, WindowId,
|
||||
WindowInvalidator,
|
||||
DispatchPhase, DisplayId, EventEmitter, FocusHandle, FocusMap, ForegroundContext,
|
||||
ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu,
|
||||
PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, PromptBuilder, PromptHandle,
|
||||
PromptLevel, Render, RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString,
|
||||
SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance,
|
||||
WindowHandle, WindowId, WindowInvalidator,
|
||||
};
|
||||
|
||||
mod async_context;
|
||||
@@ -1026,10 +1026,10 @@ impl App {
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
/// Creates an `AsyncApp`, which can be cloned and has a static lifetime
|
||||
/// Creates a `WeakAsyncApp`, which can be cloned and has a static lifetime
|
||||
/// so it can be held across `await` points.
|
||||
pub fn to_async(&self) -> AsyncApp {
|
||||
AsyncApp {
|
||||
pub fn to_async(&self) -> WeakAsyncApp {
|
||||
WeakAsyncApp {
|
||||
app: self.this.clone(),
|
||||
background_executor: self.background_executor.clone(),
|
||||
foreground_executor: self.foreground_executor.clone(),
|
||||
@@ -1054,7 +1054,11 @@ impl App {
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn(f(self.to_async()))
|
||||
let async_app = self.to_async();
|
||||
self.foreground_executor.spawn_with_context(
|
||||
ForegroundContext::app(&async_app.app),
|
||||
f(async_app.upgrade()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Schedules the given function to be run at the end of the current effect cycle, allowing entities
|
||||
@@ -1596,8 +1600,6 @@ impl App {
|
||||
}
|
||||
|
||||
impl AppContext for App {
|
||||
type Result<T> = T;
|
||||
|
||||
/// Build an entity that is owned by the application. The given function will be invoked with
|
||||
/// a `Context` and must return an object representing the entity. A `Entity` handle will be returned,
|
||||
/// which can be used to access the entity in a context.
|
||||
@@ -1621,7 +1623,7 @@ impl AppContext for App {
|
||||
})
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>> {
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
|
||||
Reservation(self.entities.reserve())
|
||||
}
|
||||
|
||||
@@ -1629,7 +1631,7 @@ impl AppContext for App {
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.update(|cx| {
|
||||
let slot = reservation.0;
|
||||
let entity = build_entity(&mut Context::new_context(cx, slot.downgrade()));
|
||||
@@ -1655,11 +1657,7 @@ impl AppContext for App {
|
||||
})
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -1704,7 +1702,7 @@ impl AppContext for App {
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
@@ -1842,3 +1840,38 @@ impl HttpClient for NullHttpClient {
|
||||
type_name::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
/// The AsyncApp, AsyncWindowContext, and executor types must not be clone, in order to
|
||||
/// protect against panics that are caused when moving an AsyncApp across an
|
||||
/// await point, in a task that doesn't share it's context.
|
||||
///
|
||||
/// For example, an issue with AsyncWindowContext:
|
||||
///
|
||||
/// ```rs
|
||||
/// fn test(cx: AsyncWindowContext) {
|
||||
/// let cx_2 = cx.clone();
|
||||
/// cx.update(|_, app: &mut App| {
|
||||
/// app.spawn(|_| {
|
||||
/// my_async_thing().await;
|
||||
/// cx_2.update(/**/) // 💥 Window wasn't checked after `.await`, this might panic
|
||||
/// })
|
||||
/// })
|
||||
/// }
|
||||
///```
|
||||
///
|
||||
/// Or using ForegroundExecutor:
|
||||
///
|
||||
/// ```rs
|
||||
/// fn test(cx: AsyncApp) {
|
||||
/// let executor = cx.foreground_executor().clone();
|
||||
/// executor.spawn(async {
|
||||
/// my_async_thing().await;
|
||||
/// cx.update(/**/) // 💥 Didn't check that the app still existed after await, this might panic
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Having these types be !Clone, ensures that they'll be locked behind a `&`
|
||||
/// when calling `spawn`, which means they won't be able to be moved inside
|
||||
/// the spawn call (which has a + 'static bound)
|
||||
pub(crate) struct NotClone;
|
||||
|
||||
@@ -1,95 +1,63 @@
|
||||
use crate::{
|
||||
AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext,
|
||||
Entity, Focusable, ForegroundExecutor, Global, PromptLevel, Render, Reservation, Result, Task,
|
||||
VisualContext, Window, WindowHandle,
|
||||
Entity, Focusable, ForegroundContext, ForegroundExecutor, Global, PromptLevel, Render,
|
||||
Reservation, Result, Task, VisualContext, Window, WindowHandle,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _};
|
||||
|
||||
use anyhow::Context as _;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use futures::channel::oneshot;
|
||||
use std::{future::Future, rc::Weak};
|
||||
|
||||
use super::Context;
|
||||
use super::{Context, NotClone};
|
||||
|
||||
/// An async-friendly version of [App] with a static lifetime so it can be held across `await` points in async code.
|
||||
/// You're provided with an instance when calling [App::spawn], and you can also create one with [App::to_async].
|
||||
/// Internally, this holds a weak reference to an `App`, so its methods are fallible to protect against cases where the [App] is dropped.
|
||||
#[derive(Clone)]
|
||||
pub struct AsyncApp {
|
||||
pub(crate) app: Weak<AppCell>,
|
||||
pub(crate) background_executor: BackgroundExecutor,
|
||||
pub(crate) foreground_executor: ForegroundExecutor,
|
||||
pub(crate) app: WeakAsyncApp,
|
||||
_not_clone: NotClone,
|
||||
}
|
||||
|
||||
impl AppContext for AsyncApp {
|
||||
type Result<T> = Result<T>;
|
||||
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.new(build_entity))
|
||||
) -> Entity<T> {
|
||||
self.app.new(build_entity).unwrap()
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.reserve_entity())
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
|
||||
self.app.reserve_entity().unwrap()
|
||||
}
|
||||
|
||||
fn insert_entity<T: 'static>(
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Result<Entity<T>> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.insert_entity(reservation, build_entity))
|
||||
) -> Entity<T> {
|
||||
self.app.insert_entity(reservation, build_entity).unwrap()
|
||||
}
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.update_entity(handle, update))
|
||||
) -> R {
|
||||
self.app.update_entity(handle, update).unwrap()
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
callback: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, callback: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let lock = app.borrow();
|
||||
Ok(lock.read_entity(handle, callback))
|
||||
self.app.read_entity(handle, callback).unwrap()
|
||||
}
|
||||
|
||||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(AnyView, &mut Window, &mut App) -> T,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let mut lock = app.borrow_mut();
|
||||
lock.update_window(window, f)
|
||||
self.app.update_window(window, f).unwrap()
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
@@ -100,58 +68,44 @@ impl AppContext for AsyncApp {
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let lock = app.borrow();
|
||||
lock.read_window(window, read)
|
||||
self.app.read_window(window, read).unwrap()
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.background_executor.spawn(future)
|
||||
self.app.background_spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.update(|this| this.read_global(callback)))
|
||||
self.app.read_global(callback).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncApp {
|
||||
/// Schedules all windows in the application to be redrawn.
|
||||
pub fn refresh(&self) -> Result<()> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut lock = app.borrow_mut();
|
||||
lock.refresh_windows();
|
||||
Ok(())
|
||||
pub fn refresh(&self) {
|
||||
self.app.refresh().unwrap()
|
||||
}
|
||||
|
||||
/// Get an executor which can be used to spawn futures in the background.
|
||||
pub fn background_executor(&self) -> &BackgroundExecutor {
|
||||
&self.background_executor
|
||||
self.app.background_executor()
|
||||
}
|
||||
|
||||
/// Get an executor which can be used to spawn futures in the foreground.
|
||||
pub fn foreground_executor(&self) -> &ForegroundExecutor {
|
||||
&self.foreground_executor
|
||||
self.app.foreground_executor()
|
||||
}
|
||||
|
||||
/// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
|
||||
pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.update(f))
|
||||
/// Panics if the app has been dropped since this was created
|
||||
pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
|
||||
self.app.update(f).unwrap()
|
||||
}
|
||||
|
||||
/// Open a window with the given options based on the root view returned by the given function.
|
||||
@@ -163,12 +117,7 @@ impl AsyncApp {
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut lock = app.borrow_mut();
|
||||
lock.open_window(options, build_root_view)
|
||||
self.app.open_window(options, build_root_view).unwrap()
|
||||
}
|
||||
|
||||
/// Schedule a future to be polled in the background.
|
||||
@@ -178,31 +127,21 @@ impl AsyncApp {
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn(f(self.clone()))
|
||||
self.app.spawn(f)
|
||||
}
|
||||
|
||||
/// Determine whether global state of the specified type has been assigned.
|
||||
/// Returns an error if the `App` has been dropped.
|
||||
pub fn has_global<G: Global>(&self) -> Result<bool> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let app = app.borrow_mut();
|
||||
Ok(app.has_global::<G>())
|
||||
pub fn has_global<G: Global>(&self) -> bool {
|
||||
self.app.has_global::<G>().unwrap()
|
||||
}
|
||||
|
||||
/// Reads the global state of the specified type, passing it to the given callback.
|
||||
///
|
||||
/// Panics if no global state of the specified type has been assigned.
|
||||
/// Returns an error if the `App` has been dropped.
|
||||
pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let app = app.borrow_mut();
|
||||
Ok(read(app.global(), &app))
|
||||
pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> R {
|
||||
self.app.read_global(read).unwrap()
|
||||
}
|
||||
|
||||
/// Reads the global state of the specified type, passing it to the given callback.
|
||||
@@ -212,39 +151,32 @@ impl AsyncApp {
|
||||
///
|
||||
/// Returns an error if no state of the specified type has been assigned the `App` has been dropped.
|
||||
pub fn try_read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Option<R> {
|
||||
let app = self.app.upgrade()?;
|
||||
let app = app.borrow_mut();
|
||||
Some(read(app.try_global()?, &app))
|
||||
self.app.try_read_global(read).unwrap()
|
||||
}
|
||||
|
||||
/// A convenience method for [App::update_global]
|
||||
/// for updating the global state of the specified type.
|
||||
pub fn update_global<G: Global, R>(
|
||||
&self,
|
||||
update: impl FnOnce(&mut G, &mut App) -> R,
|
||||
) -> Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("app was released"))?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.update(|cx| cx.update_global(update)))
|
||||
pub fn update_global<G: Global, R>(&self, update: impl FnOnce(&mut G, &mut App) -> R) -> R {
|
||||
self.app.update_global(update).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A cloneable, owned handle to the application context,
|
||||
/// composed with the window associated with the current task.
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct AsyncWindowContext {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
app: AsyncApp,
|
||||
window: AnyWindowHandle,
|
||||
cx: WeakAsyncWindowContext,
|
||||
_not_clone: NotClone,
|
||||
}
|
||||
|
||||
impl AsyncWindowContext {
|
||||
pub(crate) fn new_context(app: AsyncApp, window: AnyWindowHandle) -> Self {
|
||||
Self { app, window }
|
||||
Self {
|
||||
cx: WeakAsyncWindowContext::new_context(app.app, window),
|
||||
_not_clone: NotClone,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the handle of the window this context is associated with.
|
||||
@@ -253,33 +185,26 @@ impl AsyncWindowContext {
|
||||
}
|
||||
|
||||
/// A convenience method for [`App::update_window`].
|
||||
pub fn update<R>(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> Result<R> {
|
||||
self.app
|
||||
.update_window(self.window, |_, window, cx| update(window, cx))
|
||||
pub fn update<R>(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> R {
|
||||
self.cx.update(update).unwrap()
|
||||
}
|
||||
|
||||
/// A convenience method for [`App::update_window`].
|
||||
pub fn update_root<R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
|
||||
) -> Result<R> {
|
||||
self.app.update_window(self.window, update)
|
||||
) -> R {
|
||||
self.cx.update_root(update).unwrap()
|
||||
}
|
||||
|
||||
/// A convenience method for [`Window::on_next_frame`].
|
||||
pub fn on_next_frame(&mut self, f: impl FnOnce(&mut Window, &mut App) + 'static) {
|
||||
self.window
|
||||
.update(self, |_, window, _| window.on_next_frame(f))
|
||||
.ok();
|
||||
self.cx.on_next_frame(f).unwrap()
|
||||
}
|
||||
|
||||
/// A convenience method for [`App::global`].
|
||||
pub fn read_global<G: Global, R>(
|
||||
&mut self,
|
||||
read: impl FnOnce(&G, &Window, &App) -> R,
|
||||
) -> Result<R> {
|
||||
self.window
|
||||
.update(self, |_, window, cx| read(cx.global(), window, cx))
|
||||
pub fn read_global<G: Global, R>(&mut self, read: impl FnOnce(&G, &Window, &App) -> R) -> R {
|
||||
self.cx.read_global(read).unwrap()
|
||||
}
|
||||
|
||||
/// A convenience method for [`App::update_global`].
|
||||
@@ -287,13 +212,11 @@ impl AsyncWindowContext {
|
||||
pub fn update_global<G, R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(&mut G, &mut Window, &mut App) -> R,
|
||||
) -> Result<R>
|
||||
) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
self.window.update(self, |_, window, cx| {
|
||||
cx.update_global(|global, cx| update(global, window, cx))
|
||||
})
|
||||
self.cx.update_global(update).unwrap()
|
||||
}
|
||||
|
||||
/// Schedule a future to be executed on the main thread. This is used for collecting
|
||||
@@ -304,7 +227,7 @@ impl AsyncWindowContext {
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn(f(self.clone()))
|
||||
self.cx.spawn(f)
|
||||
}
|
||||
|
||||
/// Present a platform dialog.
|
||||
@@ -321,58 +244,50 @@ impl AsyncWindowContext {
|
||||
.update(self, |_, window, cx| {
|
||||
window.prompt(level, message, detail, answers, cx)
|
||||
})
|
||||
.unwrap_or_else(|_| oneshot::channel().1)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl AppContext for AsyncWindowContext {
|
||||
type Result<T> = Result<T>;
|
||||
|
||||
fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T) -> Result<Entity<T>>
|
||||
fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T) -> Entity<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.window.update(self, |_, _, cx| cx.new(build_entity))
|
||||
self.cx.new(build_entity).unwrap()
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
|
||||
self.window.update(self, |_, _, cx| cx.reserve_entity())
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
|
||||
self.cx.reserve_entity().unwrap()
|
||||
}
|
||||
|
||||
fn insert_entity<T: 'static>(
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
self.window
|
||||
.update(self, |_, _, cx| cx.insert_entity(reservation, build_entity))
|
||||
) -> Entity<T> {
|
||||
self.cx.insert_entity(reservation, build_entity).unwrap()
|
||||
}
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Result<R> {
|
||||
self.window
|
||||
.update(self, |_, _, cx| cx.update_entity(handle, update))
|
||||
) -> R {
|
||||
self.cx.update_entity(handle, update).unwrap()
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.app.read_entity(handle, read)
|
||||
self.cx.read_entity(handle, read).unwrap()
|
||||
}
|
||||
|
||||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(AnyView, &mut Window, &mut App) -> T,
|
||||
{
|
||||
self.app.update_window(window, update)
|
||||
self.cx.update_window(window, update)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
@@ -383,21 +298,21 @@ impl AppContext for AsyncWindowContext {
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.app.read_window(window, read)
|
||||
self.cx.read_window(window, read)
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.app.background_executor.spawn(future)
|
||||
self.cx.background_spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
self.app.read_global(callback)
|
||||
self.app.read_global(callback).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,38 +324,432 @@ impl VisualContext for AsyncWindowContext {
|
||||
fn new_window_entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Window, &mut Context<T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
self.window
|
||||
.update(self, |_, window, cx| cx.new(|cx| build_entity(window, cx)))
|
||||
) -> Entity<T> {
|
||||
self.cx.new_window_entity(build_entity).unwrap()
|
||||
}
|
||||
|
||||
fn update_window_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
view: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
self.window.update(self, |_, window, cx| {
|
||||
view.update(cx, |entity, cx| update(entity, window, cx))
|
||||
})
|
||||
) -> R {
|
||||
self.cx.update_window_entity(view, update).unwrap()
|
||||
}
|
||||
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
|
||||
) -> Self::Result<Entity<V>>
|
||||
) -> Entity<V>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
self.window
|
||||
.update(self, |_, window, cx| window.replace_root(cx, build_view))
|
||||
self.cx.replace_root_view(build_view).unwrap()
|
||||
}
|
||||
|
||||
fn focus<V>(&mut self, view: &Entity<V>) -> Self::Result<()>
|
||||
fn focus<V>(&mut self, view: &Entity<V>)
|
||||
where
|
||||
V: Focusable,
|
||||
{
|
||||
self.window.update(self, |_, window, cx| {
|
||||
view.read(cx).focus_handle(cx).clone().focus(window);
|
||||
self.cx.focus(view).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// An async-friendly version of [App] with a static lifetime so it can be held across `await` points in async code.
|
||||
/// You're provided with an instance when calling [App::spawn], and you can also create one with [App::to_async].
|
||||
/// Internally, this holds a weak reference to an `App`, so its methods are fallible to protect against cases where the [App] is dropped.
|
||||
pub struct WeakAsyncApp {
|
||||
pub(crate) app: Weak<AppCell>,
|
||||
pub(crate) background_executor: BackgroundExecutor,
|
||||
pub(crate) foreground_executor: ForegroundExecutor,
|
||||
}
|
||||
|
||||
impl Clone for WeakAsyncApp {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
app: self.app.clone(),
|
||||
background_executor: self.background_executor.clone(),
|
||||
foreground_executor: self.foreground_executor.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WeakAsyncApp {
|
||||
pub(crate) fn upgrade(self) -> AsyncApp {
|
||||
AsyncApp {
|
||||
app: self,
|
||||
_not_clone: NotClone,
|
||||
}
|
||||
}
|
||||
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Result<Entity<T>> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.new(build_entity))
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.reserve_entity())
|
||||
}
|
||||
|
||||
fn insert_entity<T: 'static>(
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Result<Entity<T>> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.insert_entity(reservation, build_entity))
|
||||
}
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Result<R> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.update_entity(handle, update))
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
callback: impl FnOnce(&T, &App) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let lock = app.borrow();
|
||||
Ok(lock.read_entity(handle, callback))
|
||||
}
|
||||
|
||||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<Result<T>>
|
||||
where
|
||||
F: FnOnce(AnyView, &mut Window, &mut App) -> T,
|
||||
{
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.update_window(window, f))
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(Entity<T>, &App) -> R,
|
||||
) -> Result<Result<R>>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let lock = app.borrow();
|
||||
Ok(lock.read_window(window, read))
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Result<R>
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.update(|this| this.read_global(callback)))
|
||||
}
|
||||
|
||||
/// Schedules all windows in the application to be redrawn.
|
||||
pub fn refresh(&self) -> Result<()> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.refresh_windows())
|
||||
}
|
||||
|
||||
/// Get an executor which can be used to spawn futures in the background.
|
||||
pub fn background_executor(&self) -> &BackgroundExecutor {
|
||||
&self.background_executor
|
||||
}
|
||||
|
||||
/// Get an executor which can be used to spawn futures in the foreground.
|
||||
pub fn foreground_executor(&self) -> &ForegroundExecutor {
|
||||
&self.foreground_executor
|
||||
}
|
||||
|
||||
/// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
|
||||
/// Panics if the app has been dropped since this was created
|
||||
pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> Result<R> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(f(&mut lock))
|
||||
}
|
||||
|
||||
/// Open a window with the given options based on the root view returned by the given function.
|
||||
pub fn open_window<V>(
|
||||
&self,
|
||||
options: crate::WindowOptions,
|
||||
build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
|
||||
) -> Result<Result<WindowHandle<V>>>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut lock = app.borrow_mut();
|
||||
Ok(lock.open_window(options, build_root_view))
|
||||
}
|
||||
|
||||
/// Schedule a future to be polled in the background.
|
||||
#[track_caller]
|
||||
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncApp) -> Fut) -> Task<R>
|
||||
where
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor
|
||||
.spawn_with_context(ForegroundContext::app(&self.app), f(self.clone().upgrade()))
|
||||
}
|
||||
|
||||
/// Determine whether global state of the specified type has been assigned.
|
||||
/// Returns an error if the `App` has been dropped.
|
||||
pub fn has_global<G: Global>(&self) -> Result<bool> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let app = app.borrow_mut();
|
||||
Ok(app.has_global::<G>())
|
||||
}
|
||||
|
||||
/// Reads the global state of the specified type, passing it to the given callback.
|
||||
///
|
||||
/// Similar to [`AsyncApp::read_global`], but returns an error instead of panicking
|
||||
/// if no state of the specified type has been assigned.
|
||||
///
|
||||
/// Returns an error if no state of the specified type has been assigned the `App` has been dropped.
|
||||
pub fn try_read_global<G: Global, R>(
|
||||
&self,
|
||||
read: impl FnOnce(&G, &App) -> R,
|
||||
) -> Result<Option<R>> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let app = app.borrow_mut();
|
||||
let Some(global) = app.try_global::<G>() else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(read(global, &app)))
|
||||
}
|
||||
|
||||
/// A convenience method for [App::update_global]
|
||||
/// for updating the global state of the specified type.
|
||||
pub fn update_global<G: Global, R>(
|
||||
&self,
|
||||
update: impl FnOnce(&mut G, &mut App) -> R,
|
||||
) -> Result<R> {
|
||||
let app = self.app.upgrade().context("App dropped")?;
|
||||
let mut app = app.borrow_mut();
|
||||
Ok(app.update(|cx| cx.update_global(update)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A cloneable, owned handle to the application context,
|
||||
/// composed with the window associated with the current task.
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
pub struct WeakAsyncWindowContext {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
app: WeakAsyncApp,
|
||||
window: AnyWindowHandle,
|
||||
}
|
||||
|
||||
impl WeakAsyncWindowContext {
|
||||
pub(crate) fn new_context(app: WeakAsyncApp, window: AnyWindowHandle) -> Self {
|
||||
Self { app, window }
|
||||
}
|
||||
|
||||
pub(crate) fn upgrade(self) -> AsyncWindowContext {
|
||||
AsyncWindowContext {
|
||||
cx: self,
|
||||
_not_clone: NotClone,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the handle of the window this context is associated with.
|
||||
pub fn window_handle(&self) -> AnyWindowHandle {
|
||||
self.window
|
||||
}
|
||||
|
||||
/// A convenience method for [`App::update_window`].
|
||||
pub fn update<R>(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> Result<R> {
|
||||
crate::Flatten::flatten(
|
||||
self.app
|
||||
.update_window(self.window, |_, window, cx| update(window, cx)),
|
||||
)
|
||||
}
|
||||
|
||||
/// A convenience method for [`App::update_window`].
|
||||
pub fn update_root<R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
|
||||
) -> Result<R> {
|
||||
crate::Flatten::flatten(self.app.update_window(self.window, update))
|
||||
}
|
||||
|
||||
/// A convenience method for [`Window::on_next_frame`].
|
||||
pub fn on_next_frame(&mut self, f: impl FnOnce(&mut Window, &mut App) + 'static) -> Result<()> {
|
||||
self.update_window(self.window, |_, window, _| window.on_next_frame(f))
|
||||
}
|
||||
|
||||
/// A convenience method for [`App::global`].
|
||||
pub fn read_global<G: Global, R>(
|
||||
&mut self,
|
||||
read: impl FnOnce(&G, &Window, &App) -> R,
|
||||
) -> Result<R> {
|
||||
self.update_window(self.window, |_, window, cx| read(cx.global(), window, cx))
|
||||
}
|
||||
|
||||
/// A convenience method for [`App::update_global`].
|
||||
/// for updating the global state of the specified type.
|
||||
pub fn update_global<G, R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(&mut G, &mut Window, &mut App) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
self.update_window(self.window, |_, window, cx| {
|
||||
cx.update_global(|global, cx| update(global, window, cx))
|
||||
})
|
||||
}
|
||||
|
||||
/// Schedule a future to be executed on the main thread. This is used for collecting
|
||||
/// the results of background tasks and updating the UI.
|
||||
#[track_caller]
|
||||
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R>
|
||||
where
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn_with_context(
|
||||
ForegroundContext::window(&self.app.app, self.window.id),
|
||||
f(self.clone().upgrade()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Present a platform dialog.
|
||||
/// The provided message will be presented, along with buttons for each answer.
|
||||
/// When a button is clicked, the returned Receiver will receive the index of the clicked button.
|
||||
pub fn prompt(
|
||||
&mut self,
|
||||
level: PromptLevel,
|
||||
message: &str,
|
||||
detail: Option<&str>,
|
||||
answers: &[&str],
|
||||
) -> Result<oneshot::Receiver<usize>> {
|
||||
self.update_window(self.window, |_, window, cx| {
|
||||
window.prompt(level, message, detail, answers, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T) -> Result<Entity<T>>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.update_window(self.window, |_, _, cx| cx.new(build_entity))
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
|
||||
self.update_window(self.window, |_, _, cx| cx.reserve_entity())
|
||||
}
|
||||
|
||||
fn insert_entity<T: 'static>(
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Result<Entity<T>> {
|
||||
self.update_window(self.window, |_, _, cx| {
|
||||
cx.insert_entity(reservation, build_entity)
|
||||
})
|
||||
}
|
||||
|
||||
fn update_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Result<R> {
|
||||
self.update_window(self.window, |_, _, cx| cx.update_entity(handle, update))
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.app.read_entity(handle, read)
|
||||
}
|
||||
|
||||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(AnyView, &mut Window, &mut App) -> T,
|
||||
{
|
||||
crate::Flatten::flatten(self.app.update_window(window, update))
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(Entity<T>, &App) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
crate::Flatten::flatten(self.app.read_window(window, read))
|
||||
}
|
||||
|
||||
fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
|
||||
where
|
||||
R: Send + 'static,
|
||||
{
|
||||
self.app.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn new_window_entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Window, &mut Context<T>) -> T,
|
||||
) -> Result<Entity<T>> {
|
||||
self.update_window(self.window, |_, window, cx| {
|
||||
cx.new(|cx| build_entity(window, cx))
|
||||
})
|
||||
}
|
||||
|
||||
fn update_window_entity<T: 'static, R>(
|
||||
&mut self,
|
||||
view: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
|
||||
) -> Result<R> {
|
||||
self.update(|window, cx| view.update(cx, |entity, cx| update(entity, window, cx)))
|
||||
}
|
||||
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
|
||||
) -> Result<Entity<V>>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
self.update_window(self.window, |_, window, cx| {
|
||||
window.replace_root(cx, build_view)
|
||||
})
|
||||
}
|
||||
|
||||
fn focus<V>(&mut self, view: &Entity<V>) -> Result<()>
|
||||
where
|
||||
V: Focusable,
|
||||
{
|
||||
self.update_window(self.window, |_, window, cx| {
|
||||
view.read(cx).focus_handle(cx).clone().focus(window)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,8 +680,6 @@ impl<T> Context<'_, T> {
|
||||
}
|
||||
|
||||
impl<T> AppContext for Context<'_, T> {
|
||||
type Result<U> = U;
|
||||
|
||||
fn new<U: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, U>) -> U,
|
||||
@@ -697,7 +695,7 @@ impl<T> AppContext for Context<'_, T> {
|
||||
&mut self,
|
||||
reservation: Reservation<U>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, U>) -> U,
|
||||
) -> Self::Result<Entity<U>> {
|
||||
) -> Entity<U> {
|
||||
self.app.insert_entity(reservation, build_entity)
|
||||
}
|
||||
|
||||
@@ -709,11 +707,7 @@ impl<T> AppContext for Context<'_, T> {
|
||||
self.app.update_entity(handle, update)
|
||||
}
|
||||
|
||||
fn read_entity<U, R>(
|
||||
&self,
|
||||
handle: &Entity<U>,
|
||||
read: impl FnOnce(&U, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<U, R>(&self, handle: &Entity<U>, read: impl FnOnce(&U, &App) -> R) -> R
|
||||
where
|
||||
U: 'static,
|
||||
{
|
||||
@@ -745,7 +739,7 @@ impl<T> AppContext for Context<'_, T> {
|
||||
self.app.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
|
||||
@@ -418,11 +418,7 @@ impl<T: 'static> Entity<T> {
|
||||
}
|
||||
|
||||
/// Read the entity referenced by this handle with the given function.
|
||||
pub fn read_with<R, C: AppContext>(
|
||||
&self,
|
||||
cx: &C,
|
||||
f: impl FnOnce(&T, &App) -> R,
|
||||
) -> C::Result<R> {
|
||||
pub fn read_with<R, C: AppContext>(&self, cx: &C, f: impl FnOnce(&T, &App) -> R) -> R {
|
||||
cx.read_entity(self, f)
|
||||
}
|
||||
|
||||
@@ -435,7 +431,7 @@ impl<T: 'static> Entity<T> {
|
||||
&self,
|
||||
cx: &mut C,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> C::Result<R>
|
||||
) -> R
|
||||
where
|
||||
C: AppContext,
|
||||
{
|
||||
@@ -449,7 +445,7 @@ impl<T: 'static> Entity<T> {
|
||||
&self,
|
||||
cx: &mut C,
|
||||
update: impl FnOnce(&mut T, &mut Window, &mut Context<'_, T>) -> R,
|
||||
) -> C::Result<R>
|
||||
) -> R
|
||||
where
|
||||
C: VisualContext,
|
||||
{
|
||||
@@ -646,13 +642,10 @@ impl<T: 'static> WeakEntity<T> {
|
||||
) -> Result<R>
|
||||
where
|
||||
C: AppContext,
|
||||
Result<C::Result<R>>: crate::Flatten<R>,
|
||||
{
|
||||
crate::Flatten::flatten(
|
||||
self.upgrade()
|
||||
.ok_or_else(|| anyhow!("entity released"))
|
||||
.map(|this| cx.update_entity(&this, update)),
|
||||
)
|
||||
self.upgrade()
|
||||
.ok_or_else(|| anyhow!("entity released"))
|
||||
.map(|this| cx.update_entity(&this, update))
|
||||
}
|
||||
|
||||
/// Updates the entity referenced by this handle with the given function if
|
||||
@@ -665,14 +658,13 @@ impl<T: 'static> WeakEntity<T> {
|
||||
) -> Result<R>
|
||||
where
|
||||
C: VisualContext,
|
||||
Result<C::Result<R>>: crate::Flatten<R>,
|
||||
{
|
||||
let window = cx.window_handle();
|
||||
let this = self.upgrade().ok_or_else(|| anyhow!("entity released"))?;
|
||||
|
||||
crate::Flatten::flatten(window.update(cx, |_, window, cx| {
|
||||
window.update(cx, |_, window, cx| {
|
||||
this.update(cx, |entity, cx| update(entity, window, cx))
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads the entity referenced by this handle with the given function if
|
||||
@@ -681,13 +673,10 @@ impl<T: 'static> WeakEntity<T> {
|
||||
pub fn read_with<C, R>(&self, cx: &C, read: impl FnOnce(&T, &App) -> R) -> Result<R>
|
||||
where
|
||||
C: AppContext,
|
||||
Result<C::Result<R>>: crate::Flatten<R>,
|
||||
{
|
||||
crate::Flatten::flatten(
|
||||
self.upgrade()
|
||||
.ok_or_else(|| anyhow!("entity release"))
|
||||
.map(|this| cx.read_entity(&this, read)),
|
||||
)
|
||||
self.upgrade()
|
||||
.ok_or_else(|| anyhow!("entity release"))
|
||||
.map(|this| cx.read_entity(&this, read))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace,
|
||||
BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, DrawPhase, Drawable, Element,
|
||||
Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers,
|
||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
|
||||
Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
|
||||
Empty, EventEmitter, ForegroundContext, ForegroundExecutor, Global, InputEvent, Keystroke,
|
||||
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
||||
Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
|
||||
TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds,
|
||||
WindowHandle, WindowOptions,
|
||||
};
|
||||
@@ -13,7 +13,6 @@ use std::{cell::RefCell, future::Future, ops::Deref, rc::Rc, sync::Arc, time::Du
|
||||
|
||||
/// A TestAppContext is provided to tests created with `#[gpui::test]`, it provides
|
||||
/// an implementation of `Context` with additional methods that are useful in tests.
|
||||
#[derive(Clone)]
|
||||
pub struct TestAppContext {
|
||||
#[doc(hidden)]
|
||||
pub app: Rc<AppCell>,
|
||||
@@ -29,18 +28,31 @@ pub struct TestAppContext {
|
||||
on_quit: Rc<RefCell<Vec<Box<dyn FnOnce() + 'static>>>>,
|
||||
}
|
||||
|
||||
impl AppContext for TestAppContext {
|
||||
type Result<T> = T;
|
||||
impl Clone for TestAppContext {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
app: self.app.clone(),
|
||||
background_executor: self.background_executor.clone(),
|
||||
foreground_executor: self.foreground_executor.clone(),
|
||||
dispatcher: self.dispatcher.clone(),
|
||||
test_platform: self.test_platform.clone(),
|
||||
text_system: self.text_system.clone(),
|
||||
fn_name: self.fn_name.clone(),
|
||||
on_quit: self.on_quit.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppContext for TestAppContext {
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
let mut app = self.app.borrow_mut();
|
||||
app.new(build_entity)
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
|
||||
fn reserve_entity<T: 'static>(&mut self) -> crate::Reservation<T> {
|
||||
let mut app = self.app.borrow_mut();
|
||||
app.reserve_entity()
|
||||
}
|
||||
@@ -49,7 +61,7 @@ impl AppContext for TestAppContext {
|
||||
&mut self,
|
||||
reservation: crate::Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
let mut app = self.app.borrow_mut();
|
||||
app.insert_entity(reservation, build_entity)
|
||||
}
|
||||
@@ -58,16 +70,12 @@ impl AppContext for TestAppContext {
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
) -> R {
|
||||
let mut app = self.app.borrow_mut();
|
||||
app.update_entity(handle, update)
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -102,7 +110,7 @@ impl AppContext for TestAppContext {
|
||||
self.background_executor.spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
@@ -329,7 +337,10 @@ impl TestAppContext {
|
||||
Fut: Future<Output = R> + 'static,
|
||||
R: 'static,
|
||||
{
|
||||
self.foreground_executor.spawn(f(self.to_async()))
|
||||
self.foreground_executor.spawn_with_context(
|
||||
ForegroundContext::app(&Rc::downgrade(&self.app)),
|
||||
f(self.to_async()),
|
||||
)
|
||||
}
|
||||
|
||||
/// true if the given global is defined
|
||||
@@ -363,14 +374,15 @@ impl TestAppContext {
|
||||
lock.update(|cx| cx.update_global(update))
|
||||
}
|
||||
|
||||
/// Returns an `AsyncApp` which can be used to run tasks that expect to be on a background
|
||||
/// Returns a `AsyncApp` which can be used to run tasks that expect to be on a background
|
||||
/// thread on the current thread in tests.
|
||||
pub fn to_async(&self) -> AsyncApp {
|
||||
AsyncApp {
|
||||
WeakAsyncApp {
|
||||
app: Rc::downgrade(&self.app),
|
||||
background_executor: self.background_executor.clone(),
|
||||
foreground_executor: self.foreground_executor.clone(),
|
||||
}
|
||||
.upgrade()
|
||||
}
|
||||
|
||||
/// Wait until there are no more pending tasks.
|
||||
@@ -644,7 +656,7 @@ impl<V> Entity<V> {
|
||||
|
||||
use derive_more::{Deref, DerefMut};
|
||||
|
||||
use super::{Context, Entity};
|
||||
use super::{Context, Entity, WeakAsyncApp};
|
||||
#[derive(Deref, DerefMut, Clone)]
|
||||
/// A VisualTestContext is the test-equivalent of a `Window` and `App`. It allows you to
|
||||
/// run window-specific test code. It can be dereferenced to a `TextAppContext`.
|
||||
@@ -868,16 +880,14 @@ impl VisualTestContext {
|
||||
}
|
||||
|
||||
impl AppContext for VisualTestContext {
|
||||
type Result<T> = <TestAppContext as AppContext>::Result<T>;
|
||||
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.cx.new(build_entity)
|
||||
}
|
||||
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
|
||||
fn reserve_entity<T: 'static>(&mut self) -> crate::Reservation<T> {
|
||||
self.cx.reserve_entity()
|
||||
}
|
||||
|
||||
@@ -885,7 +895,7 @@ impl AppContext for VisualTestContext {
|
||||
&mut self,
|
||||
reservation: crate::Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.cx.insert_entity(reservation, build_entity)
|
||||
}
|
||||
|
||||
@@ -893,18 +903,14 @@ impl AppContext for VisualTestContext {
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Self::Result<R>
|
||||
) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.cx.update_entity(handle, update)
|
||||
}
|
||||
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
@@ -936,7 +942,7 @@ impl AppContext for VisualTestContext {
|
||||
self.cx.background_spawn(future)
|
||||
}
|
||||
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global,
|
||||
{
|
||||
@@ -953,7 +959,7 @@ impl VisualContext for VisualTestContext {
|
||||
fn new_window_entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Window, &mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>> {
|
||||
) -> Entity<T> {
|
||||
self.window
|
||||
.update(&mut self.cx, |_, window, cx| {
|
||||
cx.new(|cx| build_entity(window, cx))
|
||||
@@ -965,7 +971,7 @@ impl VisualContext for VisualTestContext {
|
||||
&mut self,
|
||||
view: &Entity<V>,
|
||||
update: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
) -> R {
|
||||
self.window
|
||||
.update(&mut self.cx, |_, window, cx| {
|
||||
view.update(cx, |v, cx| update(v, window, cx))
|
||||
@@ -976,7 +982,7 @@ impl VisualContext for VisualTestContext {
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
|
||||
) -> Self::Result<Entity<V>>
|
||||
) -> Entity<V>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
@@ -987,7 +993,7 @@ impl VisualContext for VisualTestContext {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn focus<V: crate::Focusable>(&mut self, view: &Entity<V>) -> Self::Result<()> {
|
||||
fn focus<V: crate::Focusable>(&mut self, view: &Entity<V>) {
|
||||
self.window
|
||||
.update(&mut self.cx, |_, window, cx| {
|
||||
view.read(cx).focus_handle(cx).clone().focus(window)
|
||||
|
||||
@@ -2509,8 +2509,7 @@ fn handle_tooltip_mouse_move(
|
||||
});
|
||||
*active_tooltip.borrow_mut() = new_tooltip;
|
||||
window.refresh();
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
});
|
||||
active_tooltip
|
||||
@@ -2577,7 +2576,7 @@ fn handle_tooltip_check_visible_and_update(
|
||||
.timer(HOVERABLE_TOOLTIP_HIDE_DELAY)
|
||||
.await;
|
||||
if active_tooltip.borrow_mut().take().is_some() {
|
||||
cx.update(|window, _cx| window.refresh()).ok();
|
||||
cx.update(|window, _cx| window.refresh());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -361,8 +361,7 @@ impl Element for Img {
|
||||
cx.background_executor().timer(LOADING_DELAY).await;
|
||||
cx.update(move |_, cx| {
|
||||
cx.notify(current_view);
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
});
|
||||
state.started_loading = Some((Instant::now(), task));
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
use crate::{App, PlatformDispatcher};
|
||||
use async_task::Runnable;
|
||||
use crate::{App, ForegroundContext, NotClone, PlatformDispatcher};
|
||||
use async_task::Builder;
|
||||
use futures::channel::mpsc;
|
||||
use smol::prelude::*;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::panic::Location;
|
||||
use std::thread::{self, ThreadId};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
mem::{self, ManuallyDrop},
|
||||
num::NonZeroUsize,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
@@ -17,6 +14,7 @@ use std::{
|
||||
Arc,
|
||||
},
|
||||
task::{Context, Poll},
|
||||
thread::ThreadId,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use util::TryFutureExt;
|
||||
@@ -27,10 +25,10 @@ use rand::rngs::StdRng;
|
||||
|
||||
/// A pointer to the executor that is currently running,
|
||||
/// for spawning background tasks.
|
||||
#[derive(Clone)]
|
||||
pub struct BackgroundExecutor {
|
||||
#[doc(hidden)]
|
||||
pub dispatcher: Arc<dyn PlatformDispatcher>,
|
||||
_not_clone: NotClone,
|
||||
}
|
||||
|
||||
/// A pointer to the executor that is currently running,
|
||||
@@ -40,10 +38,10 @@ pub struct BackgroundExecutor {
|
||||
/// `ForegroundExecutor::spawn` does not require `Send` but checks at runtime that the future is
|
||||
/// only polled from the same thread it was spawned from. These checks would fail when spawning
|
||||
/// foreground tasks from from background threads.
|
||||
#[derive(Clone)]
|
||||
pub struct ForegroundExecutor {
|
||||
#[doc(hidden)]
|
||||
pub dispatcher: Arc<dyn PlatformDispatcher>,
|
||||
_not_clone: NotClone,
|
||||
not_send: PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
@@ -55,15 +53,18 @@ pub struct ForegroundExecutor {
|
||||
/// the task to continue running, but with no way to return a value.
|
||||
#[must_use]
|
||||
#[derive(Debug)]
|
||||
pub struct Task<T>(TaskState<T>);
|
||||
pub struct Task<T>(TaskState<T, async_task::Task<T, ForegroundContext>, async_task::Task<T>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TaskState<T> {
|
||||
enum TaskState<T, F, B> {
|
||||
/// A task that is ready to return a value
|
||||
Ready(Option<T>),
|
||||
|
||||
/// A task that is currently running.
|
||||
Spawned(async_task::Task<T>),
|
||||
/// A task that is currently running on the foreground.
|
||||
ForegroundSpawned(F),
|
||||
|
||||
/// A task that is currently running on the background
|
||||
BackgroundSpawned(B),
|
||||
}
|
||||
|
||||
impl<T> Task<T> {
|
||||
@@ -76,7 +77,23 @@ impl<T> Task<T> {
|
||||
pub fn detach(self) {
|
||||
match self {
|
||||
Task(TaskState::Ready(_)) => {}
|
||||
Task(TaskState::Spawned(task)) => task.detach(),
|
||||
Task(TaskState::ForegroundSpawned(task)) => task.detach(),
|
||||
Task(TaskState::BackgroundSpawned(task)) => task.detach(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a task that can be safely awaited outside of GPUI.
|
||||
/// If the app quits while this task is incomplete, it will
|
||||
/// produce None instead of panicking.
|
||||
pub fn external(self) -> ExternalTask<T> {
|
||||
match self.0 {
|
||||
TaskState::Ready(r) => ExternalTask(TaskState::Ready(r)),
|
||||
TaskState::ForegroundSpawned(task) => {
|
||||
ExternalTask(TaskState::ForegroundSpawned(task.fallible()))
|
||||
}
|
||||
TaskState::BackgroundSpawned(task) => {
|
||||
ExternalTask(TaskState::BackgroundSpawned(task.fallible()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,14 +113,34 @@ where
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Future for Task<T> {
|
||||
type Output = T;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
match unsafe { self.get_unchecked_mut() } {
|
||||
Task(TaskState::Ready(val)) => Poll::Ready(val.take().unwrap()),
|
||||
Task(TaskState::Spawned(task)) => task.poll(cx),
|
||||
Task(TaskState::ForegroundSpawned(task)) => task.poll(cx),
|
||||
Task(TaskState::BackgroundSpawned(task)) => task.poll(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An ExternalTask is the same as a task, except it can be safely
|
||||
/// awaited outside of GPUI, without panicking.
|
||||
#[must_use]
|
||||
#[derive(Debug)]
|
||||
pub struct ExternalTask<T>(
|
||||
TaskState<T, async_task::FallibleTask<T, ForegroundContext>, async_task::FallibleTask<T>>,
|
||||
);
|
||||
|
||||
impl<T> Future for ExternalTask<T> {
|
||||
type Output = Option<T>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
match unsafe { self.get_unchecked_mut() } {
|
||||
ExternalTask(TaskState::Ready(val)) => Poll::Ready(val.take()),
|
||||
ExternalTask(TaskState::ForegroundSpawned(task)) => task.poll(cx),
|
||||
ExternalTask(TaskState::BackgroundSpawned(task)) => task.poll(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,8 +164,6 @@ impl TaskLabel {
|
||||
}
|
||||
}
|
||||
|
||||
type AnyLocalFuture<R> = Pin<Box<dyn 'static + Future<Output = R>>>;
|
||||
|
||||
type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
|
||||
|
||||
/// BackgroundExecutor lets you run things on background threads.
|
||||
@@ -138,7 +173,20 @@ type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
|
||||
impl BackgroundExecutor {
|
||||
#[doc(hidden)]
|
||||
pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
|
||||
Self { dispatcher }
|
||||
Self {
|
||||
dispatcher,
|
||||
_not_clone: NotClone,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cloning executors can cause runtime panics, see the documentation on `NotClone` for details.
|
||||
/// Use this power wisely.
|
||||
#[doc(hidden)]
|
||||
pub fn clone(&self) -> Self {
|
||||
Self {
|
||||
dispatcher: self.dispatcher.clone(),
|
||||
_not_clone: NotClone,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enqueues the given future to be run to completion on a background thread.
|
||||
@@ -171,7 +219,7 @@ impl BackgroundExecutor {
|
||||
let (runnable, task) =
|
||||
async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable, label));
|
||||
runnable.schedule();
|
||||
Task(TaskState::Spawned(task))
|
||||
Task(TaskState::BackgroundSpawned(task))
|
||||
}
|
||||
|
||||
/// Used by the test harness to run an async test in a synchronous fashion.
|
||||
@@ -348,7 +396,7 @@ impl BackgroundExecutor {
|
||||
move |runnable| dispatcher.dispatch_after(duration, runnable)
|
||||
});
|
||||
runnable.schedule();
|
||||
Task(TaskState::Spawned(task))
|
||||
Task(TaskState::BackgroundSpawned(task))
|
||||
}
|
||||
|
||||
/// in tests, start_waiting lets you indicate which task is waiting (for debugging only)
|
||||
@@ -447,6 +495,18 @@ impl ForegroundExecutor {
|
||||
pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
|
||||
Self {
|
||||
dispatcher,
|
||||
_not_clone: NotClone,
|
||||
not_send: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cloning executors can cause runtime panics, see the documentation on `NotClone` for details.
|
||||
/// Use this power wisely.
|
||||
#[doc(hidden)]
|
||||
pub fn clone(&self) -> Self {
|
||||
Self {
|
||||
dispatcher: self.dispatcher.clone(),
|
||||
_not_clone: NotClone,
|
||||
not_send: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -457,86 +517,92 @@ impl ForegroundExecutor {
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
let mut context = ForegroundContext::none();
|
||||
let task = self.spawn_internal(future, context);
|
||||
Task(TaskState::ForegroundSpawned(task))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn inner<R: 'static>(
|
||||
dispatcher: Arc<dyn PlatformDispatcher>,
|
||||
future: AnyLocalFuture<R>,
|
||||
) -> Task<R> {
|
||||
let (runnable, task) = spawn_local_with_source_location(future, move |runnable| {
|
||||
dispatcher.dispatch_on_main_thread(runnable)
|
||||
});
|
||||
runnable.schedule();
|
||||
Task(TaskState::Spawned(task))
|
||||
/// Enqueues the given Task to run on the main thread at some point in the future,
|
||||
/// with a context parameter that will be checked before each turn
|
||||
#[track_caller]
|
||||
pub(crate) fn spawn_with_context<R>(
|
||||
&self,
|
||||
mut context: ForegroundContext,
|
||||
future: impl Future<Output = R> + 'static,
|
||||
) -> Task<R>
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
let task = self.spawn_internal(future, context);
|
||||
Task(TaskState::ForegroundSpawned(task))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn spawn_internal<R>(
|
||||
&self,
|
||||
future: impl Future<Output = R> + 'static,
|
||||
mut context: ForegroundContext,
|
||||
) -> smol::Task<R, ForegroundContext>
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
/// Declarations here are copy-modified from:
|
||||
/// https://github.com/smol-rs/async-task/blob/ca9dbe1db9c422fd765847fa91306e30a6bb58a9/src/runnable.rs#L405
|
||||
#[inline]
|
||||
pub(crate) fn thread_id() -> ThreadId {
|
||||
std::thread_local! {
|
||||
static ID: ThreadId = std::thread::current().id();
|
||||
}
|
||||
ID.try_with(|id| *id)
|
||||
.unwrap_or_else(|_| std::thread::current().id())
|
||||
}
|
||||
inner::<R>(dispatcher, Box::pin(future))
|
||||
}
|
||||
}
|
||||
|
||||
/// Variant of `async_task::spawn_local` that includes the source location of the spawn in panics.
|
||||
///
|
||||
/// Copy-modified from:
|
||||
/// https://github.com/smol-rs/async-task/blob/ca9dbe1db9c422fd765847fa91306e30a6bb58a9/src/runnable.rs#L405
|
||||
#[track_caller]
|
||||
fn spawn_local_with_source_location<Fut, S>(
|
||||
future: Fut,
|
||||
schedule: S,
|
||||
) -> (Runnable<()>, async_task::Task<Fut::Output, ()>)
|
||||
where
|
||||
Fut: Future + 'static,
|
||||
Fut::Output: 'static,
|
||||
S: async_task::Schedule<()> + Send + Sync + 'static,
|
||||
{
|
||||
#[inline]
|
||||
fn thread_id() -> ThreadId {
|
||||
std::thread_local! {
|
||||
static ID: ThreadId = thread::current().id();
|
||||
struct Checked<F> {
|
||||
id: ThreadId,
|
||||
location: core::panic::Location<'static>,
|
||||
inner: ManuallyDrop<F>,
|
||||
}
|
||||
ID.try_with(|id| *id)
|
||||
.unwrap_or_else(|_| thread::current().id())
|
||||
}
|
||||
|
||||
struct Checked<F> {
|
||||
id: ThreadId,
|
||||
inner: ManuallyDrop<F>,
|
||||
location: &'static Location<'static>,
|
||||
}
|
||||
|
||||
impl<F> Drop for Checked<F> {
|
||||
fn drop(&mut self) {
|
||||
assert!(
|
||||
self.id == thread_id(),
|
||||
"local task dropped by a thread that didn't spawn it. Task spawned at {}",
|
||||
self.location
|
||||
);
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.inner);
|
||||
impl<F> Drop for Checked<F> {
|
||||
fn drop(&mut self) {
|
||||
assert!(
|
||||
self.id == thread_id(),
|
||||
"local task dropped by a thread that didn't spawn it. Task spawned at {}",
|
||||
self.location
|
||||
);
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Future> Future for Checked<F> {
|
||||
type Output = F::Output;
|
||||
impl<F: Future> Future for Checked<F> {
|
||||
type Output = F::Output;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
assert!(
|
||||
self.id == thread_id(),
|
||||
"local task polled by a thread that didn't spawn it. Task spawned at {}",
|
||||
self.location
|
||||
);
|
||||
unsafe { self.map_unchecked_mut(|c| &mut *c.inner).poll(cx) }
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
assert!(
|
||||
self.id == thread_id(),
|
||||
"local task polled by a thread that didn't spawn it. Task spawned at {}",
|
||||
self.location
|
||||
);
|
||||
unsafe { self.map_unchecked_mut(|c| &mut *c.inner).poll(cx) }
|
||||
}
|
||||
}
|
||||
|
||||
let checked = Checked {
|
||||
id: thread_id(),
|
||||
location: *core::panic::Location::caller(),
|
||||
inner: ManuallyDrop::new(future),
|
||||
};
|
||||
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
let (runnable, task) = Builder::new().metadata(context).spawn_local(
|
||||
|_| checked,
|
||||
move |runnable| dispatcher.dispatch_on_main_thread(runnable),
|
||||
);
|
||||
runnable.schedule();
|
||||
task
|
||||
}
|
||||
|
||||
// Wrap the future into one that checks which thread it's on.
|
||||
let future = Checked {
|
||||
id: thread_id(),
|
||||
inner: ManuallyDrop::new(future),
|
||||
location: Location::caller(),
|
||||
};
|
||||
|
||||
unsafe { async_task::spawn_unchecked(future, schedule) }
|
||||
}
|
||||
|
||||
/// Scope manages a set of tasks that are enqueued and waited on together. See [`BackgroundExecutor::scoped`].
|
||||
|
||||
@@ -161,19 +161,13 @@ use taffy::TaffyLayoutEngine;
|
||||
/// The context trait, allows the different contexts in GPUI to be used
|
||||
/// interchangeably for certain operations.
|
||||
pub trait AppContext {
|
||||
/// The result type for this context, used for async contexts that
|
||||
/// can't hold a direct reference to the application context.
|
||||
type Result<T>;
|
||||
|
||||
/// Create a new entity in the app context.
|
||||
fn new<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>>;
|
||||
fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T)
|
||||
-> Entity<T>;
|
||||
|
||||
/// Reserve a slot for a entity to be inserted later.
|
||||
/// The returned [Reservation] allows you to obtain the [EntityId] for the future entity.
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Self::Result<Reservation<T>>;
|
||||
fn reserve_entity<T: 'static>(&mut self) -> Reservation<T>;
|
||||
|
||||
/// Insert a new entity in the app context based on a [Reservation] previously obtained from [`reserve_entity`].
|
||||
///
|
||||
@@ -182,23 +176,19 @@ pub trait AppContext {
|
||||
&mut self,
|
||||
reservation: Reservation<T>,
|
||||
build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>>;
|
||||
) -> Entity<T>;
|
||||
|
||||
/// Update a entity in the app context.
|
||||
fn update_entity<T, R>(
|
||||
&mut self,
|
||||
handle: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
|
||||
) -> Self::Result<R>
|
||||
) -> R
|
||||
where
|
||||
T: 'static;
|
||||
|
||||
/// Read a entity from the app context.
|
||||
fn read_entity<T, R>(
|
||||
&self,
|
||||
handle: &Entity<T>,
|
||||
read: impl FnOnce(&T, &App) -> R,
|
||||
) -> Self::Result<R>
|
||||
fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
|
||||
where
|
||||
T: 'static;
|
||||
|
||||
@@ -222,7 +212,7 @@ pub trait AppContext {
|
||||
R: Send + 'static;
|
||||
|
||||
/// Read a global from this app context
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
|
||||
fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
|
||||
where
|
||||
G: Global;
|
||||
}
|
||||
@@ -249,24 +239,24 @@ pub trait VisualContext: AppContext {
|
||||
&mut self,
|
||||
entity: &Entity<T>,
|
||||
update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
|
||||
) -> Self::Result<R>;
|
||||
) -> R;
|
||||
|
||||
/// Update a view with the given callback
|
||||
fn new_window_entity<T: 'static>(
|
||||
&mut self,
|
||||
build_entity: impl FnOnce(&mut Window, &mut Context<'_, T>) -> T,
|
||||
) -> Self::Result<Entity<T>>;
|
||||
) -> Entity<T>;
|
||||
|
||||
/// Replace the root view of a window with a new view.
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
|
||||
) -> Self::Result<Entity<V>>
|
||||
) -> Entity<V>
|
||||
where
|
||||
V: 'static + Render;
|
||||
|
||||
/// Focus a entity in the window, if it implements the [`Focusable`] trait.
|
||||
fn focus<V>(&mut self, entity: &Entity<V>) -> Self::Result<()>
|
||||
fn focus<V>(&mut self, entity: &Entity<V>)
|
||||
where
|
||||
V: Focusable;
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ mod test;
|
||||
mod windows;
|
||||
|
||||
use crate::{
|
||||
point, Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
|
||||
DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor,
|
||||
GlyphId, GpuSpecs, ImageSource, Keymap, LineLayout, Pixels, PlatformInput, Point,
|
||||
RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, ScaledPixels, Scene,
|
||||
SharedString, Size, SvgRenderer, SvgSize, Task, TaskLabel, Window, DEFAULT_WINDOW_SIZE,
|
||||
point, Action, AnyWindowHandle, App, AppCell, BackgroundExecutor, Bounds, DevicePixels,
|
||||
DispatchEventResult, Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlyphId, GpuSpecs,
|
||||
ImageSource, Keymap, LineLayout, Pixels, PlatformInput, Point, RenderGlyphParams, RenderImage,
|
||||
RenderImageParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, SvgRenderer,
|
||||
SvgSize, Task, TaskLabel, WeakAsyncWindowContext, Window, WindowId, DEFAULT_WINDOW_SIZE,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_task::Runnable;
|
||||
@@ -47,6 +47,7 @@ use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Cursor;
|
||||
use std::ops;
|
||||
use std::rc::Weak;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
@@ -450,13 +451,80 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Contexts {
|
||||
App {
|
||||
app: Weak<AppCell>,
|
||||
},
|
||||
Window {
|
||||
app: Weak<AppCell>,
|
||||
window: WindowId,
|
||||
},
|
||||
}
|
||||
|
||||
/// The context of a foreground future, to be checked before polling the future
|
||||
#[derive(Debug)]
|
||||
pub struct ForegroundContext {
|
||||
inner: Option<Contexts>,
|
||||
}
|
||||
|
||||
// SAFETY: These fields are only accessed during `Runnable::run()` calls on the foreground,
|
||||
// As enforced by the GPUI foreground executor and platform dispatcher implementations
|
||||
unsafe impl Send for ForegroundContext {}
|
||||
unsafe impl Sync for ForegroundContext {}
|
||||
|
||||
impl ForegroundContext {
|
||||
/// This ForegroundContext will enforce that the app exists before polling
|
||||
#[track_caller]
|
||||
pub fn app(app: &Weak<AppCell>) -> Self {
|
||||
Self {
|
||||
inner: Some(Contexts::App { app: app.clone() }),
|
||||
}
|
||||
}
|
||||
|
||||
/// This ForegroundContext will enforce that the app and window exists before polling
|
||||
#[track_caller]
|
||||
pub fn window(app: &Weak<AppCell>, window: WindowId) -> Self {
|
||||
Self {
|
||||
inner: Some(Contexts::Window {
|
||||
app: app.clone(),
|
||||
window,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// This foreground context will do nothing
|
||||
#[track_caller]
|
||||
pub fn none() -> Self {
|
||||
Self { inner: None }
|
||||
}
|
||||
|
||||
/// Check if the context is currently valid
|
||||
pub fn context_is_valid(&self) -> bool {
|
||||
match &self.inner {
|
||||
Some(Contexts::App { app }) => app.upgrade().is_some(),
|
||||
Some(Contexts::Window { app, window }) => {
|
||||
if let Some(app) = app.upgrade() {
|
||||
let lock = app.borrow();
|
||||
let result = lock.windows.contains_key(*window);
|
||||
drop(lock);
|
||||
result
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This type is public so that our test macro can generate and use it, but it should not
|
||||
/// be considered part of our public API.
|
||||
#[doc(hidden)]
|
||||
pub trait PlatformDispatcher: Send + Sync {
|
||||
fn is_main_thread(&self) -> bool;
|
||||
fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>);
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable);
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable<ForegroundContext>);
|
||||
fn dispatch_after(&self, duration: Duration, runnable: Runnable);
|
||||
fn park(&self, timeout: Option<Duration>) -> bool;
|
||||
fn unparker(&self) -> Unparker;
|
||||
@@ -683,7 +751,7 @@ impl From<TileId> for etagere::AllocId {
|
||||
}
|
||||
|
||||
pub(crate) struct PlatformInputHandler {
|
||||
cx: AsyncWindowContext,
|
||||
cx: WeakAsyncWindowContext,
|
||||
handler: Box<dyn InputHandler>,
|
||||
}
|
||||
|
||||
@@ -695,7 +763,7 @@ pub(crate) struct PlatformInputHandler {
|
||||
allow(dead_code)
|
||||
)]
|
||||
impl PlatformInputHandler {
|
||||
pub fn new(cx: AsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
|
||||
pub fn new(cx: WeakAsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
|
||||
Self { cx, handler }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::{Action, App, Platform, SharedString};
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::{Action, App, Platform, SharedString};
|
||||
|
||||
/// A menu of the application, either a main menu or a submenu
|
||||
pub struct Menu {
|
||||
/// The name of the menu
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::{PlatformDispatcher, TaskLabel};
|
||||
use crate::{ForegroundContext, PlatformDispatcher, TaskLabel};
|
||||
use async_task::Runnable;
|
||||
use objc::{
|
||||
class, msg_send,
|
||||
@@ -63,12 +63,12 @@ impl PlatformDispatcher for MacDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable<ForegroundContext>) {
|
||||
unsafe {
|
||||
dispatch_async_f(
|
||||
dispatch_get_main_queue(),
|
||||
runnable.into_raw().as_ptr() as *mut c_void,
|
||||
Some(trampoline),
|
||||
Some(context_trampoline),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -105,3 +105,15 @@ extern "C" fn trampoline(runnable: *mut c_void) {
|
||||
let task = unsafe { Runnable::<()>::from_raw(NonNull::new_unchecked(runnable as *mut ())) };
|
||||
task.run();
|
||||
}
|
||||
|
||||
extern "C" fn context_trampoline(runnable: *mut c_void) {
|
||||
let task = unsafe {
|
||||
Runnable::<ForegroundContext>::from_raw(NonNull::new_unchecked(runnable as *mut ()))
|
||||
};
|
||||
|
||||
if task.metadata().context_is_valid() {
|
||||
task.run();
|
||||
} else {
|
||||
drop(task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{PlatformDispatcher, TaskLabel};
|
||||
use crate::{platform::ForegroundContext, PlatformDispatcher, TaskLabel};
|
||||
use async_task::Runnable;
|
||||
use backtrace::Backtrace;
|
||||
use collections::{HashMap, HashSet, VecDeque};
|
||||
@@ -26,9 +26,13 @@ pub struct TestDispatcher {
|
||||
unparker: Unparker,
|
||||
}
|
||||
|
||||
// SAFETY: The test dispatcher is only ever run single threaded
|
||||
unsafe impl Send for TestDispatcher {}
|
||||
unsafe impl Sync for TestDispatcher {}
|
||||
|
||||
struct TestDispatcherState {
|
||||
random: StdRng,
|
||||
foreground: HashMap<TestDispatcherId, VecDeque<Runnable>>,
|
||||
foreground: HashMap<TestDispatcherId, VecDeque<Runnable<ForegroundContext>>>,
|
||||
background: Vec<Runnable>,
|
||||
deprioritized_background: Vec<Runnable>,
|
||||
delayed: Vec<(Duration, Runnable)>,
|
||||
@@ -135,7 +139,8 @@ impl TestDispatcher {
|
||||
};
|
||||
let background_len = state.background.len();
|
||||
|
||||
let runnable;
|
||||
let mut background_runnable = None;
|
||||
let mut foreground_runnable = None;
|
||||
let main_thread;
|
||||
if foreground_len == 0 && background_len == 0 {
|
||||
let deprioritized_background_len = state.deprioritized_background.len();
|
||||
@@ -144,7 +149,7 @@ impl TestDispatcher {
|
||||
}
|
||||
let ix = state.random.gen_range(0..deprioritized_background_len);
|
||||
main_thread = false;
|
||||
runnable = state.deprioritized_background.swap_remove(ix);
|
||||
background_runnable = Some(state.deprioritized_background.swap_remove(ix));
|
||||
} else {
|
||||
main_thread = state.random.gen_ratio(
|
||||
foreground_len as u32,
|
||||
@@ -152,24 +157,36 @@ impl TestDispatcher {
|
||||
);
|
||||
if main_thread {
|
||||
let state = &mut *state;
|
||||
runnable = state
|
||||
.foreground
|
||||
.values_mut()
|
||||
.filter(|runnables| !runnables.is_empty())
|
||||
.choose(&mut state.random)
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.unwrap();
|
||||
foreground_runnable = Some(
|
||||
state
|
||||
.foreground
|
||||
.values_mut()
|
||||
.filter(|runnables| !runnables.is_empty())
|
||||
.choose(&mut state.random)
|
||||
.unwrap()
|
||||
.pop_front()
|
||||
.unwrap(),
|
||||
);
|
||||
} else {
|
||||
let ix = state.random.gen_range(0..background_len);
|
||||
runnable = state.background.swap_remove(ix);
|
||||
background_runnable = Some(state.background.swap_remove(ix));
|
||||
};
|
||||
};
|
||||
|
||||
let was_main_thread = state.is_main_thread;
|
||||
state.is_main_thread = main_thread;
|
||||
drop(state);
|
||||
runnable.run();
|
||||
|
||||
if let Some(background_runnable) = background_runnable {
|
||||
background_runnable.run();
|
||||
} else if let Some(foreground_runnable) = foreground_runnable {
|
||||
if foreground_runnable.metadata().context_is_valid() {
|
||||
foreground_runnable.run();
|
||||
} else {
|
||||
drop(foreground_runnable);
|
||||
}
|
||||
}
|
||||
|
||||
self.state.lock().is_main_thread = was_main_thread;
|
||||
|
||||
true
|
||||
@@ -272,7 +289,7 @@ impl PlatformDispatcher for TestDispatcher {
|
||||
self.unparker.unpark();
|
||||
}
|
||||
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable) {
|
||||
fn dispatch_on_main_thread(&self, runnable: Runnable<ForegroundContext>) {
|
||||
self.state
|
||||
.lock()
|
||||
.foreground
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use crate::{
|
||||
point, prelude::*, px, size, transparent_black, Action, AnyDrag, AnyElement, AnyTooltip,
|
||||
AnyView, App, AppContext, Arena, Asset, AsyncWindowContext, AvailableSpace, Background, Bounds,
|
||||
BoxShadow, Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
|
||||
DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
|
||||
FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero,
|
||||
KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId,
|
||||
LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent,
|
||||
MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
||||
PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render,
|
||||
RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge,
|
||||
ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, SubscriberSet,
|
||||
Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix,
|
||||
Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
|
||||
point, prelude::*, px, size, svg_renderer::SMOOTH_SVG_SCALE_FACTOR, transparent_black, Action,
|
||||
AnyDrag, AnyElement, AnyTooltip, AnyView, App, AppContext, Arena, Asset, AsyncWindowContext,
|
||||
AvailableSpace, Background, Bounds, BoxShadow, Context, Corners, CursorStyle, Decorations,
|
||||
DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
|
||||
Entity, EntityId, EventEmitter, FileDropEvent, FontId, Global, GlobalElementId, GlyphId,
|
||||
GpuSpecs, Hsla, InputHandler, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent,
|
||||
Keystroke, KeystrokeEvent, LayoutId, LineLayoutIndex, Modifiers, ModifiersChangedEvent,
|
||||
MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
||||
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams,
|
||||
RenderSvgParams, Replay, ResizeEdge, ScaledPixels, Scene, Shadow, SharedString, Size,
|
||||
StrikethroughStyle, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle,
|
||||
TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, WeakAsyncApp,
|
||||
WeakAsyncWindowContext, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
|
||||
WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
|
||||
SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS,
|
||||
SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
@@ -765,7 +766,7 @@ impl Window {
|
||||
platform_window.on_close(Box::new({
|
||||
let mut cx = cx.to_async();
|
||||
move || {
|
||||
let _ = handle.update(&mut cx, |_, window, _| window.remove_window());
|
||||
let _ = handle.update_weak(&mut cx, |_, window, _| window.remove_window());
|
||||
}
|
||||
}));
|
||||
platform_window.on_request_frame(Box::new({
|
||||
@@ -779,7 +780,7 @@ impl Window {
|
||||
let next_frame_callbacks = next_frame_callbacks.take();
|
||||
if !next_frame_callbacks.is_empty() {
|
||||
handle
|
||||
.update(&mut cx, |_, window, cx| {
|
||||
.update_weak(&mut cx, |_, window, cx| {
|
||||
for callback in next_frame_callbacks {
|
||||
callback(window, cx);
|
||||
}
|
||||
@@ -797,7 +798,7 @@ impl Window {
|
||||
if invalidator.is_dirty() {
|
||||
measure("frame duration", || {
|
||||
handle
|
||||
.update(&mut cx, |_, window, cx| {
|
||||
.update_weak(&mut cx, |_, window, cx| {
|
||||
window.draw(cx);
|
||||
window.present();
|
||||
})
|
||||
@@ -805,12 +806,12 @@ impl Window {
|
||||
})
|
||||
} else if needs_present {
|
||||
handle
|
||||
.update(&mut cx, |_, window, _| window.present())
|
||||
.update_weak(&mut cx, |_, window, _| window.present())
|
||||
.log_err();
|
||||
}
|
||||
|
||||
handle
|
||||
.update(&mut cx, |_, window, _| {
|
||||
.update_weak(&mut cx, |_, window, _| {
|
||||
window.complete_frame();
|
||||
})
|
||||
.log_err();
|
||||
@@ -820,7 +821,7 @@ impl Window {
|
||||
let mut cx = cx.to_async();
|
||||
move |_, _| {
|
||||
handle
|
||||
.update(&mut cx, |_, window, cx| window.bounds_changed(cx))
|
||||
.update_weak(&mut cx, |_, window, cx| window.bounds_changed(cx))
|
||||
.log_err();
|
||||
}
|
||||
}));
|
||||
@@ -828,7 +829,7 @@ impl Window {
|
||||
let mut cx = cx.to_async();
|
||||
move || {
|
||||
handle
|
||||
.update(&mut cx, |_, window, cx| window.bounds_changed(cx))
|
||||
.update_weak(&mut cx, |_, window, cx| window.bounds_changed(cx))
|
||||
.log_err();
|
||||
}
|
||||
}));
|
||||
@@ -836,7 +837,7 @@ impl Window {
|
||||
let mut cx = cx.to_async();
|
||||
move || {
|
||||
handle
|
||||
.update(&mut cx, |_, window, cx| window.appearance_changed(cx))
|
||||
.update_weak(&mut cx, |_, window, cx| window.appearance_changed(cx))
|
||||
.log_err();
|
||||
}
|
||||
}));
|
||||
@@ -844,7 +845,7 @@ impl Window {
|
||||
let mut cx = cx.to_async();
|
||||
move |active| {
|
||||
handle
|
||||
.update(&mut cx, |_, window, cx| {
|
||||
.update_weak(&mut cx, |_, window, cx| {
|
||||
window.active.set(active);
|
||||
window
|
||||
.activation_observers
|
||||
@@ -859,7 +860,7 @@ impl Window {
|
||||
let mut cx = cx.to_async();
|
||||
move |active| {
|
||||
handle
|
||||
.update(&mut cx, |_, window, _| {
|
||||
.update_weak(&mut cx, |_, window, _| {
|
||||
window.hovered.set(active);
|
||||
window.refresh();
|
||||
})
|
||||
@@ -870,7 +871,7 @@ impl Window {
|
||||
let mut cx = cx.to_async();
|
||||
Box::new(move |event| {
|
||||
handle
|
||||
.update(&mut cx, |_, window, cx| window.dispatch_event(event, cx))
|
||||
.update_weak(&mut cx, |_, window, cx| window.dispatch_event(event, cx))
|
||||
.log_err()
|
||||
.unwrap_or(DispatchEventResult::default())
|
||||
})
|
||||
@@ -1268,8 +1269,8 @@ impl Window {
|
||||
|
||||
/// Creates an [`AsyncWindowContext`], which has a static lifetime and can be held across
|
||||
/// await points in async code.
|
||||
pub fn to_async(&self, cx: &App) -> AsyncWindowContext {
|
||||
AsyncWindowContext::new_context(cx.to_async(), self.handle)
|
||||
pub fn to_async(&self, cx: &App) -> WeakAsyncWindowContext {
|
||||
WeakAsyncWindowContext::new_context(cx.to_async(), self.handle)
|
||||
}
|
||||
|
||||
/// Schedule the given closure to be run directly after the current frame is rendered.
|
||||
@@ -3260,8 +3261,7 @@ impl Window {
|
||||
.flush_dispatch(currently_pending.keystrokes, &dispatch_path);
|
||||
|
||||
window.replay_pending_input(to_replay, cx)
|
||||
})
|
||||
.log_err();
|
||||
});
|
||||
}));
|
||||
self.pending_input = Some(currently_pending);
|
||||
self.pending_input_changed(cx);
|
||||
@@ -3946,6 +3946,17 @@ impl AnyWindowHandle {
|
||||
cx.update_window(self, update)
|
||||
}
|
||||
|
||||
/// Updates the state of the root view of this window.
|
||||
///
|
||||
/// This will fail if the window has been closed.
|
||||
pub fn update_weak<R>(
|
||||
self,
|
||||
cx: &mut WeakAsyncApp,
|
||||
update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
|
||||
) -> Result<R> {
|
||||
cx.update(|cx| cx.update_window(self, update))?
|
||||
}
|
||||
|
||||
/// Read the state of the root view of this window.
|
||||
///
|
||||
/// This will fail if the window has been closed.
|
||||
|
||||
Reference in New Issue
Block a user