mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Change open paths to replace the existing window if dispatched from a window
co-authored-by: Max <max@zed.dev>
This commit is contained in:
parent
ab4b3293d1
commit
904993dfc9
@ -48,7 +48,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
|
||||
async move {
|
||||
let (journal_dir, entry_path) = create_entry.await?;
|
||||
let (workspace, _) = cx
|
||||
.update(|cx| workspace::open_paths(&[journal_dir], &app_state, cx))
|
||||
.update(|cx| workspace::open_paths(&[journal_dir], &app_state, None, cx))
|
||||
.await;
|
||||
|
||||
let opened = workspace
|
||||
|
@ -44,7 +44,8 @@ use gpui::{
|
||||
platform::{CursorStyle, WindowOptions},
|
||||
AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle,
|
||||
MouseButton, MutableAppContext, PathPromptOptions, Platform, PromptLevel, RenderContext,
|
||||
SizeConstraint, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowBounds,
|
||||
SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
WindowBounds,
|
||||
};
|
||||
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
|
||||
use language::LanguageRegistry;
|
||||
@ -188,12 +189,54 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
|
||||
dock::init(cx);
|
||||
notifications::init(cx);
|
||||
|
||||
cx.add_global_action(open);
|
||||
cx.add_global_action(|_: &Open, cx: &mut MutableAppContext| {
|
||||
let mut paths = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: true,
|
||||
multiple: true,
|
||||
});
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
if let Some(paths) = paths.recv().await.flatten() {
|
||||
cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths }));
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
cx.add_action(|_, _: &Open, cx: &mut ViewContext<Workspace>| {
|
||||
let mut paths = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: true,
|
||||
multiple: true,
|
||||
});
|
||||
|
||||
let handle = cx.handle().downgrade();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
if let Some(paths) = paths.recv().await.flatten() {
|
||||
cx.update(|cx| {
|
||||
cx.dispatch_action_at(handle.window_id(), handle.id(), OpenPaths { paths })
|
||||
})
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
cx.add_global_action({
|
||||
let app_state = Arc::downgrade(&app_state);
|
||||
move |action: &OpenPaths, cx: &mut MutableAppContext| {
|
||||
if let Some(app_state) = app_state.upgrade() {
|
||||
open_paths(&action.paths, &app_state, cx).detach();
|
||||
open_paths(&action.paths, &app_state, None, cx).detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
cx.add_action({
|
||||
let app_state = Arc::downgrade(&app_state);
|
||||
move |_, action: &OpenPaths, cx: &mut ViewContext<Workspace>| {
|
||||
if let Some(app_state) = app_state.upgrade() {
|
||||
let window_id = cx.window_id();
|
||||
let action = action.clone();
|
||||
cx.as_mut().defer(move |cx| {
|
||||
open_paths(&action.paths, &app_state, Some(window_id), cx).detach();
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -488,6 +531,7 @@ pub struct Workspace {
|
||||
active_call: Option<(ModelHandle<ActiveCall>, Vec<gpui::Subscription>)>,
|
||||
leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>,
|
||||
database_id: WorkspaceId,
|
||||
_window_subscriptions: [Subscription; 3],
|
||||
_apply_leader_updates: Task<Result<()>>,
|
||||
_observe_current_user: Task<()>,
|
||||
}
|
||||
@ -519,10 +563,6 @@ impl Workspace {
|
||||
dock_default_factory: DockDefaultItemFactory,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
cx.observe_fullscreen(|_, _, cx| cx.notify()).detach();
|
||||
|
||||
cx.observe_window_activation(Self::on_window_activation_changed)
|
||||
.detach();
|
||||
cx.observe(&project, |_, _, cx| cx.notify()).detach();
|
||||
cx.subscribe(&project, move |this, _, event, cx| {
|
||||
match event {
|
||||
@ -629,6 +669,28 @@ impl Workspace {
|
||||
active_call = Some((call, subscriptions));
|
||||
}
|
||||
|
||||
let subscriptions = [
|
||||
cx.observe_fullscreen(|_, _, cx| cx.notify()),
|
||||
cx.observe_window_activation(Self::on_window_activation_changed),
|
||||
cx.observe_window_bounds(move |_, mut bounds, display, cx| {
|
||||
// Transform fixed bounds to be stored in terms of the containing display
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
cx.background()
|
||||
.spawn(DB.set_window_bounds(workspace_id, bounds, display))
|
||||
.detach_and_log_err(cx);
|
||||
}),
|
||||
];
|
||||
|
||||
let mut this = Workspace {
|
||||
modal: None,
|
||||
weak_self: weak_handle.clone(),
|
||||
@ -660,6 +722,7 @@ impl Workspace {
|
||||
_observe_current_user,
|
||||
_apply_leader_updates,
|
||||
leader_updates_tx,
|
||||
_window_subscriptions: subscriptions,
|
||||
};
|
||||
this.project_remote_id_changed(project.read(cx).remote_id(), cx);
|
||||
cx.defer(|this, cx| this.update_window_title(cx));
|
||||
@ -676,6 +739,7 @@ impl Workspace {
|
||||
fn new_local(
|
||||
abs_paths: Vec<PathBuf>,
|
||||
app_state: Arc<AppState>,
|
||||
requesting_window_id: Option<usize>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Task<(
|
||||
ViewHandle<Workspace>,
|
||||
@ -731,42 +795,9 @@ impl Workspace {
|
||||
))
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Some((bounds, display))
|
||||
})
|
||||
.unzip()
|
||||
};
|
||||
|
||||
// Use the serialized workspace to construct the new window
|
||||
let (_, workspace) = cx.add_window(
|
||||
(app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
|
||||
|cx| {
|
||||
let build_workspace =
|
||||
|cx: &mut ViewContext<Workspace>,
|
||||
serialized_workspace: Option<SerializedWorkspace>| {
|
||||
let mut workspace = Workspace::new(
|
||||
serialized_workspace,
|
||||
workspace_id,
|
||||
@ -775,29 +806,53 @@ impl Workspace {
|
||||
cx,
|
||||
);
|
||||
(app_state.initialize_workspace)(&mut workspace, &app_state, cx);
|
||||
cx.observe_window_bounds(move |_, mut bounds, display, cx| {
|
||||
// Transform fixed bounds to be stored in terms of the containing display
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
cx.background()
|
||||
.spawn(DB.set_window_bounds(workspace_id, bounds, display))
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
.detach();
|
||||
workspace
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
let workspace = if let Some(window_id) = requesting_window_id {
|
||||
cx.update(|cx| {
|
||||
cx.replace_root_view(window_id, |cx| build_workspace(cx, serialized_workspace))
|
||||
})
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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| build_workspace(cx, serialized_workspace),
|
||||
)
|
||||
.1
|
||||
};
|
||||
|
||||
notify_if_database_failed(&workspace, &mut cx);
|
||||
|
||||
@ -893,7 +948,7 @@ impl Workspace {
|
||||
if self.project.read(cx).is_local() {
|
||||
Task::Ready(Some(callback(self, cx)))
|
||||
} else {
|
||||
let task = Self::new_local(Vec::new(), app_state.clone(), cx);
|
||||
let task = Self::new_local(Vec::new(), app_state.clone(), None, cx);
|
||||
cx.spawn(|_vh, mut cx| async move {
|
||||
let (workspace, _) = task.await;
|
||||
workspace.update(&mut cx, callback)
|
||||
@ -2809,21 +2864,6 @@ impl std::fmt::Debug for OpenPaths {
|
||||
}
|
||||
}
|
||||
|
||||
fn open(_: &Open, cx: &mut MutableAppContext) {
|
||||
let mut paths = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: true,
|
||||
multiple: true,
|
||||
});
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
if let Some(paths) = paths.recv().await.flatten() {
|
||||
cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths }));
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub struct WorkspaceCreated(WeakViewHandle<Workspace>);
|
||||
|
||||
pub fn activate_workspace_for_project(
|
||||
@ -2850,6 +2890,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>,
|
||||
cx: &mut MutableAppContext,
|
||||
) -> Task<(
|
||||
ViewHandle<Workspace>,
|
||||
@ -2880,7 +2921,8 @@ pub fn open_paths(
|
||||
.contains(&false);
|
||||
|
||||
cx.update(|cx| {
|
||||
let task = Workspace::new_local(abs_paths, app_state.clone(), cx);
|
||||
let task =
|
||||
Workspace::new_local(abs_paths, app_state.clone(), requesting_window_id, cx);
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
let (workspace, items) = task.await;
|
||||
@ -2904,7 +2946,7 @@ pub fn open_new(
|
||||
cx: &mut MutableAppContext,
|
||||
init: impl FnOnce(&mut Workspace, &mut ViewContext<Workspace>) + 'static,
|
||||
) -> Task<()> {
|
||||
let task = Workspace::new_local(Vec::new(), app_state.clone(), cx);
|
||||
let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx);
|
||||
cx.spawn(|mut cx| async move {
|
||||
let (workspace, opened_paths) = task.await;
|
||||
|
||||
|
@ -216,7 +216,7 @@ fn main() {
|
||||
cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
|
||||
.detach();
|
||||
} else if let Ok(Some(paths)) = open_paths_rx.try_next() {
|
||||
cx.update(|cx| workspace::open_paths(&paths, &app_state, cx))
|
||||
cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
|
||||
.detach();
|
||||
} else {
|
||||
cx.spawn(|cx| async move { restore_or_create_workspace(cx).await })
|
||||
@ -237,7 +237,7 @@ fn main() {
|
||||
let app_state = app_state.clone();
|
||||
async move {
|
||||
while let Some(paths) = open_paths_rx.next().await {
|
||||
cx.update(|cx| workspace::open_paths(&paths, &app_state, cx))
|
||||
cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
@ -603,7 +603,7 @@ async fn handle_cli_connection(
|
||||
paths
|
||||
};
|
||||
let (workspace, items) = cx
|
||||
.update(|cx| workspace::open_paths(&paths, &app_state, cx))
|
||||
.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
|
||||
.await;
|
||||
|
||||
let mut errored = false;
|
||||
|
@ -728,6 +728,10 @@ mod tests {
|
||||
"ca": null,
|
||||
"cb": null,
|
||||
},
|
||||
"d": {
|
||||
"da": null,
|
||||
"db": null,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
@ -736,13 +740,14 @@ mod tests {
|
||||
open_paths(
|
||||
&[PathBuf::from("/root/a"), PathBuf::from("/root/b")],
|
||||
&app_state,
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await;
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
.await;
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
let workspace_1 = cx.root_view::<Workspace>(cx.window_ids()[0]).unwrap();
|
||||
@ -756,11 +761,37 @@ mod tests {
|
||||
open_paths(
|
||||
&[PathBuf::from("/root/b"), PathBuf::from("/root/c")],
|
||||
&app_state,
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await;
|
||||
assert_eq!(cx.window_ids().len(), 2);
|
||||
|
||||
// Replace existing windows
|
||||
let window_id = cx.window_ids()[0];
|
||||
cx.update(|cx| {
|
||||
open_paths(
|
||||
&[PathBuf::from("/root/c"), PathBuf::from("/root/d")],
|
||||
&app_state,
|
||||
Some(window_id),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await;
|
||||
assert_eq!(cx.window_ids().len(), 2);
|
||||
let workspace_1 = cx.root_view::<Workspace>(window_id).unwrap();
|
||||
workspace_1.read_with(cx, |workspace, cx| {
|
||||
assert_eq!(
|
||||
workspace
|
||||
.worktrees(cx)
|
||||
.map(|w| w.read(cx).abs_path())
|
||||
.collect::<Vec<_>>(),
|
||||
&[Path::new("/root/c").into(), Path::new("/root/d").into()]
|
||||
);
|
||||
assert!(workspace.left_sidebar().read(cx).is_open());
|
||||
assert!(workspace.active_pane().is_focused(cx));
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@ -772,7 +803,7 @@ mod tests {
|
||||
.insert_tree("/root", json!({"a": "hey"}))
|
||||
.await;
|
||||
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
.await;
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
|
||||
@ -810,7 +841,7 @@ mod tests {
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
|
||||
// Opening the buffer again doesn't impact the window's edited state.
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
.await;
|
||||
let editor = workspace.read_with(cx, |workspace, cx| {
|
||||
workspace
|
||||
|
Loading…
Reference in New Issue
Block a user