Merge branch 'gpui2' into marshall/gpui2-playground

This commit is contained in:
Marshall Bowers
2023-10-04 17:32:21 -04:00
19 changed files with 588 additions and 1326 deletions
+2
View File
@@ -19,6 +19,8 @@ fn generate_dispatch_bindings() {
let bindings = bindgen::Builder::default()
.header("src/platform/mac/dispatch.h")
.allowlist_var("_dispatch_main_q")
.allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
.allowlist_function("dispatch_get_global_queue")
.allowlist_function("dispatch_async_f")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.layout_tests(false)
+63 -37
View File
@@ -8,9 +8,9 @@ pub use model_context::*;
use refineable::Refineable;
use crate::{
current_platform, image_cache::ImageCache, run_on_main, spawn_on_main, AssetSource, Context,
LayoutId, MainThread, MainThreadOnly, Platform, PlatformDispatcher, RootView, SvgRenderer,
TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
current_platform, image_cache::ImageCache, AssetSource, Context, Executor, LayoutId,
MainThread, MainThreadOnly, Platform, RootView, SvgRenderer, Task, TextStyle,
TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, VecDeque};
@@ -29,7 +29,7 @@ use util::{
};
#[derive(Clone)]
pub struct App(Arc<Mutex<MainThread<AppContext>>>);
pub struct App(Arc<Mutex<AppContext>>);
impl App {
pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
@@ -50,15 +50,15 @@ impl App {
asset_source: Arc<dyn AssetSource>,
http_client: Arc<dyn HttpClient>,
) -> Self {
let dispatcher = platform.dispatcher();
let executor = platform.executor();
let text_system = Arc::new(TextSystem::new(platform.text_system()));
let entities = EntityMap::new();
let unit_entity = entities.redeem(entities.reserve(), ());
let unit_entity = entities.insert(entities.reserve(), ());
Self(Arc::new_cyclic(|this| {
Mutex::new(MainThread::new(AppContext {
Mutex::new(AppContext {
this: this.clone(),
platform: MainThreadOnly::new(platform, dispatcher.clone()),
dispatcher,
platform: MainThreadOnly::new(platform, executor.clone()),
executor,
text_system,
svg_renderer: SvgRenderer::new(asset_source),
image_cache: ImageCache::new(http_client),
@@ -71,7 +71,7 @@ impl App {
pending_effects: Default::default(),
observers: Default::default(),
layout_id_buffer: Default::default(),
}))
})
}))
}
@@ -83,6 +83,7 @@ impl App {
let platform = self.0.lock().platform.clone();
platform.borrow_on_main_thread().run(Box::new(move || {
let cx = &mut *this.0.lock();
let cx = unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) };
on_finish_launching(cx);
}));
}
@@ -91,11 +92,11 @@ impl App {
type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
pub struct AppContext {
this: Weak<Mutex<MainThread<AppContext>>>,
this: Weak<Mutex<AppContext>>,
platform: MainThreadOnly<dyn Platform>,
dispatcher: Arc<dyn PlatformDispatcher>,
text_system: Arc<TextSystem>,
pending_updates: usize,
pub(crate) executor: Executor,
pub(crate) svg_renderer: SvgRenderer,
pub(crate) image_cache: ImageCache,
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
@@ -109,7 +110,7 @@ pub struct AppContext {
}
impl AppContext {
fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
self.pending_updates += 1;
let result = update(self);
if self.pending_updates == 1 {
@@ -144,6 +145,7 @@ impl AppContext {
}
fn flush_effects(&mut self) {
dbg!("flush effects");
while let Some(effect) = self.pending_effects.pop_front() {
match effect {
Effect::Notify(entity_id) => self.apply_notify_effect(entity_id),
@@ -180,19 +182,44 @@ impl AppContext {
}
}
pub fn to_async(&self) -> AsyncContext {
AsyncContext(unsafe { mem::transmute(self.this.clone()) })
pub fn to_async(&self) -> AsyncAppContext {
AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
}
pub fn executor(&self) -> &Executor {
&self.executor
}
pub fn run_on_main<R>(
&self,
&mut self,
f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
) -> impl Future<Output = R>
) -> Task<R>
where
R: Send + 'static,
{
if self.executor.is_main_thread() {
Task::ready(f(unsafe {
mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(self)
}))
} else {
let this = self.this.upgrade().unwrap();
self.executor.run_on_main(move || {
let cx = &mut *this.lock();
cx.update(|cx| f(unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(cx) }))
})
}
}
pub fn spawn_on_main<F, R>(
&self,
f: impl FnOnce(&mut MainThread<AppContext>) -> F + Send + 'static,
) -> Task<R>
where
F: Future<Output = R> + Send + 'static,
R: Send + 'static,
{
let this = self.this.upgrade().unwrap();
run_on_main(self.dispatcher.clone(), move || {
self.executor.spawn_on_main(move || {
let cx = &mut *this.lock();
cx.update(|cx| {
f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
@@ -200,20 +227,15 @@ impl AppContext {
})
}
pub fn spawn_on_main<F, R>(
&self,
f: impl FnOnce(&mut MainThread<AppContext>) -> F + Send + 'static,
) -> impl Future<Output = R>
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
where
F: Future<Output = R> + 'static,
Fut: Future<Output = R> + Send + 'static,
R: Send + 'static,
{
let this = self.this.upgrade().unwrap();
spawn_on_main(self.dispatcher.clone(), move || {
let cx = &mut *this.lock();
cx.update(|cx| {
f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
})
let cx = self.to_async();
self.executor.spawn(async move {
let future = f(cx);
future.await
})
}
@@ -278,9 +300,11 @@ impl Context for AppContext {
&mut self,
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
) -> Handle<T> {
let slot = self.entities.reserve();
let entity = build_entity(&mut ModelContext::mutable(self, slot.id));
self.entities.redeem(slot, entity)
self.update(|cx| {
let slot = cx.entities.reserve();
let entity = build_entity(&mut ModelContext::mutable(cx, slot.id));
cx.entities.insert(slot, entity)
})
}
fn update_entity<T: Send + Sync + 'static, R>(
@@ -288,10 +312,12 @@ impl Context for AppContext {
handle: &Handle<T>,
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> R {
let mut entity = self.entities.lease(handle);
let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
self.entities.end_lease(entity);
result
self.update(|cx| {
let mut entity = cx.entities.lease(handle);
let result = update(&mut entity, &mut ModelContext::mutable(cx, handle.id));
cx.entities.end_lease(entity);
result
})
}
}
@@ -307,7 +333,7 @@ impl MainThread<AppContext> {
pub(crate) fn update_window<R>(
&mut self,
id: WindowId,
update: impl FnOnce(&mut WindowContext) -> R,
update: impl FnOnce(&mut MainThread<WindowContext>) -> R,
) -> Result<R> {
self.0.update_window(id, |cx| {
update(unsafe {
+46 -10
View File
@@ -1,13 +1,15 @@
use crate::{AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, WindowContext};
use crate::{
AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, ViewContext, WindowContext,
};
use anyhow::anyhow;
use parking_lot::Mutex;
use std::sync::Weak;
#[derive(Clone)]
pub struct AsyncContext(pub(crate) Weak<Mutex<AppContext>>);
pub struct AsyncAppContext(pub(crate) Weak<Mutex<AppContext>>);
impl Context for AsyncContext {
type EntityContext<'a, 'b, T: 'static + Send + Sync> = ModelContext<'a, T>;
impl Context for AsyncAppContext {
type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>;
type Result<T> = Result<T>;
fn entity<T: Send + Sync + 'static>(
@@ -18,7 +20,7 @@ impl Context for AsyncContext {
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock();
let mut lock = app.lock(); // Does not compile without this variable.
Ok(lock.entity(build_entity))
}
@@ -31,17 +33,17 @@ impl Context for AsyncContext {
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut lock = app.lock();
let mut lock = app.lock(); // Does not compile without this variable.
Ok(lock.update_entity(handle, update))
}
}
impl AsyncContext {
pub fn update_window<T>(
impl AsyncAppContext {
pub fn update_window<R>(
&self,
handle: AnyWindowHandle,
update: impl FnOnce(&mut WindowContext) -> T + Send + Sync,
) -> Result<T> {
update: impl FnOnce(&mut WindowContext) -> R,
) -> Result<R> {
let app = self
.0
.upgrade()
@@ -50,3 +52,37 @@ impl AsyncContext {
app_context.update_window(handle.id, update)
}
}
#[derive(Clone)]
pub struct AsyncWindowContext {
app: AsyncAppContext,
window: AnyWindowHandle,
}
impl AsyncWindowContext {
pub fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
Self { app, window }
}
}
impl Context for AsyncWindowContext {
type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>;
type Result<T> = Result<T>;
fn entity<R: Send + Sync + 'static>(
&mut self,
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, R>) -> R,
) -> Result<Handle<R>> {
self.app
.update_window(self.window, |cx| cx.entity(build_entity))
}
fn update_entity<T: Send + Sync + 'static, R>(
&mut self,
handle: &Handle<T>,
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> Result<R> {
self.app
.update_window(self.window, |cx| cx.update_entity(handle, update))
}
}
+45 -18
View File
@@ -14,14 +14,6 @@ use std::{
slotmap::new_key_type! { pub struct EntityId; }
#[derive(Deref, DerefMut)]
pub struct Lease<T> {
#[deref]
#[deref_mut]
entity: Box<T>,
pub id: EntityId,
}
pub(crate) struct EntityMap {
ref_counts: Arc<RwLock<RefCounts>>,
entities: Arc<Mutex<SecondaryMap<EntityId, Box<dyn Any + Send + Sync>>>>,
@@ -35,31 +27,38 @@ impl EntityMap {
}
}
/// Reserve a slot for an entity, which you can subsequently use with `insert`.
pub fn reserve<T: 'static + Send + Sync>(&self) -> Slot<T> {
let id = self.ref_counts.write().insert(1.into());
Slot(Handle::new(id, Arc::downgrade(&self.ref_counts)))
}
pub fn redeem<T: 'static + Any + Send + Sync>(&self, slot: Slot<T>, entity: T) -> Handle<T> {
/// Insert an entity into a slot obtained by calling `reserve`.
pub fn insert<T: 'static + Any + Send + Sync>(&self, slot: Slot<T>, entity: T) -> Handle<T> {
let handle = slot.0;
self.entities.lock().insert(handle.id, Box::new(entity));
handle
}
/// Move an entity to the stack.
pub fn lease<T: 'static + Send + Sync>(&self, handle: &Handle<T>) -> Lease<T> {
let id = handle.id;
let entity = self
.entities
.lock()
.remove(id)
.expect("Circular entity lease. Is the entity already being updated?")
.downcast::<T>()
.unwrap();
let entity = Some(
self.entities
.lock()
.remove(id)
.expect("Circular entity lease. Is the entity already being updated?")
.downcast::<T>()
.unwrap(),
);
Lease { id, entity }
}
pub fn end_lease<T: 'static + Send + Sync>(&mut self, lease: Lease<T>) {
self.entities.lock().insert(lease.id, lease.entity);
/// Return an entity after moving it to the stack.
pub fn end_lease<T: 'static + Send + Sync>(&mut self, mut lease: Lease<T>) {
self.entities
.lock()
.insert(lease.id, lease.entity.take().unwrap());
}
pub fn weak_handle<T: 'static + Send + Sync>(&self, id: EntityId) -> WeakHandle<T> {
@@ -71,6 +70,34 @@ impl EntityMap {
}
}
pub struct Lease<T> {
entity: Option<Box<T>>,
pub id: EntityId,
}
impl<T> core::ops::Deref for Lease<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.entity.as_ref().unwrap()
}
}
impl<T> core::ops::DerefMut for Lease<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.entity.as_mut().unwrap()
}
}
impl<T> Drop for Lease<T> {
fn drop(&mut self) {
assert!(
self.entity.is_none(),
"Leases must be ended with EntityMap::end_lease"
);
}
}
#[derive(Deref, DerefMut)]
pub struct Slot<T: Send + Sync + 'static>(Handle<T>);
+16 -12
View File
@@ -1,5 +1,6 @@
use crate::{
Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled, ViewContext,
BorrowWindow, Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled,
ViewContext,
};
use futures::FutureExt;
use refineable::RefinementCascade;
@@ -34,7 +35,7 @@ impl<S> Img<S> {
}
}
impl<S: 'static> Element for Img<S> {
impl<S: Send + Sync + 'static> Element for Img<S> {
type State = S;
type FrameState = ();
@@ -64,22 +65,25 @@ impl<S: 'static> Element for Img<S> {
style.paint(order, bounds, cx);
if let Some(uri) = &self.uri {
let image_future = cx.image_cache.get(uri.clone());
if let Some(uri) = self.uri.clone() {
let image_future = cx.image_cache.get(uri);
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
cx.paint_image(bounds, order, data, self.grayscale)?;
let corner_radii = style.corner_radii.to_pixels(bounds, cx.rem_size());
cx.paint_image(bounds, corner_radii, order, data, self.grayscale)?;
} else {
log::warn!("image not loaded yet");
// cx.spawn(|this, mut cx| async move {
// if image_future.await.log_err().is_some() {
// this.update(&mut cx, |_, cx| cx.notify()).ok();
// }
// })
// .detach();
cx.spawn(|view, mut cx| async move {
if image_future.await.log_err().is_some() {
view.update(&mut cx, |_, cx| {
cx.notify();
})
.ok();
}
})
.detach()
}
}
Ok(())
File diff suppressed because it is too large Load Diff
+45 -9
View File
@@ -1,7 +1,10 @@
use core::fmt::Debug;
use derive_more::{Add, AddAssign, Div, Mul, Sub, SubAssign};
use refineable::Refineable;
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign};
use std::{
cmp,
ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign},
};
#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
#[refineable(debug)]
@@ -84,7 +87,7 @@ impl<T: Clone + Debug + Div<S, Output = T>, S: Clone> Div<S> for Point<T> {
}
}
impl<T: Clone + Debug + std::cmp::PartialOrd> Point<T> {
impl<T: Clone + Debug + cmp::PartialOrd> Point<T> {
pub fn max(&self, other: &Self) -> Self {
Point {
x: if self.x >= other.x {
@@ -99,6 +102,21 @@ impl<T: Clone + Debug + std::cmp::PartialOrd> Point<T> {
},
}
}
pub fn min(&self, other: &Self) -> Self {
Point {
x: if self.x <= other.x {
self.x.clone()
} else {
other.x.clone()
},
y: if self.y <= other.y {
self.y.clone()
} else {
other.y.clone()
},
}
}
}
impl<T: Clone + Debug> Clone for Point<T> {
@@ -239,6 +257,14 @@ impl<T: Clone + Debug + Sub<Output = T>> Bounds<T> {
}
}
impl<T: Clone + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> {
pub fn intersect(&self, other: &Self) -> Self {
let upper_left = self.origin.max(&other.origin);
let lower_right = self.lower_right().min(&other.lower_right());
Self::from_corners(upper_left, lower_right)
}
}
impl<T, Rhs> Mul<Rhs> for Bounds<T>
where
T: Mul<Rhs, Output = Rhs> + Clone + Debug,
@@ -433,12 +459,13 @@ pub struct Corners<T: Clone + Debug> {
}
impl Corners<AbsoluteLength> {
pub fn to_pixels(&self, rem_size: Pixels) -> Corners<Pixels> {
pub fn to_pixels(&self, bounds: Bounds<Pixels>, rem_size: Pixels) -> Corners<Pixels> {
let max = bounds.size.width.max(bounds.size.height) / 2.;
Corners {
top_left: self.top_left.to_pixels(rem_size),
top_right: self.top_right.to_pixels(rem_size),
bottom_right: self.bottom_right.to_pixels(rem_size),
bottom_left: self.bottom_left.to_pixels(rem_size),
top_left: self.top_left.to_pixels(rem_size).min(max),
top_right: self.top_right.to_pixels(rem_size).min(max),
bottom_right: self.bottom_right.to_pixels(rem_size).min(max),
bottom_left: self.bottom_left.to_pixels(rem_size).min(max),
}
}
}
@@ -536,7 +563,7 @@ impl Mul<Pixels> for Pixels {
impl Eq for Pixels {}
impl Ord for Pixels {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
@@ -925,6 +952,15 @@ impl<T: IsZero + Debug + Clone> IsZero for Size<T> {
impl<T: IsZero + Debug + Clone> IsZero for Bounds<T> {
fn is_zero(&self) -> bool {
self.origin.is_zero() && self.size.is_zero()
self.size.is_zero()
}
}
impl<T: IsZero + Debug + Clone> IsZero for Corners<T> {
fn is_zero(&self) -> bool {
self.top_left.is_zero()
&& self.top_right.is_zero()
&& self.bottom_right.is_zero()
&& self.bottom_left.is_zero()
}
}
+43 -13
View File
@@ -27,8 +27,6 @@ pub use elements::*;
pub use executor::*;
pub use geometry::*;
pub use gpui3_macros::*;
pub use svg_renderer::*;
pub use platform::*;
pub use refineable::*;
pub use scene::*;
@@ -37,12 +35,14 @@ pub use serde_json;
pub use smallvec;
pub use smol::Timer;
use std::{
mem,
ops::{Deref, DerefMut},
sync::Arc,
};
pub use style::*;
pub use style_helpers::*;
pub use styled::*;
pub use svg_renderer::*;
use taffy::TaffyLayoutEngine;
pub use taffy::{AvailableSpace, LayoutId};
pub use text_system::*;
@@ -69,12 +69,6 @@ pub trait Context {
#[repr(transparent)]
pub struct MainThread<T>(T);
impl<T> MainThread<T> {
fn new(value: T) -> Self {
Self(value)
}
}
impl<T> Deref for MainThread<T> {
type Target = T;
@@ -89,6 +83,42 @@ impl<T> DerefMut for MainThread<T> {
}
}
impl<C: Context> Context for MainThread<C> {
type EntityContext<'a, 'w, T: 'static + Send + Sync> = MainThread<C::EntityContext<'a, 'w, T>>;
type Result<T> = C::Result<T>;
fn entity<T: Send + Sync + 'static>(
&mut self,
build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
) -> Self::Result<Handle<T>> {
self.0.entity(|cx| {
let cx = unsafe {
mem::transmute::<
&mut C::EntityContext<'_, '_, T>,
&mut MainThread<C::EntityContext<'_, '_, T>>,
>(cx)
};
build_entity(cx)
})
}
fn update_entity<T: Send + Sync + 'static, R>(
&mut self,
handle: &Handle<T>,
update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
) -> Self::Result<R> {
self.0.update_entity(handle, |entity, cx| {
let cx = unsafe {
mem::transmute::<
&mut C::EntityContext<'_, '_, T>,
&mut MainThread<C::EntityContext<'_, '_, T>>,
>(cx)
};
update(entity, cx)
})
}
}
pub trait BorrowAppContext {
fn app_mut(&mut self) -> &mut AppContext;
@@ -190,14 +220,14 @@ impl<'a, T> DerefMut for Reference<'a, T> {
}
pub(crate) struct MainThreadOnly<T: ?Sized> {
dispatcher: Arc<dyn PlatformDispatcher>,
executor: Executor,
value: Arc<T>,
}
impl<T: ?Sized> Clone for MainThreadOnly<T> {
fn clone(&self) -> Self {
Self {
dispatcher: self.dispatcher.clone(),
executor: self.executor.clone(),
value: self.value.clone(),
}
}
@@ -206,12 +236,12 @@ impl<T: ?Sized> Clone for MainThreadOnly<T> {
/// Allows a value to be accessed only on the main thread, allowing a non-`Send` type
/// to become `Send`.
impl<T: 'static + ?Sized> MainThreadOnly<T> {
pub(crate) fn new(value: Arc<T>, dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
Self { dispatcher, value }
pub(crate) fn new(value: Arc<T>, executor: Executor) -> Self {
Self { executor, value }
}
pub(crate) fn borrow_on_main_thread(&self) -> &T {
assert!(self.dispatcher.is_main_thread());
assert!(self.executor.is_main_thread());
&self.value
}
}
+5 -4
View File
@@ -7,8 +7,8 @@ mod test;
use crate::image_cache::RenderImageParams;
use crate::{
AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
AnyWindowHandle, Bounds, DevicePixels, Executor, Font, FontId, FontMetrics, GlyphId, Pixels,
Point, RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
};
use anyhow::anyhow;
use async_task::Runnable;
@@ -43,7 +43,7 @@ pub(crate) fn current_platform() -> Arc<dyn Platform> {
}
pub trait Platform: 'static {
fn dispatcher(&self) -> Arc<dyn PlatformDispatcher>;
fn executor(&self) -> Executor;
fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
@@ -154,7 +154,8 @@ pub trait PlatformWindow {
pub trait PlatformDispatcher: Send + Sync {
fn is_main_thread(&self) -> bool;
fn run_on_main_thread(&self, task: Runnable);
fn dispatch(&self, task: Runnable);
fn dispatch_on_main_thread(&self, task: Runnable);
}
pub trait PlatformTextSystem: Send + Sync {
+37 -6
View File
@@ -25,7 +25,17 @@ impl PlatformDispatcher for MacDispatcher {
is_main_thread == YES
}
fn run_on_main_thread(&self, runnable: Runnable) {
fn dispatch(&self, runnable: Runnable) {
unsafe {
dispatch_async_f(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
runnable.into_raw() as *mut c_void,
Some(trampoline),
);
}
}
fn dispatch_on_main_thread(&self, runnable: Runnable) {
unsafe {
dispatch_async_f(
dispatch_get_main_queue(),
@@ -33,10 +43,31 @@ impl PlatformDispatcher for MacDispatcher {
Some(trampoline),
);
}
extern "C" fn trampoline(runnable: *mut c_void) {
let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
task.run();
}
}
}
extern "C" fn trampoline(runnable: *mut c_void) {
let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
task.run();
}
// #include <dispatch/dispatch.h>
// int main(void) {
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// // Do some lengthy background work here...
// printf("Background Work\n");
// dispatch_async(dispatch_get_main_queue(), ^{
// // Once done, update your UI on the main queue here.
// printf("UI Updated\n");
// });
// });
// sleep(3); // prevent the program from terminating immediately
// return 0;
// }
// ```
@@ -131,6 +131,8 @@ impl MetalRenderer {
}
pub fn draw(&mut self, scene: &mut Scene) {
dbg!("draw scene");
let layer = self.layer.clone();
let viewport_size = layer.drawable_size();
let viewport_size: Size<DevicePixels> = size(
+22 -19
View File
@@ -1,8 +1,8 @@
use super::BoolExt;
use crate::{
AnyWindowHandle, ClipboardItem, CursorStyle, Event, MacDispatcher, MacScreen, MacTextSystem,
MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem, PlatformWindow,
Result, ScreenId, SemanticVersion, WindowOptions,
AnyWindowHandle, ClipboardItem, CursorStyle, Event, Executor, MacDispatcher, MacScreen,
MacTextSystem, MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem,
PlatformWindow, Result, ScreenId, SemanticVersion, WindowOptions,
};
use anyhow::anyhow;
use block::ConcreteBlock;
@@ -142,7 +142,7 @@ unsafe fn build_classes() {
pub struct MacPlatform(Mutex<MacPlatformState>);
pub struct MacPlatformState {
dispatcher: Arc<MacDispatcher>,
executor: Executor,
text_system: Arc<MacTextSystem>,
pasteboard: id,
text_hash_pasteboard_type: id,
@@ -163,7 +163,7 @@ pub struct MacPlatformState {
impl MacPlatform {
pub fn new() -> Self {
Self(Mutex::new(MacPlatformState {
dispatcher: Arc::new(MacDispatcher),
executor: Executor::new(Arc::new(MacDispatcher)),
text_system: Arc::new(MacTextSystem::new()),
pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
@@ -343,8 +343,8 @@ impl MacPlatform {
}
impl Platform for MacPlatform {
fn dispatcher(&self) -> Arc<dyn crate::PlatformDispatcher> {
Arc::new(MacDispatcher)
fn executor(&self) -> Executor {
self.0.lock().executor.clone()
}
fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
@@ -479,7 +479,7 @@ impl Platform for MacPlatform {
handle: AnyWindowHandle,
options: WindowOptions,
) -> Box<dyn PlatformWindow> {
Box::new(MacWindow::open(handle, options, self))
Box::new(MacWindow::open(handle, options, self.executor()))
}
fn open_url(&self, url: &str) {
@@ -566,17 +566,20 @@ impl Platform for MacPlatform {
fn reveal_path(&self, path: &Path) {
unsafe {
let path = path.to_path_buf();
let dispatcher = self.0.lock().dispatcher.clone();
let _ = crate::spawn_on_main_local(dispatcher, async move {
let full_path = ns_string(path.to_str().unwrap_or(""));
let root_full_path = ns_string("");
let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
let _: BOOL = msg_send![
workspace,
selectFile: full_path
inFileViewerRootedAtPath: root_full_path
];
});
self.0
.lock()
.executor
.spawn_on_main_local(async move {
let full_path = ns_string(path.to_str().unwrap_or(""));
let root_full_path = ns_string("");
let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
let _: BOOL = msg_send![
workspace,
selectFile: full_path
inFileViewerRootedAtPath: root_full_path
];
})
.detach();
}
}
+15 -12
View File
@@ -106,11 +106,7 @@ fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
color = float4(premultiplied_output_rgb, output_alpha);
}
float clip_distance =
quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii);
return color *
float4(1., 1., 1.,
saturate(0.5 - distance) * saturate(0.5 - clip_distance));
return color * float4(1., 1., 1., saturate(0.5 - distance));
}
struct MonochromeSpriteVertexOutput {
@@ -131,8 +127,9 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
float2 unit_vertex = unit_vertices[unit_vertex_id];
MonochromeSprite sprite = sprites[sprite_id];
// Don't apply content mask at the vertex level because we don't have time to make sampling from the texture match the mask.
float4 device_position = to_device_position(
unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
unit_vertex, sprite.bounds, sprite.bounds, viewport_size);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
float4 color = hsla_to_rgba(sprite.color);
return MonochromeSpriteVertexOutput{device_position, tile_position, color,
@@ -148,8 +145,11 @@ fragment float4 monochrome_sprite_fragment(
min_filter::linear);
float4 sample =
atlas_texture.sample(atlas_texture_sampler, input.tile_position);
float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
sprite.content_mask.corner_radii);
float clip_distance = quad_sdf(
input.position.xy,
sprite.content_mask.bounds,
Corners_ScaledPixels { 0., 0., 0., 0. }
);
float4 color = input.color;
color.a *= sample.a * saturate(0.5 - clip_distance);
return color;
@@ -172,8 +172,9 @@ vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex(
float2 unit_vertex = unit_vertices[unit_vertex_id];
PolychromeSprite sprite = sprites[sprite_id];
// Don't apply content mask at the vertex level because we don't have time to make sampling from the texture match the mask.
float4 device_position = to_device_position(
unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
unit_vertex, sprite.bounds, sprite.bounds, viewport_size);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
return PolychromeSpriteVertexOutput{device_position, tile_position,
sprite_id};
@@ -188,8 +189,10 @@ fragment float4 polychrome_sprite_fragment(
min_filter::linear);
float4 sample =
atlas_texture.sample(atlas_texture_sampler, input.tile_position);
float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
sprite.content_mask.corner_radii);
float quad_distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii);
float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds, Corners_ScaledPixels { 0., 0., 0., 0. });
float distance = max(quad_distance, clip_distance);
float4 color = sample;
if (sprite.grayscale) {
float grayscale = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
@@ -197,7 +200,7 @@ fragment float4 polychrome_sprite_fragment(
color.g = grayscale;
color.b = grayscale;
}
color.a *= saturate(0.5 - clip_distance);
color.a *= saturate(0.5 - distance);
return color;
}
+61 -54
View File
@@ -1,10 +1,10 @@
use super::{ns_string, MetalRenderer, NSRange};
use crate::{
point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt,
Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen,
PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind,
WindowOptions, WindowPromptLevel,
point, px, size, AnyWindowHandle, Bounds, Event, Executor, KeyDownEvent, Keystroke, MacScreen,
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent,
NSRectExt, Pixels, PlatformAtlas, PlatformInputHandler, PlatformScreen, PlatformWindow, Point,
Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
WindowPromptLevel,
};
use block::ConcreteBlock;
use cocoa::{
@@ -279,7 +279,7 @@ struct InsertText {
struct MacWindowState {
handle: AnyWindowHandle,
dispatcher: Arc<dyn PlatformDispatcher>,
executor: Executor,
native_window: id,
renderer: MetalRenderer,
scene_to_render: Option<Scene>,
@@ -415,7 +415,7 @@ unsafe impl Send for MacWindowState {}
pub struct MacWindow(Arc<Mutex<MacWindowState>>);
impl MacWindow {
pub fn open(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self {
pub fn open(handle: AnyWindowHandle, options: WindowOptions, executor: Executor) -> Self {
unsafe {
let pool = NSAutoreleasePool::new(nil);
@@ -479,7 +479,7 @@ impl MacWindow {
let window = Self(Arc::new(Mutex::new(MacWindowState {
handle,
dispatcher: platform.dispatcher(),
executor,
native_window,
renderer: MetalRenderer::new(true),
scene_to_render: None,
@@ -616,12 +616,12 @@ impl MacWindow {
impl Drop for MacWindow {
fn drop(&mut self) {
let this = self.0.clone();
let dispatcher = self.0.lock().dispatcher.clone();
let _ = crate::spawn_on_main(dispatcher, || async move {
unsafe {
let executor = self.0.lock().executor.clone();
executor
.run_on_main(move || unsafe {
this.lock().native_window.close();
}
});
})
.detach();
}
}
@@ -739,14 +739,16 @@ impl PlatformWindow for MacWindow {
});
let block = block.copy();
let native_window = self.0.lock().native_window;
let dispatcher = self.0.lock().dispatcher.clone();
let _ = crate::spawn_on_main_local(dispatcher, async move {
let _: () = msg_send![
alert,
beginSheetModalForWindow: native_window
completionHandler: block
];
});
let executor = self.0.lock().executor.clone();
executor
.spawn_on_main_local(async move {
let _: () = msg_send![
alert,
beginSheetModalForWindow: native_window
completionHandler: block
];
})
.detach();
done_rx
}
@@ -754,12 +756,14 @@ impl PlatformWindow for MacWindow {
fn activate(&self) {
let window = self.0.lock().native_window;
let dispatcher = self.0.lock().dispatcher.clone();
let _ = crate::spawn_on_main_local(dispatcher.clone(), async move {
unsafe {
let _: () = msg_send![window, makeKeyAndOrderFront: nil];
}
});
let executor = self.0.lock().executor.clone();
executor
.spawn_on_main_local(async move {
unsafe {
let _: () = msg_send![window, makeKeyAndOrderFront: nil];
}
})
.detach();
}
fn set_title(&mut self, title: &str) {
@@ -802,23 +806,25 @@ impl PlatformWindow for MacWindow {
fn zoom(&self) {
let this = self.0.lock();
let window = this.native_window;
let dispatcher = this.dispatcher.clone();
let _ = crate::spawn_on_main_local(dispatcher, async move {
unsafe {
window.zoom_(nil);
}
});
this.executor
.spawn_on_main_local(async move {
unsafe {
window.zoom_(nil);
}
})
.detach();
}
fn toggle_full_screen(&self) {
let this = self.0.lock();
let window = this.native_window;
let dispatcher = this.dispatcher.clone();
let _ = crate::spawn_on_main_local(dispatcher, async move {
unsafe {
window.toggleFullScreen_(nil);
}
});
this.executor
.spawn_on_main_local(async move {
unsafe {
window.toggleFullScreen_(nil);
}
})
.detach();
}
fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
@@ -1114,15 +1120,14 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
},
) => {
lock.synthetic_drag_counter += 1;
let dispatcher = lock.dispatcher.clone();
let _ = crate::spawn_on_main_local(
dispatcher,
synthetic_drag(
let executor = lock.executor.clone();
executor
.spawn_on_main_local(synthetic_drag(
weak_window_state,
lock.synthetic_drag_counter,
event.clone(),
),
);
))
.detach();
}
Event::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
@@ -1241,16 +1246,18 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
}
}
let dispatcher = lock.dispatcher.clone();
let executor = lock.executor.clone();
drop(lock);
let _ = crate::spawn_on_main_local(dispatcher, async move {
let mut lock = window_state.as_ref().lock();
if let Some(mut callback) = lock.activate_callback.take() {
drop(lock);
callback(is_active);
window_state.lock().activate_callback = Some(callback);
};
});
executor
.spawn_on_main_local(async move {
let mut lock = window_state.as_ref().lock();
if let Some(mut callback) = lock.activate_callback.take() {
drop(lock);
callback(is_active);
window_state.lock().activate_callback = Some(callback);
};
})
.detach();
}
extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
+2 -2
View File
@@ -1,5 +1,5 @@
use super::Platform;
use crate::ScreenId;
use crate::{Executor, ScreenId};
pub struct TestPlatform;
@@ -11,7 +11,7 @@ impl TestPlatform {
// todo!("implement out what our tests needed in GPUI 1")
impl Platform for TestPlatform {
fn dispatcher(&self) -> std::sync::Arc<dyn crate::PlatformDispatcher> {
fn executor(&self) -> Executor {
unimplemented!()
}
+1
View File
@@ -275,6 +275,7 @@ pub struct PolychromeSprite {
pub order: u32,
pub bounds: Bounds<ScaledPixels>,
pub content_mask: ScaledContentMask,
pub corner_radii: Corners<ScaledPixels>,
pub tile: AtlasTile,
pub grayscale: bool,
}
-2
View File
@@ -202,7 +202,6 @@ impl Style {
let min = current_mask.bounds.origin;
let max = current_mask.bounds.lower_right();
let mask_corner_radii = Corners::default();
let mask_bounds = match (
self.overflow.x == Overflow::Visible,
self.overflow.y == Overflow::Visible,
@@ -224,7 +223,6 @@ impl Style {
};
let mask = ContentMask {
bounds: mask_bounds,
corner_radii: mask_corner_radii,
};
cx.with_content_mask(mask, f)
+11 -13
View File
@@ -1,18 +1,16 @@
use smol::future::FutureExt;
use std::{future::Future, time::Duration};
pub use util::*;
pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
where
F: Future<Output = T>,
{
let timer = async {
smol::Timer::after(timeout).await;
Err(())
};
let future = async move { Ok(f.await) };
timer.race(future).await
}
// pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
// where
// F: Future<Output = T>,
// {
// let timer = async {
// smol::Timer::after(timeout).await;
// Err(())
// };
// let future = async move { Ok(f.await) };
// timer.race(future).await
// }
#[cfg(any(test, feature = "test"))]
pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace);
+93 -53
View File
@@ -1,15 +1,14 @@
use crate::{
image_cache::RenderImageParams, px, AnyView, AppContext, AvailableSpace, BorrowAppContext,
Bounds, Context, Corners, DevicePixels, Effect, Element, EntityId, FontId, GlyphId, Handle,
Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread, MainThreadOnly, MonochromeSprite,
Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference, RenderGlyphParams,
RenderSvgParams, ScaledPixels, Scene, SharedString, Size, Style, TaffyLayoutEngine, WeakHandle,
WindowOptions, SUBPIXEL_VARIANTS,
image_cache::RenderImageParams, px, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
BorrowAppContext, Bounds, Context, Corners, DevicePixels, Effect, Element, EntityId, FontId,
GlyphId, Handle, Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread, MainThreadOnly,
MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference,
RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, Style,
TaffyLayoutEngine, Task, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::Result;
use futures::Future;
use smallvec::SmallVec;
use std::{any::TypeId, borrow::Cow, marker::PhantomData, mem, sync::Arc};
use std::{any::TypeId, borrow::Cow, future::Future, marker::PhantomData, mem, sync::Arc};
use util::ResultExt;
pub struct AnyWindow {}
@@ -53,8 +52,7 @@ impl Window {
}
}));
let platform_window =
MainThreadOnly::new(Arc::new(platform_window), cx.platform().dispatcher());
let platform_window = MainThreadOnly::new(Arc::new(platform_window), cx.executor.clone());
Window {
handle,
@@ -76,23 +74,25 @@ impl Window {
#[derive(Clone, Debug)]
pub struct ContentMask {
pub bounds: Bounds<Pixels>,
pub corner_radii: Corners<Pixels>,
}
impl ContentMask {
pub fn scale(&self, factor: f32) -> ScaledContentMask {
ScaledContentMask {
bounds: self.bounds.scale(factor),
corner_radii: self.corner_radii.scale(factor),
}
}
pub fn intersect(&self, other: &Self) -> Self {
let bounds = self.bounds.intersect(&other.bounds);
ContentMask { bounds }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct ScaledContentMask {
bounds: Bounds<ScaledPixels>,
corner_radii: Corners<ScaledPixels>,
}
pub struct WindowContext<'a, 'w> {
@@ -112,6 +112,43 @@ impl<'a, 'w> WindowContext<'a, 'w> {
self.window.dirty = true;
}
pub fn run_on_main<R>(
&mut self,
f: impl FnOnce(&mut MainThread<WindowContext<'_, '_>>) -> R + Send + 'static,
) -> Task<Result<R>>
where
R: Send + 'static,
{
if self.executor.is_main_thread() {
Task::ready(Ok(f(unsafe {
mem::transmute::<&mut Self, &mut MainThread<Self>>(self)
})))
} else {
let id = self.window.handle.id;
self.app.run_on_main(move |cx| cx.update_window(id, f))
}
}
pub fn to_async(&self) -> AsyncWindowContext {
AsyncWindowContext::new(self.app.to_async(), self.window.handle)
}
pub fn spawn<Fut, R>(
&mut self,
f: impl FnOnce(AnyWindowHandle, AsyncWindowContext) -> Fut + Send + 'static,
) -> Task<R>
where
R: Send + 'static,
Fut: Future<Output = R> + Send + 'static,
{
let window = self.window.handle;
self.app.spawn(move |app| {
let cx = AsyncWindowContext::new(app, window);
let future = f(window, cx);
async move { future.await }
})
}
pub fn request_layout(
&mut self,
style: Style,
@@ -170,44 +207,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
result
}
pub fn clip<F, R>(
&mut self,
bounds: Bounds<Pixels>,
corner_radii: Corners<Pixels>,
f: impl FnOnce(&mut Self) -> R,
) -> R {
let clip_mask = ContentMask {
bounds,
corner_radii,
};
self.window.content_mask_stack.push(clip_mask);
let result = f(self);
self.window.content_mask_stack.pop();
result
}
pub fn current_layer_id(&self) -> LayerId {
self.window.current_layer_id.clone()
}
pub fn run_on_main<R>(
&self,
f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,
) -> impl Future<Output = Result<R>>
where
R: Send + 'static,
{
let id = self.window.handle.id;
self.app.run_on_main(move |cx| {
cx.update_window(id, |cx| {
f(unsafe {
mem::transmute::<&mut WindowContext, &mut MainThread<WindowContext>>(cx)
})
})
})
}
pub fn paint_glyph(
&mut self,
origin: Point<Pixels>,
@@ -303,6 +306,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
PolychromeSprite {
order,
bounds,
corner_radii: Default::default(),
content_mask,
tile,
grayscale: false,
@@ -356,6 +360,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
pub fn paint_image(
&mut self,
bounds: Bounds<Pixels>,
corner_radii: Corners<Pixels>,
order: u32,
data: Arc<ImageData>,
grayscale: bool,
@@ -372,6 +377,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
Ok((data.size(), Cow::Borrowed(data.as_bytes())))
})?;
let content_mask = self.content_mask().scale(scale_factor);
let corner_radii = corner_radii.scale(scale_factor);
self.window.scene.insert(
layer_id,
@@ -379,6 +385,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
order,
bounds,
content_mask,
corner_radii,
tile,
grayscale,
},
@@ -389,7 +396,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
pub(crate) fn draw(&mut self) -> Result<()> {
let unit_entity = self.unit_entity.clone();
self.update_entity(&unit_entity, |_, cx| {
self.update_entity(&unit_entity, |view, cx| {
let mut root_view = cx.window.root_view.take().unwrap();
let (root_layout_id, mut frame_state) = root_view.layout(&mut (), cx)?;
let available_space = cx.window.content_size.map(Into::into);
@@ -403,14 +410,15 @@ impl<'a, 'w> WindowContext<'a, 'w> {
cx.window.root_view = Some(root_view);
let scene = cx.window.scene.take();
let _ = cx.run_on_main(|cx| {
cx.run_on_main(view, |_, cx| {
cx.window
.platform_window
.borrow_on_main_thread()
.draw(scene);
});
cx.window.dirty = false;
})
.detach();
cx.window.dirty = false;
Ok(())
})
}
@@ -430,7 +438,7 @@ impl Context for WindowContext<'_, '_> {
&mut self.window,
slot.id,
));
self.entities.redeem(slot, entity)
self.entities.insert(slot, entity)
}
fn update_entity<T: Send + Sync + 'static, R>(
@@ -473,6 +481,7 @@ pub trait BorrowWindow: BorrowAppContext {
fn window_mut(&mut self) -> &mut Window;
fn with_content_mask<R>(&mut self, mask: ContentMask, f: impl FnOnce(&mut Self) -> R) -> R {
let mask = mask.intersect(&self.content_mask());
self.window_mut().content_mask_stack.push(mask);
let result = f(self);
self.window_mut().content_mask_stack.pop();
@@ -489,7 +498,6 @@ pub trait BorrowWindow: BorrowAppContext {
origin: Point::default(),
size: self.window().content_size,
},
corner_radii: Default::default(),
})
}
@@ -596,6 +604,38 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
.push_back(Effect::Notify(self.entity_id));
}
pub fn run_on_main<R>(
&mut self,
view: &mut S,
f: impl FnOnce(&mut S, &mut MainThread<ViewContext<'_, '_, S>>) -> R + Send + 'static,
) -> Task<Result<R>>
where
R: Send + 'static,
{
if self.executor.is_main_thread() {
let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) };
Task::ready(Ok(f(view, cx)))
} else {
let handle = self.handle().upgrade(self).unwrap();
self.window_cx.run_on_main(move |cx| handle.update(cx, f))
}
}
pub fn spawn<Fut, R>(
&mut self,
f: impl FnOnce(WeakHandle<S>, AsyncWindowContext) -> Fut + Send + 'static,
) -> Task<R>
where
R: Send + 'static,
Fut: Future<Output = R> + Send + 'static,
{
let handle = self.handle();
self.window_cx.spawn(move |_, cx| {
let result = f(handle, cx);
async move { result.await }
})
}
pub(crate) fn erase_state<R>(&mut self, f: impl FnOnce(&mut ViewContext<()>) -> R) -> R {
let entity_id = self.unit_entity.id;
let mut cx = ViewContext::mutable(