This commit is contained in:
Antonio Scandurra 2023-10-22 12:21:28 +02:00
parent 909fbb9538
commit f4135e6bcf
12 changed files with 860 additions and 637 deletions

2
Cargo.lock generated
View file

@ -1460,7 +1460,6 @@ dependencies = [
"db", "db",
"feature_flags", "feature_flags",
"futures 0.3.28", "futures 0.3.28",
"gpui",
"gpui2", "gpui2",
"image", "image",
"lazy_static", "lazy_static",
@ -1473,6 +1472,7 @@ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
"settings", "settings",
"settings2",
"smol", "smol",
"sum_tree", "sum_tree",
"sysinfo", "sysinfo",

View file

@ -9,7 +9,7 @@ path = "src/client2.rs"
doctest = false doctest = false
[features] [features]
test-support = ["collections/test-support", "gpui/test-support", "rpc/test-support"] test-support = ["collections/test-support", "gpui2/test-support", "rpc/test-support"]
[dependencies] [dependencies]
collections = { path = "../collections" } collections = { path = "../collections" }
@ -18,7 +18,7 @@ gpui2 = { path = "../gpui2" }
util = { path = "../util" } util = { path = "../util" }
rpc = { path = "../rpc" } rpc = { path = "../rpc" }
text = { path = "../text" } text = { path = "../text" }
settings = { path = "../settings" } settings2 = { path = "../settings2" }
feature_flags = { path = "../feature_flags" } feature_flags = { path = "../feature_flags" }
sum_tree = { path = "../sum_tree" } sum_tree = { path = "../sum_tree" }

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL}; use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
use gpui::{executor::Background, serde_json, AppContext, Task}; use gpui2::{serde_json, AppContext, Executor, Task};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use parking_lot::Mutex; use parking_lot::Mutex;
use serde::Serialize; use serde::Serialize;
@ -11,7 +11,7 @@ use util::{channel::ReleaseChannel, TryFutureExt};
pub struct Telemetry { pub struct Telemetry {
http_client: Arc<dyn HttpClient>, http_client: Arc<dyn HttpClient>,
executor: Arc<Background>, executor: Executor,
state: Mutex<TelemetryState>, state: Mutex<TelemetryState>,
} }

View file

@ -1,215 +1,215 @@
use crate::{Client, Connection, Credentials, EstablishConnectionError, UserStore}; // use crate::{Client, Connection, Credentials, EstablishConnectionError, UserStore};
use anyhow::{anyhow, Result}; // use anyhow::{anyhow, Result};
use futures::{stream::BoxStream, StreamExt}; // use futures::{stream::BoxStream, StreamExt};
use gpui::{executor, ModelHandle, TestAppContext}; // use gpui2::{Executor, Handle, TestAppContext};
use parking_lot::Mutex; // use parking_lot::Mutex;
use rpc::{ // use rpc::{
proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse}, // proto::{self, GetPrivateUserInfo, GetPrivateUserInfoResponse},
ConnectionId, Peer, Receipt, TypedEnvelope, // ConnectionId, Peer, Receipt, TypedEnvelope,
}; // };
use std::{rc::Rc, sync::Arc}; // use std::{rc::Rc, sync::Arc};
use util::http::FakeHttpClient; // use util::http::FakeHttpClient;
pub struct FakeServer { // pub struct FakeServer {
peer: Arc<Peer>, // peer: Arc<Peer>,
state: Arc<Mutex<FakeServerState>>, // state: Arc<Mutex<FakeServerState>>,
user_id: u64, // user_id: u64,
executor: Rc<executor::Foreground>, // executor: Executor,
} // }
#[derive(Default)] // #[derive(Default)]
struct FakeServerState { // struct FakeServerState {
incoming: Option<BoxStream<'static, Box<dyn proto::AnyTypedEnvelope>>>, // incoming: Option<BoxStream<'static, Box<dyn proto::AnyTypedEnvelope>>>,
connection_id: Option<ConnectionId>, // connection_id: Option<ConnectionId>,
forbid_connections: bool, // forbid_connections: bool,
auth_count: usize, // auth_count: usize,
access_token: usize, // access_token: usize,
} // }
impl FakeServer { // impl FakeServer {
pub async fn for_client( // pub async fn for_client(
client_user_id: u64, // client_user_id: u64,
client: &Arc<Client>, // client: &Arc<Client>,
cx: &TestAppContext, // cx: &TestAppContext,
) -> Self { // ) -> Self {
let server = Self { // let server = Self {
peer: Peer::new(0), // peer: Peer::new(0),
state: Default::default(), // state: Default::default(),
user_id: client_user_id, // user_id: client_user_id,
executor: cx.foreground(), // executor: cx.foreground(),
}; // };
client // client
.override_authenticate({ // .override_authenticate({
let state = Arc::downgrade(&server.state); // let state = Arc::downgrade(&server.state);
move |cx| { // move |cx| {
let state = state.clone(); // let state = state.clone();
cx.spawn(move |_| async move { // cx.spawn(move |_| async move {
let state = state.upgrade().ok_or_else(|| anyhow!("server dropped"))?; // let state = state.upgrade().ok_or_else(|| anyhow!("server dropped"))?;
let mut state = state.lock(); // let mut state = state.lock();
state.auth_count += 1; // state.auth_count += 1;
let access_token = state.access_token.to_string(); // let access_token = state.access_token.to_string();
Ok(Credentials { // Ok(Credentials {
user_id: client_user_id, // user_id: client_user_id,
access_token, // access_token,
}) // })
}) // })
} // }
}) // })
.override_establish_connection({ // .override_establish_connection({
let peer = Arc::downgrade(&server.peer); // let peer = Arc::downgrade(&server.peer);
let state = Arc::downgrade(&server.state); // let state = Arc::downgrade(&server.state);
move |credentials, cx| { // move |credentials, cx| {
let peer = peer.clone(); // let peer = peer.clone();
let state = state.clone(); // let state = state.clone();
let credentials = credentials.clone(); // let credentials = credentials.clone();
cx.spawn(move |cx| async move { // cx.spawn(move |cx| async move {
let state = state.upgrade().ok_or_else(|| anyhow!("server dropped"))?; // let state = state.upgrade().ok_or_else(|| anyhow!("server dropped"))?;
let peer = peer.upgrade().ok_or_else(|| anyhow!("server dropped"))?; // let peer = peer.upgrade().ok_or_else(|| anyhow!("server dropped"))?;
if state.lock().forbid_connections { // if state.lock().forbid_connections {
Err(EstablishConnectionError::Other(anyhow!( // Err(EstablishConnectionError::Other(anyhow!(
"server is forbidding connections" // "server is forbidding connections"
)))? // )))?
} // }
assert_eq!(credentials.user_id, client_user_id); // assert_eq!(credentials.user_id, client_user_id);
if credentials.access_token != state.lock().access_token.to_string() { // if credentials.access_token != state.lock().access_token.to_string() {
Err(EstablishConnectionError::Unauthorized)? // Err(EstablishConnectionError::Unauthorized)?
} // }
let (client_conn, server_conn, _) = Connection::in_memory(cx.background()); // let (client_conn, server_conn, _) = Connection::in_memory(cx.background());
let (connection_id, io, incoming) = // let (connection_id, io, incoming) =
peer.add_test_connection(server_conn, cx.background()); // peer.add_test_connection(server_conn, cx.background());
cx.background().spawn(io).detach(); // cx.background().spawn(io).detach();
{ // {
let mut state = state.lock(); // let mut state = state.lock();
state.connection_id = Some(connection_id); // state.connection_id = Some(connection_id);
state.incoming = Some(incoming); // state.incoming = Some(incoming);
} // }
peer.send( // peer.send(
connection_id, // connection_id,
proto::Hello { // proto::Hello {
peer_id: Some(connection_id.into()), // peer_id: Some(connection_id.into()),
}, // },
) // )
.unwrap(); // .unwrap();
Ok(client_conn) // Ok(client_conn)
}) // })
} // }
}); // });
client // client
.authenticate_and_connect(false, &cx.to_async()) // .authenticate_and_connect(false, &cx.to_async())
.await // .await
.unwrap(); // .unwrap();
server // server
} // }
pub fn disconnect(&self) { // pub fn disconnect(&self) {
if self.state.lock().connection_id.is_some() { // if self.state.lock().connection_id.is_some() {
self.peer.disconnect(self.connection_id()); // self.peer.disconnect(self.connection_id());
let mut state = self.state.lock(); // let mut state = self.state.lock();
state.connection_id.take(); // state.connection_id.take();
state.incoming.take(); // state.incoming.take();
} // }
} // }
pub fn auth_count(&self) -> usize { // pub fn auth_count(&self) -> usize {
self.state.lock().auth_count // self.state.lock().auth_count
} // }
pub fn roll_access_token(&self) { // pub fn roll_access_token(&self) {
self.state.lock().access_token += 1; // self.state.lock().access_token += 1;
} // }
pub fn forbid_connections(&self) { // pub fn forbid_connections(&self) {
self.state.lock().forbid_connections = true; // self.state.lock().forbid_connections = true;
} // }
pub fn allow_connections(&self) { // pub fn allow_connections(&self) {
self.state.lock().forbid_connections = false; // self.state.lock().forbid_connections = false;
} // }
pub fn send<T: proto::EnvelopedMessage>(&self, message: T) { // pub fn send<T: proto::EnvelopedMessage>(&self, message: T) {
self.peer.send(self.connection_id(), message).unwrap(); // self.peer.send(self.connection_id(), message).unwrap();
} // }
#[allow(clippy::await_holding_lock)] // #[allow(clippy::await_holding_lock)]
pub async fn receive<M: proto::EnvelopedMessage>(&self) -> Result<TypedEnvelope<M>> { // pub async fn receive<M: proto::EnvelopedMessage>(&self) -> Result<TypedEnvelope<M>> {
self.executor.start_waiting(); // self.executor.start_waiting();
loop { // loop {
let message = self // let message = self
.state // .state
.lock() // .lock()
.incoming // .incoming
.as_mut() // .as_mut()
.expect("not connected") // .expect("not connected")
.next() // .next()
.await // .await
.ok_or_else(|| anyhow!("other half hung up"))?; // .ok_or_else(|| anyhow!("other half hung up"))?;
self.executor.finish_waiting(); // self.executor.finish_waiting();
let type_name = message.payload_type_name(); // let type_name = message.payload_type_name();
let message = message.into_any(); // let message = message.into_any();
if message.is::<TypedEnvelope<M>>() { // if message.is::<TypedEnvelope<M>>() {
return Ok(*message.downcast().unwrap()); // return Ok(*message.downcast().unwrap());
} // }
if message.is::<TypedEnvelope<GetPrivateUserInfo>>() { // if message.is::<TypedEnvelope<GetPrivateUserInfo>>() {
self.respond( // self.respond(
message // message
.downcast::<TypedEnvelope<GetPrivateUserInfo>>() // .downcast::<TypedEnvelope<GetPrivateUserInfo>>()
.unwrap() // .unwrap()
.receipt(), // .receipt(),
GetPrivateUserInfoResponse { // GetPrivateUserInfoResponse {
metrics_id: "the-metrics-id".into(), // metrics_id: "the-metrics-id".into(),
staff: false, // staff: false,
flags: Default::default(), // flags: Default::default(),
}, // },
); // );
continue; // continue;
} // }
panic!( // panic!(
"fake server received unexpected message type: {:?}", // "fake server received unexpected message type: {:?}",
type_name // type_name
); // );
} // }
} // }
pub fn respond<T: proto::RequestMessage>(&self, receipt: Receipt<T>, response: T::Response) { // pub fn respond<T: proto::RequestMessage>(&self, receipt: Receipt<T>, response: T::Response) {
self.peer.respond(receipt, response).unwrap() // self.peer.respond(receipt, response).unwrap()
} // }
fn connection_id(&self) -> ConnectionId { // fn connection_id(&self) -> ConnectionId {
self.state.lock().connection_id.expect("not connected") // self.state.lock().connection_id.expect("not connected")
} // }
pub async fn build_user_store( // pub async fn build_user_store(
&self, // &self,
client: Arc<Client>, // client: Arc<Client>,
cx: &mut TestAppContext, // cx: &mut TestAppContext,
) -> ModelHandle<UserStore> { // ) -> ModelHandle<UserStore> {
let http_client = FakeHttpClient::with_404_response(); // let http_client = FakeHttpClient::with_404_response();
let user_store = cx.add_model(|cx| UserStore::new(client, http_client, cx)); // let user_store = cx.add_model(|cx| UserStore::new(client, http_client, cx));
assert_eq!( // assert_eq!(
self.receive::<proto::GetUsers>() // self.receive::<proto::GetUsers>()
.await // .await
.unwrap() // .unwrap()
.payload // .payload
.user_ids, // .user_ids,
&[self.user_id] // &[self.user_id]
); // );
user_store // user_store
} // }
} // }
impl Drop for FakeServer { // impl Drop for FakeServer {
fn drop(&mut self) { // fn drop(&mut self) {
self.disconnect(); // self.disconnect();
} // }
} // }

View file

@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result};
use collections::{hash_map::Entry, HashMap, HashSet}; use collections::{hash_map::Entry, HashMap, HashSet};
use feature_flags::FeatureFlagAppExt; use feature_flags::FeatureFlagAppExt;
use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt}; use futures::{channel::mpsc, future, AsyncReadExt, Future, StreamExt};
use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task}; use gpui2::{AsyncAppContext, EventEmitter, Handle, ImageData, ModelContext, Task};
use postage::{sink::Sink, watch}; use postage::{sink::Sink, watch};
use rpc::proto::{RequestMessage, UsersResponse}; use rpc::proto::{RequestMessage, UsersResponse};
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
@ -103,7 +103,7 @@ pub enum ContactEventKind {
Cancelled, Cancelled,
} }
impl Entity for UserStore { impl EventEmitter for UserStore {
type Event = Event; type Event = Event;
} }
@ -217,7 +217,7 @@ impl UserStore {
} }
async fn handle_update_invite_info( async fn handle_update_invite_info(
this: ModelHandle<Self>, this: Handle<Self>,
message: TypedEnvelope<proto::UpdateInviteInfo>, message: TypedEnvelope<proto::UpdateInviteInfo>,
_: Arc<Client>, _: Arc<Client>,
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
@ -233,7 +233,7 @@ impl UserStore {
} }
async fn handle_show_contacts( async fn handle_show_contacts(
this: ModelHandle<Self>, this: Handle<Self>,
_: TypedEnvelope<proto::ShowContacts>, _: TypedEnvelope<proto::ShowContacts>,
_: Arc<Client>, _: Arc<Client>,
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
@ -247,7 +247,7 @@ impl UserStore {
} }
async fn handle_update_contacts( async fn handle_update_contacts(
this: ModelHandle<Self>, this: Handle<Self>,
message: TypedEnvelope<proto::UpdateContacts>, message: TypedEnvelope<proto::UpdateContacts>,
_: Arc<Client>, _: Arc<Client>,
mut cx: AsyncAppContext, mut cx: AsyncAppContext,
@ -689,7 +689,7 @@ impl User {
impl Contact { impl Contact {
async fn from_proto( async fn from_proto(
contact: proto::Contact, contact: proto::Contact,
user_store: &ModelHandle<UserStore>, user_store: &Handle<UserStore>,
cx: &mut AsyncAppContext, cx: &mut AsyncAppContext,
) -> Result<Self> { ) -> Result<Self> {
let user = user_store let user = user_store

View file

@ -9,10 +9,11 @@ use refineable::Refineable;
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{ use crate::{
current_platform, image_cache::ImageCache, Action, AssetSource, Context, DisplayId, Executor, current_platform, image_cache::ImageCache, Action, AssetSource, Context, DispatchPhase,
FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId,
Platform, SemanticVersion, SharedString, SubscriberSet, SvgRenderer, Task, TextStyle, MainThread, MainThreadOnly, Platform, SemanticVersion, SharedString, SubscriberSet,
TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, WindowId, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext,
WindowHandle, WindowId,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
@ -67,6 +68,7 @@ impl App {
entities, entities,
windows: SlotMap::with_key(), windows: SlotMap::with_key(),
keymap: Arc::new(RwLock::new(Keymap::default())), keymap: Arc::new(RwLock::new(Keymap::default())),
global_action_listeners: HashMap::default(),
action_builders: HashMap::default(), action_builders: HashMap::default(),
pending_notifications: Default::default(), pending_notifications: Default::default(),
pending_effects: Default::default(), pending_effects: Default::default(),
@ -74,6 +76,7 @@ impl App {
event_handlers: SubscriberSet::new(), event_handlers: SubscriberSet::new(),
release_handlers: SubscriberSet::new(), release_handlers: SubscriberSet::new(),
layout_id_buffer: Default::default(), layout_id_buffer: Default::default(),
propagate_event: true,
}) })
})) }))
} }
@ -168,6 +171,8 @@ pub struct AppContext {
pub(crate) entities: EntityMap, pub(crate) entities: EntityMap,
pub(crate) windows: SlotMap<WindowId, Option<Window>>, pub(crate) windows: SlotMap<WindowId, Option<Window>>,
pub(crate) keymap: Arc<RwLock<Keymap>>, pub(crate) keymap: Arc<RwLock<Keymap>>,
pub(crate) global_action_listeners:
HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self) + Send + Sync>>>,
action_builders: HashMap<SharedString, ActionBuilder>, action_builders: HashMap<SharedString, ActionBuilder>,
pub(crate) pending_notifications: HashSet<EntityId>, pub(crate) pending_notifications: HashSet<EntityId>,
pending_effects: VecDeque<Effect>, pending_effects: VecDeque<Effect>,
@ -175,6 +180,7 @@ pub struct AppContext {
pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>, pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>,
pub(crate) release_handlers: SubscriberSet<EntityId, ReleaseHandler>, pub(crate) release_handlers: SubscriberSet<EntityId, ReleaseHandler>,
pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests. pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
pub(crate) propagate_event: bool,
} }
impl AppContext { impl AppContext {
@ -508,6 +514,21 @@ impl AppContext {
self.push_effect(Effect::Refresh); self.push_effect(Effect::Refresh);
} }
pub fn on_action<A: Action>(
&mut self,
listener: impl Fn(&A, &mut Self) + Send + Sync + 'static,
) {
self.global_action_listeners
.entry(TypeId::of::<A>())
.or_default()
.push(Box::new(move |action, phase, cx| {
if phase == DispatchPhase::Bubble {
let action = action.as_any().downcast_ref().unwrap();
listener(action, cx)
}
}));
}
pub fn register_action_type<A: Action>(&mut self) { pub fn register_action_type<A: Action>(&mut self) {
self.action_builders.insert(A::qualified_name(), A::build); self.action_builders.insert(A::qualified_name(), A::build);
} }
@ -523,6 +544,10 @@ impl AppContext {
.ok_or_else(|| anyhow!("no action type registered for {}", name))?; .ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build)(params) (build)(params)
} }
pub fn stop_propagation(&mut self) {
self.propagate_event = false;
}
} }
impl Context for AppContext { impl Context for AppContext {
@ -610,6 +635,18 @@ impl MainThread<AppContext> {
self.platform().activate(ignoring_other_apps); self.platform().activate(ignoring_other_apps);
} }
pub fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
self.platform().write_credentials(url, username, password)
}
pub fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
self.platform().read_credentials(url)
}
pub fn delete_credentials(&self, url: &str) -> Result<()> {
self.platform().delete_credentials(url)
}
pub fn open_window<S: 'static + Send + Sync>( pub fn open_window<S: 'static + Send + Sync>(
&mut self, &mut self,
options: crate::WindowOptions, options: crate::WindowOptions,

View file

@ -1,8 +1,9 @@
use crate::{ use crate::{
AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, Task, ViewContext, AnyWindowHandle, AppContext, Context, Executor, Handle, MainThread, ModelContext, Result, Task,
WindowContext, ViewContext, WindowContext,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use derive_more::{Deref, DerefMut};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{future::Future, sync::Weak}; use std::{future::Future, sync::Weak};
@ -75,6 +76,15 @@ impl Context for AsyncAppContext {
} }
impl AsyncAppContext { impl AsyncAppContext {
pub fn executor(&self) -> Result<Executor> {
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let lock = app.lock(); // Need this to compile
Ok(lock.executor().clone())
}
pub fn read_window<R>( pub fn read_window<R>(
&self, &self,
handle: AnyWindowHandle, handle: AnyWindowHandle,
@ -116,10 +126,27 @@ impl AsyncAppContext {
let app_context = app.lock(); let app_context = app.lock();
Ok(app_context.spawn(f)) Ok(app_context.spawn(f))
} }
pub fn run_on_main<R>(
&self,
f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
) -> Result<Task<R>>
where
R: Send + 'static,
{
let app = self
.0
.upgrade()
.ok_or_else(|| anyhow!("app was released"))?;
let mut app_context = app.lock();
Ok(app_context.run_on_main(f))
}
} }
#[derive(Clone)] #[derive(Clone, Deref, DerefMut)]
pub struct AsyncWindowContext { pub struct AsyncWindowContext {
#[deref]
#[deref_mut]
app: AsyncAppContext, app: AsyncAppContext,
window: AnyWindowHandle, window: AnyWindowHandle,
} }

View file

@ -4,7 +4,7 @@ use derive_more::{Deref, DerefMut};
use parking_lot::{RwLock, RwLockUpgradableReadGuard}; use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use slotmap::{SecondaryMap, SlotMap}; use slotmap::{SecondaryMap, SlotMap};
use std::{ use std::{
any::Any, any::{Any, TypeId},
marker::PhantomData, marker::PhantomData,
mem, mem,
sync::{ sync::{
@ -70,9 +70,12 @@ impl EntityMap {
pub fn weak_handle<T: 'static + Send + Sync>(&self, id: EntityId) -> WeakHandle<T> { pub fn weak_handle<T: 'static + Send + Sync>(&self, id: EntityId) -> WeakHandle<T> {
WeakHandle { WeakHandle {
id, any_handle: AnyWeakHandle {
id,
entity_type: TypeId::of::<T>(),
entity_map: Arc::downgrade(&self.0),
},
entity_type: PhantomData, entity_type: PhantomData,
entity_map: Arc::downgrade(&self.0),
} }
} }
@ -112,44 +115,45 @@ impl<T> Drop for Lease<T> {
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]
pub struct Slot<T: Send + Sync + 'static>(Handle<T>); pub struct Slot<T: Send + Sync + 'static>(Handle<T>);
pub struct Handle<T: Send + Sync> { pub struct AnyHandle {
pub(crate) id: EntityId, pub(crate) id: EntityId,
entity_type: PhantomData<T>, entity_type: TypeId,
entity_map: Weak<RwLock<EntityMapState>>, entity_map: Weak<RwLock<EntityMapState>>,
} }
impl<T: 'static + Send + Sync> Handle<T> { impl AnyHandle {
fn new(id: EntityId, entity_map: Weak<RwLock<EntityMapState>>) -> Self { fn new(id: EntityId, entity_type: TypeId, entity_map: Weak<RwLock<EntityMapState>>) -> Self {
Self { Self {
id, id,
entity_type: PhantomData, entity_type,
entity_map, entity_map,
} }
} }
pub fn downgrade(&self) -> WeakHandle<T> { pub fn downgrade(&self) -> AnyWeakHandle {
WeakHandle { AnyWeakHandle {
id: self.id, id: self.id,
entity_type: self.entity_type, entity_type: self.entity_type,
entity_map: self.entity_map.clone(), entity_map: self.entity_map.clone(),
} }
} }
/// Update the entity referenced by this handle with the given function. pub fn downcast<T>(&self) -> Option<Handle<T>>
/// where
/// The update function receives a context appropriate for its environment. T: 'static + Send + Sync,
/// When updating in an `AppContext`, it receives a `ModelContext`. {
/// When updating an a `WindowContext`, it receives a `ViewContext`. if TypeId::of::<T>() == self.entity_type {
pub fn update<C: Context, R>( Some(Handle {
&self, any_handle: self.clone(),
cx: &mut C, entity_type: PhantomData,
update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R, })
) -> C::Result<R> { } else {
cx.update_entity(self, update) None
}
} }
} }
impl<T: Send + Sync> Clone for Handle<T> { impl Clone for AnyHandle {
fn clone(&self) -> Self { fn clone(&self) -> Self {
if let Some(entity_map) = self.entity_map.upgrade() { if let Some(entity_map) = self.entity_map.upgrade() {
let entity_map = entity_map.read(); let entity_map = entity_map.read();
@ -163,13 +167,13 @@ impl<T: Send + Sync> Clone for Handle<T> {
Self { Self {
id: self.id, id: self.id,
entity_type: PhantomData, entity_type: self.entity_type,
entity_map: self.entity_map.clone(), entity_map: self.entity_map.clone(),
} }
} }
} }
impl<T: Send + Sync> Drop for Handle<T> { impl Drop for AnyHandle {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(entity_map) = self.entity_map.upgrade() { if let Some(entity_map) = self.entity_map.upgrade() {
let entity_map = entity_map.upgradable_read(); let entity_map = entity_map.upgradable_read();
@ -193,36 +197,117 @@ impl<T: Send + Sync> Drop for Handle<T> {
} }
} }
pub struct WeakHandle<T> { impl<T> From<Handle<T>> for AnyHandle
pub(crate) id: EntityId, where
entity_type: PhantomData<T>, T: 'static + Send + Sync,
entity_map: Weak<RwLock<EntityMapState>>, {
fn from(handle: Handle<T>) -> Self {
handle.any_handle
}
} }
impl<T: 'static + Send + Sync> Clone for WeakHandle<T> { #[derive(Deref, DerefMut)]
pub struct Handle<T: Send + Sync> {
#[deref]
#[deref_mut]
any_handle: AnyHandle,
entity_type: PhantomData<T>,
}
impl<T: 'static + Send + Sync> Handle<T> {
fn new(id: EntityId, entity_map: Weak<RwLock<EntityMapState>>) -> Self {
Self {
any_handle: AnyHandle::new(id, TypeId::of::<T>(), entity_map),
entity_type: PhantomData,
}
}
pub fn downgrade(&self) -> WeakHandle<T> {
WeakHandle {
any_handle: self.any_handle.downgrade(),
entity_type: self.entity_type,
}
}
/// Update the entity referenced by this handle with the given function.
///
/// The update function receives a context appropriate for its environment.
/// When updating in an `AppContext`, it receives a `ModelContext`.
/// When updating an a `WindowContext`, it receives a `ViewContext`.
pub fn update<C: Context, R>(
&self,
cx: &mut C,
update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
) -> C::Result<R> {
cx.update_entity(self, update)
}
}
impl<T: Send + Sync> Clone for Handle<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
id: self.id, any_handle: self.any_handle.clone(),
entity_type: self.entity_type, entity_type: self.entity_type,
entity_map: self.entity_map.clone(),
} }
} }
} }
impl<T: Send + Sync + 'static> WeakHandle<T> { #[derive(Clone)]
pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> { pub struct AnyWeakHandle {
pub(crate) id: EntityId,
entity_type: TypeId,
entity_map: Weak<RwLock<EntityMapState>>,
}
impl AnyWeakHandle {
pub fn upgrade(&self) -> Option<AnyHandle> {
let entity_map = &self.entity_map.upgrade()?; let entity_map = &self.entity_map.upgrade()?;
entity_map entity_map
.read() .read()
.ref_counts .ref_counts
.get(self.id)? .get(self.id)?
.fetch_add(1, SeqCst); .fetch_add(1, SeqCst);
Some(Handle { Some(AnyHandle {
id: self.id, id: self.id,
entity_type: self.entity_type, entity_type: self.entity_type,
entity_map: self.entity_map.clone(), entity_map: self.entity_map.clone(),
}) })
} }
}
impl<T> From<WeakHandle<T>> for AnyWeakHandle
where
T: 'static + Send + Sync,
{
fn from(handle: WeakHandle<T>) -> Self {
handle.any_handle
}
}
#[derive(Deref, DerefMut)]
pub struct WeakHandle<T> {
#[deref]
#[deref_mut]
any_handle: AnyWeakHandle,
entity_type: PhantomData<T>,
}
impl<T: 'static + Send + Sync> Clone for WeakHandle<T> {
fn clone(&self) -> Self {
Self {
any_handle: self.any_handle.clone(),
entity_type: self.entity_type,
}
}
}
impl<T: Send + Sync + 'static> WeakHandle<T> {
pub fn upgrade(&self) -> Option<Handle<T>> {
Some(Handle {
any_handle: self.any_handle.upgrade()?,
entity_type: self.entity_type,
})
}
/// Update the entity referenced by this handle with the given function if /// Update the entity referenced by this handle with the given function if
/// the referenced entity still exists. Returns an error if the entity has /// the referenced entity still exists. Returns an error if the entity has
@ -240,7 +325,7 @@ impl<T: Send + Sync + 'static> WeakHandle<T> {
Result<C::Result<R>>: crate::Flatten<R>, Result<C::Result<R>>: crate::Flatten<R>,
{ {
crate::Flatten::flatten( crate::Flatten::flatten(
self.upgrade(cx) self.upgrade()
.ok_or_else(|| anyhow!("entity release")) .ok_or_else(|| anyhow!("entity release"))
.map(|this| cx.update_entity(&this, update)), .map(|this| cx.update_entity(&this, update)),
) )

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
AppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference, Subscription, AppContext, Context, Effect, EntityId, EventEmitter, Executor, Handle, Reference, Subscription,
WeakHandle, WeakHandle,
}; };
use std::marker::PhantomData; use std::marker::PhantomData;
@ -51,7 +51,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
self.app.observers.insert( self.app.observers.insert(
handle.id, handle.id,
Box::new(move |cx| { Box::new(move |cx| {
if let Some((this, handle)) = this.upgrade(cx).zip(handle.upgrade(cx)) { if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
this.update(cx, |this, cx| on_notify(this, handle, cx)); this.update(cx, |this, cx| on_notify(this, handle, cx));
true true
} else { } else {
@ -75,7 +75,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
handle.id, handle.id,
Box::new(move |event, cx| { Box::new(move |event, cx| {
let event = event.downcast_ref().expect("invalid event type"); let event = event.downcast_ref().expect("invalid event type");
if let Some((this, handle)) = this.upgrade(cx).zip(handle.upgrade(cx)) { if let Some((this, handle)) = this.upgrade().zip(handle.upgrade()) {
this.update(cx, |this, cx| on_event(this, handle, event, cx)); this.update(cx, |this, cx| on_event(this, handle, event, cx));
true true
} else { } else {
@ -108,7 +108,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
handle.id, handle.id,
Box::new(move |entity, cx| { Box::new(move |entity, cx| {
let entity = entity.downcast_mut().expect("invalid entity type"); let entity = entity.downcast_mut().expect("invalid entity type");
if let Some(this) = this.upgrade(cx) { if let Some(this) = this.upgrade() {
this.update(cx, |this, cx| on_release(this, entity, cx)); this.update(cx, |this, cx| on_release(this, entity, cx));
} }
}), }),

View file

@ -8,6 +8,7 @@ use std::{
pin::Pin, pin::Pin,
sync::Arc, sync::Arc,
task::{Context, Poll}, task::{Context, Poll},
time::Duration,
}; };
use util::TryFutureExt; use util::TryFutureExt;
@ -151,6 +152,11 @@ impl Executor {
} }
} }
pub fn timer(&self, duration: Duration) -> smol::Timer {
// todo!("integrate with deterministic dispatcher")
smol::Timer::after(duration)
}
pub fn is_main_thread(&self) -> bool { pub fn is_main_thread(&self) -> bool {
self.dispatcher.is_main_thread() self.dispatcher.is_main_thread()
} }

View file

@ -1,13 +1,13 @@
use crate::{ use crate::{
px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext,
DisplayId, Edges, Effect, Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId, DisplayId, Edges, Effect, Element, EntityId, EventEmitter, Executor, FocusEvent, FontId,
GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, GlobalElementId, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, KeyMatcher, Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent,
Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, Path, Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad,
RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder,
SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline,
WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
}; };
use anyhow::Result; use anyhow::Result;
use collections::HashMap; use collections::HashMap;
@ -167,7 +167,6 @@ pub struct Window {
focus_parents_by_child: HashMap<FocusId, FocusId>, focus_parents_by_child: HashMap<FocusId, FocusId>,
pub(crate) focus_listeners: Vec<AnyFocusListener>, pub(crate) focus_listeners: Vec<AnyFocusListener>,
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>, pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
propagate: bool,
default_prevented: bool, default_prevented: bool,
mouse_position: Point<Pixels>, mouse_position: Point<Pixels>,
scale_factor: f32, scale_factor: f32,
@ -243,7 +242,6 @@ impl Window {
focus_parents_by_child: HashMap::default(), focus_parents_by_child: HashMap::default(),
focus_listeners: Vec::new(), focus_listeners: Vec::new(),
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())), focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
propagate: true,
default_prevented: true, default_prevented: true,
mouse_position, mouse_position,
scale_factor, scale_factor,
@ -302,6 +300,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
} }
} }
pub fn window_handle(&self) -> AnyWindowHandle {
self.window.handle
}
pub fn notify(&mut self) { pub fn notify(&mut self) {
self.window.dirty = true; self.window.dirty = true;
} }
@ -477,10 +479,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
.to_pixels(text_style.font_size.into(), rem_size) .to_pixels(text_style.font_size.into(), rem_size)
} }
pub fn stop_propagation(&mut self) {
self.window.propagate = false;
}
pub fn prevent_default(&mut self) { pub fn prevent_default(&mut self) {
self.window.default_prevented = true; self.window.default_prevented = true;
} }
@ -878,7 +876,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
} }
// Handlers may set this to false by calling `stop_propagation` // Handlers may set this to false by calling `stop_propagation`
self.window.propagate = true; self.app.propagate_event = true;
self.window.default_prevented = false; self.window.default_prevented = false;
if let Some(mut handlers) = self if let Some(mut handlers) = self
@ -893,16 +891,16 @@ impl<'a, 'w> WindowContext<'a, 'w> {
// special purposes, such as detecting events outside of a given Bounds. // special purposes, such as detecting events outside of a given Bounds.
for (_, handler) in &handlers { for (_, handler) in &handlers {
handler(any_mouse_event, DispatchPhase::Capture, self); handler(any_mouse_event, DispatchPhase::Capture, self);
if !self.window.propagate { if !self.app.propagate_event {
break; break;
} }
} }
// Bubble phase, where most normal handlers do their work. // Bubble phase, where most normal handlers do their work.
if self.window.propagate { if self.app.propagate_event {
for (_, handler) in handlers.iter().rev() { for (_, handler) in handlers.iter().rev() {
handler(any_mouse_event, DispatchPhase::Bubble, self); handler(any_mouse_event, DispatchPhase::Bubble, self);
if !self.window.propagate { if !self.app.propagate_event {
break; break;
} }
} }
@ -940,7 +938,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
) { ) {
self.dispatch_action(action, &key_dispatch_stack[..ix]); self.dispatch_action(action, &key_dispatch_stack[..ix]);
} }
if !self.window.propagate { if !self.app.propagate_event {
break; break;
} }
} }
@ -951,7 +949,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
} }
} }
if self.window.propagate { if self.app.propagate_event {
for (ix, frame) in key_dispatch_stack.iter().enumerate().rev() { for (ix, frame) in key_dispatch_stack.iter().enumerate().rev() {
match frame { match frame {
KeyDispatchStackFrame::Listener { KeyDispatchStackFrame::Listener {
@ -968,7 +966,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
self.dispatch_action(action, &key_dispatch_stack[..ix]); self.dispatch_action(action, &key_dispatch_stack[..ix]);
} }
if !self.window.propagate { if !self.app.propagate_event {
break; break;
} }
} }
@ -1015,22 +1013,41 @@ impl<'a, 'w> WindowContext<'a, 'w> {
dispatch_stack: &[KeyDispatchStackFrame], dispatch_stack: &[KeyDispatchStackFrame],
) { ) {
let action_type = action.as_any().type_id(); let action_type = action.as_any().type_id();
for stack_frame in dispatch_stack {
if let KeyDispatchStackFrame::Listener { if let Some(mut global_listeners) = self.app.global_action_listeners.remove(&action_type) {
event_type, for listener in &global_listeners {
listener, listener(action.as_ref(), DispatchPhase::Capture, self);
} = stack_frame if !self.app.propagate_event {
{ break;
if action_type == *event_type { }
listener(action.as_any(), &[], DispatchPhase::Capture, self); }
if !self.window.propagate { global_listeners.extend(
break; self.global_action_listeners
.remove(&action_type)
.unwrap_or_default(),
);
self.global_action_listeners
.insert(action_type, global_listeners);
}
if self.app.propagate_event {
for stack_frame in dispatch_stack {
if let KeyDispatchStackFrame::Listener {
event_type,
listener,
} = stack_frame
{
if action_type == *event_type {
listener(action.as_any(), &[], DispatchPhase::Capture, self);
if !self.app.propagate_event {
break;
}
} }
} }
} }
} }
if self.window.propagate { if self.app.propagate_event {
for stack_frame in dispatch_stack.iter().rev() { for stack_frame in dispatch_stack.iter().rev() {
if let KeyDispatchStackFrame::Listener { if let KeyDispatchStackFrame::Listener {
event_type, event_type,
@ -1039,13 +1056,33 @@ impl<'a, 'w> WindowContext<'a, 'w> {
{ {
if action_type == *event_type { if action_type == *event_type {
listener(action.as_any(), &[], DispatchPhase::Bubble, self); listener(action.as_any(), &[], DispatchPhase::Bubble, self);
if !self.window.propagate { if !self.app.propagate_event {
break; break;
} }
} }
} }
} }
} }
if self.app.propagate_event {
if let Some(mut global_listeners) =
self.app.global_action_listeners.remove(&action_type)
{
for listener in global_listeners.iter().rev() {
listener(action.as_ref(), DispatchPhase::Bubble, self);
if !self.app.propagate_event {
break;
}
}
global_listeners.extend(
self.global_action_listeners
.remove(&action_type)
.unwrap_or_default(),
);
self.global_action_listeners
.insert(action_type, global_listeners);
}
}
} }
} }
@ -1313,7 +1350,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
handle.id, handle.id,
Box::new(move |cx| { Box::new(move |cx| {
cx.update_window(window_handle.id, |cx| { cx.update_window(window_handle.id, |cx| {
if let Some(handle) = handle.upgrade(cx) { if let Some(handle) = handle.upgrade() {
this.update(cx, |this, cx| on_notify(this, handle, cx)) this.update(cx, |this, cx| on_notify(this, handle, cx))
.is_ok() .is_ok()
} else { } else {
@ -1340,7 +1377,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
handle.id, handle.id,
Box::new(move |event, cx| { Box::new(move |event, cx| {
cx.update_window(window_handle.id, |cx| { cx.update_window(window_handle.id, |cx| {
if let Some(handle) = handle.upgrade(cx) { if let Some(handle) = handle.upgrade() {
let event = event.downcast_ref().expect("invalid event type"); let event = event.downcast_ref().expect("invalid event type");
this.update(cx, |this, cx| on_event(this, handle, event, cx)) this.update(cx, |this, cx| on_event(this, handle, event, cx))
.is_ok() .is_ok()
@ -1504,7 +1541,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) }; let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) };
Task::ready(Ok(f(view, cx))) Task::ready(Ok(f(view, cx)))
} else { } else {
let handle = self.handle().upgrade(self).unwrap(); let handle = self.handle().upgrade().unwrap();
self.window_cx.run_on_main(move |cx| handle.update(cx, f)) self.window_cx.run_on_main(move |cx| handle.update(cx, f))
} }
} }
@ -1528,7 +1565,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
&mut self, &mut self,
handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static, handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static,
) { ) {
let handle = self.handle().upgrade(self).unwrap(); let handle = self.handle().upgrade().unwrap();
self.window_cx.on_mouse_event(move |event, phase, cx| { self.window_cx.on_mouse_event(move |event, phase, cx| {
handle.update(cx, |view, cx| { handle.update(cx, |view, cx| {
handler(view, event, phase, cx); handler(view, event, phase, cx);