From 029538fe21576f9a9a466714dec844d12e44dd16 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 1 May 2023 11:45:35 +0200 Subject: [PATCH 1/7] Make `dispatch_global_action` private --- crates/gpui/src/app.rs | 2 +- crates/zed/src/main.rs | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index c201febed5..a949b64c04 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1048,7 +1048,7 @@ impl AppContext { } } - pub fn dispatch_global_action(&mut self, action: A) { + fn dispatch_global_action(&mut self, action: A) { self.dispatch_global_action_any(&action); } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 2724e8587f..58a53b9e40 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -29,8 +29,16 @@ use settings::{ use simplelog::ConfigBuilder; use smol::process::Command; use std::{ - env, ffi::OsStr, fs::OpenOptions, io::Write as _, os::unix::prelude::OsStrExt, panic, - path::PathBuf, sync::Arc, thread, time::Duration, + env, + ffi::OsStr, + fs::OpenOptions, + io::Write as _, + os::unix::prelude::OsStrExt, + panic, + path::PathBuf, + sync::{Arc, Weak}, + thread, + time::Duration, }; use terminal_view::{get_working_directory, TerminalView}; use util::http::{self, HttpClient}; @@ -104,7 +112,13 @@ fn main() { .log_err(); } }) - .on_reopen(move |cx| cx.dispatch_global_action(NewFile)); + .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(); + } + } + }); app.run(move |cx| { cx.set_global(*RELEASE_CHANNEL); @@ -190,6 +204,7 @@ fn main() { dock_default_item_factory, background_actions, }); + cx.set_global(Arc::downgrade(&app_state)); auto_update::init(http, client::ZED_SERVER_URL.clone(), cx); workspace::init(app_state.clone(), cx); @@ -274,7 +289,7 @@ 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| { - cx.dispatch_global_action(NewFile); + workspace::open_new(app_state, cx, |_, cx| cx.dispatch_action(NewFile)).detach(); }); } } From d815fc88ae917d2180b17966ad815b06531ac87e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 1 May 2023 14:09:39 +0200 Subject: [PATCH 2/7] Remove `ViewContext::dispatch_any_action` --- .../src/activity_indicator.rs | 40 ++--- crates/copilot/src/copilot.rs | 2 +- crates/copilot_button/src/copilot_button.rs | 16 +- crates/editor/src/editor.rs | 12 +- crates/gpui/src/app.rs | 9 +- crates/recent_projects/src/recent_projects.rs | 2 +- crates/search/src/buffer_search.rs | 24 +-- crates/search/src/project_search.rs | 11 +- crates/workspace/src/notifications.rs | 84 +++++----- crates/workspace/src/workspace.rs | 144 ++++++++++++------ crates/zed/src/zed.rs | 55 +------ 11 files changed, 197 insertions(+), 202 deletions(-) diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs index 4ea031985e..d5ee1364b3 100644 --- a/crates/activity_indicator/src/activity_indicator.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -5,7 +5,7 @@ use gpui::{ actions, anyhow, elements::*, platform::{CursorStyle, MouseButton}, - Action, AppContext, Entity, ModelHandle, View, ViewContext, ViewHandle, + AppContext, Entity, ModelHandle, View, ViewContext, ViewHandle, }; use language::{LanguageRegistry, LanguageServerBinaryStatus}; use project::{LanguageServerProgress, Project}; @@ -45,7 +45,7 @@ struct PendingWork<'a> { struct Content { icon: Option<&'static str>, message: String, - action: Option>, + on_click: Option)>>, } pub fn init(cx: &mut AppContext) { @@ -199,7 +199,7 @@ impl ActivityIndicator { return Content { icon: None, message, - action: None, + on_click: None, }; } @@ -230,7 +230,7 @@ impl ActivityIndicator { downloading.join(", "), if downloading.len() > 1 { "s" } else { "" } ), - action: None, + on_click: None, }; } else if !checking_for_update.is_empty() { return Content { @@ -244,7 +244,7 @@ impl ActivityIndicator { "" } ), - action: None, + on_click: None, }; } else if !failed.is_empty() { return Content { @@ -254,7 +254,9 @@ impl ActivityIndicator { failed.join(", "), if failed.len() > 1 { "s" } else { "" } ), - action: Some(Box::new(ShowErrorMessage)), + on_click: Some(Arc::new(|this, cx| { + this.show_error_message(&Default::default(), cx) + })), }; } @@ -264,27 +266,31 @@ impl ActivityIndicator { AutoUpdateStatus::Checking => Content { icon: Some(DOWNLOAD_ICON), message: "Checking for Zed updates…".to_string(), - action: None, + on_click: None, }, AutoUpdateStatus::Downloading => Content { icon: Some(DOWNLOAD_ICON), message: "Downloading Zed update…".to_string(), - action: None, + on_click: None, }, AutoUpdateStatus::Installing => Content { icon: Some(DOWNLOAD_ICON), message: "Installing Zed update…".to_string(), - action: None, + on_click: None, }, AutoUpdateStatus::Updated => Content { icon: None, message: "Click to restart and update Zed".to_string(), - action: Some(Box::new(workspace::Restart)), + on_click: Some(Arc::new(|_, cx| { + workspace::restart(&Default::default(), cx) + })), }, AutoUpdateStatus::Errored => Content { icon: Some(WARNING_ICON), message: "Auto update failed".to_string(), - action: Some(Box::new(DismissErrorMessage)), + on_click: Some(Arc::new(|this, cx| { + this.dismiss_error_message(&Default::default(), cx) + })), }, AutoUpdateStatus::Idle => Default::default(), }; @@ -294,7 +300,7 @@ impl ActivityIndicator { return Content { icon: None, message: most_recent_active_task.to_string(), - action: None, + on_click: None, }; } @@ -315,7 +321,7 @@ impl View for ActivityIndicator { let Content { icon, message, - action, + on_click, } = self.content_to_render(cx); let mut element = MouseEventHandler::::new(0, cx, |state, cx| { @@ -325,7 +331,7 @@ impl View for ActivityIndicator { .workspace .status_bar .lsp_status; - let style = if state.hovered() && action.is_some() { + let style = if state.hovered() && on_click.is_some() { theme.hover.as_ref().unwrap_or(&theme.default) } else { &theme.default @@ -353,12 +359,10 @@ impl View for ActivityIndicator { .aligned() }); - if let Some(action) = action { + if let Some(on_click) = on_click.clone() { element = element .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_any_action(action.boxed_clone()) - }); + .on_click(MouseButton::Left, move |_, this, cx| on_click(this, cx)); } element.into_any() diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index f7ff163424..ea25355065 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -560,7 +560,7 @@ impl Copilot { } } - fn reinstall(&mut self, cx: &mut ModelContext) -> Task<()> { + pub fn reinstall(&mut self, cx: &mut ModelContext) -> Task<()> { let start_task = cx .spawn({ let http = self.http.clone(); diff --git a/crates/copilot_button/src/copilot_button.rs b/crates/copilot_button/src/copilot_button.rs index a597bb7e47..832bdaf3da 100644 --- a/crates/copilot_button/src/copilot_button.rs +++ b/crates/copilot_button/src/copilot_button.rs @@ -1,5 +1,5 @@ use context_menu::{ContextMenu, ContextMenuItem}; -use copilot::{Copilot, Reinstall, SignOut, Status}; +use copilot::{Copilot, SignOut, Status}; use editor::Editor; use gpui::{ elements::*, @@ -103,11 +103,21 @@ impl View for CopilotButton { { workspace.update(cx, |workspace, cx| { workspace.show_toast( - Toast::new_action( + Toast::new( COPILOT_ERROR_TOAST_ID, format!("Copilot can't be started: {}", e), + ) + .on_click( "Reinstall Copilot", - Reinstall, + |cx| { + if let Some(copilot) = Copilot::global(cx) { + copilot + .update(cx, |copilot, cx| { + copilot.reinstall(cx) + }) + .detach(); + } + }, ), cx, ); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index b88e38850e..d8c2e81ce1 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3199,11 +3199,13 @@ impl Editor { .with_cursor_style(CursorStyle::PointingHand) .with_padding(Padding::uniform(3.)) .on_click(MouseButton::Left, { - move |_, _, cx| { - cx.dispatch_any_action(match fold_status { - FoldStatus::Folded => Box::new(UnfoldAt { buffer_row }), - FoldStatus::Foldable => Box::new(FoldAt { buffer_row }), - }); + move |_, editor, cx| match fold_status { + FoldStatus::Folded => { + editor.unfold_at(&UnfoldAt { buffer_row }, cx); + } + FoldStatus::Foldable => { + editor.fold_at(&FoldAt { buffer_row }, cx); + } } }) .into_any() diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index a949b64c04..7343a7245d 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1745,7 +1745,7 @@ impl AppContext { self.pending_effects.push_back(Effect::RefreshWindows); } - pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: impl Action) { + 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)); } @@ -3196,13 +3196,6 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> { .dispatch_action_at(window_id, view_id, action) } - pub fn dispatch_any_action(&mut self, action: Box) { - let window_id = self.window_id; - let view_id = self.view_id; - self.window_context - .dispatch_any_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/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 6429448f75..414b3e9323 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -61,7 +61,7 @@ fn toggle(app_state: Weak, cx: &mut ViewContext) -> Option< }); } else { workspace.show_notification(0, cx, |cx| { - cx.add_view(|_| MessageNotification::new_message("No recent projects to open.")) + cx.add_view(|_| MessageNotification::new("No recent projects to open.")) }) } })?; diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 91ca99c5c3..ee5a2e8332 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -338,8 +338,8 @@ impl BufferSearchBar { .contained() .with_style(style.container) }) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_any_action(option.to_toggle_action()) + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_search_option(option, cx); }) .with_cursor_style(CursorStyle::PointingHand) .with_tooltip::( @@ -386,8 +386,10 @@ impl BufferSearchBar { .with_style(style.container) }) .on_click(MouseButton::Left, { - let action = action.boxed_clone(); - move |_, _, cx| cx.dispatch_any_action(action.boxed_clone()) + move |_, this, cx| match direction { + Direction::Prev => this.select_prev_match(&Default::default(), cx), + Direction::Next => this.select_next_match(&Default::default(), cx), + } }) .with_cursor_style(CursorStyle::PointingHand) .with_tooltip::( @@ -405,7 +407,6 @@ impl BufferSearchBar { theme: &theme::Search, cx: &mut ViewContext, ) -> AnyElement { - let action = Box::new(Dismiss); let tooltip = "Dismiss Buffer Search"; let tooltip_style = cx.global::().theme.tooltip.clone(); @@ -422,12 +423,17 @@ impl BufferSearchBar { .contained() .with_style(style.container) }) - .on_click(MouseButton::Left, { - let action = action.boxed_clone(); - move |_, _, cx| cx.dispatch_any_action(action.boxed_clone()) + .on_click(MouseButton::Left, move |_, this, cx| { + this.dismiss(&Default::default(), cx) }) .with_cursor_style(CursorStyle::PointingHand) - .with_tooltip::(0, tooltip.to_string(), Some(action), tooltip_style, cx) + .with_tooltip::( + 0, + tooltip.to_string(), + Some(Box::new(Dismiss)), + tooltip_style, + cx, + ) .into_any() } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index ac478a8a2c..ea29f9cfda 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -788,9 +788,10 @@ impl ProjectSearchBar { .contained() .with_style(style.container) }) - .on_click(MouseButton::Left, { - let action = action.boxed_clone(); - move |_, _, cx| cx.dispatch_any_action(action.boxed_clone()) + .on_click(MouseButton::Left, move |_, this, cx| { + if let Some(search) = this.active_project_search.as_ref() { + search.update(cx, |search, cx| search.select_match(direction, cx)); + } }) .with_cursor_style(CursorStyle::PointingHand) .with_tooltip::( @@ -822,8 +823,8 @@ impl ProjectSearchBar { .contained() .with_style(style.container) }) - .on_click(MouseButton::Left, move |_, _, cx| { - cx.dispatch_any_action(option.to_toggle_action()) + .on_click(MouseButton::Left, move |_, this, cx| { + this.toggle_search_option(option, cx); }) .with_cursor_style(CursorStyle::PointingHand) .with_tooltip::( diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 57749a5c2b..455ffb2bb0 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -114,17 +114,14 @@ impl Workspace { pub fn show_toast(&mut self, toast: Toast, cx: &mut ViewContext) { self.dismiss_notification::(toast.id, cx); self.show_notification(toast.id, cx, |cx| { - cx.add_view(|_cx| match &toast.click { - Some((click_msg, action)) => { - simple_message_notification::MessageNotification::new_boxed_action( - toast.msg.clone(), - action.boxed_clone(), - click_msg.clone(), - ) - } - None => { - simple_message_notification::MessageNotification::new_message(toast.msg.clone()) + cx.add_view(|_cx| match toast.on_click.as_ref() { + Some((click_msg, on_click)) => { + let on_click = on_click.clone(); + simple_message_notification::MessageNotification::new(toast.msg.clone()) + .with_click_message(click_msg.clone()) + .on_click(move |cx| on_click(cx)) } + None => simple_message_notification::MessageNotification::new(toast.msg.clone()), }) }) } @@ -152,19 +149,17 @@ impl Workspace { } pub mod simple_message_notification { - - use std::borrow::Cow; - use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text}, impl_actions, platform::{CursorStyle, MouseButton}, - Action, AppContext, Element, Entity, View, ViewContext, + AppContext, Element, Entity, View, ViewContext, }; use menu::Cancel; use serde::Deserialize; use settings::Settings; + use std::{borrow::Cow, sync::Arc}; use crate::Workspace; @@ -194,7 +189,7 @@ pub mod simple_message_notification { pub struct MessageNotification { message: Cow<'static, str>, - click_action: Option>, + on_click: Option)>>, click_message: Option>, } @@ -207,36 +202,31 @@ pub mod simple_message_notification { } impl MessageNotification { - pub fn new_message>>(message: S) -> MessageNotification { + pub fn new(message: S) -> MessageNotification + where + S: Into>, + { Self { message: message.into(), - click_action: None, + on_click: None, click_message: None, } } - pub fn new_boxed_action>, S2: Into>>( - message: S1, - click_action: Box, - click_message: S2, - ) -> Self { - Self { - message: message.into(), - click_action: Some(click_action), - click_message: Some(click_message.into()), - } + pub fn with_click_message(mut self, message: S) -> Self + where + S: Into>, + { + self.click_message = Some(message.into()); + self } - pub fn new>, A: Action, S2: Into>>( - message: S1, - click_action: A, - click_message: S2, - ) -> Self { - Self { - message: message.into(), - click_action: Some(Box::new(click_action) as Box), - click_message: Some(click_message.into()), - } + pub fn on_click(mut self, on_click: F) -> Self + where + F: 'static + Fn(&mut ViewContext), + { + self.on_click = Some(Arc::new(on_click)); + self } pub fn dismiss(&mut self, _: &CancelMessageNotification, cx: &mut ViewContext) { @@ -255,14 +245,10 @@ pub mod simple_message_notification { enum MessageNotificationTag {} - let click_action = self - .click_action - .as_ref() - .map(|action| action.boxed_clone()); - let click_message = self.click_message.as_ref().map(|message| message.clone()); + let click_message = self.click_message.clone(); let message = self.message.clone(); - - let has_click_action = click_action.is_some(); + let on_click = self.on_click.clone(); + let has_click_action = on_click.is_some(); MouseEventHandler::::new(0, cx, |state, cx| { Flex::column() @@ -326,10 +312,10 @@ pub mod simple_message_notification { // Since we're not using a proper overlay, we have to capture these extra events .on_down(MouseButton::Left, |_, _, _| {}) .on_up(MouseButton::Left, |_, _, _| {}) - .on_click(MouseButton::Left, move |_, _, cx| { - if let Some(click_action) = click_action.as_ref() { - cx.dispatch_any_action(click_action.boxed_clone()); - cx.dispatch_action(CancelMessageNotification) + .on_click(MouseButton::Left, move |_, this, cx| { + if let Some(on_click) = on_click.as_ref() { + on_click(cx); + this.dismiss(&Default::default(), cx); } }) .with_cursor_style(if has_click_action { @@ -372,7 +358,7 @@ where Err(err) => { workspace.show_notification(0, cx, |cx| { cx.add_view(|_cx| { - simple_message_notification::MessageNotification::new_message(format!( + simple_message_notification::MessageNotification::new(format!( "Error: {:?}", err, )) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 1a622babb3..6a1f7aa8bb 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -43,8 +43,9 @@ use gpui::{ CursorStyle, MouseButton, PathPromptOptions, Platform, PromptLevel, WindowBounds, WindowOptions, }, - Action, AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, - ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, + AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, + SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, + WindowContext, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; use language::LanguageRegistry; @@ -59,7 +60,7 @@ use std::{ }; use crate::{ - notifications::simple_message_notification::{MessageNotification, OsOpen}, + notifications::simple_message_notification::MessageNotification, persistence::model::{SerializedPane, SerializedPaneGroup, SerializedWorkspace}, }; use lazy_static::lazy_static; @@ -137,7 +138,7 @@ pub struct ActivatePane(pub usize); pub struct Toast { id: usize, msg: Cow<'static, str>, - click: Option<(Cow<'static, str>, Box)>, + on_click: Option<(Cow<'static, str>, Arc)>, } impl Toast { @@ -145,21 +146,17 @@ impl Toast { Toast { id, msg: msg.into(), - click: None, + on_click: None, } } - pub fn new_action>, I2: Into>>( - id: usize, - msg: I1, - click_msg: I2, - action: impl Action, - ) -> Self { - Toast { - id, - msg: msg.into(), - click: Some((click_msg.into(), Box::new(action))), - } + pub fn on_click(mut self, message: M, on_click: F) -> Self + where + M: Into>, + F: Fn(&mut WindowContext) + 'static, + { + self.on_click = Some((message.into(), Arc::new(on_click))); + self } } @@ -167,7 +164,7 @@ impl PartialEq for Toast { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.msg == other.msg - && self.click.is_some() == other.click.is_some() + && self.on_click.is_some() == other.on_click.is_some() } } @@ -176,10 +173,7 @@ impl Clone for Toast { Toast { id: self.id, msg: self.msg.to_owned(), - click: self - .click - .as_ref() - .map(|(msg, click)| (msg.to_owned(), click.boxed_clone())), + on_click: self.on_click.clone(), } } } @@ -260,6 +254,7 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { cx.add_async_action(Workspace::follow_next_collaborator); cx.add_async_action(Workspace::close); cx.add_global_action(Workspace::close_global); + cx.add_global_action(restart); cx.add_async_action(Workspace::save_all); cx.add_action(Workspace::add_folder_to_project); cx.add_action( @@ -303,9 +298,7 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { } else { workspace.show_notification(1, cx, |cx| { cx.add_view(|_| { - MessageNotification::new_message( - "Successfully installed the `zed` binary", - ) + MessageNotification::new("Successfully installed the `zed` binary") }) }); } @@ -2668,36 +2661,37 @@ impl Workspace { } fn notify_if_database_failed(workspace: &WeakViewHandle, cx: &mut AsyncAppContext) { - workspace.update(cx, |workspace, cx| { - if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) { - workspace.show_notification_once(0, cx, |cx| { - cx.add_view(|_| { - MessageNotification::new( - "Failed to load any database file.", - OsOpen::new("https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml".to_string()), - "Click to let us know about this error" - ) - }) - }); - } else { - let backup_path = (*db::BACKUP_DB_PATH).read(); - if let Some(backup_path) = &*backup_path { + const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/community/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml"; + + workspace + .update(cx, |workspace, cx| { + if (*db::ALL_FILE_DB_FAILED).load(std::sync::atomic::Ordering::Acquire) { workspace.show_notification_once(0, cx, |cx| { cx.add_view(|_| { - let backup_path = backup_path.to_string_lossy(); - MessageNotification::new( - format!( - "Database file was corrupted. Old database backed up to {}", - backup_path - ), - OsOpen::new(backup_path.to_string()), - "Click to show old database in finder", - ) + MessageNotification::new("Failed to load any database file.") + .with_click_message("Click to let us know about this error") + .on_click(|cx| cx.platform().open_url(REPORT_ISSUE_URL)) }) }); + } else { + let backup_path = (*db::BACKUP_DB_PATH).read(); + if let Some(backup_path) = backup_path.clone() { + workspace.show_notification_once(0, cx, move |cx| { + cx.add_view(move |_| { + MessageNotification::new(format!( + "Database file was corrupted. Old database backed up to {}", + backup_path.display() + )) + .with_click_message("Click to show old database in finder") + .on_click(move |cx| { + cx.platform().open_url(&backup_path.to_string_lossy()) + }) + }) + }); + } } - } - }).log_err(); + }) + .log_err(); } impl Entity for Workspace { @@ -3062,6 +3056,58 @@ pub fn join_remote_project( }) } +pub fn restart(_: &Restart, cx: &mut AppContext) { + let mut workspaces = cx + .window_ids() + .filter_map(|window_id| { + Some( + cx.root_view(window_id)? + .clone() + .downcast::()? + .downgrade(), + ) + }) + .collect::>(); + + // If multiple windows have unsaved changes, and need a save prompt, + // prompt in the active window before switching to a different window. + workspaces.sort_by_key(|workspace| !cx.window_is_active(workspace.window_id())); + + let should_confirm = cx.global::().confirm_quit; + cx.spawn(|mut cx| async move { + if let (true, Some(workspace)) = (should_confirm, workspaces.first()) { + let answer = cx.prompt( + workspace.window_id(), + PromptLevel::Info, + "Are you sure you want to restart?", + &["Restart", "Cancel"], + ); + + if let Some(mut answer) = answer { + let answer = answer.next().await; + if answer != Some(0) { + return Ok(()); + } + } + } + + // If the user cancels any save prompt, then keep the app open. + for workspace in workspaces { + if !workspace + .update(&mut cx, |workspace, cx| { + workspace.prepare_to_close(true, cx) + })? + .await? + { + return Ok(()); + } + } + cx.platform().restart(); + anyhow::Ok(()) + }) + .detach_and_log_err(cx); +} + fn parse_pixel_position_env_var(value: &str) -> Option { let mut parts = value.split(','); let width: usize = parts.next()?.parse().ok()?; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 01a5decfd0..28b17c297d 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -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, Restart, Workspace}; +use workspace::{sidebar::SidebarSide, AppState, Workspace}; #[derive(Deserialize, Clone, PartialEq)] pub struct OpenBrowser { @@ -113,7 +113,6 @@ pub fn init(app_state: &Arc, cx: &mut gpui::AppContext) { }, ); cx.add_global_action(quit); - cx.add_global_action(restart); cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url)); cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| { cx.update_global::(|settings, cx| { @@ -370,58 +369,6 @@ pub fn build_window_options( } } -fn restart(_: &Restart, cx: &mut gpui::AppContext) { - let mut workspaces = cx - .window_ids() - .filter_map(|window_id| { - Some( - cx.root_view(window_id)? - .clone() - .downcast::()? - .downgrade(), - ) - }) - .collect::>(); - - // If multiple windows have unsaved changes, and need a save prompt, - // prompt in the active window before switching to a different window. - workspaces.sort_by_key(|workspace| !cx.window_is_active(workspace.window_id())); - - let should_confirm = cx.global::().confirm_quit; - cx.spawn(|mut cx| async move { - if let (true, Some(workspace)) = (should_confirm, workspaces.first()) { - let answer = cx.prompt( - workspace.window_id(), - PromptLevel::Info, - "Are you sure you want to restart?", - &["Restart", "Cancel"], - ); - - if let Some(mut answer) = answer { - let answer = answer.next().await; - if answer != Some(0) { - return Ok(()); - } - } - } - - // If the user cancels any save prompt, then keep the app open. - for workspace in workspaces { - if !workspace - .update(&mut cx, |workspace, cx| { - workspace.prepare_to_close(true, cx) - })? - .await? - { - return Ok(()); - } - } - cx.platform().restart(); - anyhow::Ok(()) - }) - .detach_and_log_err(cx); -} - fn quit(_: &Quit, cx: &mut gpui::AppContext) { let mut workspaces = cx .window_ids() From c4472b0786af91237b22c5aed82c8b257165b1b1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 1 May 2023 15:48:41 +0200 Subject: [PATCH 3/7] Remove `ViewContext::dispatch_action` --- crates/auto_update/src/auto_update.rs | 40 +++-- crates/auto_update/src/update_notification.rs | 6 +- crates/breadcrumbs/src/breadcrumbs.rs | 16 +- crates/collab_ui/src/collab_titlebar_item.rs | 64 ++----- crates/collab_ui/src/collab_ui.rs | 1 - .../src/collaborator_list_popover.rs | 161 ----------------- crates/collab_ui/src/contact_list.rs | 56 +++--- crates/collab_ui/src/contacts_popover.rs | 10 +- .../collab_ui/src/sharing_status_indicator.rs | 5 +- crates/context_menu/src/context_menu.rs | 8 +- crates/copilot/src/sign_in.rs | 6 +- crates/diagnostics/src/items.rs | 25 ++- crates/editor/src/editor.rs | 42 +++-- crates/editor/src/element.rs | 15 +- crates/editor/src/hover_popover.rs | 13 +- crates/feedback/src/deploy_feedback_button.rs | 19 +- crates/feedback/src/feedback.rs | 57 ++---- crates/feedback/src/feedback_editor.rs | 67 +++---- crates/feedback/src/feedback_info_text.rs | 4 +- crates/feedback/src/submit_feedback_button.rs | 24 ++- crates/gpui/src/app.rs | 11 -- crates/gpui/src/views/select.rs | 20 +-- .../src/active_buffer_language.rs | 22 +-- .../src/language_selector.rs | 14 +- crates/outline/src/outline.rs | 2 +- crates/project_panel/src/project_panel.rs | 23 ++- crates/recent_projects/src/recent_projects.rs | 39 ++--- crates/terminal_view/src/terminal_button.rs | 12 +- crates/theme/src/ui.rs | 19 +- crates/theme_selector/src/theme_selector.rs | 14 +- crates/welcome/src/base_keymap_picker.rs | 2 +- crates/welcome/src/welcome.rs | 48 ++++-- crates/workspace/src/dock.rs | 8 +- .../workspace/src/dock/toggle_dock_button.rs | 22 ++- crates/workspace/src/notifications.rs | 4 +- crates/workspace/src/pane.rs | 46 +++-- crates/workspace/src/sidebar.rs | 6 +- crates/workspace/src/toolbar.rs | 40 ++++- crates/workspace/src/workspace.rs | 68 +++----- crates/zed/src/main.rs | 22 ++- crates/zed/src/zed.rs | 163 ++++++++++-------- 41 files changed, 574 insertions(+), 670 deletions(-) delete mode 100644 crates/collab_ui/src/collaborator_list_popover.rs 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 From eb2cce98a715ae078579efdf90a9776604649da9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 1 May 2023 16:40:57 +0200 Subject: [PATCH 4/7] Move `dispatch_action_any_action_at` to `AsyncAppContext` --- crates/command_palette/src/command_palette.rs | 8 +-- crates/context_menu/src/context_menu.rs | 27 ++++++---- crates/gpui/src/app.rs | 51 ++++++------------- 3 files changed, 37 insertions(+), 49 deletions(-) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 1d9ac62c2c..5b1559bd29 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -167,9 +167,11 @@ impl PickerDelegate for CommandPaletteDelegate { let focused_view_id = self.focused_view_id; let action_ix = self.matches[self.selected_ix].candidate_id; let action = self.actions.remove(action_ix).action; - cx.defer(move |_, cx| { - cx.dispatch_any_action_at(window_id, focused_view_id, action); - }); + cx.app_context() + .spawn(move |mut cx| async move { + cx.dispatch_action(window_id, focused_view_id, action.as_ref()) + }) + .detach_and_log_err(cx); } cx.emit(PickerEvent::Dismiss); } diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index 3a3ca9b66b..e0429bd01b 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -227,11 +227,13 @@ impl ContextMenu { match action { ContextMenuItemAction::Action(action) => { let window_id = cx.window_id(); - cx.dispatch_any_action_at( - window_id, - self.parent_view_id, - action.boxed_clone(), - ); + let view_id = self.parent_view_id; + let action = action.boxed_clone(); + cx.app_context() + .spawn(|mut cx| async move { + cx.dispatch_action(window_id, view_id, action.as_ref()) + }) + .detach_and_log_err(cx); } ContextMenuItemAction::Handler(handler) => handler(cx), } @@ -459,11 +461,16 @@ impl ContextMenu { let window_id = cx.window_id(); match &action { ContextMenuItemAction::Action(action) => { - cx.dispatch_any_action_at( - window_id, - view_id, - action.boxed_clone(), - ); + let action = action.boxed_clone(); + cx.app_context() + .spawn(|mut cx| async move { + cx.dispatch_action( + window_id, + view_id, + action.as_ref(), + ) + }) + .detach_and_log_err(cx); } ContextMenuItemAction::Handler(handler) => handler(cx), } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 02d6c1a2ac..c757e7f383 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -309,6 +309,20 @@ impl AsyncAppContext { self.0.borrow_mut().update_window(window_id, callback) } + pub fn dispatch_action( + &mut self, + window_id: usize, + view_id: usize, + action: &dyn Action, + ) -> Result<()> { + self.0 + .borrow_mut() + .update_window(window_id, |window| { + window.handle_dispatch_action_from_effect(Some(view_id), action); + }) + .ok_or_else(|| anyhow!("window not found")) + } + pub fn add_model(&mut self, build_model: F) -> ModelHandle where T: Entity, @@ -1619,17 +1633,7 @@ impl AppContext { Effect::RefreshWindows => { refreshing = true; } - Effect::DispatchActionFrom { - window_id, - view_id, - action, - } => { - self.handle_dispatch_action_from_effect( - window_id, - Some(view_id), - action.as_ref(), - ); - } + Effect::ActionDispatchNotification { action_id } => { self.handle_action_dispatch_notification_effect(action_id) } @@ -1745,19 +1749,6 @@ impl AppContext { self.pending_effects.push_back(Effect::RefreshWindows); } - pub fn dispatch_any_action_at( - &mut self, - window_id: usize, - view_id: usize, - action: Box, - ) { - self.pending_effects.push_back(Effect::DispatchActionFrom { - window_id, - view_id, - action, - }); - } - fn perform_window_refresh(&mut self) { let window_ids = self.windows.keys().cloned().collect::>(); for window_id in window_ids { @@ -2155,11 +2146,6 @@ pub enum Effect { result: MatchResult, }, RefreshWindows, - DispatchActionFrom { - window_id: usize, - view_id: usize, - action: Box, - }, ActionDispatchNotification { action_id: TypeId, }, @@ -2248,13 +2234,6 @@ impl Debug for Effect { .field("view_id", view_id) .field("subscription_id", subscription_id) .finish(), - Effect::DispatchActionFrom { - window_id, view_id, .. - } => f - .debug_struct("Effect::DispatchActionFrom") - .field("window_id", window_id) - .field("view_id", view_id) - .finish(), Effect::ActionDispatchNotification { action_id, .. } => f .debug_struct("Effect::ActionDispatchNotification") .field("action_id", action_id) From 6c931ab9da7f73344fd2698b237a1d31483cdeb1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 1 May 2023 16:49:17 +0200 Subject: [PATCH 5/7] Inline test-only `AppContext` methods --- crates/gpui/src/app.rs | 15 --------------- crates/gpui/src/app/test_app_context.rs | 12 +++++++----- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index c757e7f383..589ec8f1af 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1062,10 +1062,6 @@ impl AppContext { } } - fn dispatch_global_action(&mut self, action: A) { - self.dispatch_global_action_any(&action); - } - fn dispatch_global_action_any(&mut self, action: &dyn Action) -> bool { self.update(|this| { if let Some((name, mut handler)) = this.global_actions.remove_entry(&action.id()) { @@ -1907,17 +1903,6 @@ impl AppContext { }); } - fn handle_dispatch_action_from_effect( - &mut self, - window_id: usize, - view_id: Option, - action: &dyn Action, - ) { - self.update_window(window_id, |cx| { - cx.handle_dispatch_action_from_effect(view_id, action) - }); - } - fn handle_action_dispatch_notification_effect(&mut self, action_id: TypeId) { self.action_dispatch_observations .clone() diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 3a03a81c47..79f35cd923 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -72,14 +72,16 @@ impl TestAppContext { } pub fn dispatch_action(&self, window_id: usize, action: A) { - let mut cx = self.cx.borrow_mut(); - if let Some(view_id) = cx.windows.get(&window_id).and_then(|w| w.focused_view_id) { - cx.handle_dispatch_action_from_effect(window_id, Some(view_id), &action); - } + self.cx + .borrow_mut() + .update_window(window_id, |window| { + window.handle_dispatch_action_from_effect(window.focused_view_id(), &action); + }) + .expect("window not found"); } pub fn dispatch_global_action(&self, action: A) { - self.cx.borrow_mut().dispatch_global_action(action); + self.cx.borrow_mut().dispatch_global_action_any(&action); } pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) { From e3b2407ebfb18a9dc2f59892a9e9966e72338d40 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 1 May 2023 16:58:07 +0200 Subject: [PATCH 6/7] Run until parked now that the command palette spawns to dispatch action --- crates/command_palette/src/command_palette.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 5b1559bd29..441fbb84a6 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -268,9 +268,11 @@ impl std::fmt::Debug for Command { #[cfg(test)] mod tests { + use std::sync::Arc; + use super::*; use editor::Editor; - use gpui::TestAppContext; + use gpui::{executor::Deterministic, TestAppContext}; use project::Project; use workspace::{AppState, Workspace}; @@ -291,7 +293,8 @@ mod tests { } #[gpui::test] - async fn test_command_palette(cx: &mut TestAppContext) { + async fn test_command_palette(deterministic: Arc, cx: &mut TestAppContext) { + deterministic.forbid_parking(); let app_state = cx.update(AppState::test); cx.update(|cx| { @@ -333,7 +336,7 @@ mod tests { assert_eq!(palette.delegate().matches[0].string, "editor: backspace"); palette.confirm(&Default::default(), cx); }); - + deterministic.run_until_parked(); editor.read_with(cx, |editor, cx| { assert_eq!(editor.text(cx), "ab"); }); From 780ece551e58fcd6cebc4fb7af727bdf54826945 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 1 May 2023 17:06:05 +0200 Subject: [PATCH 7/7] Defer hiding the dock and going back/forward when Pane is on the stack --- crates/workspace/src/pane.rs | 8 +++++--- crates/workspace/src/toolbar.rs | 20 +++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 2631f72fd4..8bd42fed04 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1645,9 +1645,11 @@ impl Pane { cx, |this, cx| { if let Some(workspace) = this.workspace.upgrade(cx) { - workspace.update(cx, |workspace, cx| { - Dock::hide_dock(workspace, &Default::default(), cx) - }) + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + Dock::hide_dock(workspace, &Default::default(), cx) + }) + }); } }, None, diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index a940bd09b2..eac9963d38 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -138,10 +138,13 @@ impl View for Toolbar { .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); - }); + let pane = pane.clone(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + Pane::go_back(workspace, Some(pane.clone()), cx) + .detach_and_log_err(cx); + }); + }) } } }, @@ -163,9 +166,12 @@ impl View for Toolbar { .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); + let pane = pane.clone(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + Pane::go_forward(workspace, Some(pane.clone()), cx) + .detach_and_log_err(cx); + }); }); } }