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",
"feature_flags",
"futures 0.3.28",
"gpui",
"gpui2",
"image",
"lazy_static",
@ -1473,6 +1472,7 @@ dependencies = [
"serde",
"serde_derive",
"settings",
"settings2",
"smol",
"sum_tree",
"sysinfo",

View File

@ -9,7 +9,7 @@ path = "src/client2.rs"
doctest = false
[features]
test-support = ["collections/test-support", "gpui/test-support", "rpc/test-support"]
test-support = ["collections/test-support", "gpui2/test-support", "rpc/test-support"]
[dependencies]
collections = { path = "../collections" }
@ -18,7 +18,7 @@ gpui2 = { path = "../gpui2" }
util = { path = "../util" }
rpc = { path = "../rpc" }
text = { path = "../text" }
settings = { path = "../settings" }
settings2 = { path = "../settings2" }
feature_flags = { path = "../feature_flags" }
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 gpui::{executor::Background, serde_json, AppContext, Task};
use gpui2::{serde_json, AppContext, Executor, Task};
use lazy_static::lazy_static;
use parking_lot::Mutex;
use serde::Serialize;
@ -11,7 +11,7 @@ use util::{channel::ReleaseChannel, TryFutureExt};
pub struct Telemetry {
http_client: Arc<dyn HttpClient>,
executor: Arc<Background>,
executor: Executor,
state: Mutex<TelemetryState>,
}

View File

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

View File

@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result};
use collections::{hash_map::Entry, HashMap, HashSet};
use feature_flags::FeatureFlagAppExt;
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 rpc::proto::{RequestMessage, UsersResponse};
use std::sync::{Arc, Weak};
@ -103,7 +103,7 @@ pub enum ContactEventKind {
Cancelled,
}
impl Entity for UserStore {
impl EventEmitter for UserStore {
type Event = Event;
}
@ -217,7 +217,7 @@ impl UserStore {
}
async fn handle_update_invite_info(
this: ModelHandle<Self>,
this: Handle<Self>,
message: TypedEnvelope<proto::UpdateInviteInfo>,
_: Arc<Client>,
mut cx: AsyncAppContext,
@ -233,7 +233,7 @@ impl UserStore {
}
async fn handle_show_contacts(
this: ModelHandle<Self>,
this: Handle<Self>,
_: TypedEnvelope<proto::ShowContacts>,
_: Arc<Client>,
mut cx: AsyncAppContext,
@ -247,7 +247,7 @@ impl UserStore {
}
async fn handle_update_contacts(
this: ModelHandle<Self>,
this: Handle<Self>,
message: TypedEnvelope<proto::UpdateContacts>,
_: Arc<Client>,
mut cx: AsyncAppContext,
@ -689,7 +689,7 @@ impl User {
impl Contact {
async fn from_proto(
contact: proto::Contact,
user_store: &ModelHandle<UserStore>,
user_store: &Handle<UserStore>,
cx: &mut AsyncAppContext,
) -> Result<Self> {
let user = user_store

View File

@ -9,10 +9,11 @@ use refineable::Refineable;
use smallvec::SmallVec;
use crate::{
current_platform, image_cache::ImageCache, Action, AssetSource, Context, DisplayId, Executor,
FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly,
Platform, SemanticVersion, SharedString, SubscriberSet, SvgRenderer, Task, TextStyle,
TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
current_platform, image_cache::ImageCache, Action, AssetSource, Context, DispatchPhase,
DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId,
MainThread, MainThreadOnly, Platform, SemanticVersion, SharedString, SubscriberSet,
SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext,
WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@ -67,6 +68,7 @@ impl App {
entities,
windows: SlotMap::with_key(),
keymap: Arc::new(RwLock::new(Keymap::default())),
global_action_listeners: HashMap::default(),
action_builders: HashMap::default(),
pending_notifications: Default::default(),
pending_effects: Default::default(),
@ -74,6 +76,7 @@ impl App {
event_handlers: SubscriberSet::new(),
release_handlers: SubscriberSet::new(),
layout_id_buffer: Default::default(),
propagate_event: true,
})
}))
}
@ -168,6 +171,8 @@ pub struct AppContext {
pub(crate) entities: EntityMap,
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
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>,
pub(crate) pending_notifications: HashSet<EntityId>,
pending_effects: VecDeque<Effect>,
@ -175,6 +180,7 @@ pub struct AppContext {
pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>,
pub(crate) release_handlers: SubscriberSet<EntityId, ReleaseHandler>,
pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
pub(crate) propagate_event: bool,
}
impl AppContext {
@ -508,6 +514,21 @@ impl AppContext {
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) {
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))?;
(build)(params)
}
pub fn stop_propagation(&mut self) {
self.propagate_event = false;
}
}
impl Context for AppContext {
@ -610,6 +635,18 @@ impl MainThread<AppContext> {
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>(
&mut self,
options: crate::WindowOptions,

View File

@ -1,8 +1,9 @@
use crate::{
AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, Task, ViewContext,
WindowContext,
AnyWindowHandle, AppContext, Context, Executor, Handle, MainThread, ModelContext, Result, Task,
ViewContext, WindowContext,
};
use anyhow::anyhow;
use derive_more::{Deref, DerefMut};
use parking_lot::Mutex;
use std::{future::Future, sync::Weak};
@ -75,6 +76,15 @@ impl Context for 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>(
&self,
handle: AnyWindowHandle,
@ -116,10 +126,27 @@ impl AsyncAppContext {
let app_context = app.lock();
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 {
#[deref]
#[deref_mut]
app: AsyncAppContext,
window: AnyWindowHandle,
}

View File

@ -4,7 +4,7 @@ use derive_more::{Deref, DerefMut};
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use slotmap::{SecondaryMap, SlotMap};
use std::{
any::Any,
any::{Any, TypeId},
marker::PhantomData,
mem,
sync::{
@ -70,9 +70,12 @@ impl EntityMap {
pub fn weak_handle<T: 'static + Send + Sync>(&self, id: EntityId) -> WeakHandle<T> {
WeakHandle {
id,
any_handle: AnyWeakHandle {
id,
entity_type: TypeId::of::<T>(),
entity_map: Arc::downgrade(&self.0),
},
entity_type: PhantomData,
entity_map: Arc::downgrade(&self.0),
}
}
@ -112,44 +115,45 @@ impl<T> Drop for Lease<T> {
#[derive(Deref, DerefMut)]
pub struct Slot<T: Send + Sync + 'static>(Handle<T>);
pub struct Handle<T: Send + Sync> {
pub struct AnyHandle {
pub(crate) id: EntityId,
entity_type: PhantomData<T>,
entity_type: TypeId,
entity_map: Weak<RwLock<EntityMapState>>,
}
impl<T: 'static + Send + Sync> Handle<T> {
fn new(id: EntityId, entity_map: Weak<RwLock<EntityMapState>>) -> Self {
impl AnyHandle {
fn new(id: EntityId, entity_type: TypeId, entity_map: Weak<RwLock<EntityMapState>>) -> Self {
Self {
id,
entity_type: PhantomData,
entity_type,
entity_map,
}
}
pub fn downgrade(&self) -> WeakHandle<T> {
WeakHandle {
pub fn downgrade(&self) -> AnyWeakHandle {
AnyWeakHandle {
id: self.id,
entity_type: self.entity_type,
entity_map: self.entity_map.clone(),
}
}
/// 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)
pub fn downcast<T>(&self) -> Option<Handle<T>>
where
T: 'static + Send + Sync,
{
if TypeId::of::<T>() == self.entity_type {
Some(Handle {
any_handle: self.clone(),
entity_type: PhantomData,
})
} else {
None
}
}
}
impl<T: Send + Sync> Clone for Handle<T> {
impl Clone for AnyHandle {
fn clone(&self) -> Self {
if let Some(entity_map) = self.entity_map.upgrade() {
let entity_map = entity_map.read();
@ -163,13 +167,13 @@ impl<T: Send + Sync> Clone for Handle<T> {
Self {
id: self.id,
entity_type: PhantomData,
entity_type: self.entity_type,
entity_map: self.entity_map.clone(),
}
}
}
impl<T: Send + Sync> Drop for Handle<T> {
impl Drop for AnyHandle {
fn drop(&mut self) {
if let Some(entity_map) = self.entity_map.upgrade() {
let entity_map = entity_map.upgradable_read();
@ -193,36 +197,117 @@ impl<T: Send + Sync> Drop for Handle<T> {
}
}
pub struct WeakHandle<T> {
pub(crate) id: EntityId,
entity_type: PhantomData<T>,
entity_map: Weak<RwLock<EntityMapState>>,
impl<T> From<Handle<T>> for AnyHandle
where
T: 'static + Send + Sync,
{
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 {
Self {
id: self.id,
any_handle: self.any_handle.clone(),
entity_type: self.entity_type,
entity_map: self.entity_map.clone(),
}
}
}
impl<T: Send + Sync + 'static> WeakHandle<T> {
pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
#[derive(Clone)]
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()?;
entity_map
.read()
.ref_counts
.get(self.id)?
.fetch_add(1, SeqCst);
Some(Handle {
Some(AnyHandle {
id: self.id,
entity_type: self.entity_type,
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
/// 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>,
{
crate::Flatten::flatten(
self.upgrade(cx)
self.upgrade()
.ok_or_else(|| anyhow!("entity release"))
.map(|this| cx.update_entity(&this, update)),
)

View File

@ -1,5 +1,5 @@
use crate::{
AppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference, Subscription,
AppContext, Context, Effect, EntityId, EventEmitter, Executor, Handle, Reference, Subscription,
WeakHandle,
};
use std::marker::PhantomData;
@ -51,7 +51,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
self.app.observers.insert(
handle.id,
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));
true
} else {
@ -75,7 +75,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
handle.id,
Box::new(move |event, cx| {
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));
true
} else {
@ -108,7 +108,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
handle.id,
Box::new(move |entity, cx| {
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));
}
}),

View File

@ -8,6 +8,7 @@ use std::{
pin::Pin,
sync::Arc,
task::{Context, Poll},
time::Duration,
};
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 {
self.dispatcher.is_main_thread()
}

View File

@ -1,13 +1,13 @@
use crate::{
px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext,
DisplayId, Edges, Effect, Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId,
GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher,
Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path,
Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference,
RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow,
SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle,
WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
DisplayId, Edges, Effect, Element, EntityId, EventEmitter, Executor, FocusEvent, FontId,
GlobalElementId, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
KeyMatcher, Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent,
Path, Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad,
Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder,
Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline,
UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::Result;
use collections::HashMap;
@ -167,7 +167,6 @@ pub struct Window {
focus_parents_by_child: HashMap<FocusId, FocusId>,
pub(crate) focus_listeners: Vec<AnyFocusListener>,
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
propagate: bool,
default_prevented: bool,
mouse_position: Point<Pixels>,
scale_factor: f32,
@ -243,7 +242,6 @@ impl Window {
focus_parents_by_child: HashMap::default(),
focus_listeners: Vec::new(),
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
propagate: true,
default_prevented: true,
mouse_position,
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) {
self.window.dirty = true;
}
@ -477,10 +479,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
.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) {
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`
self.window.propagate = true;
self.app.propagate_event = true;
self.window.default_prevented = false;
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.
for (_, handler) in &handlers {
handler(any_mouse_event, DispatchPhase::Capture, self);
if !self.window.propagate {
if !self.app.propagate_event {
break;
}
}
// Bubble phase, where most normal handlers do their work.
if self.window.propagate {
if self.app.propagate_event {
for (_, handler) in handlers.iter().rev() {
handler(any_mouse_event, DispatchPhase::Bubble, self);
if !self.window.propagate {
if !self.app.propagate_event {
break;
}
}
@ -940,7 +938,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
) {
self.dispatch_action(action, &key_dispatch_stack[..ix]);
}
if !self.window.propagate {
if !self.app.propagate_event {
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() {
match frame {
KeyDispatchStackFrame::Listener {
@ -968,7 +966,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
self.dispatch_action(action, &key_dispatch_stack[..ix]);
}
if !self.window.propagate {
if !self.app.propagate_event {
break;
}
}
@ -1015,22 +1013,41 @@ impl<'a, 'w> WindowContext<'a, 'w> {
dispatch_stack: &[KeyDispatchStackFrame],
) {
let action_type = action.as_any().type_id();
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.window.propagate {
break;
if let Some(mut global_listeners) = self.app.global_action_listeners.remove(&action_type) {
for listener in &global_listeners {
listener(action.as_ref(), DispatchPhase::Capture, 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);
}
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() {
if let KeyDispatchStackFrame::Listener {
event_type,
@ -1039,13 +1056,33 @@ impl<'a, 'w> WindowContext<'a, 'w> {
{
if action_type == *event_type {
listener(action.as_any(), &[], DispatchPhase::Bubble, self);
if !self.window.propagate {
if !self.app.propagate_event {
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,
Box::new(move |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))
.is_ok()
} else {
@ -1340,7 +1377,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
handle.id,
Box::new(move |event, 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");
this.update(cx, |this, cx| on_event(this, handle, event, cx))
.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) };
Task::ready(Ok(f(view, cx)))
} 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))
}
}
@ -1528,7 +1565,7 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> {
&mut self,
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| {
handle.update(cx, |view, cx| {
handler(view, event, phase, cx);