diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 02cbab21d0..2b3c7e1c63 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -51,9 +51,8 @@ impl Entity for AutoUpdater { pub fn init(http_client: Arc, server_url: String, cx: &mut AppContext) { if let Some(version) = (*ZED_APP_VERSION).or_else(|| cx.platform().app_version().ok()) { - let server_url = server_url; let auto_updater = cx.add_model(|cx| { - let updater = AutoUpdater::new(version, http_client, server_url.clone()); + let updater = AutoUpdater::new(version, http_client, server_url); let mut update_subscription = cx .global::() @@ -74,25 +73,32 @@ pub fn init(http_client: Arc, server_url: String, cx: &mut AppCo updater }); cx.set_global(Some(auto_updater)); - cx.add_global_action(|_: &Check, cx| { - if let Some(updater) = AutoUpdater::get(cx) { - updater.update(cx, |updater, cx| updater.poll(cx)); - } - }); - cx.add_global_action(move |_: &ViewReleaseNotes, cx| { - let latest_release_url = if cx.has_global::() - && *cx.global::() == ReleaseChannel::Preview - { - format!("{server_url}/releases/preview/latest") - } else { - format!("{server_url}/releases/latest") - }; - cx.platform().open_url(&latest_release_url); - }); + cx.add_global_action(check); + cx.add_global_action(view_release_notes); cx.add_action(UpdateNotification::dismiss); } } +pub fn check(_: &Check, cx: &mut AppContext) { + if let Some(updater) = AutoUpdater::get(cx) { + updater.update(cx, |updater, cx| updater.poll(cx)); + } +} + +fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) { + if let Some(auto_updater) = AutoUpdater::get(cx) { + let server_url = &auto_updater.read(cx).server_url; + let latest_release_url = if cx.has_global::() + && *cx.global::() == ReleaseChannel::Preview + { + format!("{server_url}/releases/preview/latest") + } else { + format!("{server_url}/releases/latest") + }; + cx.platform().open_url(&latest_release_url); + } +} + pub fn notify_of_any_new_update( workspace: WeakViewHandle, cx: &mut AppContext, diff --git a/crates/auto_update/src/update_notification.rs b/crates/auto_update/src/update_notification.rs index c0b88fdf5e..b48ac2a413 100644 --- a/crates/auto_update/src/update_notification.rs +++ b/crates/auto_update/src/update_notification.rs @@ -63,8 +63,8 @@ impl View for UpdateNotification { .with_height(style.button_width) }) .with_padding(Padding::uniform(5.)) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(Cancel) + .on_click(MouseButton::Left, move |_, this, cx| { + this.dismiss(&Default::default(), cx) }) .aligned() .constrained() @@ -84,7 +84,7 @@ impl View for UpdateNotification { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(ViewReleaseNotes) + crate::view_release_notes(&Default::default(), cx) }) .into_any_named("update notification") } diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs index c09706f378..f3be60f8de 100644 --- a/crates/breadcrumbs/src/breadcrumbs.rs +++ b/crates/breadcrumbs/src/breadcrumbs.rs @@ -1,13 +1,13 @@ use gpui::{ elements::*, platform::MouseButton, AppContext, Entity, Subscription, View, ViewContext, - ViewHandle, + ViewHandle, WeakViewHandle, }; use itertools::Itertools; use search::ProjectSearchView; use settings::Settings; use workspace::{ item::{ItemEvent, ItemHandle}, - ToolbarItemLocation, ToolbarItemView, + ToolbarItemLocation, ToolbarItemView, Workspace, }; pub enum Event { @@ -19,15 +19,17 @@ pub struct Breadcrumbs { active_item: Option>, project_search: Option>, subscription: Option, + workspace: WeakViewHandle, } impl Breadcrumbs { - pub fn new() -> Self { + pub fn new(workspace: &Workspace) -> Self { Self { pane_focused: false, active_item: Default::default(), subscription: Default::default(), project_search: Default::default(), + workspace: workspace.weak_handle(), } } } @@ -85,8 +87,12 @@ impl View for Breadcrumbs { let style = style.style_for(state, false); crumbs.with_style(style.container) }) - .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(outline::Toggle); + .on_click(MouseButton::Left, |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + outline::toggle(workspace, &Default::default(), cx) + }) + } }) .with_tooltip::( 0, diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 95fe26fa11..a9894dade1 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -1,7 +1,6 @@ use crate::{ - collaborator_list_popover, collaborator_list_popover::CollaboratorListPopover, contact_notification::ContactNotification, contacts_popover, face_pile::FacePile, - ToggleScreenSharing, + toggle_screen_sharing, ToggleScreenSharing, }; use call::{ActiveCall, ParticipantLocation, Room}; use client::{proto::PeerId, ContactEventKind, SignIn, SignOut, User, UserStore}; @@ -27,7 +26,6 @@ use workspace::{FollowNextCollaborator, Workspace}; actions!( collab, [ - ToggleCollaboratorList, ToggleContactsMenu, ToggleUserMenu, ShareProject, @@ -36,7 +34,6 @@ actions!( ); pub fn init(cx: &mut AppContext) { - cx.add_action(CollabTitlebarItem::toggle_collaborator_list_popover); cx.add_action(CollabTitlebarItem::toggle_contacts_popover); cx.add_action(CollabTitlebarItem::share_project); cx.add_action(CollabTitlebarItem::unshare_project); @@ -48,7 +45,6 @@ pub struct CollabTitlebarItem { user_store: ModelHandle, contacts_popover: Option>, user_menu: ViewHandle, - collaborator_list_popover: Option>, _subscriptions: Vec, } @@ -172,7 +168,6 @@ impl CollabTitlebarItem { menu.set_position_mode(OverlayPositionMode::Local); menu }), - collaborator_list_popover: None, _subscriptions: subscriptions, } } @@ -217,36 +212,6 @@ impl CollabTitlebarItem { } } - pub fn toggle_collaborator_list_popover( - &mut self, - _: &ToggleCollaboratorList, - cx: &mut ViewContext, - ) { - match self.collaborator_list_popover.take() { - Some(_) => {} - None => { - if let Some(workspace) = self.workspace.upgrade(cx) { - let user_store = workspace.read(cx).user_store().clone(); - let view = cx.add_view(|cx| CollaboratorListPopover::new(user_store, cx)); - - cx.subscribe(&view, |this, _, event, cx| { - match event { - collaborator_list_popover::Event::Dismissed => { - this.collaborator_list_popover = None; - } - } - - cx.notify(); - }) - .detach(); - - self.collaborator_list_popover = Some(view); - } - } - } - cx.notify(); - } - pub fn toggle_contacts_popover(&mut self, _: &ToggleContactsMenu, cx: &mut ViewContext) { if self.contacts_popover.take().is_none() { if let Some(workspace) = self.workspace.upgrade(cx) { @@ -357,8 +322,8 @@ impl CollabTitlebarItem { .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(ToggleContactsMenu); + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_contacts_popover(&Default::default(), cx) }) .with_tooltip::( 0, @@ -405,7 +370,7 @@ impl CollabTitlebarItem { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(ToggleScreenSharing); + toggle_screen_sharing(&Default::default(), cx) }) .with_tooltip::( 0, @@ -451,11 +416,11 @@ impl CollabTitlebarItem { .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { + .on_click(MouseButton::Left, move |_, this, cx| { if is_shared { - cx.dispatch_action(UnshareProject); + this.unshare_project(&Default::default(), cx); } else { - cx.dispatch_action(ShareProject); + this.share_project(&Default::default(), cx); } }) .with_tooltip::( @@ -496,8 +461,8 @@ impl CollabTitlebarItem { .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(ToggleUserMenu); + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_user_menu(&Default::default(), cx) }) .with_tooltip::( 0, @@ -527,8 +492,13 @@ impl CollabTitlebarItem { .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(SignIn); + .on_click(MouseButton::Left, move |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let client = workspace.read(cx).app_state().client.clone(); + cx.app_context() + .spawn(|cx| async move { client.authenticate_and_connect(true, &cx).await }) + .detach_and_log_err(cx); + } }) .into_any() } @@ -862,7 +832,7 @@ impl CollabTitlebarItem { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(auto_update::Check); + auto_update::check(&Default::default(), cx); }) .into_any(), ), diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 3f3998fb6d..c0734388b1 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -1,5 +1,4 @@ mod collab_titlebar_item; -mod collaborator_list_popover; mod contact_finder; mod contact_list; mod contact_notification; diff --git a/crates/collab_ui/src/collaborator_list_popover.rs b/crates/collab_ui/src/collaborator_list_popover.rs deleted file mode 100644 index 6820644441..0000000000 --- a/crates/collab_ui/src/collaborator_list_popover.rs +++ /dev/null @@ -1,161 +0,0 @@ -use call::ActiveCall; -use client::UserStore; -use gpui::Action; -use gpui::{actions, elements::*, platform::MouseButton, Entity, ModelHandle, View, ViewContext}; -use settings::Settings; - -use crate::collab_titlebar_item::ToggleCollaboratorList; - -pub(crate) enum Event { - Dismissed, -} - -enum Collaborator { - SelfUser { username: String }, - RemoteUser { username: String }, -} - -actions!(collaborator_list_popover, [NoOp]); - -pub(crate) struct CollaboratorListPopover { - list_state: ListState, -} - -impl Entity for CollaboratorListPopover { - type Event = Event; -} - -impl View for CollaboratorListPopover { - fn ui_name() -> &'static str { - "CollaboratorListPopover" - } - - fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - let theme = cx.global::().theme.clone(); - - MouseEventHandler::::new(0, cx, |_, _| { - List::new(self.list_state.clone()) - .contained() - .with_style(theme.contacts_popover.container) //TODO: Change the name of this theme key - .constrained() - .with_width(theme.contacts_popover.width) - .with_height(theme.contacts_popover.height) - }) - .on_down_out(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(ToggleCollaboratorList); - }) - .into_any() - } - - fn focus_out(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext) { - cx.emit(Event::Dismissed); - } -} - -impl CollaboratorListPopover { - pub fn new(user_store: ModelHandle, cx: &mut ViewContext) -> Self { - let active_call = ActiveCall::global(cx); - - let mut collaborators = user_store - .read(cx) - .current_user() - .map(|u| Collaborator::SelfUser { - username: u.github_login.clone(), - }) - .into_iter() - .collect::>(); - - //TODO: What should the canonical sort here look like, consult contacts list implementation - if let Some(room) = active_call.read(cx).room() { - for participant in room.read(cx).remote_participants() { - collaborators.push(Collaborator::RemoteUser { - username: participant.1.user.github_login.clone(), - }); - } - } - - Self { - list_state: ListState::new( - collaborators.len(), - Orientation::Top, - 0., - move |_, index, cx| match &collaborators[index] { - Collaborator::SelfUser { username } => render_collaborator_list_entry( - index, - username, - None::, - None, - Svg::new("icons/chevron_right_12.svg"), - NoOp, - "Leave call".to_owned(), - cx, - ), - - Collaborator::RemoteUser { username } => render_collaborator_list_entry( - index, - username, - Some(NoOp), - Some(format!("Follow {username}")), - Svg::new("icons/x_mark_12.svg"), - NoOp, - format!("Remove {username} from call"), - cx, - ), - }, - ), - } - } -} - -fn render_collaborator_list_entry( - index: usize, - username: &str, - username_action: Option, - username_tooltip: Option, - icon: Svg, - icon_action: IA, - icon_tooltip: String, - cx: &mut ViewContext, -) -> AnyElement { - enum Username {} - enum UsernameTooltip {} - enum Icon {} - enum IconTooltip {} - - let theme = &cx.global::().theme; - let username_theme = theme.contact_list.contact_username.text.clone(); - let tooltip_theme = theme.tooltip.clone(); - - let username = - MouseEventHandler::::new(index, cx, |_, _| { - Label::new(username.to_owned(), username_theme.clone()) - }) - .on_click(MouseButton::Left, move |_, _, cx| { - if let Some(username_action) = username_action.clone() { - cx.dispatch_action(username_action); - } - }); - - Flex::row() - .with_child(if let Some(username_tooltip) = username_tooltip { - username - .with_tooltip::( - index, - username_tooltip, - None, - tooltip_theme.clone(), - cx, - ) - .into_any() - } else { - username.into_any() - }) - .with_child( - MouseEventHandler::::new(index, cx, |_, _| icon) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(icon_action.clone()) - }) - .with_tooltip::(index, icon_tooltip, None, tooltip_theme, cx), - ) - .into_any() -} diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 319df337d7..0429182bf3 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -1,4 +1,3 @@ -use crate::contacts_popover; use call::ActiveCall; use client::{proto::PeerId, Contact, User, UserStore}; use editor::{Cancel, Editor}; @@ -140,6 +139,7 @@ pub struct RespondToContactRequest { } pub enum Event { + ToggleContactFinder, Dismissed, } @@ -1116,11 +1116,14 @@ impl ContactList { ) .with_padding(Padding::uniform(2.)) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(RemoveContact { - user_id, - github_login: github_login.clone(), - }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.remove_contact( + &RemoveContact { + user_id, + github_login: github_login.clone(), + }, + cx, + ); }) .flex_float(), ) @@ -1203,11 +1206,14 @@ impl ContactList { render_icon_button(button_style, "icons/x_mark_8.svg").aligned() }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(RespondToContactRequest { - user_id, - accept: false, - }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.respond_to_contact_request( + &RespondToContactRequest { + user_id, + accept: false, + }, + cx, + ); }) .contained() .with_margin_right(button_spacing), @@ -1225,11 +1231,14 @@ impl ContactList { .flex_float() }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(RespondToContactRequest { - user_id, - accept: true, - }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.respond_to_contact_request( + &RespondToContactRequest { + user_id, + accept: true, + }, + cx, + ); }), ); } else { @@ -1246,11 +1255,14 @@ impl ContactList { }) .with_padding(Padding::uniform(2.)) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(RemoveContact { - user_id, - github_login: github_login.clone(), - }) + .on_click(MouseButton::Left, move |_, this, cx| { + this.remove_contact( + &RemoveContact { + user_id, + github_login: github_login.clone(), + }, + cx, + ); }) .flex_float(), ); @@ -1318,7 +1330,7 @@ impl View for ContactList { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(contacts_popover::ToggleContactFinder) + cx.emit(Event::ToggleContactFinder) }) .with_tooltip::( 0, diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 60f0bf0e73..b35eb09b31 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -1,7 +1,6 @@ use crate::{ contact_finder::{build_contact_finder, ContactFinder}, contact_list::ContactList, - ToggleContactsMenu, }; use client::UserStore; use gpui::{ @@ -72,8 +71,11 @@ impl ContactsPopover { let child = cx .add_view(|cx| ContactList::new(&workspace, cx).with_editor_text(editor_text, cx)); cx.focus(&child); - self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event { + self._subscription = Some(cx.subscribe(&child, |this, _, event, cx| match event { crate::contact_list::Event::Dismissed => cx.emit(Event::Dismissed), + crate::contact_list::Event::ToggleContactFinder => { + this.toggle_contact_finder(&Default::default(), cx) + } })); self.child = Child::ContactList(child); cx.notify(); @@ -106,9 +108,7 @@ impl View for ContactsPopover { .with_width(theme.contacts_popover.width) .with_height(theme.contacts_popover.height) }) - .on_down_out(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(ToggleContactsMenu); - }) + .on_down_out(MouseButton::Left, move |_, _, cx| cx.emit(Event::Dismissed)) .into_any() } diff --git a/crates/collab_ui/src/sharing_status_indicator.rs b/crates/collab_ui/src/sharing_status_indicator.rs index 42c3c886ad..447b561b95 100644 --- a/crates/collab_ui/src/sharing_status_indicator.rs +++ b/crates/collab_ui/src/sharing_status_indicator.rs @@ -1,3 +1,4 @@ +use crate::toggle_screen_sharing; use call::ActiveCall; use gpui::{ color::Color, @@ -7,8 +8,6 @@ use gpui::{ }; use settings::Settings; -use crate::ToggleScreenSharing; - pub fn init(cx: &mut AppContext) { let active_call = ActiveCall::global(cx); @@ -54,7 +53,7 @@ impl View for SharingStatusIndicator { .aligned() }) .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(ToggleScreenSharing); + toggle_screen_sharing(&Default::default(), cx) }) .into_any() } diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index 6f66d710cb..3a3ca9b66b 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -485,7 +485,11 @@ impl ContextMenu { .contained() .with_style(style.container) }) - .on_down_out(MouseButton::Left, |_, _, cx| cx.dispatch_action(Cancel)) - .on_down_out(MouseButton::Right, |_, _, cx| cx.dispatch_action(Cancel)) + .on_down_out(MouseButton::Left, |_, this, cx| { + this.cancel(&Default::default(), cx); + }) + .on_down_out(MouseButton::Right, |_, this, cx| { + this.cancel(&Default::default(), cx); + }) } } diff --git a/crates/copilot/src/sign_in.rs b/crates/copilot/src/sign_in.rs index fdb4828cd0..02a5b347d4 100644 --- a/crates/copilot/src/sign_in.rs +++ b/crates/copilot/src/sign_in.rs @@ -196,7 +196,7 @@ impl CopilotCodeVerification { .contained() .with_style(style.auth.prompting.hint.container.clone()), ) - .with_child(theme::ui::cta_button_with_click::( + .with_child(theme::ui::cta_button::( if connect_clicked { "Waiting for connection..." } else { @@ -250,7 +250,7 @@ impl CopilotCodeVerification { .contained() .with_style(enabled_style.hint.container), ) - .with_child(theme::ui::cta_button_with_click::( + .with_child(theme::ui::cta_button::( "Done", style.auth.content_width, &style.auth.cta_button, @@ -304,7 +304,7 @@ impl CopilotCodeVerification { .contained() .with_style(unauthorized_style.warning.container), ) - .with_child(theme::ui::cta_button_with_click::( + .with_child(theme::ui::cta_button::( "Subscribe on GitHub", style.auth.content_width, &style.auth.cta_button, diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 19b1506509..f0ceacc619 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -3,18 +3,19 @@ use editor::{Editor, GoToDiagnostic}; use gpui::{ elements::*, platform::{CursorStyle, MouseButton}, - serde_json, AppContext, Entity, ModelHandle, Subscription, View, ViewContext, ViewHandle, - WeakViewHandle, + serde_json, AppContext, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; use language::Diagnostic; use lsp::LanguageServerId; -use project::Project; use settings::Settings; -use workspace::{item::ItemHandle, StatusItemView}; +use workspace::{item::ItemHandle, StatusItemView, Workspace}; + +use crate::ProjectDiagnosticsEditor; pub struct DiagnosticIndicator { summary: project::DiagnosticSummary, active_editor: Option>, + workspace: WeakViewHandle, current_diagnostic: Option, in_progress_checks: HashSet, _observe_active_editor: Option, @@ -25,7 +26,8 @@ pub fn init(cx: &mut AppContext) { } impl DiagnosticIndicator { - pub fn new(project: &ModelHandle, cx: &mut ViewContext) -> Self { + pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { + let project = workspace.project(); cx.subscribe(project, |this, project, event, cx| match event { project::Event::DiskBasedDiagnosticsStarted { language_server_id } => { this.in_progress_checks.insert(*language_server_id); @@ -46,6 +48,7 @@ impl DiagnosticIndicator { .language_servers_running_disk_based_diagnostics() .collect(), active_editor: None, + workspace: workspace.weak_handle(), current_diagnostic: None, _observe_active_editor: None, } @@ -163,8 +166,12 @@ impl View for DiagnosticIndicator { }) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(crate::Deploy) + .on_click(MouseButton::Left, |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + ProjectDiagnosticsEditor::deploy(workspace, &Default::default(), cx) + }) + } }) .with_tooltip::( 0, @@ -200,8 +207,8 @@ impl View for DiagnosticIndicator { .with_margin_left(item_spacing) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(GoToDiagnostic) + .on_click(MouseButton::Left, |_, this, cx| { + this.go_to_next_diagnostic(&Default::default(), cx) }), ); } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d8c2e81ce1..0849c0ef93 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -809,10 +809,13 @@ impl CompletionsMenu { }, ) .with_cursor_style(CursorStyle::PointingHand) - .on_down(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(ConfirmCompletion { - item_ix: Some(item_ix), - }); + .on_down(MouseButton::Left, move |_, this, cx| { + this.confirm_completion( + &ConfirmCompletion { + item_ix: Some(item_ix), + }, + cx, + ); }) .into_any(), ); @@ -970,9 +973,23 @@ impl CodeActionsMenu { .with_style(item_style) }) .with_cursor_style(CursorStyle::PointingHand) - .on_down(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(ConfirmCodeAction { - item_ix: Some(item_ix), + .on_down(MouseButton::Left, move |_, this, cx| { + let workspace = this + .workspace + .as_ref() + .and_then(|(workspace, _)| workspace.upgrade(cx)); + cx.window_context().defer(move |cx| { + if let Some(workspace) = workspace { + workspace.update(cx, |workspace, cx| { + if let Some(task) = Editor::confirm_code_action( + workspace, + &Default::default(), + cx, + ) { + task.detach_and_log_err(cx); + } + }); + } }); }) .into_any(), @@ -3138,10 +3155,13 @@ impl Editor { }) .with_cursor_style(CursorStyle::PointingHand) .with_padding(Padding::uniform(3.)) - .on_down(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(ToggleCodeActions { - deployed_from_indicator: true, - }); + .on_down(MouseButton::Left, |_, this, cx| { + this.toggle_code_actions( + &ToggleCodeActions { + deployed_from_indicator: true, + }, + cx, + ); }) .into_any(), ) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 75bd572d95..7c43885763 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -211,10 +211,13 @@ impl EditorElement { enum GutterHandlers {} scene.push_mouse_region( MouseRegion::new::(cx.view_id(), cx.view_id() + 1, gutter_bounds) - .on_hover(|hover, _: &mut Editor, cx| { - cx.dispatch_action(GutterHover { - hovered: hover.started, - }) + .on_hover(|hover, editor: &mut Editor, cx| { + editor.gutter_hover( + &GutterHover { + hovered: hover.started, + }, + cx, + ); }), ) } @@ -754,8 +757,8 @@ impl EditorElement { scene.push_mouse_region( MouseRegion::new::(cx.view_id(), *id as usize, bound) - .on_click(MouseButton::Left, move |_, _: &mut Editor, cx| { - cx.dispatch_action(UnfoldAt { buffer_row }) + .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| { + editor.unfold_at(&UnfoldAt { buffer_row }, cx) }) .with_notify_on_hover(true) .with_notify_on_click(true), diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 2932fa547e..438c662ed1 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -1,3 +1,7 @@ +use crate::{ + display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSnapshot, + EditorStyle, RangeToAnchorExt, +}; use futures::FutureExt; use gpui::{ actions, @@ -12,11 +16,6 @@ use settings::Settings; use std::{ops::Range, sync::Arc, time::Duration}; use util::TryFutureExt; -use crate::{ - display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSnapshot, - EditorStyle, GoToDiagnostic, RangeToAnchorExt, -}; - pub const HOVER_DELAY_MILLIS: u64 = 350; pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200; @@ -668,8 +667,8 @@ impl DiagnosticPopover { ..Default::default() }) .on_move(|_, _, _| {}) // Consume move events so they don't reach regions underneath. - .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(GoToDiagnostic) + .on_click(MouseButton::Left, |_, this, cx| { + this.go_to_diagnostic(&Default::default(), cx) }) .with_cursor_style(CursorStyle::PointingHand) .with_tooltip::( diff --git a/crates/feedback/src/deploy_feedback_button.rs b/crates/feedback/src/deploy_feedback_button.rs index 9536477c74..b464d00887 100644 --- a/crates/feedback/src/deploy_feedback_button.rs +++ b/crates/feedback/src/deploy_feedback_button.rs @@ -1,15 +1,16 @@ use gpui::{ elements::*, platform::{CursorStyle, MouseButton}, - Entity, View, ViewContext, + Entity, View, ViewContext, WeakViewHandle, }; use settings::Settings; -use workspace::{item::ItemHandle, StatusItemView}; +use workspace::{item::ItemHandle, StatusItemView, Workspace}; use crate::feedback_editor::{FeedbackEditor, GiveFeedback}; pub struct DeployFeedbackButton { active: bool, + workspace: WeakViewHandle, } impl Entity for DeployFeedbackButton { @@ -17,8 +18,11 @@ impl Entity for DeployFeedbackButton { } impl DeployFeedbackButton { - pub fn new() -> Self { - DeployFeedbackButton { active: false } + pub fn new(workspace: &Workspace) -> Self { + DeployFeedbackButton { + active: false, + workspace: workspace.weak_handle(), + } } } @@ -52,9 +56,12 @@ impl View for DeployFeedbackButton { .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { + .on_click(MouseButton::Left, move |_, this, cx| { if !active { - cx.dispatch_action(GiveFeedback) + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace + .update(cx, |workspace, cx| FeedbackEditor::deploy(workspace, cx)) + } } }) .with_tooltip::( diff --git a/crates/feedback/src/feedback.rs b/crates/feedback/src/feedback.rs index a8860f7bc5..7cbb3a673b 100644 --- a/crates/feedback/src/feedback.rs +++ b/crates/feedback/src/feedback.rs @@ -3,20 +3,10 @@ pub mod feedback_editor; pub mod feedback_info_text; pub mod submit_feedback_button; -use std::sync::Arc; - mod system_specs; -use gpui::{actions, impl_actions, platform::PromptLevel, AppContext, ClipboardItem, ViewContext}; -use serde::Deserialize; +use gpui::{actions, platform::PromptLevel, AppContext, ClipboardItem, ViewContext}; use system_specs::SystemSpecs; -use workspace::{AppState, Workspace}; - -#[derive(Deserialize, Clone, PartialEq)] -pub struct OpenBrowser { - pub url: Arc, -} - -impl_actions!(zed, [OpenBrowser]); +use workspace::Workspace; actions!( zed, @@ -28,29 +18,20 @@ actions!( ] ); -pub fn init(app_state: Arc, cx: &mut AppContext) { - let system_specs = SystemSpecs::new(&cx); - let system_specs_text = system_specs.to_string(); - - feedback_editor::init(system_specs, app_state, cx); - - cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url)); - - let url = format!( - "https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml&environment={}", - urlencoding::encode(&system_specs_text) - ); +pub fn init(cx: &mut AppContext) { + feedback_editor::init(cx); cx.add_action( move |_: &mut Workspace, _: &CopySystemSpecsIntoClipboard, cx: &mut ViewContext| { + let specs = SystemSpecs::new(&cx).to_string(); cx.prompt( PromptLevel::Info, - &format!("Copied into clipboard:\n\n{system_specs_text}"), + &format!("Copied into clipboard:\n\n{specs}"), &["OK"], ); - let item = ClipboardItem::new(system_specs_text.clone()); + let item = ClipboardItem::new(specs.clone()); cx.write_to_clipboard(item); }, ); @@ -58,24 +39,24 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { cx.add_action( |_: &mut Workspace, _: &RequestFeature, cx: &mut ViewContext| { let url = "https://github.com/zed-industries/community/issues/new?assignees=&labels=enhancement%2Ctriage&template=0_feature_request.yml"; - cx.dispatch_action(OpenBrowser { - url: url.into(), - }); + cx.platform().open_url(url); }, ); cx.add_action( move |_: &mut Workspace, _: &FileBugReport, cx: &mut ViewContext| { - cx.dispatch_action(OpenBrowser { - url: url.clone().into(), - }); + let url = format!( + "https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml&environment={}", + urlencoding::encode(&SystemSpecs::new(&cx).to_string()) + ); + cx.platform().open_url(&url); }, ); - cx.add_action( - |_: &mut Workspace, _: &OpenZedCommunityRepo, cx: &mut ViewContext| { - let url = "https://github.com/zed-industries/community"; - cx.dispatch_action(OpenBrowser { url: url.into() }); - }, - ); + cx.add_global_action(open_zed_community_repo); +} + +pub fn open_zed_community_repo(_: &OpenZedCommunityRepo, cx: &mut AppContext) { + let url = "https://github.com/zed-industries/community"; + cx.platform().open_url(&url); } diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index 8f41762eed..253cc511ee 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -1,10 +1,4 @@ -use std::{ - any::TypeId, - borrow::Cow, - ops::{Range, RangeInclusive}, - sync::Arc, -}; - +use crate::system_specs::SystemSpecs; use anyhow::bail; use client::{Client, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL}; use editor::{Anchor, Editor}; @@ -19,40 +13,34 @@ use gpui::{ use isahc::Request; use language::Buffer; use postage::prelude::Stream; - use project::Project; use serde::Serialize; +use std::{ + any::TypeId, + borrow::Cow, + ops::{Range, RangeInclusive}, + sync::Arc, +}; use util::ResultExt; use workspace::{ - item::{Item, ItemHandle}, + item::{Item, ItemEvent, ItemHandle}, searchable::{SearchableItem, SearchableItemHandle}, - AppState, Workspace, + smallvec::SmallVec, + Workspace, }; -use crate::{submit_feedback_button::SubmitFeedbackButton, system_specs::SystemSpecs}; - const FEEDBACK_CHAR_LIMIT: RangeInclusive = 10..=5000; const FEEDBACK_SUBMISSION_ERROR_TEXT: &str = "Feedback failed to submit, see error log for details."; actions!(feedback, [GiveFeedback, SubmitFeedback]); -pub fn init(system_specs: SystemSpecs, app_state: Arc, cx: &mut AppContext) { +pub fn init(cx: &mut AppContext) { cx.add_action({ move |workspace: &mut Workspace, _: &GiveFeedback, cx: &mut ViewContext| { - FeedbackEditor::deploy(system_specs.clone(), workspace, app_state.clone(), cx); + FeedbackEditor::deploy(workspace, cx); } }); - - cx.add_async_action( - |submit_feedback_button: &mut SubmitFeedbackButton, _: &SubmitFeedback, cx| { - if let Some(active_item) = submit_feedback_button.active_item.as_ref() { - Some(active_item.update(cx, |feedback_editor, cx| feedback_editor.handle_save(cx))) - } else { - None - } - }, - ); } #[derive(Serialize)] @@ -94,7 +82,7 @@ impl FeedbackEditor { } } - fn handle_save(&mut self, cx: &mut ViewContext) -> Task> { + pub fn submit(&mut self, cx: &mut ViewContext) -> Task> { let feedback_text = self.editor.read(cx).text(cx); let feedback_char_count = feedback_text.chars().count(); let feedback_text = feedback_text.trim().to_string(); @@ -133,10 +121,8 @@ impl FeedbackEditor { if answer == Some(0) { match FeedbackEditor::submit_feedback(&feedback_text, client, specs).await { Ok(_) => { - this.update(&mut cx, |_, cx| { - cx.dispatch_action(workspace::CloseActiveItem); - }) - .log_err(); + this.update(&mut cx, |_, cx| cx.emit(editor::Event::Closed)) + .log_err(); } Err(error) => { log::error!("{}", error); @@ -198,22 +184,21 @@ impl FeedbackEditor { } impl FeedbackEditor { - pub fn deploy( - system_specs: SystemSpecs, - _: &mut Workspace, - app_state: Arc, - cx: &mut ViewContext, - ) { - let markdown = app_state.languages.language_for_name("Markdown"); + pub fn deploy(workspace: &mut Workspace, cx: &mut ViewContext) { + let markdown = workspace + .app_state() + .languages + .language_for_name("Markdown"); cx.spawn(|workspace, mut cx| async move { let markdown = markdown.await.log_err(); workspace .update(&mut cx, |workspace, cx| { - workspace.with_local_workspace(&app_state, cx, |workspace, cx| { + workspace.with_local_workspace(cx, |workspace, cx| { let project = workspace.project().clone(); let buffer = project .update(cx, |project, cx| project.create_buffer("", markdown, cx)) .expect("creating buffers on a local workspace always succeeds"); + let system_specs = SystemSpecs::new(cx); let feedback_editor = cx .add_view(|cx| FeedbackEditor::new(system_specs, project, buffer, cx)); workspace.add_item(Box::new(feedback_editor), cx); @@ -291,7 +276,7 @@ impl Item for FeedbackEditor { _: ModelHandle, cx: &mut ViewContext, ) -> Task> { - self.handle_save(cx) + self.submit(cx) } fn save_as( @@ -300,7 +285,7 @@ impl Item for FeedbackEditor { _: std::path::PathBuf, cx: &mut ViewContext, ) -> Task> { - self.handle_save(cx) + self.submit(cx) } fn reload( @@ -353,6 +338,10 @@ impl Item for FeedbackEditor { None } } + + fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> { + Editor::to_item_events(event) + } } impl SearchableItem for FeedbackEditor { diff --git a/crates/feedback/src/feedback_info_text.rs b/crates/feedback/src/feedback_info_text.rs index b557c4f7e1..9aee4e0e68 100644 --- a/crates/feedback/src/feedback_info_text.rs +++ b/crates/feedback/src/feedback_info_text.rs @@ -6,7 +6,7 @@ use gpui::{ use settings::Settings; use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView}; -use crate::{feedback_editor::FeedbackEditor, OpenZedCommunityRepo}; +use crate::{feedback_editor::FeedbackEditor, open_zed_community_repo, OpenZedCommunityRepo}; pub struct FeedbackInfoText { active_item: Option>, @@ -57,7 +57,7 @@ impl View for FeedbackInfoText { }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(OpenZedCommunityRepo) + open_zed_community_repo(&Default::default(), cx) }), ) .with_child( diff --git a/crates/feedback/src/submit_feedback_button.rs b/crates/feedback/src/submit_feedback_button.rs index 918c74bed8..ccd58c3dc9 100644 --- a/crates/feedback/src/submit_feedback_button.rs +++ b/crates/feedback/src/submit_feedback_button.rs @@ -1,12 +1,16 @@ +use crate::feedback_editor::{FeedbackEditor, SubmitFeedback}; +use anyhow::Result; use gpui::{ elements::{Label, MouseEventHandler}, platform::{CursorStyle, MouseButton}, - AnyElement, Element, Entity, View, ViewContext, ViewHandle, + AnyElement, AppContext, Element, Entity, Task, View, ViewContext, ViewHandle, }; use settings::Settings; use workspace::{item::ItemHandle, ToolbarItemLocation, ToolbarItemView}; -use crate::feedback_editor::{FeedbackEditor, SubmitFeedback}; +pub fn init(cx: &mut AppContext) { + cx.add_async_action(SubmitFeedbackButton::submit); +} pub struct SubmitFeedbackButton { pub(crate) active_item: Option>, @@ -18,6 +22,18 @@ impl SubmitFeedbackButton { active_item: Default::default(), } } + + pub fn submit( + &mut self, + _: &SubmitFeedback, + cx: &mut ViewContext, + ) -> Option>> { + if let Some(active_item) = self.active_item.as_ref() { + Some(active_item.update(cx, |feedback_editor, cx| feedback_editor.submit(cx))) + } else { + None + } + } } impl Entity for SubmitFeedbackButton { @@ -39,8 +55,8 @@ impl View for SubmitFeedbackButton { .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(SubmitFeedback) + .on_click(MouseButton::Left, |_, this, cx| { + this.submit(&Default::default(), cx); }) .aligned() .contained() diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 7343a7245d..02d6c1a2ac 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1745,10 +1745,6 @@ impl AppContext { self.pending_effects.push_back(Effect::RefreshWindows); } - fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: impl Action) { - self.dispatch_any_action_at(window_id, view_id, Box::new(action)); - } - pub fn dispatch_any_action_at( &mut self, window_id: usize, @@ -3189,13 +3185,6 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { self.window_context.notify_view(window_id, view_id); } - pub fn dispatch_action(&mut self, action: impl Action) { - let window_id = self.window_id; - let view_id = self.view_id; - self.window_context - .dispatch_action_at(window_id, view_id, action) - } - pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext)) { let handle = self.handle(); self.window_context diff --git a/crates/gpui/src/views/select.rs b/crates/gpui/src/views/select.rs index 285f37639e..f3be9de3ec 100644 --- a/crates/gpui/src/views/select.rs +++ b/crates/gpui/src/views/select.rs @@ -1,8 +1,8 @@ use serde::Deserialize; use crate::{ - actions, elements::*, impl_actions, platform::MouseButton, AppContext, Entity, EventContext, - View, ViewContext, WeakViewHandle, + actions, elements::*, impl_actions, platform::MouseButton, AppContext, Entity, View, + ViewContext, WeakViewHandle, }; pub struct Select { @@ -116,10 +116,9 @@ impl View for Select { .contained() .with_style(style.header) }) - .on_click( - MouseButton::Left, - move |_, _, cx: &mut EventContext| cx.dispatch_action(ToggleSelect), - ), + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle(&Default::default(), cx); + }), ); if self.is_open { result.add_child(Overlay::new( @@ -143,12 +142,9 @@ impl View for Select { cx, ) }) - .on_click( - MouseButton::Left, - move |_, _, cx: &mut EventContext| { - cx.dispatch_action(SelectItem(ix)) - }, - ) + .on_click(MouseButton::Left, move |_, this, cx| { + this.select_item(&SelectItem(ix), cx); + }) .into_any() })) }, diff --git a/crates/language_selector/src/active_buffer_language.rs b/crates/language_selector/src/active_buffer_language.rs index 17e53b378c..425f4c8dd7 100644 --- a/crates/language_selector/src/active_buffer_language.rs +++ b/crates/language_selector/src/active_buffer_language.rs @@ -2,27 +2,23 @@ use editor::Editor; use gpui::{ elements::*, platform::{CursorStyle, MouseButton}, - Entity, Subscription, View, ViewContext, ViewHandle, + Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, }; use settings::Settings; use std::sync::Arc; -use workspace::{item::ItemHandle, StatusItemView}; +use workspace::{item::ItemHandle, StatusItemView, Workspace}; pub struct ActiveBufferLanguage { active_language: Option>>, + workspace: WeakViewHandle, _observe_active_editor: Option, } -impl Default for ActiveBufferLanguage { - fn default() -> Self { - Self::new() - } -} - impl ActiveBufferLanguage { - pub fn new() -> Self { + pub fn new(workspace: &Workspace) -> Self { Self { active_language: None, + workspace: workspace.weak_handle(), _observe_active_editor: None, } } @@ -66,8 +62,12 @@ impl View for ActiveBufferLanguage { .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(crate::Toggle) + .on_click(MouseButton::Left, |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + crate::toggle(workspace, &Default::default(), cx) + }); + } }) .into_any() } else { diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index 29da7c926d..fd43111443 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -11,21 +11,18 @@ use project::Project; use settings::Settings; use std::sync::Arc; use util::ResultExt; -use workspace::{AppState, Workspace}; +use workspace::Workspace; actions!(language_selector, [Toggle]); -pub fn init(app_state: Arc, cx: &mut AppContext) { +pub fn init(cx: &mut AppContext) { Picker::::init(cx); - cx.add_action({ - let language_registry = app_state.languages.clone(); - move |workspace, _: &Toggle, cx| toggle(workspace, language_registry.clone(), cx) - }); + cx.add_action(toggle); } -fn toggle( +pub fn toggle( workspace: &mut Workspace, - registry: Arc, + _: &Toggle, cx: &mut ViewContext, ) -> Option<()> { let (_, buffer, _) = workspace @@ -34,6 +31,7 @@ fn toggle( .read(cx) .active_excerpt(cx)?; workspace.toggle_modal(cx, |workspace, cx| { + let registry = workspace.app_state().languages.clone(); cx.add_view(|cx| { Picker::new( LanguageSelectorDelegate::new(buffer, workspace.project().clone(), registry), diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index b2154e7bb2..6ecaf370e4 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -24,7 +24,7 @@ pub fn init(cx: &mut AppContext) { OutlineView::init(cx); } -fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { +pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { if let Some(editor) = workspace .active_item(cx) .and_then(|item| item.downcast::()) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 0ca187bfe5..373417b167 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -13,7 +13,7 @@ use gpui::{ keymap_matcher::KeymapContext, platform::{CursorStyle, MouseButton, PromptLevel}, AnyElement, AppContext, ClipboardItem, Element, Entity, ModelHandle, Task, View, ViewContext, - ViewHandle, + ViewHandle, WeakViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; use project::{Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; @@ -44,6 +44,7 @@ pub struct ProjectPanel { clipboard_entry: Option, context_menu: ViewHandle, dragged_entry_destination: Option>, + workspace: WeakViewHandle, } #[derive(Copy, Clone)] @@ -137,7 +138,8 @@ pub enum Event { } impl ProjectPanel { - pub fn new(project: ModelHandle, cx: &mut ViewContext) -> ViewHandle { + pub fn new(workspace: &mut Workspace, cx: &mut ViewContext) -> ViewHandle { + let project = workspace.project().clone(); let project_panel = cx.add_view(|cx: &mut ViewContext| { cx.observe(&project, |this, _, cx| { this.update_visible_entries(None, cx); @@ -206,6 +208,7 @@ impl ProjectPanel { clipboard_entry: None, context_menu: cx.add_view(ContextMenu::new), dragged_entry_destination: None, + workspace: workspace.weak_handle(), }; this.update_visible_entries(None, cx); this @@ -1296,8 +1299,14 @@ impl View for ProjectPanel { ) } }) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(workspace::Open) + .on_click(MouseButton::Left, move |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + if let Some(task) = workspace.open(&Default::default(), cx) { + task.detach_and_log_err(cx); + } + }) + } }) .with_cursor_style(CursorStyle::PointingHand), ) @@ -1400,7 +1409,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); - let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx)); + let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); assert_eq!( visible_entries_as_strings(&panel, 0..50, cx), &[ @@ -1492,7 +1501,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); - let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx)); + let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); select_path(&panel, "root1", cx); assert_eq!( @@ -1785,7 +1794,7 @@ mod tests { let project = Project::test(fs.clone(), ["/root1".as_ref()], cx).await; let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); - let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx)); + let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); panel.update(cx, |panel, cx| { panel.select_next(&Default::default(), cx); diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 414b3e9323..644e74d878 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -11,24 +11,24 @@ use highlighted_workspace_location::HighlightedWorkspaceLocation; use ordered_float::OrderedFloat; use picker::{Picker, PickerDelegate, PickerEvent}; use settings::Settings; -use std::sync::{Arc, Weak}; +use std::sync::Arc; use workspace::{ - notifications::simple_message_notification::MessageNotification, AppState, Workspace, - WorkspaceLocation, WORKSPACE_DB, + notifications::simple_message_notification::MessageNotification, Workspace, WorkspaceLocation, + WORKSPACE_DB, }; actions!(projects, [OpenRecent]); -pub fn init(cx: &mut AppContext, app_state: Weak) { - cx.add_async_action( - move |_: &mut Workspace, _: &OpenRecent, cx: &mut ViewContext| { - toggle(app_state.clone(), cx) - }, - ); +pub fn init(cx: &mut AppContext) { + cx.add_async_action(toggle); RecentProjects::init(cx); } -fn toggle(app_state: Weak, cx: &mut ViewContext) -> Option>> { +fn toggle( + _: &mut Workspace, + _: &OpenRecent, + cx: &mut ViewContext, +) -> Option>> { Some(cx.spawn(|workspace, mut cx| async move { let workspace_locations: Vec<_> = cx .background() @@ -49,11 +49,7 @@ fn toggle(app_state: Weak, cx: &mut ViewContext) -> Option< let workspace = cx.weak_handle(); cx.add_view(|cx| { RecentProjects::new( - RecentProjectsDelegate::new( - workspace, - workspace_locations, - app_state.clone(), - ), + RecentProjectsDelegate::new(workspace, workspace_locations), cx, ) .with_max_size(800., 1200.) @@ -74,7 +70,6 @@ type RecentProjects = Picker; struct RecentProjectsDelegate { workspace: WeakViewHandle, workspace_locations: Vec, - app_state: Weak, selected_match_index: usize, matches: Vec, } @@ -83,12 +78,10 @@ impl RecentProjectsDelegate { fn new( workspace: WeakViewHandle, workspace_locations: Vec, - app_state: Weak, ) -> Self { Self { workspace, workspace_locations, - app_state, selected_match_index: 0, matches: Default::default(), } @@ -155,20 +148,16 @@ impl PickerDelegate for RecentProjectsDelegate { } fn confirm(&mut self, cx: &mut ViewContext) { - if let Some(((selected_match, workspace), app_state)) = self + if let Some((selected_match, workspace)) = self .matches .get(self.selected_index()) .zip(self.workspace.upgrade(cx)) - .zip(self.app_state.upgrade()) { let workspace_location = &self.workspace_locations[selected_match.candidate_id]; workspace .update(cx, |workspace, cx| { - workspace.open_workspace_for_paths( - workspace_location.paths().as_ref().clone(), - app_state, - cx, - ) + workspace + .open_workspace_for_paths(workspace_location.paths().as_ref().clone(), cx) }) .detach_and_log_err(cx); cx.emit(PickerEvent::Dismiss); diff --git a/crates/terminal_view/src/terminal_button.rs b/crates/terminal_view/src/terminal_button.rs index 6349cbbfa4..8edf03f527 100644 --- a/crates/terminal_view/src/terminal_button.rs +++ b/crates/terminal_view/src/terminal_button.rs @@ -7,7 +7,11 @@ use gpui::{ }; use settings::Settings; use std::any::TypeId; -use workspace::{dock::FocusDock, item::ItemHandle, NewTerminal, StatusItemView, Workspace}; +use workspace::{ + dock::{Dock, FocusDock}, + item::ItemHandle, + NewTerminal, StatusItemView, Workspace, +}; pub struct TerminalButton { workspace: WeakViewHandle, @@ -80,7 +84,11 @@ impl View for TerminalButton { this.deploy_terminal_menu(cx); } else { if !active { - cx.dispatch_action(FocusDock); + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + Dock::focus_dock(workspace, &Default::default(), cx) + }) + } } }; }) diff --git a/crates/theme/src/ui.rs b/crates/theme/src/ui.rs index 1198e81e92..b86bfca8c4 100644 --- a/crates/theme/src/ui.rs +++ b/crates/theme/src/ui.rs @@ -156,24 +156,7 @@ pub fn keystroke_label( pub type ButtonStyle = Interactive; -pub fn cta_button( - label: L, - action: A, - max_width: f32, - style: &ButtonStyle, - cx: &mut ViewContext, -) -> MouseEventHandler -where - L: Into>, - A: 'static + Action + Clone, - V: View, -{ - cta_button_with_click::(label, max_width, style, cx, move |_, _, cx| { - cx.dispatch_action(action.clone()) - }) -} - -pub fn cta_button_with_click( +pub fn cta_button( label: L, max_width: f32, style: &ButtonStyle, diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 1f2d73df14..21332114e2 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -6,20 +6,18 @@ use staff_mode::StaffMode; use std::sync::Arc; use theme::{Theme, ThemeMeta, ThemeRegistry}; use util::ResultExt; -use workspace::{AppState, Workspace}; +use workspace::Workspace; actions!(theme_selector, [Toggle, Reload]); -pub fn init(app_state: Arc, cx: &mut AppContext) { - cx.add_action({ - let theme_registry = app_state.themes.clone(); - move |workspace, _: &Toggle, cx| toggle(workspace, theme_registry.clone(), cx) - }); +pub fn init(cx: &mut AppContext) { + cx.add_action(toggle); ThemeSelector::init(cx); } -fn toggle(workspace: &mut Workspace, themes: Arc, cx: &mut ViewContext) { - workspace.toggle_modal(cx, |_, cx| { +pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { + workspace.toggle_modal(cx, |workspace, cx| { + let themes = workspace.app_state().themes.clone(); cx.add_view(|cx| ThemeSelector::new(ThemeSelectorDelegate::new(themes, cx), cx)) }); } diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index 7347a559a9..260c279e18 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -18,7 +18,7 @@ pub fn init(cx: &mut AppContext) { BaseKeymapSelector::init(cx); } -fn toggle( +pub fn toggle( workspace: &mut Workspace, _: &ToggleBaseKeymapSelector, cx: &mut ViewContext, diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 629e6f3989..a3d91adc91 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -5,7 +5,7 @@ use std::{borrow::Cow, sync::Arc}; use db::kvp::KEY_VALUE_STORE; use gpui::{ elements::{Flex, Label, ParentElement}, - AnyElement, AppContext, Element, Entity, Subscription, View, ViewContext, + AnyElement, AppContext, Element, Entity, Subscription, View, ViewContext, WeakViewHandle, }; use settings::{settings_file::SettingsFile, Settings}; @@ -20,7 +20,7 @@ pub const FIRST_OPEN: &str = "first_open"; pub fn init(cx: &mut AppContext) { cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { - let welcome_page = cx.add_view(WelcomePage::new); + let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); workspace.add_item(Box::new(welcome_page), cx) }); @@ -30,7 +30,7 @@ pub fn init(cx: &mut AppContext) { pub fn show_welcome_experience(app_state: &Arc, cx: &mut AppContext) { open_new(&app_state, cx, |workspace, cx| { workspace.toggle_sidebar(SidebarSide::Left, cx); - let welcome_page = cx.add_view(|cx| WelcomePage::new(cx)); + let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); cx.focus(&welcome_page); cx.notify(); @@ -43,6 +43,7 @@ pub fn show_welcome_experience(app_state: &Arc, cx: &mut AppContext) { } pub struct WelcomePage { + workspace: WeakViewHandle, _settings_subscription: Subscription, } @@ -97,26 +98,46 @@ impl View for WelcomePage { ) .with_child( Flex::column() - .with_child(theme::ui::cta_button( + .with_child(theme::ui::cta_button::( "Choose a theme", - theme_selector::Toggle, width, &theme.welcome.button, cx, + |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + theme_selector::toggle(workspace, &Default::default(), cx) + }) + } + }, )) - .with_child(theme::ui::cta_button( + .with_child(theme::ui::cta_button::( "Choose a keymap", - ToggleBaseKeymapSelector, width, &theme.welcome.button, cx, + |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + base_keymap_picker::toggle( + workspace, + &Default::default(), + cx, + ) + }) + } + }, )) - .with_child(theme::ui::cta_button( + .with_child(theme::ui::cta_button::( "Install the CLI", - install_cli::Install, width, &theme.welcome.button, cx, + |_, _, cx| { + cx.app_context() + .spawn(|cx| async move { install_cli::install_cli(&cx).await }) + .detach_and_log_err(cx); + }, )) .contained() .with_style(theme.welcome.button_group) @@ -190,8 +211,9 @@ impl View for WelcomePage { } impl WelcomePage { - pub fn new(cx: &mut ViewContext) -> Self { + pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { WelcomePage { + workspace: workspace.weak_handle(), _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), } } @@ -220,11 +242,15 @@ impl Item for WelcomePage { fn show_toolbar(&self) -> bool { false } + fn clone_on_split( &self, _workspace_id: WorkspaceId, cx: &mut ViewContext, ) -> Option { - Some(WelcomePage::new(cx)) + Some(WelcomePage { + workspace: self.workspace.clone(), + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + }) } } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 33cd833019..8ac432dc47 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -271,11 +271,11 @@ impl Dock { } } - fn focus_dock(workspace: &mut Workspace, _: &FocusDock, cx: &mut ViewContext) { + pub fn focus_dock(workspace: &mut Workspace, _: &FocusDock, cx: &mut ViewContext) { Self::set_dock_position(workspace, workspace.dock.position.show(), true, cx); } - fn hide_dock(workspace: &mut Workspace, _: &HideDock, cx: &mut ViewContext) { + pub fn hide_dock(workspace: &mut Workspace, _: &HideDock, cx: &mut ViewContext) { Self::set_dock_position(workspace, workspace.dock.position.hide(), true, cx); } @@ -374,8 +374,8 @@ impl Dock { .with_background_color(style.wash_color) }) .capture_all() - .on_down(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(HideDock); + .on_down(MouseButton::Left, |_, workspace, cx| { + Dock::hide_dock(workspace, &Default::default(), cx) }) .with_cursor_style(CursorStyle::Arrow), ) diff --git a/crates/workspace/src/dock/toggle_dock_button.rs b/crates/workspace/src/dock/toggle_dock_button.rs index bf85183938..1fda55b783 100644 --- a/crates/workspace/src/dock/toggle_dock_button.rs +++ b/crates/workspace/src/dock/toggle_dock_button.rs @@ -1,3 +1,5 @@ +use super::{icon_for_dock_anchor, Dock, FocusDock, HideDock}; +use crate::{handle_dropped_item, StatusItemView, Workspace}; use gpui::{ elements::{Empty, MouseEventHandler, Svg}, platform::CursorStyle, @@ -6,10 +8,6 @@ use gpui::{ }; use settings::Settings; -use crate::{handle_dropped_item, StatusItemView, Workspace}; - -use super::{icon_for_dock_anchor, FocusDock, HideDock}; - pub struct ToggleDockButton { workspace: WeakViewHandle, } @@ -82,8 +80,12 @@ impl View for ToggleDockButton { if dock_position.is_visible() { button - .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(HideDock); + .on_click(MouseButton::Left, |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + Dock::hide_dock(workspace, &Default::default(), cx) + }) + } }) .with_tooltip::( 0, @@ -94,8 +96,12 @@ impl View for ToggleDockButton { ) } else { button - .on_click(MouseButton::Left, |_, _, cx| { - cx.dispatch_action(FocusDock); + .on_click(MouseButton::Left, |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + Dock::focus_dock(workspace, &Default::default(), cx) + }) + } }) .with_tooltip::( 0, diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 455ffb2bb0..7881603bbc 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -278,8 +278,8 @@ pub mod simple_message_notification { .with_height(style.button_width) }) .with_padding(Padding::uniform(5.)) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(CancelMessageNotification) + .on_click(MouseButton::Left, move |_, this, cx| { + this.dismiss(&Default::default(), cx); }) .with_cursor_style(CursorStyle::PointingHand) .aligned() diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 41f4d5d111..2631f72fd4 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2,7 +2,7 @@ mod dragged_item_receiver; use super::{ItemHandle, SplitDirection}; use crate::{ - dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, ExpandDock, HideDock}, + dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, Dock, ExpandDock}, item::WeakItemHandle, toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, Workspace, @@ -259,6 +259,10 @@ impl Pane { } } + pub(crate) fn workspace(&self) -> &WeakViewHandle { + &self.workspace + } + pub fn is_active(&self) -> bool { self.is_active } @@ -1340,8 +1344,8 @@ impl Pane { cx, ) }) - .on_down(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(ActivateItem(ix)); + .on_down(MouseButton::Left, move |_, this, cx| { + this.activate_item(ix, true, true, cx); }) .on_click(MouseButton::Middle, { let item_id = item.id(); @@ -1639,7 +1643,13 @@ impl Pane { 3, "icons/x_mark_8.svg", cx, - |_, cx| cx.dispatch_action(HideDock), + |this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + Dock::hide_dock(workspace, &Default::default(), cx) + }) + } + }, None, ) })) @@ -1693,8 +1703,8 @@ impl View for Pane { }) .on_down( MouseButton::Left, - move |_, _, cx| { - cx.dispatch_action(ActivateItem(active_item_index)); + move |_, this, cx| { + this.activate_item(active_item_index, true, true, cx); }, ), ); @@ -1759,15 +1769,27 @@ impl View for Pane { }) .on_down( MouseButton::Navigate(NavigationDirection::Back), - move |_, _, cx| { - let pane = cx.weak_handle(); - cx.dispatch_action(GoBack { pane: Some(pane) }); + move |_, pane, cx| { + if let Some(workspace) = pane.workspace.upgrade(cx) { + let pane = cx.weak_handle(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + Pane::go_back(workspace, Some(pane), cx).detach_and_log_err(cx) + }) + }) + } }, ) .on_down(MouseButton::Navigate(NavigationDirection::Forward), { - move |_, _, cx| { - let pane = cx.weak_handle(); - cx.dispatch_action(GoForward { pane: Some(pane) }) + move |_, pane, cx| { + if let Some(workspace) = pane.workspace.upgrade(cx) { + let pane = cx.weak_handle(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + Pane::go_forward(workspace, Some(pane), cx).detach_and_log_err(cx) + }) + }) + } } }) .into_any_named("pane") diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index 2581c87f42..2b114d83ec 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -279,9 +279,9 @@ impl View for SidebarButtons { .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, { - let action = action.clone(); - move |_, _, cx| cx.dispatch_action(action.clone()) + .on_click(MouseButton::Left, move |_, this, cx| { + this.sidebar + .update(cx, |sidebar, cx| sidebar.toggle_item(ix, cx)); }) .with_tooltip::( ix, diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index e9cc90f64d..a940bd09b2 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -130,8 +130,20 @@ impl View for Toolbar { tooltip_style.clone(), enable_go_backward, spacing, - super::GoBack { - pane: Some(pane.clone()), + { + let pane = pane.clone(); + move |toolbar, cx| { + if let Some(workspace) = toolbar + .pane + .upgrade(cx) + .and_then(|pane| pane.read(cx).workspace().upgrade(cx)) + { + workspace.update(cx, |workspace, cx| { + Pane::go_back(workspace, Some(pane.clone()), cx) + .detach_and_log_err(cx); + }); + } + } }, super::GoBack { pane: None }, "Go Back", @@ -143,7 +155,21 @@ impl View for Toolbar { tooltip_style, enable_go_forward, spacing, - super::GoForward { pane: Some(pane) }, + { + let pane = pane.clone(); + move |toolbar, cx| { + if let Some(workspace) = toolbar + .pane + .upgrade(cx) + .and_then(|pane| pane.read(cx).workspace().upgrade(cx)) + { + workspace.update(cx, |workspace, cx| { + Pane::go_forward(workspace, Some(pane.clone()), cx) + .detach_and_log_err(cx); + }); + } + } + }, super::GoForward { pane: None }, "Go Forward", cx, @@ -161,13 +187,13 @@ impl View for Toolbar { } #[allow(clippy::too_many_arguments)] -fn nav_button( +fn nav_button)>( svg_path: &'static str, style: theme::Interactive, tooltip_style: TooltipStyle, enabled: bool, spacing: f32, - action: A, + on_click: F, tooltip_action: A, action_name: &str, cx: &mut ViewContext, @@ -195,8 +221,8 @@ fn nav_button( } else { CursorStyle::default() }) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_action(action.clone()) + .on_click(MouseButton::Left, move |_, toolbar, cx| { + on_click(toolbar, cx) }) .with_tooltip::( 0, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 6a1f7aa8bb..2a7748af0f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -208,48 +208,7 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { } } }); - cx.add_action({ - let app_state = Arc::downgrade(&app_state); - move |_, _: &Open, cx: &mut ViewContext| { - let mut paths = cx.prompt_for_paths(PathPromptOptions { - files: true, - directories: true, - multiple: true, - }); - - if let Some(app_state) = app_state.upgrade() { - cx.spawn(|this, mut cx| async move { - if let Some(paths) = paths.recv().await.flatten() { - if let Some(task) = this - .update(&mut cx, |this, cx| { - this.open_workspace_for_paths(paths, app_state, cx) - }) - .log_err() - { - task.await.log_err(); - } - } - }) - .detach(); - } - } - }); - cx.add_global_action({ - let app_state = Arc::downgrade(&app_state); - move |_: &NewWindow, cx: &mut AppContext| { - if let Some(app_state) = app_state.upgrade() { - open_new(&app_state, cx, |_, cx| cx.dispatch_action(NewFile)).detach(); - } - } - }); - cx.add_global_action({ - let app_state = Arc::downgrade(&app_state); - move |_: &NewFile, cx: &mut AppContext| { - if let Some(app_state) = app_state.upgrade() { - open_new(&app_state, cx, |_, cx| cx.dispatch_action(NewFile)).detach(); - } - } - }); + cx.add_async_action(Workspace::open); cx.add_async_action(Workspace::follow_next_collaborator); cx.add_async_action(Workspace::close); @@ -913,7 +872,6 @@ impl Workspace { /// to the callback. Otherwise, a new empty window will be created. pub fn with_local_workspace( &mut self, - app_state: &Arc, cx: &mut ViewContext, callback: F, ) -> Task> @@ -924,7 +882,7 @@ impl Workspace { if self.project.read(cx).is_local() { Task::Ready(Some(Ok(callback(self, cx)))) } else { - let task = Self::new_local(Vec::new(), app_state.clone(), None, cx); + let task = Self::new_local(Vec::new(), self.app_state.clone(), None, cx); cx.spawn(|_vh, mut cx| async move { let (workspace, _) = task.await; workspace.update(&mut cx, callback) @@ -1093,10 +1051,29 @@ impl Workspace { }) } + pub fn open(&mut self, _: &Open, cx: &mut ViewContext) -> Option>> { + let mut paths = cx.prompt_for_paths(PathPromptOptions { + files: true, + directories: true, + multiple: true, + }); + + Some(cx.spawn(|this, mut cx| async move { + if let Some(paths) = paths.recv().await.flatten() { + if let Some(task) = this + .update(&mut cx, |this, cx| this.open_workspace_for_paths(paths, cx)) + .log_err() + { + task.await? + } + } + Ok(()) + })) + } + pub fn open_workspace_for_paths( &mut self, paths: Vec, - app_state: Arc, cx: &mut ViewContext, ) -> Task> { let window_id = cx.window_id(); @@ -1108,6 +1085,7 @@ impl Workspace { } else { Some(self.prepare_to_close(false, cx)) }; + let app_state = self.app_state.clone(); cx.spawn(|_, mut cx| async move { let window_id_to_replace = if let Some(close_task) = close_task { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 58a53b9e40..7a66953cff 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -10,6 +10,7 @@ use cli::{ }; use client::{self, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN}; use db::kvp::KEY_VALUE_STORE; +use editor::Editor; use futures::{ channel::{mpsc, oneshot}, FutureExt, SinkExt, StreamExt, @@ -51,8 +52,7 @@ use staff_mode::StaffMode; use theme::ThemeRegistry; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; use workspace::{ - self, dock::FocusDock, item::ItemHandle, notifications::NotifyResultExt, AppState, NewFile, - Workspace, + self, dock::FocusDock, item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace, }; use zed::{self, build_window_options, initialize_workspace, languages, menus, OpenSettings}; @@ -115,7 +115,10 @@ fn main() { .on_reopen(move |cx| { if cx.has_global::>() { if let Some(app_state) = cx.global::>().upgrade() { - workspace::open_new(&app_state, cx, |_, cx| cx.dispatch_action(NewFile)).detach(); + workspace::open_new(&app_state, cx, |workspace, cx| { + Editor::new_file(workspace, &Default::default(), cx) + }) + .detach(); } } }); @@ -208,14 +211,14 @@ fn main() { auto_update::init(http, client::ZED_SERVER_URL.clone(), cx); workspace::init(app_state.clone(), cx); - recent_projects::init(cx, Arc::downgrade(&app_state)); + recent_projects::init(cx); journal::init(app_state.clone(), cx); - language_selector::init(app_state.clone(), cx); - theme_selector::init(app_state.clone(), cx); + language_selector::init(cx); + theme_selector::init(cx); zed::init(&app_state, cx); collab_ui::init(&app_state, cx); - feedback::init(app_state.clone(), cx); + feedback::init(cx); welcome::init(cx); cx.set_menus(menus::menus()); @@ -289,7 +292,10 @@ async fn restore_or_create_workspace(app_state: &Arc, mut cx: AsyncApp cx.update(|cx| show_welcome_experience(app_state, cx)); } else { cx.update(|cx| { - workspace::open_new(app_state, cx, |_, cx| cx.dispatch_action(NewFile)).detach(); + workspace::open_new(app_state, cx, |workspace, cx| { + Editor::new_file(workspace, &Default::default(), cx) + }) + .detach(); }); } } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 28b17c297d..9e0b55d423 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -20,7 +20,7 @@ use gpui::{ geometry::vector::vec2f, impl_actions, platform::{Platform, PromptLevel, TitlebarOptions, WindowBounds, WindowKind, WindowOptions}, - AssetSource, ViewContext, + AppContext, AssetSource, ViewContext, }; use language::Rope; pub use lsp; @@ -35,7 +35,7 @@ use terminal_view::terminal_button::TerminalButton; use util::{channel::ReleaseChannel, paths, ResultExt}; use uuid::Uuid; pub use workspace; -use workspace::{sidebar::SidebarSide, AppState, Workspace}; +use workspace::{open_new, sidebar::SidebarSide, AppState, NewFile, NewWindow, Workspace}; #[derive(Deserialize, Clone, PartialEq)] pub struct OpenBrowser { @@ -147,10 +147,9 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { }) .detach_and_log_err(cx); }); - cx.add_action({ - let app_state = app_state.clone(); - move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext| { - open_config_file(&paths::SETTINGS, app_state.clone(), cx, || { + cx.add_action( + move |workspace: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext| { + open_config_file(workspace, &paths::SETTINGS, cx, || { str::from_utf8( Assets .load("settings/initial_user_settings.json") @@ -160,73 +159,68 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { .unwrap() .into() }); - } - }); - cx.add_action({ - let app_state = app_state.clone(); + }, + ); + cx.add_action( move |workspace: &mut Workspace, _: &OpenLog, cx: &mut ViewContext| { - open_log_file(workspace, app_state.clone(), cx); - } - }); - cx.add_action({ - let app_state = app_state.clone(); - move |_: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext| { + open_log_file(workspace, cx); + }, + ); + cx.add_action( + move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext| { open_bundled_file( - app_state.clone(), + workspace, "licenses.md", "Open Source License Attribution", "Markdown", cx, ); - } - }); - cx.add_action({ - let app_state = app_state.clone(); + }, + ); + cx.add_action( move |workspace: &mut Workspace, _: &OpenTelemetryLog, cx: &mut ViewContext| { - open_telemetry_log_file(workspace, app_state.clone(), cx); - } - }); - cx.add_action({ - let app_state = app_state.clone(); - move |_: &mut Workspace, _: &OpenKeymap, cx: &mut ViewContext| { - open_config_file(&paths::KEYMAP, app_state.clone(), cx, Default::default); - } - }); - cx.add_action({ - let app_state = app_state.clone(); - move |_: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { + open_telemetry_log_file(workspace, cx); + }, + ); + cx.add_action( + move |workspace: &mut Workspace, _: &OpenKeymap, cx: &mut ViewContext| { + open_config_file(workspace, &paths::KEYMAP, cx, Default::default); + }, + ); + cx.add_action( + move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { open_bundled_file( - app_state.clone(), + workspace, "keymaps/default.json", "Default Key Bindings", "JSON", cx, ); - } - }); - cx.add_action({ - let app_state = app_state.clone(); - move |_: &mut Workspace, _: &OpenDefaultSettings, cx: &mut ViewContext| { + }, + ); + cx.add_action( + move |workspace: &mut Workspace, + _: &OpenDefaultSettings, + cx: &mut ViewContext| { open_bundled_file( - app_state.clone(), + workspace, "settings/default.json", "Default Settings", "JSON", cx, ); - } - }); + }, + ); cx.add_action({ - let app_state = app_state.clone(); - move |_: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { - let app_state = app_state.clone(); + move |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { + let app_state = workspace.app_state().clone(); let markdown = app_state.languages.language_for_name("JSON"); let content = to_string_pretty(&cx.debug_elements()).unwrap(); cx.spawn(|workspace, mut cx| async move { let markdown = markdown.await.log_err(); workspace .update(&mut cx, |workspace, cx| { - workspace.with_local_workspace(&app_state, cx, move |workspace, cx| { + workspace.with_local_workspace(cx, move |workspace, cx| { let project = workspace.project().clone(); let buffer = project @@ -258,6 +252,28 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { workspace.toggle_sidebar_item_focus(SidebarSide::Left, 0, cx); }, ); + cx.add_global_action({ + let app_state = Arc::downgrade(&app_state); + move |_: &NewWindow, cx: &mut AppContext| { + if let Some(app_state) = app_state.upgrade() { + open_new(&app_state, cx, |workspace, cx| { + Editor::new_file(workspace, &Default::default(), cx) + }) + .detach(); + } + } + }); + cx.add_global_action({ + let app_state = Arc::downgrade(&app_state); + move |_: &NewFile, cx: &mut AppContext| { + if let Some(app_state) = app_state.upgrade() { + open_new(&app_state, cx, |workspace, cx| { + Editor::new_file(workspace, &Default::default(), cx) + }) + .detach(); + } + } + }); activity_indicator::init(cx); lsp_log::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); @@ -275,7 +291,7 @@ pub fn initialize_workspace( if let workspace::Event::PaneAdded(pane) = event { pane.update(cx, |pane, cx| { pane.toolbar().update(cx, |toolbar, cx| { - let breadcrumbs = cx.add_view(|_| Breadcrumbs::new()); + let breadcrumbs = cx.add_view(|_| Breadcrumbs::new(workspace)); toolbar.add_item(breadcrumbs, cx); let buffer_search_bar = cx.add_view(BufferSearchBar::new); toolbar.add_item(buffer_search_bar, cx); @@ -304,7 +320,7 @@ pub fn initialize_workspace( }); workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx); - let project_panel = ProjectPanel::new(workspace.project().clone(), cx); + let project_panel = ProjectPanel::new(workspace, cx); workspace.left_sidebar().update(cx, |sidebar, cx| { sidebar.add_item( "icons/folder_tree_16.svg", @@ -317,12 +333,13 @@ pub fn initialize_workspace( let toggle_terminal = cx.add_view(|cx| TerminalButton::new(workspace_handle.clone(), cx)); let copilot = cx.add_view(|cx| copilot_button::CopilotButton::new(cx)); let diagnostic_summary = - cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace.project(), cx)); + cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx)); let activity_indicator = activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx); - let active_buffer_language = cx.add_view(|_| language_selector::ActiveBufferLanguage::new()); + let active_buffer_language = + cx.add_view(|_| language_selector::ActiveBufferLanguage::new(workspace)); let feedback_button = - cx.add_view(|_| feedback::deploy_feedback_button::DeployFeedbackButton::new()); + cx.add_view(|_| feedback::deploy_feedback_button::DeployFeedbackButton::new(workspace)); let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(diagnostic_summary, cx); @@ -428,13 +445,13 @@ fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext) { } fn open_config_file( + workspace: &mut Workspace, path: &'static Path, - app_state: Arc, cx: &mut ViewContext, default_content: impl 'static + Send + FnOnce() -> Rope, ) { + let fs = workspace.app_state().fs.clone(); cx.spawn(|workspace, mut cx| async move { - let fs = &app_state.fs; if !fs.is_file(path).await { fs.create_file(path, Default::default()).await?; fs.save(path, &default_content(), Default::default()) @@ -443,7 +460,7 @@ fn open_config_file( workspace .update(&mut cx, |workspace, cx| { - workspace.with_local_workspace(&app_state, cx, |workspace, cx| { + workspace.with_local_workspace(cx, |workspace, cx| { workspace.open_paths(vec![path.to_path_buf()], false, cx) }) })? @@ -454,20 +471,15 @@ fn open_config_file( .detach_and_log_err(cx) } -fn open_log_file( - workspace: &mut Workspace, - app_state: Arc, - cx: &mut ViewContext, -) { +fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext) { const MAX_LINES: usize = 1000; workspace - .with_local_workspace(&app_state.clone(), cx, move |_, cx| { + .with_local_workspace(cx, move |workspace, cx| { + let fs = workspace.app_state().fs.clone(); cx.spawn(|workspace, mut cx| async move { - let (old_log, new_log) = futures::join!( - app_state.fs.load(&paths::OLD_LOG), - app_state.fs.load(&paths::LOG) - ); + let (old_log, new_log) = + futures::join!(fs.load(&paths::OLD_LOG), fs.load(&paths::LOG)); let mut lines = VecDeque::with_capacity(MAX_LINES); for line in old_log @@ -512,12 +524,9 @@ fn open_log_file( .detach(); } -fn open_telemetry_log_file( - workspace: &mut Workspace, - app_state: Arc, - cx: &mut ViewContext, -) { - workspace.with_local_workspace(&app_state.clone(), cx, move |_, cx| { +fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext) { + workspace.with_local_workspace(cx, move |workspace, cx| { + let app_state = workspace.app_state().clone(); cx.spawn(|workspace, mut cx| async move { async fn fetch_log_string(app_state: &Arc) -> Option { let path = app_state.client.telemetry().log_file_path()?; @@ -573,18 +582,18 @@ fn open_telemetry_log_file( } fn open_bundled_file( - app_state: Arc, + workspace: &mut Workspace, asset_path: &'static str, title: &'static str, language: &'static str, cx: &mut ViewContext, ) { - let language = app_state.languages.language_for_name(language); + let language = workspace.app_state().languages.language_for_name(language); cx.spawn(|workspace, mut cx| async move { let language = language.await.log_err(); workspace .update(&mut cx, |workspace, cx| { - workspace.with_local_workspace(&app_state, cx, |workspace, cx| { + workspace.with_local_workspace(cx, |workspace, cx| { let project = workspace.project(); let buffer = project.update(cx, |project, cx| { let text = Assets::get(asset_path) @@ -815,8 +824,12 @@ mod tests { #[gpui::test] async fn test_new_empty_workspace(cx: &mut TestAppContext) { let app_state = init(cx); - cx.update(|cx| open_new(&app_state, cx, |_, cx| cx.dispatch_action(NewFile))) - .await; + cx.update(|cx| { + open_new(&app_state, cx, |workspace, cx| { + Editor::new_file(workspace, &Default::default(), cx) + }) + }) + .await; let window_id = *cx.window_ids().first().unwrap(); let workspace = cx