Get workspace compiling with new event emitters

This commit is contained in:
Mikayla 2023-11-08 19:29:00 -08:00
parent 26fc36ee0e
commit a97c8bf58f
No known key found for this signature in database
24 changed files with 224 additions and 319 deletions

1
Cargo.lock generated
View file

@ -6109,7 +6109,6 @@ dependencies = [
"settings2", "settings2",
"theme2", "theme2",
"util", "util",
"workspace2",
] ]
[[package]] [[package]]

View file

@ -17,6 +17,7 @@ use gpui::{
}; };
use postage::watch; use postage::watch;
use project::Project; use project::Project;
use room::Event;
use settings::Settings; use settings::Settings;
use std::sync::Arc; use std::sync::Arc;
@ -85,9 +86,7 @@ pub struct ActiveCall {
_subscriptions: Vec<client::Subscription>, _subscriptions: Vec<client::Subscription>,
} }
impl EventEmitter for ActiveCall { impl EventEmitter<Event> for ActiveCall {}
type Event = room::Event;
}
impl ActiveCall { impl ActiveCall {
fn new(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut ModelContext<Self>) -> Self { fn new(client: Arc<Client>, user_store: Model<UserStore>, cx: &mut ModelContext<Self>) -> Self {

View file

@ -79,9 +79,7 @@ pub struct Room {
maintain_connection: Option<Task<Option<()>>>, maintain_connection: Option<Task<Option<()>>>,
} }
impl EventEmitter for Room { impl EventEmitter<Event> for Room {}
type Event = Event;
}
impl Room { impl Room {
pub fn channel_id(&self) -> Option<u64> { pub fn channel_id(&self) -> Option<u64> {

View file

@ -38,9 +38,7 @@ pub enum ChannelBufferEvent {
ChannelChanged, ChannelChanged,
} }
impl EventEmitter for ChannelBuffer { impl EventEmitter<ChannelBufferEvent> for ChannelBuffer {}
type Event = ChannelBufferEvent;
}
impl ChannelBuffer { impl ChannelBuffer {
pub(crate) async fn new( pub(crate) async fn new(

View file

@ -76,9 +76,7 @@ pub enum ChannelChatEvent {
}, },
} }
impl EventEmitter for ChannelChat { impl EventEmitter<ChannelChatEvent> for ChannelChat {}
type Event = ChannelChatEvent;
}
pub fn init(client: &Arc<Client>) { pub fn init(client: &Arc<Client>) {
client.add_model_message_handler(ChannelChat::handle_message_sent); client.add_model_message_handler(ChannelChat::handle_message_sent);
client.add_model_message_handler(ChannelChat::handle_message_removed); client.add_model_message_handler(ChannelChat::handle_message_removed);

View file

@ -114,9 +114,7 @@ pub enum ChannelEvent {
ChannelRenamed(ChannelId), ChannelRenamed(ChannelId),
} }
impl EventEmitter for ChannelStore { impl EventEmitter<ChannelEvent> for ChannelStore {}
type Event = ChannelEvent;
}
enum OpenedModelHandle<E> { enum OpenedModelHandle<E> {
Open(WeakModel<E>), Open(WeakModel<E>),

View file

@ -103,9 +103,7 @@ pub enum ContactEventKind {
Cancelled, Cancelled,
} }
impl EventEmitter for UserStore { impl EventEmitter<Event> for UserStore {}
type Event = Event;
}
enum UpdateContacts { enum UpdateContacts {
Update(proto::UpdateContacts), Update(proto::UpdateContacts),

View file

@ -284,9 +284,7 @@ pub enum Event {
CopilotLanguageServerStarted, CopilotLanguageServerStarted,
} }
impl EventEmitter for Copilot { impl EventEmitter<Event> for Copilot {}
type Event = Event;
}
impl Copilot { impl Copilot {
pub fn global(cx: &AppContext) -> Option<Model<Self>> { pub fn global(cx: &AppContext) -> Option<Model<Self>> {

View file

@ -9407,9 +9407,7 @@ pub struct EditorReleased(pub WeakView<Editor>);
// } // }
// } // }
// //
impl EventEmitter for Editor { impl EventEmitter<Event> for Editor {}
type Event = Event;
}
impl Render for Editor { impl Render for Editor {
type Element = EditorElement; type Element = EditorElement;

View file

@ -201,7 +201,7 @@ pub struct AppContext {
pub(crate) pending_notifications: HashSet<EntityId>, pub(crate) pending_notifications: HashSet<EntityId>,
pub(crate) pending_global_notifications: HashSet<TypeId>, pub(crate) pending_global_notifications: HashSet<TypeId>,
pub(crate) observers: SubscriberSet<EntityId, Handler>, pub(crate) observers: SubscriberSet<EntityId, Handler>,
// (Entity, Event Type) // TypeId is the type of the event that the listener callback expects
pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>, pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>, pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
pub(crate) global_observers: SubscriberSet<TypeId, Handler>, pub(crate) global_observers: SubscriberSet<TypeId, Handler>,

View file

@ -1813,24 +1813,6 @@ impl<'a, V: 'static> ViewContext<'a, V> {
) )
} }
// Options for simplifying this new event API:
//
// - Make a new stlye of API which does partial application of the arguments to capture
// the types involved e.g.
// `cx.for_entity(handle).subscribe::<ItemEvents>(..)`
//
// - Make it so there are less types:
// - Bail on this idea all together, go back to associated types.
// causes our event enums to be a blob of anything that could happen ever, and
// makes applications have some translation boilerplate
//
// - Move some of the types into the method names,
// `cx.subscribe_model::<_, ItemEvents>(handle)`
//
// - Do something drastic like removing views and models, or removing the multiple
// kind of contexts. (Not going to happen, we already tried this before.)
//
// - Accept it, and use `cx.subscribe::<_, _, ItemEvents>(handle, ...)`
pub fn subscribe<V2, E, Evt>( pub fn subscribe<V2, E, Evt>(
&mut self, &mut self,
entity: &E, entity: &E,

View file

@ -1815,9 +1815,7 @@ impl Buffer {
} }
} }
impl EventEmitter for Buffer { impl EventEmitter<Event> for Buffer {}
type Event = Event;
}
impl Deref for Buffer { impl Deref for Buffer {
type Target = TextBuffer; type Target = TextBuffer;

View file

@ -1872,9 +1872,7 @@ impl MultiBuffer {
} }
} }
impl EventEmitter for MultiBuffer { impl EventEmitter<Event> for MultiBuffer {}
type Event = Event;
}
impl MultiBufferSnapshot { impl MultiBufferSnapshot {
pub fn text(&self) -> String { pub fn text(&self) -> String {

View file

@ -15,7 +15,6 @@ menu = { package = "menu2", path = "../menu2" }
settings = { package = "settings2", path = "../settings2" } settings = { package = "settings2", path = "../settings2" }
util = { path = "../util" } util = { path = "../util" }
theme = { package = "theme2", path = "../theme2" } theme = { package = "theme2", path = "../theme2" }
workspace = { package = "workspace2", path = "../workspace2" }
parking_lot.workspace = true parking_lot.workspace = true
@ -23,6 +22,5 @@ parking_lot.workspace = true
editor = { package = "editor2", path = "../editor2", features = ["test-support"] } editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] } gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
serde_json.workspace = true serde_json.workspace = true
workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
ctor.workspace = true ctor.workspace = true
env_logger.workspace = true env_logger.workspace = true

View file

@ -9062,9 +9062,7 @@ impl<'a> Iterator for PathMatchCandidateSetIter<'a> {
} }
} }
impl EventEmitter for Project { impl EventEmitter<Event> for Project {}
type Event = Event;
}
impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath { impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
fn from((worktree_id, path): (WorktreeId, P)) -> Self { fn from((worktree_id, path): (WorktreeId, P)) -> Self {

View file

@ -281,9 +281,7 @@ pub enum Event {
UpdatedGitRepositories(UpdatedGitRepositoriesSet), UpdatedGitRepositories(UpdatedGitRepositoriesSet),
} }
impl EventEmitter for Worktree { impl EventEmitter<Event> for Worktree {}
type Event = Event;
}
impl Worktree { impl Worktree {
pub async fn local( pub async fn local(

View file

@ -1351,9 +1351,7 @@ impl Drop for Terminal {
} }
} }
impl EventEmitter for Terminal { impl EventEmitter<Event> for Terminal {}
type Event = Event;
}
/// Based on alacritty/src/display/hint.rs > regex_match_at /// Based on alacritty/src/display/hint.rs > regex_match_at
/// Retrieve the match, if the specified point is inside the content matching the regex. /// Retrieve the match, if the specified point is inside the content matching the regex.

View file

@ -7,7 +7,16 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
pub trait Panel: Render + EventEmitter { pub enum PanelEvent {
ChangePosition,
ZoomIn,
ZoomOut,
Activate,
Close,
Focus,
}
pub trait Panel: Render + EventEmitter<PanelEvent> {
fn persistent_name(&self) -> &'static str; fn persistent_name(&self) -> &'static str;
fn position(&self, cx: &WindowContext) -> DockPosition; fn position(&self, cx: &WindowContext) -> DockPosition;
fn position_is_valid(&self, position: DockPosition) -> bool; fn position_is_valid(&self, position: DockPosition) -> bool;
@ -19,26 +28,12 @@ pub trait Panel: Render + EventEmitter {
fn icon_label(&self, _: &WindowContext) -> Option<String> { fn icon_label(&self, _: &WindowContext) -> Option<String> {
None None
} }
fn should_change_position_on_event(_: &Self::Event) -> bool;
fn should_zoom_in_on_event(_: &Self::Event) -> bool {
false
}
fn should_zoom_out_on_event(_: &Self::Event) -> bool {
false
}
fn is_zoomed(&self, _cx: &WindowContext) -> bool { fn is_zoomed(&self, _cx: &WindowContext) -> bool {
false false
} }
fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {} fn set_zoomed(&mut self, _zoomed: bool, _cx: &mut ViewContext<Self>) {}
fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {} fn set_active(&mut self, _active: bool, _cx: &mut ViewContext<Self>) {}
fn should_activate_on_event(_: &Self::Event) -> bool {
false
}
fn should_close_on_event(_: &Self::Event) -> bool {
false
}
fn has_focus(&self, cx: &WindowContext) -> bool; fn has_focus(&self, cx: &WindowContext) -> bool;
fn is_focus_event(_: &Self::Event) -> bool;
} }
pub trait PanelHandle: Send + Sync { pub trait PanelHandle: Send + Sync {
@ -268,21 +263,37 @@ impl Dock {
let subscriptions = [ let subscriptions = [
cx.observe(&panel, |_, _, cx| cx.notify()), cx.observe(&panel, |_, _, cx| cx.notify()),
cx.subscribe(&panel, |this, panel, event, cx| { cx.subscribe(&panel, |this, panel, event, cx| {
if T::should_activate_on_event(event) { match event {
if let Some(ix) = this PanelEvent::ChangePosition => {
.panel_entries //todo!()
.iter() // see: Workspace::add_panel_with_extra_event_handler
.position(|entry| entry.panel.id() == panel.id())
{
this.set_open(true, cx);
this.activate_panel(ix, cx);
// todo!()
// cx.focus(&panel);
} }
} else if T::should_close_on_event(event) PanelEvent::ZoomIn => {
&& this.visible_panel().map_or(false, |p| p.id() == panel.id()) //todo!()
{ // see: Workspace::add_panel_with_extra_event_handler
this.set_open(false, cx); }
PanelEvent::ZoomOut => {
// todo!()
// // see: Workspace::add_panel_with_extra_event_handler
}
PanelEvent::Activate => {
if let Some(ix) = this
.panel_entries
.iter()
.position(|entry| entry.panel.id() == panel.id())
{
this.set_open(true, cx);
this.activate_panel(ix, cx);
//` todo!()
// cx.focus(&panel);
}
}
PanelEvent::Close => {
if this.visible_panel().map_or(false, |p| p.id() == panel.id()) {
this.set_open(false, cx);
}
}
PanelEvent::Focus => todo!(),
} }
}), }),
]; ];
@ -452,10 +463,6 @@ impl PanelButtons {
} }
} }
impl EventEmitter for PanelButtons {
type Event = ();
}
// impl Render for PanelButtons { // impl Render for PanelButtons {
// type Element = (); // type Element = ();
@ -625,7 +632,7 @@ impl StatusItemView for PanelButtons {
_active_pane_item: Option<&dyn crate::ItemHandle>, _active_pane_item: Option<&dyn crate::ItemHandle>,
_cx: &mut ViewContext<Self>, _cx: &mut ViewContext<Self>,
) { ) {
// todo!(This is empty in the old `workspace::dock`) // Nothing to do, panel buttons don't depend on the active center item
} }
} }
@ -634,16 +641,6 @@ pub mod test {
use super::*; use super::*;
use gpui::{div, Div, ViewContext, WindowContext}; use gpui::{div, Div, ViewContext, WindowContext};
#[derive(Debug)]
pub enum TestPanelEvent {
PositionChanged,
Activated,
Closed,
ZoomIn,
ZoomOut,
Focus,
}
pub struct TestPanel { pub struct TestPanel {
pub position: DockPosition, pub position: DockPosition,
pub zoomed: bool, pub zoomed: bool,
@ -652,9 +649,7 @@ pub mod test {
pub size: f32, pub size: f32,
} }
impl EventEmitter for TestPanel { impl EventEmitter<PanelEvent> for TestPanel {}
type Event = TestPanelEvent;
}
impl TestPanel { impl TestPanel {
pub fn new(position: DockPosition) -> Self { pub fn new(position: DockPosition) -> Self {
@ -691,7 +686,7 @@ pub mod test {
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) { fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
self.position = position; self.position = position;
cx.emit(TestPanelEvent::PositionChanged); cx.emit(PanelEvent::ChangePosition);
} }
fn size(&self, _: &WindowContext) -> f32 { fn size(&self, _: &WindowContext) -> f32 {
@ -710,18 +705,6 @@ pub mod test {
("Test Panel".into(), None) ("Test Panel".into(), None)
} }
fn should_change_position_on_event(event: &Self::Event) -> bool {
matches!(event, TestPanelEvent::PositionChanged)
}
fn should_zoom_in_on_event(event: &Self::Event) -> bool {
matches!(event, TestPanelEvent::ZoomIn)
}
fn should_zoom_out_on_event(event: &Self::Event) -> bool {
matches!(event, TestPanelEvent::ZoomOut)
}
fn is_zoomed(&self, _: &WindowContext) -> bool { fn is_zoomed(&self, _: &WindowContext) -> bool {
self.zoomed self.zoomed
} }
@ -734,20 +717,8 @@ pub mod test {
self.active = active; self.active = active;
} }
fn should_activate_on_event(event: &Self::Event) -> bool {
matches!(event, TestPanelEvent::Activated)
}
fn should_close_on_event(event: &Self::Event) -> bool {
matches!(event, TestPanelEvent::Closed)
}
fn has_focus(&self, _cx: &WindowContext) -> bool { fn has_focus(&self, _cx: &WindowContext) -> bool {
self.has_focus self.has_focus
} }
fn is_focus_event(event: &Self::Event) -> bool {
matches!(event, TestPanelEvent::Focus)
}
} }
} }

View file

@ -91,7 +91,7 @@ pub struct BreadcrumbText {
pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>, pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
} }
pub trait Item: Render + EventEmitter { pub trait Item: Render + EventEmitter<ItemEvent> {
fn focus_handle(&self) -> FocusHandle; fn focus_handle(&self) -> FocusHandle;
fn deactivated(&mut self, _: &mut ViewContext<Self>) {} fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {} fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
@ -106,12 +106,13 @@ pub trait Item: Render + EventEmitter {
} }
fn tab_content<V: 'static>(&self, detail: Option<usize>, cx: &AppContext) -> AnyElement<V>; fn tab_content<V: 'static>(&self, detail: Option<usize>, cx: &AppContext) -> AnyElement<V>;
/// (model id, Item)
fn for_each_project_item( fn for_each_project_item(
&self, &self,
_: &AppContext, _: &AppContext,
_: &mut dyn FnMut(EntityId, &dyn project2::Item), _: &mut dyn FnMut(EntityId, &dyn project2::Item),
) { ) {
} // (model id, Item) }
fn is_singleton(&self, _cx: &AppContext) -> bool { fn is_singleton(&self, _cx: &AppContext) -> bool {
false false
} }
@ -153,15 +154,6 @@ pub trait Item: Render + EventEmitter {
) -> Task<Result<()>> { ) -> Task<Result<()>> {
unimplemented!("reload() must be implemented if can_save() returns true") unimplemented!("reload() must be implemented if can_save() returns true")
} }
fn to_item_events(_event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
SmallVec::new()
}
fn should_close_item_on_event(_: &Self::Event) -> bool {
false
}
fn should_update_tab_on_event(_: &Self::Event) -> bool {
false
}
fn act_as_type<'a>( fn act_as_type<'a>(
&'a self, &'a self,
@ -218,7 +210,7 @@ pub trait ItemHandle: 'static + Send {
fn subscribe_to_item_events( fn subscribe_to_item_events(
&self, &self,
cx: &mut WindowContext, cx: &mut WindowContext,
handler: Box<dyn Fn(ItemEvent, &mut WindowContext) + Send>, handler: Box<dyn Fn(&ItemEvent, &mut WindowContext) + Send>,
) -> gpui::Subscription; ) -> gpui::Subscription;
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString>; fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString>;
fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString>; fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString>;
@ -300,12 +292,10 @@ impl<T: Item> ItemHandle for View<T> {
fn subscribe_to_item_events( fn subscribe_to_item_events(
&self, &self,
cx: &mut WindowContext, cx: &mut WindowContext,
handler: Box<dyn Fn(ItemEvent, &mut WindowContext) + Send>, handler: Box<dyn Fn(&ItemEvent, &mut WindowContext) + Send>,
) -> gpui::Subscription { ) -> gpui::Subscription {
cx.subscribe(self, move |_, event, cx| { cx.subscribe(self, move |_, event, cx| {
for item_event in T::to_item_events(event) { handler(event, cx);
handler(item_event, cx)
}
}) })
} }
@ -433,7 +423,10 @@ impl<T: Item> ItemHandle for View<T> {
let is_project_item = item.is_project_item(cx); let is_project_item = item.is_project_item(cx);
let leader_id = workspace.leader_for_pane(&pane); let leader_id = workspace.leader_for_pane(&pane);
if leader_id.is_some() && item.should_unfollow_on_event(event, cx) { let follow_event = item.to_follow_event(event);
if leader_id.is_some()
&& matches!(follow_event, Some(FollowEvent::Unfollow))
{
workspace.unfollow(&pane, cx); workspace.unfollow(&pane, cx);
} }
@ -467,36 +460,34 @@ impl<T: Item> ItemHandle for View<T> {
} }
} }
for item_event in T::to_item_events(event).into_iter() { match event {
match item_event { ItemEvent::CloseItem => {
ItemEvent::CloseItem => { pane.update(cx, |pane, cx| {
pane.update(cx, |pane, cx| { pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx)
pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx) })
}) .detach_and_log_err(cx);
.detach_and_log_err(cx); return;
return; }
}
ItemEvent::UpdateTab => { ItemEvent::UpdateTab => {
pane.update(cx, |_, cx| { pane.update(cx, |_, cx| {
cx.emit(pane::Event::ChangeItemTitle); cx.emit(pane::Event::ChangeItemTitle);
cx.notify(); cx.notify();
});
}
ItemEvent::Edit => {
let autosave = WorkspaceSettings::get_global(cx).autosave;
if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
let delay = Duration::from_millis(milliseconds);
let item = item.clone();
pending_autosave.fire_new(delay, cx, move |workspace, cx| {
Pane::autosave_item(&item, workspace.project().clone(), cx)
}); });
} }
ItemEvent::Edit => {
let autosave = WorkspaceSettings::get_global(cx).autosave;
if let AutosaveSetting::AfterDelay { milliseconds } = autosave {
let delay = Duration::from_millis(milliseconds);
let item = item.clone();
pending_autosave.fire_new(delay, cx, move |workspace, cx| {
Pane::autosave_item(&item, workspace.project().clone(), cx)
});
}
}
_ => {}
} }
_ => {}
} }
})); }));
@ -660,7 +651,16 @@ pub trait ProjectItem: Item {
Self: Sized; Self: Sized;
} }
pub enum FollowEvent {
Unfollow,
}
pub trait FollowableEvents {
fn to_follow_event(&self) -> Option<FollowEvent>;
}
pub trait FollowableItem: Item { pub trait FollowableItem: Item {
type FollowableEvent: FollowableEvents;
fn remote_id(&self) -> Option<ViewId>; fn remote_id(&self) -> Option<ViewId>;
fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>; fn to_state_proto(&self, cx: &AppContext) -> Option<proto::view::Variant>;
fn from_state_proto( fn from_state_proto(
@ -672,7 +672,7 @@ pub trait FollowableItem: Item {
) -> Option<Task<Result<View<Self>>>>; ) -> Option<Task<Result<View<Self>>>>;
fn add_event_to_update_proto( fn add_event_to_update_proto(
&self, &self,
event: &Self::Event, event: &Self::FollowableEvent,
update: &mut Option<proto::update_view::Variant>, update: &mut Option<proto::update_view::Variant>,
cx: &AppContext, cx: &AppContext,
) -> bool; ) -> bool;
@ -685,7 +685,6 @@ pub trait FollowableItem: Item {
fn is_project_item(&self, cx: &AppContext) -> bool; fn is_project_item(&self, cx: &AppContext) -> bool;
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>); fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>);
fn should_unfollow_on_event(event: &Self::Event, cx: &AppContext) -> bool;
} }
pub trait FollowableItemHandle: ItemHandle { pub trait FollowableItemHandle: ItemHandle {
@ -698,13 +697,13 @@ pub trait FollowableItemHandle: ItemHandle {
update: &mut Option<proto::update_view::Variant>, update: &mut Option<proto::update_view::Variant>,
cx: &AppContext, cx: &AppContext,
) -> bool; ) -> bool;
fn to_follow_event(&self, event: &dyn Any) -> Option<FollowEvent>;
fn apply_update_proto( fn apply_update_proto(
&self, &self,
project: &Model<Project>, project: &Model<Project>,
message: proto::update_view::Variant, message: proto::update_view::Variant,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> Task<Result<()>>; ) -> Task<Result<()>>;
fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool;
fn is_project_item(&self, cx: &AppContext) -> bool; fn is_project_item(&self, cx: &AppContext) -> bool;
} }
@ -739,6 +738,13 @@ impl<T: FollowableItem> FollowableItemHandle for View<T> {
} }
} }
fn to_follow_event(&self, event: &dyn Any) -> Option<FollowEvent> {
event
.downcast_ref()
.map(T::FollowableEvent::to_follow_event)
.flatten()
}
fn apply_update_proto( fn apply_update_proto(
&self, &self,
project: &Model<Project>, project: &Model<Project>,
@ -748,14 +754,6 @@ impl<T: FollowableItem> FollowableItemHandle for View<T> {
self.update(cx, |this, cx| this.apply_update_proto(project, message, cx)) self.update(cx, |this, cx| this.apply_update_proto(project, message, cx))
} }
fn should_unfollow_on_event(&self, event: &dyn Any, cx: &AppContext) -> bool {
if let Some(event) = event.downcast_ref() {
T::should_unfollow_on_event(event, cx)
} else {
false
}
}
fn is_project_item(&self, cx: &AppContext) -> bool { fn is_project_item(&self, cx: &AppContext) -> bool {
self.read(cx).is_project_item(cx) self.read(cx).is_project_item(cx)
} }

View file

@ -9,10 +9,12 @@ pub fn init(cx: &mut AppContext) {
// simple_message_notification::init(cx); // simple_message_notification::init(cx);
} }
pub trait Notification: EventEmitter + Render { pub enum NotificationEvent {
fn should_dismiss_notification_on_event(&self, event: &Self::Event) -> bool; Dismiss,
} }
pub trait Notification: EventEmitter<NotificationEvent> + Render {}
pub trait NotificationHandle: Send { pub trait NotificationHandle: Send {
fn id(&self) -> EntityId; fn id(&self) -> EntityId;
fn to_any(&self) -> AnyView; fn to_any(&self) -> AnyView;
@ -101,11 +103,14 @@ impl Workspace {
}) })
{ {
let notification = build_notification(cx); let notification = build_notification(cx);
cx.subscribe(&notification, move |this, handle, event, cx| { cx.subscribe(
if handle.read(cx).should_dismiss_notification_on_event(event) { &notification,
this.dismiss_notification_internal(type_id, id, cx); move |this, handle, event: &NotificationEvent, cx| match event {
} NotificationEvent::Dismiss => {
}) this.dismiss_notification_internal(type_id, id, cx);
}
},
)
.detach(); .detach();
self.notifications self.notifications
.push((type_id, id, Box::new(notification))); .push((type_id, id, Box::new(notification)));
@ -159,7 +164,7 @@ impl Workspace {
} }
pub mod simple_message_notification { pub mod simple_message_notification {
use super::Notification; use super::{Notification, NotificationEvent};
use gpui::{AnyElement, AppContext, Div, EventEmitter, Render, TextStyle, ViewContext}; use gpui::{AnyElement, AppContext, Div, EventEmitter, Render, TextStyle, ViewContext};
use serde::Deserialize; use serde::Deserialize;
use std::{borrow::Cow, sync::Arc}; use std::{borrow::Cow, sync::Arc};
@ -200,13 +205,7 @@ pub mod simple_message_notification {
click_message: Option<Cow<'static, str>>, click_message: Option<Cow<'static, str>>,
} }
pub enum MessageNotificationEvent { impl EventEmitter<NotificationMessage> for MessageNotification {}
Dismiss,
}
impl EventEmitter for MessageNotification {
type Event = MessageNotificationEvent;
}
impl MessageNotification { impl MessageNotification {
pub fn new<S>(message: S) -> MessageNotification pub fn new<S>(message: S) -> MessageNotification
@ -359,13 +358,8 @@ pub mod simple_message_notification {
// } // }
// } // }
impl Notification for MessageNotification { impl EventEmitter<NotificationEvent> for MessageNotification {}
fn should_dismiss_notification_on_event(&self, event: &Self::Event) -> bool { impl Notification for MessageNotification {}
match event {
MessageNotificationEvent::Dismiss => true,
}
}
}
} }
pub trait NotifyResultExt { pub trait NotifyResultExt {

View file

@ -9,8 +9,9 @@ use crate::{
use anyhow::Result; use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
use gpui::{ use gpui::{
AppContext, AsyncWindowContext, Component, Div, EntityId, EventEmitter, FocusHandle, Model, actions, register_action, AppContext, AsyncWindowContext, Component, Div, EntityId,
PromptLevel, Render, Task, View, ViewContext, VisualContext, WeakView, WindowContext, EventEmitter, FocusHandle, Model, PromptLevel, Render, Task, View, ViewContext, VisualContext,
WeakView, WindowContext,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use project2::{Project, ProjectEntryId, ProjectPath}; use project2::{Project, ProjectEntryId, ProjectPath};
@ -48,8 +49,10 @@ pub enum SaveIntent {
Skip, Skip,
} }
// #[derive(Clone, Deserialize, PartialEq)] //todo!("Do we need the default bound on actions? Decide soon")
// pub struct ActivateItem(pub usize); // #[register_action]
#[derive(Clone, Deserialize, PartialEq, Debug)]
pub struct ActivateItem(pub usize);
// #[derive(Clone, PartialEq)] // #[derive(Clone, PartialEq)]
// pub struct CloseItemById { // pub struct CloseItemById {
@ -69,40 +72,37 @@ pub enum SaveIntent {
// pub pane: WeakView<Pane>, // pub pane: WeakView<Pane>,
// } // }
// #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[register_action]
// #[serde(rename_all = "camelCase")] #[derive(Clone, PartialEq, Debug, Deserialize, Default)]
// pub struct CloseActiveItem { #[serde(rename_all = "camelCase")]
// pub save_intent: Option<SaveIntent>, pub struct CloseActiveItem {
// } pub save_intent: Option<SaveIntent>,
}
// #[derive(Clone, PartialEq, Debug, Deserialize)] #[register_action]
// #[serde(rename_all = "camelCase")] #[derive(Clone, PartialEq, Debug, Deserialize, Default)]
// pub struct CloseAllItems { #[serde(rename_all = "camelCase")]
// pub save_intent: Option<SaveIntent>, pub struct CloseAllItems {
// } pub save_intent: Option<SaveIntent>,
}
// todo!() // todo!(These used to be under pane::{Action}. Are they now workspace::pane::{Action}?)
// actions!( actions!(
// pane, ActivatePrevItem,
// [ ActivateNextItem,
// ActivatePrevItem, ActivateLastItem,
// ActivateNextItem, CloseInactiveItems,
// ActivateLastItem, CloseCleanItems,
// CloseInactiveItems, CloseItemsToTheLeft,
// CloseCleanItems, CloseItemsToTheRight,
// CloseItemsToTheLeft, GoBack,
// CloseItemsToTheRight, GoForward,
// GoBack, ReopenClosedItem,
// GoForward, SplitLeft,
// ReopenClosedItem, SplitUp,
// SplitLeft, SplitRight,
// SplitUp, SplitDown,
// SplitRight, );
// SplitDown,
// ]
// );
// impl_actions!(pane, [ActivateItem, CloseActiveItem, CloseAllItems]);
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
@ -310,9 +310,7 @@ pub struct NavigationEntry {
// .into_any_named("nav button") // .into_any_named("nav button")
// } // }
impl EventEmitter for Pane { impl EventEmitter<Event> for Pane {}
type Event = Event;
}
impl Pane { impl Pane {
pub fn new( pub fn new(

View file

@ -1,6 +1,8 @@
use std::{any::Any, sync::Arc}; use std::{any::Any, sync::Arc};
use gpui::{AnyView, AppContext, Subscription, Task, View, ViewContext, WindowContext}; use gpui::{
AnyView, AppContext, EventEmitter, Subscription, Task, View, ViewContext, WindowContext,
};
use project2::search::SearchQuery; use project2::search::SearchQuery;
use crate::{ use crate::{
@ -29,7 +31,7 @@ pub struct SearchOptions {
pub replacement: bool, pub replacement: bool,
} }
pub trait SearchableItem: Item { pub trait SearchableItem: Item + EventEmitter<SearchEvent> {
type Match: Any + Sync + Send + Clone; type Match: Any + Sync + Send + Clone;
fn supported_options() -> SearchOptions { fn supported_options() -> SearchOptions {
@ -40,11 +42,7 @@ pub trait SearchableItem: Item {
replacement: true, replacement: true,
} }
} }
fn to_search_event(
&mut self,
event: &Self::Event,
cx: &mut ViewContext<Self>,
) -> Option<SearchEvent>;
fn clear_matches(&mut self, cx: &mut ViewContext<Self>); fn clear_matches(&mut self, cx: &mut ViewContext<Self>);
fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>); fn update_matches(&mut self, matches: Vec<Self::Match>, cx: &mut ViewContext<Self>);
fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String; fn query_suggestion(&mut self, cx: &mut ViewContext<Self>) -> String;
@ -95,7 +93,7 @@ pub trait SearchableItemHandle: ItemHandle {
fn subscribe_to_search_events( fn subscribe_to_search_events(
&self, &self,
cx: &mut WindowContext, cx: &mut WindowContext,
handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>, handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
) -> Subscription; ) -> Subscription;
fn clear_matches(&self, cx: &mut WindowContext); fn clear_matches(&self, cx: &mut WindowContext);
fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext); fn update_matches(&self, matches: &Vec<Box<dyn Any + Send>>, cx: &mut WindowContext);
@ -146,14 +144,9 @@ impl<T: SearchableItem> SearchableItemHandle for View<T> {
fn subscribe_to_search_events( fn subscribe_to_search_events(
&self, &self,
cx: &mut WindowContext, cx: &mut WindowContext,
handler: Box<dyn Fn(SearchEvent, &mut WindowContext) + Send>, handler: Box<dyn Fn(&SearchEvent, &mut WindowContext) + Send>,
) -> Subscription { ) -> Subscription {
cx.subscribe(self, move |handle, event, cx| { cx.subscribe(self, move |_, event: &SearchEvent, cx| handler(event, cx))
let search_event = handle.update(cx, |handle, cx| handle.to_search_event(event, cx));
if let Some(search_event) = search_event {
handler(search_event, cx)
}
})
} }
fn clear_matches(&self, cx: &mut WindowContext) { fn clear_matches(&self, cx: &mut WindowContext) {

View file

@ -1,24 +1,17 @@
use crate::ItemHandle; use crate::ItemHandle;
use gpui::{ use gpui::{AnyView, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext};
AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext,
};
pub trait ToolbarItemView: Render + EventEmitter { pub enum ToolbarItemEvent {
ChangeLocation(ToolbarItemLocation),
}
pub trait ToolbarItemView: Render + EventEmitter<ToolbarItemEvent> {
fn set_active_pane_item( fn set_active_pane_item(
&mut self, &mut self,
active_pane_item: Option<&dyn crate::ItemHandle>, active_pane_item: Option<&dyn crate::ItemHandle>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> ToolbarItemLocation; ) -> ToolbarItemLocation;
fn location_for_event(
&self,
_event: &Self::Event,
current_location: ToolbarItemLocation,
_cx: &AppContext,
) -> ToolbarItemLocation {
current_location
}
fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {} fn pane_focus_update(&mut self, _pane_focused: bool, _cx: &mut ViewContext<Self>) {}
/// Number of times toolbar's height will be repeated to get the effective height. /// Number of times toolbar's height will be repeated to get the effective height.
@ -211,12 +204,13 @@ impl Toolbar {
if let Some((_, current_location)) = if let Some((_, current_location)) =
this.items.iter_mut().find(|(i, _)| i.id() == item.id()) this.items.iter_mut().find(|(i, _)| i.id() == item.id())
{ {
let new_location = item match event {
.read(cx) ToolbarItemEvent::ChangeLocation(new_location) => {
.location_for_event(event, *current_location, cx); if new_location != current_location {
if new_location != *current_location { *current_location = *new_location;
*current_location = new_location; cx.notify();
cx.notify(); }
}
} }
} }
}) })

View file

@ -36,7 +36,7 @@ use futures::{
Future, FutureExt, StreamExt, Future, FutureExt, StreamExt,
}; };
use gpui::{ use gpui::{
div, point, rems, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, actions, div, point, rems, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext,
AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter, FocusHandle, AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter, FocusHandle,
GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, StatefulInteractive, GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, StatefulInteractive,
Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds,
@ -88,35 +88,32 @@ lazy_static! {
// #[derive(Clone, PartialEq)] // #[derive(Clone, PartialEq)]
// pub struct RemoveWorktreeFromProject(pub WorktreeId); // pub struct RemoveWorktreeFromProject(pub WorktreeId);
// actions!( actions!(
// workspace, Open,
// [ NewFile,
// Open, NewWindow,
// NewFile, CloseWindow,
// NewWindow, CloseInactiveTabsAndPanes,
// CloseWindow, AddFolderToProject,
// CloseInactiveTabsAndPanes, Unfollow,
// AddFolderToProject, SaveAs,
// Unfollow, ReloadActiveItem,
// SaveAs, ActivatePreviousPane,
// ReloadActiveItem, ActivateNextPane,
// ActivatePreviousPane, FollowNextCollaborator,
// ActivateNextPane, NewTerminal,
// FollowNextCollaborator, NewCenterTerminal,
// NewTerminal, ToggleTerminalFocus,
// NewCenterTerminal, NewSearch,
// ToggleTerminalFocus, Feedback,
// NewSearch, Restart,
// Feedback, Welcome,
// Restart, ToggleZoom,
// Welcome, ToggleLeftDock,
// ToggleZoom, ToggleRightDock,
// ToggleLeftDock, ToggleBottomDock,
// ToggleRightDock, CloseAllDocks,
// ToggleBottomDock, );
// CloseAllDocks,
// ]
// );
// #[derive(Clone, PartialEq)] // #[derive(Clone, PartialEq)]
// pub struct OpenPaths { // pub struct OpenPaths {
@ -964,6 +961,9 @@ impl Workspace {
// let mut prev_position = panel.position(cx); // let mut prev_position = panel.position(cx);
// move |this, panel, event, cx| { // move |this, panel, event, cx| {
// if T::should_change_position_on_event(event) { // if T::should_change_position_on_event(event) {
// THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION
// See: Dock::add_panel
//
// let new_position = panel.read(cx).position(cx); // let new_position = panel.read(cx).position(cx);
// let mut was_visible = false; // let mut was_visible = false;
// dock.update(cx, |dock, cx| { // dock.update(cx, |dock, cx| {
@ -994,6 +994,9 @@ impl Workspace {
// } // }
// }); // });
// } else if T::should_zoom_in_on_event(event) { // } else if T::should_zoom_in_on_event(event) {
// THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION
// See: Dock::add_panel
//
// dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx)); // dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, true, cx));
// if !panel.has_focus(cx) { // if !panel.has_focus(cx) {
// cx.focus(&panel); // cx.focus(&panel);
@ -1001,6 +1004,9 @@ impl Workspace {
// this.zoomed = Some(panel.downgrade().into_any()); // this.zoomed = Some(panel.downgrade().into_any());
// this.zoomed_position = Some(panel.read(cx).position(cx)); // this.zoomed_position = Some(panel.read(cx).position(cx));
// } else if T::should_zoom_out_on_event(event) { // } else if T::should_zoom_out_on_event(event) {
// THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION
// See: Dock::add_panel
//
// dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx)); // dock.update(cx, |dock, cx| dock.set_panel_zoomed(&panel, false, cx));
// if this.zoomed_position == Some(prev_position) { // if this.zoomed_position == Some(prev_position) {
// this.zoomed = None; // this.zoomed = None;
@ -1008,6 +1014,9 @@ impl Workspace {
// } // }
// cx.notify(); // cx.notify();
// } else if T::is_focus_event(event) { // } else if T::is_focus_event(event) {
// THIS HAS BEEN MOVED TO NORMAL EVENT EMISSION
// See: Dock::add_panel
//
// let position = panel.read(cx).position(cx); // let position = panel.read(cx).position(cx);
// this.dismiss_zoomed_items_to_reveal(Some(position), cx); // this.dismiss_zoomed_items_to_reveal(Some(position), cx);
// if panel.is_zoomed(cx) { // if panel.is_zoomed(cx) {
@ -3691,9 +3700,7 @@ fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncA
.log_err(); .log_err();
} }
impl EventEmitter for Workspace { impl EventEmitter<Event> for Workspace {}
type Event = Event;
}
impl Render for Workspace { impl Render for Workspace {
type Element = Div<Self>; type Element = Div<Self>;
@ -4135,10 +4142,6 @@ impl WorkspaceStore {
} }
} }
impl EventEmitter for WorkspaceStore {
type Event = ();
}
impl ViewId { impl ViewId {
pub(crate) fn from_proto(message: proto::ViewId) -> Result<Self> { pub(crate) fn from_proto(message: proto::ViewId) -> Result<Self> {
Ok(Self { Ok(Self {