Merge branch 'gpui2' into marshall/gpui2-playground
This commit is contained in:
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>);
|
||||
|
||||
|
||||
@@ -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(())
|
||||
|
||||
+79
-1062
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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!()
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user