Merge branch 'main' into collab-panel
This commit is contained in:
commit
99daa73325
93 changed files with 1908 additions and 1442 deletions
|
@ -213,7 +213,7 @@ impl Dock {
|
|||
pub fn panel_index_for_ui_name(&self, ui_name: &str, cx: &AppContext) -> Option<usize> {
|
||||
self.panel_entries.iter().position(|entry| {
|
||||
let panel = entry.panel.as_any();
|
||||
cx.view_ui_name(panel.window_id(), panel.id()) == Some(ui_name)
|
||||
cx.view_ui_name(panel.window(), panel.id()) == Some(ui_name)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -542,16 +542,15 @@ impl View for PanelButtons {
|
|||
tooltip_action.as_ref().map(|action| action.boxed_clone());
|
||||
move |_, this, cx| {
|
||||
if let Some(tooltip_action) = &tooltip_action {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let view_id = this.workspace.id();
|
||||
let tooltip_action = tooltip_action.boxed_clone();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
cx.dispatch_action(
|
||||
window_id,
|
||||
window.dispatch_action(
|
||||
view_id,
|
||||
&*tooltip_action,
|
||||
)
|
||||
.ok();
|
||||
&mut cx,
|
||||
);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
|
|||
use anyhow::Result;
|
||||
use client::{proto, Client};
|
||||
use gpui::geometry::vector::Vector2F;
|
||||
use gpui::AnyWindowHandle;
|
||||
use gpui::{
|
||||
fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle, WindowContext,
|
||||
|
@ -250,7 +251,7 @@ pub trait ItemHandle: 'static + fmt::Debug {
|
|||
fn workspace_deactivated(&self, cx: &mut WindowContext);
|
||||
fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
|
||||
fn id(&self) -> usize;
|
||||
fn window_id(&self) -> usize;
|
||||
fn window(&self) -> AnyWindowHandle;
|
||||
fn as_any(&self) -> &AnyViewHandle;
|
||||
fn is_dirty(&self, cx: &AppContext) -> bool;
|
||||
fn has_conflict(&self, cx: &AppContext) -> bool;
|
||||
|
@ -280,7 +281,7 @@ pub trait ItemHandle: 'static + fmt::Debug {
|
|||
|
||||
pub trait WeakItemHandle {
|
||||
fn id(&self) -> usize;
|
||||
fn window_id(&self) -> usize;
|
||||
fn window(&self) -> AnyWindowHandle;
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>>;
|
||||
}
|
||||
|
||||
|
@ -542,8 +543,8 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
|
|||
self.id()
|
||||
}
|
||||
|
||||
fn window_id(&self) -> usize {
|
||||
self.window_id()
|
||||
fn window(&self) -> AnyWindowHandle {
|
||||
AnyViewHandle::window(self)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &AnyViewHandle {
|
||||
|
@ -649,8 +650,8 @@ impl<T: Item> WeakItemHandle for WeakViewHandle<T> {
|
|||
self.id()
|
||||
}
|
||||
|
||||
fn window_id(&self) -> usize {
|
||||
self.window_id()
|
||||
fn window(&self) -> AnyWindowHandle {
|
||||
self.window()
|
||||
}
|
||||
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
|
||||
|
|
|
@ -25,8 +25,8 @@ use gpui::{
|
|||
keymap_matcher::KeymapContext,
|
||||
platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel},
|
||||
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
||||
LayoutContext, ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle,
|
||||
WeakViewHandle, WindowContext,
|
||||
LayoutContext, ModelHandle, MouseRegion, PaintContext, Quad, Task, View, ViewContext,
|
||||
ViewHandle, WeakViewHandle, WindowContext,
|
||||
};
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
use serde::Deserialize;
|
||||
|
@ -1900,7 +1900,7 @@ impl<V: View> Element<V> for PaneBackdrop<V> {
|
|||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let background = theme::current(cx).editor.background;
|
||||
|
||||
|
@ -1917,8 +1917,8 @@ impl<V: View> Element<V> for PaneBackdrop<V> {
|
|||
MouseRegion::new::<Self>(child_view_id, 0, visible_bounds).on_down(
|
||||
gpui::platform::MouseButton::Left,
|
||||
move |_, _: &mut V, cx| {
|
||||
let window_id = cx.window_id();
|
||||
cx.app_context().focus(window_id, Some(child_view_id))
|
||||
let window = cx.window();
|
||||
cx.app_context().focus(window, Some(child_view_id))
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
|
@ -28,11 +28,11 @@ where
|
|||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
let drag_position = if (pane.can_drop)(drag_and_drop, cx) {
|
||||
drag_and_drop
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.currently_dragged::<DraggedItem>(cx.window())
|
||||
.map(|(drag_position, _)| drag_position)
|
||||
.or_else(|| {
|
||||
drag_and_drop
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
.map(|(drag_position, _)| drag_position)
|
||||
})
|
||||
} else {
|
||||
|
@ -91,10 +91,10 @@ where
|
|||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
|
||||
if drag_and_drop
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.currently_dragged::<DraggedItem>(cx.window())
|
||||
.is_some()
|
||||
|| drag_and_drop
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
.is_some()
|
||||
{
|
||||
cx.notify();
|
||||
|
@ -122,11 +122,11 @@ pub fn handle_dropped_item<V: View>(
|
|||
}
|
||||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
let action = if let Some((_, dragged_item)) =
|
||||
drag_and_drop.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
drag_and_drop.currently_dragged::<DraggedItem>(cx.window())
|
||||
{
|
||||
Action::Move(dragged_item.pane.clone(), dragged_item.handle.id())
|
||||
} else if let Some((_, project_entry)) =
|
||||
drag_and_drop.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
drag_and_drop.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
{
|
||||
Action::Open(*project_entry)
|
||||
} else {
|
||||
|
|
|
@ -595,7 +595,7 @@ mod element {
|
|||
platform::{CursorStyle, MouseButton},
|
||||
scene::MouseDrag,
|
||||
AnyElement, Axis, CursorRegion, Element, EventContext, LayoutContext, MouseRegion,
|
||||
RectFExt, SceneBuilder, SizeConstraint, Vector2FExt, ViewContext,
|
||||
PaintContext, RectFExt, SceneBuilder, SizeConstraint, Vector2FExt, ViewContext,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -856,7 +856,7 @@ mod element {
|
|||
visible_bounds: RectF,
|
||||
remaining_space: &mut Self::LayoutState,
|
||||
view: &mut Workspace,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
cx: &mut PaintContext<Workspace>,
|
||||
) -> Self::PaintState {
|
||||
let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
|
|
@ -235,7 +235,7 @@ impl From<&Box<dyn SearchableItemHandle>> for AnyViewHandle {
|
|||
|
||||
impl PartialEq for Box<dyn SearchableItemHandle> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id() && self.window_id() == other.window_id()
|
||||
self.id() == other.id() && self.window() == other.window()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,7 +259,7 @@ impl<T: SearchableItem> WeakSearchableItemHandle for WeakViewHandle<T> {
|
|||
|
||||
impl PartialEq for Box<dyn WeakSearchableItemHandle> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id() && self.window_id() == other.window_id()
|
||||
self.id() == other.id() && self.window() == other.window()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,6 +267,6 @@ impl Eq for Box<dyn WeakSearchableItemHandle> {}
|
|||
|
||||
impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(self.id(), self.window_id()).hash(state)
|
||||
(self.id(), self.window().id()).hash(state)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ use gpui::{
|
|||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
AnyElement, AnyViewHandle, Entity, LayoutContext, SceneBuilder, SizeConstraint, Subscription,
|
||||
View, ViewContext, ViewHandle, WindowContext,
|
||||
AnyElement, AnyViewHandle, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
Subscription, View, ViewContext, ViewHandle, WindowContext,
|
||||
};
|
||||
|
||||
pub trait StatusItemView: View {
|
||||
|
@ -231,7 +231,7 @@ impl Element<StatusBar> for StatusBarElement {
|
|||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut StatusBar,
|
||||
cx: &mut ViewContext<StatusBar>,
|
||||
cx: &mut PaintContext<StatusBar>,
|
||||
) -> Self::PaintState {
|
||||
let origin_y = bounds.upper_right().y();
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
|
|
@ -37,7 +37,7 @@ use gpui::{
|
|||
},
|
||||
AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
|
||||
ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
|
||||
WeakViewHandle, WindowContext,
|
||||
WeakViewHandle, WindowContext, WindowHandle,
|
||||
};
|
||||
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
|
||||
use itertools::Itertools;
|
||||
|
@ -753,7 +753,7 @@ impl Workspace {
|
|||
fn new_local(
|
||||
abs_paths: Vec<PathBuf>,
|
||||
app_state: Arc<AppState>,
|
||||
requesting_window_id: Option<usize>,
|
||||
requesting_window: Option<WindowHandle<Workspace>>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<(
|
||||
WeakViewHandle<Workspace>,
|
||||
|
@ -797,55 +797,60 @@ impl Workspace {
|
|||
DB.next_id().await.unwrap_or(0)
|
||||
};
|
||||
|
||||
let window = requesting_window_id.and_then(|window_id| {
|
||||
cx.update(|cx| {
|
||||
cx.replace_root_view(window_id, |cx| {
|
||||
Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
|
||||
})
|
||||
})
|
||||
});
|
||||
let window = window.unwrap_or_else(|| {
|
||||
let window_bounds_override = window_bounds_env_override(&cx);
|
||||
let (bounds, display) = if let Some(bounds) = window_bounds_override {
|
||||
(Some(bounds), None)
|
||||
} else {
|
||||
serialized_workspace
|
||||
.as_ref()
|
||||
.and_then(|serialized_workspace| {
|
||||
let display = serialized_workspace.display?;
|
||||
let mut bounds = serialized_workspace.bounds?;
|
||||
let window = if let Some(window) = requesting_window {
|
||||
window.replace_root(&mut cx, |cx| {
|
||||
Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
|
||||
});
|
||||
window
|
||||
} else {
|
||||
{
|
||||
let window_bounds_override = window_bounds_env_override(&cx);
|
||||
let (bounds, display) = if let Some(bounds) = window_bounds_override {
|
||||
(Some(bounds), None)
|
||||
} else {
|
||||
serialized_workspace
|
||||
.as_ref()
|
||||
.and_then(|serialized_workspace| {
|
||||
let display = serialized_workspace.display?;
|
||||
let mut bounds = serialized_workspace.bounds?;
|
||||
|
||||
// Stored bounds are relative to the containing display.
|
||||
// So convert back to global coordinates if that screen still exists
|
||||
if let WindowBounds::Fixed(mut window_bounds) = bounds {
|
||||
if let Some(screen) = cx.platform().screen_by_id(display) {
|
||||
let screen_bounds = screen.bounds();
|
||||
window_bounds.set_origin_x(
|
||||
window_bounds.origin_x() + screen_bounds.origin_x(),
|
||||
);
|
||||
window_bounds.set_origin_y(
|
||||
window_bounds.origin_y() + screen_bounds.origin_y(),
|
||||
);
|
||||
bounds = WindowBounds::Fixed(window_bounds);
|
||||
} else {
|
||||
// Screen no longer exists. Return none here.
|
||||
return None;
|
||||
// Stored bounds are relative to the containing display.
|
||||
// So convert back to global coordinates if that screen still exists
|
||||
if let WindowBounds::Fixed(mut window_bounds) = bounds {
|
||||
if let Some(screen) = cx.platform().screen_by_id(display) {
|
||||
let screen_bounds = screen.bounds();
|
||||
window_bounds.set_origin_x(
|
||||
window_bounds.origin_x() + screen_bounds.origin_x(),
|
||||
);
|
||||
window_bounds.set_origin_y(
|
||||
window_bounds.origin_y() + screen_bounds.origin_y(),
|
||||
);
|
||||
bounds = WindowBounds::Fixed(window_bounds);
|
||||
} else {
|
||||
// Screen no longer exists. Return none here.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some((bounds, display))
|
||||
})
|
||||
.unzip()
|
||||
};
|
||||
Some((bounds, display))
|
||||
})
|
||||
.unzip()
|
||||
};
|
||||
|
||||
// Use the serialized workspace to construct the new window
|
||||
cx.add_window(
|
||||
(app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
|
||||
|cx| {
|
||||
Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
|
||||
},
|
||||
)
|
||||
});
|
||||
// Use the serialized workspace to construct the new window
|
||||
cx.add_window(
|
||||
(app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
|
||||
|cx| {
|
||||
Workspace::new(
|
||||
workspace_id,
|
||||
project_handle.clone(),
|
||||
app_state.clone(),
|
||||
cx,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// We haven't yielded the main thread since obtaining the window handle,
|
||||
// so the window exists.
|
||||
|
@ -1231,14 +1236,14 @@ impl Workspace {
|
|||
|
||||
pub fn close_global(_: &CloseWindow, cx: &mut AppContext) {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let id = cx
|
||||
.window_ids()
|
||||
let window = cx
|
||||
.windows()
|
||||
.into_iter()
|
||||
.find(|&id| cx.window_is_active(id));
|
||||
if let Some(id) = id {
|
||||
.find(|window| window.is_active(&cx).unwrap_or(false));
|
||||
if let Some(window) = window {
|
||||
//This can only get called when the window's project connection has been lost
|
||||
//so we don't need to prompt the user for anything and instead just close the window
|
||||
cx.remove_window(id);
|
||||
window.remove(&mut cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
@ -1249,11 +1254,11 @@ impl Workspace {
|
|||
_: &CloseWindow,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let prepare = self.prepare_to_close(false, cx);
|
||||
Some(cx.spawn(|_, mut cx| async move {
|
||||
if prepare.await? {
|
||||
cx.remove_window(window_id);
|
||||
window.remove(&mut cx);
|
||||
}
|
||||
Ok(())
|
||||
}))
|
||||
|
@ -1265,13 +1270,13 @@ impl Workspace {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<bool>> {
|
||||
let active_call = self.active_call().cloned();
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let workspace_count = cx
|
||||
.window_ids()
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
|
||||
.filter(|window| window.root_is::<Workspace>())
|
||||
.count();
|
||||
|
||||
if let Some(active_call) = active_call {
|
||||
|
@ -1279,11 +1284,11 @@ impl Workspace {
|
|||
&& workspace_count == 1
|
||||
&& active_call.read_with(&cx, |call, _| call.room().is_some())
|
||||
{
|
||||
let answer = cx.prompt(
|
||||
window_id,
|
||||
let answer = window.prompt(
|
||||
PromptLevel::Warning,
|
||||
"Do you want to leave the current call?",
|
||||
&["Close window and hang up", "Cancel"],
|
||||
&mut cx,
|
||||
);
|
||||
|
||||
if let Some(mut answer) = answer {
|
||||
|
@ -1389,7 +1394,7 @@ impl Workspace {
|
|||
paths: Vec<PathBuf>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window().downcast::<Self>();
|
||||
let is_remote = self.project.read(cx).is_remote();
|
||||
let has_worktree = self.project.read(cx).worktrees(cx).next().is_some();
|
||||
let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx));
|
||||
|
@ -1401,15 +1406,15 @@ impl Workspace {
|
|||
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 {
|
||||
let window_to_replace = if let Some(close_task) = close_task {
|
||||
if !close_task.await? {
|
||||
return Ok(());
|
||||
}
|
||||
Some(window_id)
|
||||
window
|
||||
} else {
|
||||
None
|
||||
};
|
||||
cx.update(|cx| open_paths(&paths, &app_state, window_id_to_replace, cx))
|
||||
cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx))
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
|
@ -3180,7 +3185,7 @@ impl Workspace {
|
|||
let left_visible = left_dock.is_open();
|
||||
let left_active_panel = left_dock.visible_panel().and_then(|panel| {
|
||||
Some(
|
||||
cx.view_ui_name(panel.as_any().window_id(), panel.id())?
|
||||
cx.view_ui_name(panel.as_any().window(), panel.id())?
|
||||
.to_string(),
|
||||
)
|
||||
});
|
||||
|
@ -3193,7 +3198,7 @@ impl Workspace {
|
|||
let right_visible = right_dock.is_open();
|
||||
let right_active_panel = right_dock.visible_panel().and_then(|panel| {
|
||||
Some(
|
||||
cx.view_ui_name(panel.as_any().window_id(), panel.id())?
|
||||
cx.view_ui_name(panel.as_any().window(), panel.id())?
|
||||
.to_string(),
|
||||
)
|
||||
});
|
||||
|
@ -3206,7 +3211,7 @@ impl Workspace {
|
|||
let bottom_visible = bottom_dock.is_open();
|
||||
let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| {
|
||||
Some(
|
||||
cx.view_ui_name(panel.as_any().window_id(), panel.id())?
|
||||
cx.view_ui_name(panel.as_any().window(), panel.id())?
|
||||
.to_string(),
|
||||
)
|
||||
});
|
||||
|
@ -3841,9 +3846,9 @@ pub fn activate_workspace_for_project(
|
|||
cx: &mut AsyncAppContext,
|
||||
predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
|
||||
) -> Option<WeakViewHandle<Workspace>> {
|
||||
for window_id in cx.window_ids() {
|
||||
let handle = cx
|
||||
.update_window(window_id, |cx| {
|
||||
for window in cx.windows() {
|
||||
let handle = window
|
||||
.update(cx, |cx| {
|
||||
if let Some(workspace_handle) = cx.root_view().clone().downcast::<Workspace>() {
|
||||
let project = workspace_handle.read(cx).project.clone();
|
||||
if project.update(cx, &predicate) {
|
||||
|
@ -3870,7 +3875,7 @@ pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
|
|||
pub fn open_paths(
|
||||
abs_paths: &[PathBuf],
|
||||
app_state: &Arc<AppState>,
|
||||
requesting_window_id: Option<usize>,
|
||||
requesting_window: Option<WindowHandle<Workspace>>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<
|
||||
Result<(
|
||||
|
@ -3898,7 +3903,7 @@ pub fn open_paths(
|
|||
} else {
|
||||
Ok(cx
|
||||
.update(|cx| {
|
||||
Workspace::new_local(abs_paths, app_state.clone(), requesting_window_id, cx)
|
||||
Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
|
||||
})
|
||||
.await)
|
||||
}
|
||||
|
@ -3959,18 +3964,23 @@ pub fn join_remote_project(
|
|||
) -> Task<Result<()>> {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let existing_workspace = cx
|
||||
.window_ids()
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
|
||||
.find(|workspace| {
|
||||
cx.read_window(workspace.window_id(), |cx| {
|
||||
workspace.read(cx).project().read(cx).remote_id() == Some(project_id)
|
||||
.find_map(|window| {
|
||||
window.downcast::<Workspace>().and_then(|window| {
|
||||
window.read_root_with(&cx, |workspace, cx| {
|
||||
if workspace.project().read(cx).remote_id() == Some(project_id) {
|
||||
Some(cx.handle().downgrade())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or(false)
|
||||
});
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let workspace = if let Some(existing_workspace) = existing_workspace {
|
||||
existing_workspace.downgrade()
|
||||
existing_workspace
|
||||
} else {
|
||||
let active_call = cx.read(ActiveCall::global);
|
||||
let room = active_call
|
||||
|
@ -4009,7 +4019,7 @@ pub fn join_remote_project(
|
|||
workspace.downgrade()
|
||||
};
|
||||
|
||||
cx.activate_window(workspace.window_id());
|
||||
workspace.window().activate(&mut cx);
|
||||
cx.platform().activate(true);
|
||||
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
|
@ -4048,29 +4058,22 @@ pub fn join_remote_project(
|
|||
pub fn restart(_: &Restart, cx: &mut AppContext) {
|
||||
let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
let mut workspace_windows = cx
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.filter_map(|window| window.downcast::<Workspace>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// 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()));
|
||||
workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
|
||||
|
||||
if let (true, Some(workspace)) = (should_confirm, workspaces.first()) {
|
||||
let answer = cx.prompt(
|
||||
workspace.window_id(),
|
||||
if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
|
||||
let answer = window.prompt(
|
||||
PromptLevel::Info,
|
||||
"Are you sure you want to restart?",
|
||||
&["Restart", "Cancel"],
|
||||
&mut cx,
|
||||
);
|
||||
|
||||
if let Some(mut answer) = answer {
|
||||
|
@ -4082,14 +4085,13 @@ pub fn restart(_: &Restart, cx: &mut AppContext) {
|
|||
}
|
||||
|
||||
// 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(());
|
||||
for window in workspace_windows {
|
||||
if let Some(close) = window.update_root(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(true, cx)
|
||||
}) {
|
||||
if !close.await? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
cx.platform().restart();
|
||||
|
@ -4214,17 +4216,11 @@ mod tests {
|
|||
.map(|e| e.id)
|
||||
);
|
||||
});
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
Some("one.txt — root1")
|
||||
);
|
||||
assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root1"));
|
||||
|
||||
// Add a second item to a non-empty pane
|
||||
workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
Some("two.txt — root1")
|
||||
);
|
||||
assert_eq!(window.current_title(cx).as_deref(), Some("two.txt — root1"));
|
||||
project.read_with(cx, |project, cx| {
|
||||
assert_eq!(
|
||||
project.active_entry(),
|
||||
|
@ -4240,10 +4236,7 @@ mod tests {
|
|||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
Some("one.txt — root1")
|
||||
);
|
||||
assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root1"));
|
||||
project.read_with(cx, |project, cx| {
|
||||
assert_eq!(
|
||||
project.active_entry(),
|
||||
|
@ -4261,16 +4254,13 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
window.current_title(cx).as_deref(),
|
||||
Some("one.txt — root1, root2")
|
||||
);
|
||||
|
||||
// Remove a project folder
|
||||
project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
Some("one.txt — root2")
|
||||
);
|
||||
assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root2"));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
@ -4304,9 +4294,9 @@ mod tests {
|
|||
});
|
||||
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
|
||||
cx.foreground().run_until_parked();
|
||||
cx.simulate_prompt_answer(window.window_id(), 2 /* cancel */);
|
||||
window.simulate_prompt_answer(2, cx); // cancel
|
||||
cx.foreground().run_until_parked();
|
||||
assert!(!cx.has_pending_prompt(window.window_id()));
|
||||
assert!(!window.has_pending_prompt(cx));
|
||||
assert!(!task.await.unwrap());
|
||||
}
|
||||
|
||||
|
@ -4365,10 +4355,10 @@ mod tests {
|
|||
assert_eq!(pane.items_len(), 4);
|
||||
assert_eq!(pane.active_item().unwrap().id(), item1.id());
|
||||
});
|
||||
assert!(cx.has_pending_prompt(window.window_id()));
|
||||
assert!(window.has_pending_prompt(cx));
|
||||
|
||||
// Confirm saving item 1.
|
||||
cx.simulate_prompt_answer(window.window_id(), 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
// Item 1 is saved. There's a prompt to save item 3.
|
||||
|
@ -4379,10 +4369,10 @@ mod tests {
|
|||
assert_eq!(pane.items_len(), 3);
|
||||
assert_eq!(pane.active_item().unwrap().id(), item3.id());
|
||||
});
|
||||
assert!(cx.has_pending_prompt(window.window_id()));
|
||||
assert!(window.has_pending_prompt(cx));
|
||||
|
||||
// Cancel saving item 3.
|
||||
cx.simulate_prompt_answer(window.window_id(), 1);
|
||||
window.simulate_prompt_answer(1, cx);
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
// Item 3 is reloaded. There's a prompt to save item 4.
|
||||
|
@ -4393,10 +4383,10 @@ mod tests {
|
|||
assert_eq!(pane.items_len(), 2);
|
||||
assert_eq!(pane.active_item().unwrap().id(), item4.id());
|
||||
});
|
||||
assert!(cx.has_pending_prompt(window.window_id()));
|
||||
assert!(window.has_pending_prompt(cx));
|
||||
|
||||
// Confirm saving item 4.
|
||||
cx.simulate_prompt_answer(window.window_id(), 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
// There's a prompt for a path for item 4.
|
||||
|
@ -4499,7 +4489,7 @@ mod tests {
|
|||
&[ProjectEntryId::from_proto(0)]
|
||||
);
|
||||
});
|
||||
cx.simulate_prompt_answer(window.window_id(), 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
left_pane.read_with(cx, |pane, cx| {
|
||||
|
@ -4508,7 +4498,7 @@ mod tests {
|
|||
&[ProjectEntryId::from_proto(2)]
|
||||
);
|
||||
});
|
||||
cx.simulate_prompt_answer(window.window_id(), 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
close.await.unwrap();
|
||||
|
@ -4547,7 +4537,7 @@ mod tests {
|
|||
});
|
||||
|
||||
// Deactivating the window saves the file.
|
||||
cx.simulate_window_activation(None);
|
||||
window.simulate_deactivation(cx);
|
||||
deterministic.run_until_parked();
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 1));
|
||||
|
||||
|
@ -4568,12 +4558,12 @@ mod tests {
|
|||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
|
||||
|
||||
// Deactivating the window still saves the file.
|
||||
cx.simulate_window_activation(Some(window.window_id()));
|
||||
window.simulate_activation(cx);
|
||||
item.update(cx, |item, cx| {
|
||||
cx.focus_self();
|
||||
item.is_dirty = true;
|
||||
});
|
||||
cx.simulate_window_activation(None);
|
||||
window.simulate_deactivation(cx);
|
||||
|
||||
deterministic.run_until_parked();
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
|
||||
|
@ -4610,7 +4600,7 @@ mod tests {
|
|||
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!cx.has_pending_prompt(window.window_id()));
|
||||
assert!(!window.has_pending_prompt(cx));
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
|
||||
|
||||
// Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
|
||||
|
@ -4631,7 +4621,7 @@ mod tests {
|
|||
let _close_items =
|
||||
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id));
|
||||
deterministic.run_until_parked();
|
||||
assert!(cx.has_pending_prompt(window.window_id()));
|
||||
assert!(window.has_pending_prompt(cx));
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue