This commit is contained in:
Nathan Sobo 2023-08-06 12:45:31 -06:00
parent dcf8b00656
commit adc50469ff
21 changed files with 329 additions and 277 deletions

39
Cargo.lock generated
View File

@ -1649,6 +1649,12 @@ dependencies = [
"theme", "theme",
] ]
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]] [[package]]
name = "copilot" name = "copilot"
version = "0.1.0" version = "0.1.0"
@ -2133,6 +2139,19 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "derive_more"
version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version 0.4.0",
"syn 1.0.109",
]
[[package]] [[package]]
name = "dhat" name = "dhat"
version = "0.3.2" version = "0.3.2"
@ -3096,6 +3115,7 @@ dependencies = [
"core-graphics", "core-graphics",
"core-text", "core-text",
"ctor", "ctor",
"derive_more",
"dhat", "dhat",
"env_logger 0.9.3", "env_logger 0.9.3",
"etagere", "etagere",
@ -5106,7 +5126,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff"
dependencies = [ dependencies = [
"rustc_version", "rustc_version 0.3.3",
] ]
[[package]] [[package]]
@ -6276,7 +6296,16 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
dependencies = [ dependencies = [
"semver", "semver 0.11.0",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.18",
] ]
[[package]] [[package]]
@ -6716,6 +6745,12 @@ dependencies = [
"semver-parser", "semver-parser",
] ]
[[package]]
name = "semver"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]] [[package]]
name = "semver-parser" name = "semver-parser"
version = "0.10.2" version = "0.10.2"

View File

@ -79,6 +79,7 @@ resolver = "2"
anyhow = { version = "1.0.57" } anyhow = { version = "1.0.57" }
async-trait = { version = "0.1" } async-trait = { version = "0.1" }
ctor = { version = "0.1" } ctor = { version = "0.1" }
derive_more = { version = "0.99.17" }
env_logger = { version = "0.9" } env_logger = { version = "0.9" }
futures = { version = "0.3" } futures = { version = "0.3" }
globset = { version = "0.4" } globset = { version = "0.4" }

View File

@ -3446,7 +3446,7 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx)); let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
let mut editor_cx_a = EditorTestContext { let mut editor_cx_a = EditorTestContext {
cx: cx_a, cx: cx_a,
window_id: window_a.window_id(), window_id: window_a.id(),
editor: editor_a, editor: editor_a,
}; };
@ -3459,7 +3459,7 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx)); let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
let mut editor_cx_b = EditorTestContext { let mut editor_cx_b = EditorTestContext {
cx: cx_b, cx: cx_b,
window_id: window_b.window_id(), window_id: window_b.id(),
editor: editor_b, editor: editor_b,
}; };

View File

@ -49,7 +49,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()), |_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()),
); );
notification_windows.push(window.window_id()); notification_windows.push(window.id());
} }
} }
} }

View File

@ -52,7 +52,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
notification_windows notification_windows
.entry(*project_id) .entry(*project_id)
.or_insert(Vec::new()) .or_insert(Vec::new())
.push(window.window_id()); .push(window.id());
} }
} }
room::Event::RemoteProjectUnshared { project_id } => { room::Event::RemoteProjectUnshared { project_id } => {

View File

@ -297,7 +297,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), [], cx).await; let project = Project::test(app_state.fs.clone(), [], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let editor = cx.add_view(window_id, |cx| { let editor = cx.add_view(window_id, |cx| {
let mut editor = Editor::single_line(None, cx); let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx); editor.set_text("abc", cx);

View File

@ -857,7 +857,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
// Create some diagnostics // Create some diagnostics
project.update(cx, |project, cx| { project.update(cx, |project, cx| {
@ -1252,7 +1252,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let view = cx.add_view(window_id, |cx| { let view = cx.add_view(window_id, |cx| {
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx) ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)

View File

@ -525,7 +525,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
let project = Project::test(fs, [], cx).await; let project = Project::test(fs, [], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
cx.add_view(window_id, |cx| { cx.add_view(window_id, |cx| {
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);

View File

@ -99,7 +99,7 @@ impl<'a> EditorLspTestContext<'a> {
Self { Self {
cx: EditorTestContext { cx: EditorTestContext {
cx, cx,
window_id: window.window_id(), window_id: window.id(),
editor, editor,
}, },
lsp, lsp,

View File

@ -39,7 +39,7 @@ impl<'a> EditorTestContext<'a> {
let editor = window.root(cx); let editor = window.root(cx);
Self { Self {
cx, cx,
window_id: window.window_id(), window_id: window.id(),
editor, editor,
} }
} }

View File

@ -619,7 +619,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
cx.dispatch_action(window.window_id(), Toggle); cx.dispatch_action(window.id(), Toggle);
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap()); let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
finder finder
@ -632,8 +632,8 @@ mod tests {
}); });
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
cx.dispatch_action(window.window_id(), SelectNext); cx.dispatch_action(window.id(), SelectNext);
cx.dispatch_action(window.window_id(), Confirm); cx.dispatch_action(window.id(), Confirm);
active_pane active_pane
.condition(cx, |pane, _| pane.active_item().is_some()) .condition(cx, |pane, _| pane.active_item().is_some())
.await; .await;
@ -674,7 +674,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
cx.dispatch_action(window.window_id(), Toggle); cx.dispatch_action(window.id(), Toggle);
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap()); let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
let file_query = &first_file_name[..3]; let file_query = &first_file_name[..3];
@ -706,8 +706,8 @@ mod tests {
}); });
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
cx.dispatch_action(window.window_id(), SelectNext); cx.dispatch_action(window.id(), SelectNext);
cx.dispatch_action(window.window_id(), Confirm); cx.dispatch_action(window.id(), Confirm);
active_pane active_pane
.condition(cx, |pane, _| pane.active_item().is_some()) .condition(cx, |pane, _| pane.active_item().is_some())
.await; .await;
@ -758,7 +758,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
cx.dispatch_action(window.window_id(), Toggle); cx.dispatch_action(window.id(), Toggle);
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap()); let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
let file_query = &first_file_name[..3]; let file_query = &first_file_name[..3];
@ -790,8 +790,8 @@ mod tests {
}); });
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone()); let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
cx.dispatch_action(window.window_id(), SelectNext); cx.dispatch_action(window.id(), SelectNext);
cx.dispatch_action(window.window_id(), Confirm); cx.dispatch_action(window.id(), Confirm);
active_pane active_pane
.condition(cx, |pane, _| pane.active_item().is_some()) .condition(cx, |pane, _| pane.active_item().is_some())
.await; .await;
@ -1168,7 +1168,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let worktree_id = cx.read(|cx| { let worktree_id = cx.read(|cx| {
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>(); let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 1); assert_eq!(worktrees.len(), 1);
@ -1376,7 +1376,7 @@ mod tests {
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let worktree_id = cx.read(|cx| { let worktree_id = cx.read(|cx| {
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>(); let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
assert_eq!(worktrees.len(), 1,); assert_eq!(worktrees.len(), 1,);

View File

@ -22,6 +22,7 @@ sqlez = { path = "../sqlez" }
async-task = "4.0.3" async-task = "4.0.3"
backtrace = { version = "0.3", optional = true } backtrace = { version = "0.3", optional = true }
ctor.workspace = true ctor.workspace = true
derive_more.workspace = true
dhat = { version = "0.3", optional = true } dhat = { version = "0.3", optional = true }
env_logger = { version = "0.9", optional = true } env_logger = { version = "0.9", optional = true }
etagere = "0.2" etagere = "0.2"

View File

@ -23,6 +23,7 @@ use std::{
}; };
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use derive_more::Deref;
use parking_lot::Mutex; use parking_lot::Mutex;
use postage::oneshot; use postage::oneshot;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -132,11 +133,13 @@ pub trait BorrowAppContext {
pub trait BorrowWindowContext { pub trait BorrowWindowContext {
type Result<T>; type Result<T>;
fn read_window_with<T, F>(&self, window_id: usize, f: F) -> Self::Result<T> fn read_window_with<H, T, F>(&self, window_handle: H, f: F) -> Self::Result<T>
where where
H: Into<AnyWindowHandle>,
F: FnOnce(&WindowContext) -> T; F: FnOnce(&WindowContext) -> T;
fn update_window<T, F>(&mut self, window_id: usize, f: F) -> Self::Result<T> fn update_window<H, T, F>(&mut self, window_handle: H, f: F) -> Self::Result<T>
where where
H: Into<AnyWindowHandle>,
F: FnOnce(&mut WindowContext) -> T; F: FnOnce(&mut WindowContext) -> T;
} }
@ -300,13 +303,13 @@ impl App {
result result
} }
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>( fn update_window<H, T, F>(&mut self, handle: H, callback: F) -> Option<T>
&mut self, where
window_id: usize, H: Into<AnyWindowHandle>,
callback: F, F: FnOnce(&mut WindowContext) -> T,
) -> Option<T> { {
let mut state = self.0.borrow_mut(); let mut state = self.0.borrow_mut();
let result = state.update_window(window_id, callback); let result = state.update_window(handle, callback);
state.pending_notifications.clear(); state.pending_notifications.clear();
result result
} }
@ -333,6 +336,10 @@ impl AsyncAppContext {
self.0.borrow_mut().update(callback) self.0.borrow_mut().update(callback)
} }
pub fn windows(&self) -> Vec<AnyWindowHandle> {
self.0.borrow().windows().collect()
}
pub fn read_window<T, F: FnOnce(&WindowContext) -> T>( pub fn read_window<T, F: FnOnce(&WindowContext) -> T>(
&self, &self,
window_id: usize, window_id: usize,
@ -343,10 +350,10 @@ impl AsyncAppContext {
pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>( pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self, &mut self,
window_id: usize, handle: AnyWindowHandle,
callback: F, callback: F,
) -> Option<T> { ) -> Option<T> {
self.0.borrow_mut().update_window(window_id, callback) self.0.borrow_mut().update_window(handle, callback)
} }
pub fn debug_elements(&self, window_id: usize) -> Option<json::Value> { pub fn debug_elements(&self, window_id: usize) -> Option<json::Value> {
@ -359,14 +366,14 @@ impl AsyncAppContext {
pub fn dispatch_action( pub fn dispatch_action(
&mut self, &mut self,
window_id: usize, window: impl Into<AnyWindowHandle>,
view_id: usize, view_id: usize,
action: &dyn Action, action: &dyn Action,
) -> Result<()> { ) -> Result<()> {
self.0 self.0
.borrow_mut() .borrow_mut()
.update_window(window_id, |window| { .update_window(window, |cx| {
window.dispatch_action(Some(view_id), action); cx.dispatch_action(Some(view_id), action);
}) })
.ok_or_else(|| anyhow!("window not found")) .ok_or_else(|| anyhow!("window not found"))
} }
@ -380,22 +387,6 @@ impl AsyncAppContext {
.unwrap_or_default() .unwrap_or_default()
} }
pub fn has_window(&self, window_id: usize) -> bool {
self.read(|cx| cx.windows.contains_key(&window_id))
}
pub fn window_is_active(&self, window_id: usize) -> bool {
self.read(|cx| cx.windows.get(&window_id).map_or(false, |w| w.is_active))
}
pub fn root_view(&self, window_id: usize) -> Option<AnyViewHandle> {
self.read(|cx| cx.windows.get(&window_id).map(|w| w.root_view().clone()))
}
pub fn window_ids(&self) -> Vec<usize> {
self.read(|cx| cx.windows.keys().copied().collect())
}
pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T> pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
where where
T: Entity, T: Entity,
@ -501,7 +492,7 @@ pub struct AppContext {
models: HashMap<usize, Box<dyn AnyModel>>, models: HashMap<usize, Box<dyn AnyModel>>,
views: HashMap<(usize, usize), Box<dyn AnyView>>, views: HashMap<(usize, usize), Box<dyn AnyView>>,
views_metadata: HashMap<(usize, usize), ViewMetadata>, views_metadata: HashMap<(usize, usize), ViewMetadata>,
windows: HashMap<usize, Window>, windows: HashMap<AnyWindowHandle, Window>,
globals: HashMap<TypeId, Box<dyn Any>>, globals: HashMap<TypeId, Box<dyn Any>>,
element_states: HashMap<ElementStateId, Box<dyn Any>>, element_states: HashMap<ElementStateId, Box<dyn Any>>,
background: Arc<executor::Background>, background: Arc<executor::Background>,
@ -818,22 +809,6 @@ impl AppContext {
Some(callback(&window_context)) Some(callback(&window_context))
} }
pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
callback: F,
) -> Option<T> {
self.update(|app_context| {
let mut window = app_context.windows.remove(&window_id)?;
let mut window_context = WindowContext::mutable(app_context, &mut window, window_id);
let result = callback(&mut window_context);
if !window_context.removed {
app_context.windows.insert(window_id, window);
}
Some(result)
})
}
pub fn update_active_window<T, F: FnOnce(&mut WindowContext) -> T>( pub fn update_active_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self, &mut self,
callback: F, callback: F,
@ -1331,43 +1306,40 @@ impl AppContext {
F: FnOnce(&mut ViewContext<V>) -> V, F: FnOnce(&mut ViewContext<V>) -> V,
{ {
self.update(|this| { self.update(|this| {
let window_id = post_inc(&mut this.next_id); let window = WindowHandle::<V>::new(post_inc(&mut this.next_id));
let platform_window = let platform_window =
this.platform this.platform
.open_window(window_id, window_options, this.foreground.clone()); .open_window(window, window_options, this.foreground.clone());
let window = this.build_window(window_id, platform_window, build_root_view); let window = this.build_window(window, platform_window, build_root_view);
this.windows.insert(window_id, window); this.windows.insert(window.into(), window);
WindowHandle::new(window_id) window
}) })
} }
pub fn add_status_bar_item<V, F>(&mut self, build_root_view: F) -> (usize, ViewHandle<V>) pub fn add_status_bar_item<V, F>(&mut self, build_root_view: F) -> WindowHandle<V>
where where
V: View, V: View,
F: FnOnce(&mut ViewContext<V>) -> V, F: FnOnce(&mut ViewContext<V>) -> V,
{ {
self.update(|this| { self.update(|this| {
let window_id = post_inc(&mut this.next_id); let handle = WindowHandle::<V>::new(post_inc(&mut this.next_id));
let platform_window = this.platform.add_status_item(window_id); let platform_window = this.platform.add_status_item(handle.id());
let window = this.build_window(window_id, platform_window, build_root_view); let window = this.build_window(handle, platform_window, build_root_view);
let root_view = window.root_view().clone().downcast::<V>().unwrap();
this.windows.insert(window_id, window); this.windows.insert(handle.into(), window);
this.update_window(window_id, |cx| { handle.update_root(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
root_view.update(cx, |view, cx| view.focus_in(cx.handle().into_any(), cx)) handle
});
(window_id, root_view)
}) })
} }
pub fn build_window<V, F>( pub fn build_window<H, V, F>(
&mut self, &mut self,
window_id: usize, handle: H,
mut platform_window: Box<dyn platform::Window>, mut platform_window: Box<dyn platform::Window>,
build_root_view: F, build_root_view: F,
) -> Window ) -> Window
where where
H: Into<AnyWindowHandle>,
V: View, V: View,
F: FnOnce(&mut ViewContext<V>) -> V, F: FnOnce(&mut ViewContext<V>) -> V,
{ {
@ -1375,7 +1347,7 @@ impl AppContext {
let mut app = self.upgrade(); let mut app = self.upgrade();
platform_window.on_event(Box::new(move |event| { platform_window.on_event(Box::new(move |event| {
app.update_window(window_id, |cx| { app.update_window(handle, |cx| {
if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event { if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
if cx.dispatch_keystroke(keystroke) { if cx.dispatch_keystroke(keystroke) {
return true; return true;
@ -1391,35 +1363,35 @@ impl AppContext {
{ {
let mut app = self.upgrade(); let mut app = self.upgrade();
platform_window.on_active_status_change(Box::new(move |is_active| { platform_window.on_active_status_change(Box::new(move |is_active| {
app.update(|cx| cx.window_changed_active_status(window_id, is_active)) app.update(|cx| cx.window_changed_active_status(handle, is_active))
})); }));
} }
{ {
let mut app = self.upgrade(); let mut app = self.upgrade();
platform_window.on_resize(Box::new(move || { platform_window.on_resize(Box::new(move || {
app.update(|cx| cx.window_was_resized(window_id)) app.update(|cx| cx.window_was_resized(handle))
})); }));
} }
{ {
let mut app = self.upgrade(); let mut app = self.upgrade();
platform_window.on_moved(Box::new(move || { platform_window.on_moved(Box::new(move || {
app.update(|cx| cx.window_was_moved(window_id)) app.update(|cx| cx.window_was_moved(handle))
})); }));
} }
{ {
let mut app = self.upgrade(); let mut app = self.upgrade();
platform_window.on_fullscreen(Box::new(move |is_fullscreen| { platform_window.on_fullscreen(Box::new(move |is_fullscreen| {
app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen)) app.update(|cx| cx.window_was_fullscreen_changed(handle, is_fullscreen))
})); }));
} }
{ {
let mut app = self.upgrade(); let mut app = self.upgrade();
platform_window.on_close(Box::new(move || { platform_window.on_close(Box::new(move || {
app.update(|cx| cx.update_window(window_id, |cx| cx.remove_window())); app.update(|cx| cx.update_window(handle, |cx| cx.remove_window()));
})); }));
} }
@ -1431,27 +1403,20 @@ impl AppContext {
platform_window.set_input_handler(Box::new(WindowInputHandler { platform_window.set_input_handler(Box::new(WindowInputHandler {
app: self.upgrade().0, app: self.upgrade().0,
window_id, window_id: handle,
})); }));
let mut window = Window::new(window_id, platform_window, self, build_root_view); let mut window = Window::new(handle, platform_window, self, build_root_view);
let mut cx = WindowContext::mutable(self, &mut window, window_id); let mut cx = WindowContext::mutable(self, &mut window, handle);
cx.layout(false).expect("initial layout should not error"); cx.layout(false).expect("initial layout should not error");
let scene = cx.paint().expect("initial paint should not error"); let scene = cx.paint().expect("initial paint should not error");
window.platform_window.present_scene(scene); window.platform_window.present_scene(scene);
window window
} }
pub(crate) fn replace_root_view<V, F>( pub fn windows(&self) -> impl Iterator<Item = AnyWindowHandle> {
&mut self, todo!();
window_id: usize, None.into_iter()
build_root_view: F,
) -> Option<WindowHandle<V>>
where
V: View,
F: FnOnce(&mut ViewContext<V>) -> V,
{
self.update_window(window_id, |cx| cx.replace_root_view(build_root_view))
} }
pub fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T { pub fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
@ -2192,11 +2157,20 @@ impl BorrowWindowContext for AppContext {
AppContext::read_window(self, window_id, f) AppContext::read_window(self, window_id, f)
} }
fn update_window<T, F>(&mut self, window_id: usize, f: F) -> Self::Result<T> fn update_window<T, F>(&mut self, window: usize, f: F) -> Self::Result<T>
where where
F: FnOnce(&mut WindowContext) -> T, F: FnOnce(&mut WindowContext) -> T,
{ {
AppContext::update_window(self, window_id, f) self.update(|app_context| {
let mut window = app_context.windows.remove(&window_handle)?;
let mut window_context =
WindowContext::mutable(app_context, &mut window, window_handle);
let result = callback(&mut window_context);
if !window_context.removed {
app_context.windows.insert(window_handle, window);
}
Some(result)
})
} }
} }
@ -3862,44 +3836,28 @@ impl<T> Clone for WeakModelHandle<T> {
impl<T> Copy for WeakModelHandle<T> {} impl<T> Copy for WeakModelHandle<T> {}
#[derive(Deref, Copy, Clone)]
pub struct WindowHandle<T> { pub struct WindowHandle<T> {
window_id: usize, #[deref]
any_handle: AnyWindowHandle,
root_view_type: PhantomData<T>, root_view_type: PhantomData<T>,
} }
#[allow(dead_code)]
impl<V: View> WindowHandle<V> { impl<V: View> WindowHandle<V> {
fn new(window_id: usize) -> Self { fn new(window_id: usize) -> Self {
WindowHandle { WindowHandle {
window_id, any_handle: AnyWindowHandle {
window_id,
root_view_type: TypeId::of::<V>(),
},
root_view_type: PhantomData, root_view_type: PhantomData,
} }
} }
pub fn window_id(&self) -> usize {
self.window_id
}
pub fn root<C: BorrowWindowContext>(&self, cx: &C) -> C::Result<ViewHandle<V>> { pub fn root<C: BorrowWindowContext>(&self, cx: &C) -> C::Result<ViewHandle<V>> {
self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap()) self.read_with(cx, |cx| cx.root_view().clone().downcast().unwrap())
} }
pub fn read_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
where
C: BorrowWindowContext,
F: FnOnce(&WindowContext) -> R,
{
cx.read_window_with(self.window_id(), |cx| read(cx))
}
pub fn update<C, F, R>(&self, cx: &mut C, update: F) -> C::Result<R>
where
C: BorrowWindowContext,
F: FnOnce(&mut WindowContext) -> R,
{
cx.update_window(self.window_id(), update)
}
pub fn read_root_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R> pub fn read_root_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
where where
C: BorrowWindowContext, C: BorrowWindowContext,
@ -3918,7 +3876,7 @@ impl<V: View> WindowHandle<V> {
C: BorrowWindowContext, C: BorrowWindowContext,
F: FnOnce(&mut V, &mut ViewContext<V>) -> R, F: FnOnce(&mut V, &mut ViewContext<V>) -> R,
{ {
cx.update_window(self.window_id, |cx| { cx.update_window(*self, |cx| {
cx.root_view() cx.root_view()
.clone() .clone()
.downcast::<V>() .downcast::<V>()
@ -3927,6 +3885,53 @@ impl<V: View> WindowHandle<V> {
}) })
} }
pub fn replace_root<C, F>(&self, cx: &mut C, build_root: F) -> C::Result<ViewHandle<V>>
where
C: BorrowWindowContext,
F: FnOnce(&mut ViewContext<V>) -> V,
{
cx.update_window(self.into_any(), |cx| {
let root_view = self.add_view(cx, |cx| build_root(cx));
cx.window.root_view = Some(root_view.clone().into_any());
cx.window.focused_view_id = Some(root_view.id());
root_view
})
}
}
impl<V> Into<AnyWindowHandle> for WindowHandle<V> {
fn into(self) -> AnyWindowHandle {
self.any_handle
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct AnyWindowHandle {
window_id: usize,
root_view_type: TypeId,
}
impl AnyWindowHandle {
pub fn id(&self) -> usize {
self.window_id
}
pub fn read_with<C, F, R>(&self, cx: &C, read: F) -> C::Result<R>
where
C: BorrowWindowContext,
F: FnOnce(&WindowContext) -> R,
{
cx.read_window_with(self.window_id, |cx| read(cx))
}
pub fn update<C, F, R>(&self, cx: &mut C, update: F) -> C::Result<R>
where
C: BorrowWindowContext,
F: FnOnce(&mut WindowContext) -> R,
{
cx.update_window(self.window_id, update)
}
pub fn add_view<C, U, F>(&self, cx: &mut C, build_view: F) -> C::Result<ViewHandle<U>> pub fn add_view<C, U, F>(&self, cx: &mut C, build_view: F) -> C::Result<ViewHandle<U>>
where where
C: BorrowWindowContext, C: BorrowWindowContext,
@ -3936,21 +3941,16 @@ impl<V: View> WindowHandle<V> {
self.update(cx, |cx| cx.add_view(build_view)) self.update(cx, |cx| cx.add_view(build_view))
} }
pub(crate) fn replace_root_view<C, F>( pub fn root_is<V: View>(&self) -> bool {
&self, self.root_view_type == TypeId::of::<V>()
cx: &mut C, }
build_root_view: F,
) -> C::Result<ViewHandle<V>> pub fn is_active<C: BorrowWindowContext>(&self, cx: &C) -> C::Result<bool> {
where self.read_with(cx, |cx| cx.window.is_active)
C: BorrowWindowContext, }
F: FnOnce(&mut ViewContext<V>) -> V,
{ pub fn remove<C: BorrowWindowContext>(&self, cx: &mut C) -> C::Result<()> {
cx.update_window(self.window_id, |cx| { self.update(cx, |cx| cx.remove_window())
let root_view = self.add_view(cx, |cx| build_root_view(cx));
cx.window.root_view = Some(root_view.clone().into_any());
cx.window.focused_view_id = Some(root_view.id());
root_view
})
} }
} }
@ -5390,7 +5390,7 @@ mod tests {
TestView { events: Vec::new() } TestView { events: Vec::new() }
}); });
assert_eq!(window.read_root_with(cx, |view, _| assert_eq!(view.events, ["before notify"]))); window.read_root_with(cx, |view, _| assert_eq!(view.events, ["before notify"]));
} }
#[crate::test(self)] #[crate::test(self)]
@ -6227,7 +6227,7 @@ mod tests {
// Check that global actions do not have a binding, even if a binding does exist in another view // Check that global actions do not have a binding, even if a binding does exist in another view
assert_eq!( assert_eq!(
&available_actions(window.window_id(), view_1.id(), cx), &available_actions(window.id(), view_1.id(), cx),
&[ &[
("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
("test::GlobalAction", vec![]) ("test::GlobalAction", vec![])
@ -6236,7 +6236,7 @@ mod tests {
// Check that view 1 actions and bindings are available even when called from view 2 // Check that view 1 actions and bindings are available even when called from view 2
assert_eq!( assert_eq!(
&available_actions(window.window_id(), view_2.id(), cx), &available_actions(window.id(), view_2.id(), cx),
&[ &[
("test::Action1", vec![Keystroke::parse("a").unwrap()]), ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
("test::Action2", vec![Keystroke::parse("b").unwrap()]), ("test::Action2", vec![Keystroke::parse("b").unwrap()]),
@ -6299,7 +6299,7 @@ mod tests {
]); ]);
}); });
let actions = cx.available_actions(window.window_id(), view.id()); let actions = cx.available_actions(window.id(), view.id());
assert_eq!( assert_eq!(
actions[0].1.as_any().downcast_ref::<ActionWithArg>(), actions[0].1.as_any().downcast_ref::<ActionWithArg>(),
Some(&ActionWithArg { arg: false }) Some(&ActionWithArg { arg: false })
@ -6585,25 +6585,25 @@ mod tests {
[("window 2", false), ("window 3", true)] [("window 2", false), ("window 3", true)]
); );
cx.simulate_window_activation(Some(window_2.window_id())); cx.simulate_window_activation(Some(window_2.id()));
assert_eq!( assert_eq!(
mem::take(&mut *events.borrow_mut()), mem::take(&mut *events.borrow_mut()),
[("window 3", false), ("window 2", true)] [("window 3", false), ("window 2", true)]
); );
cx.simulate_window_activation(Some(window_1.window_id())); cx.simulate_window_activation(Some(window_1.id()));
assert_eq!( assert_eq!(
mem::take(&mut *events.borrow_mut()), mem::take(&mut *events.borrow_mut()),
[("window 2", false), ("window 1", true)] [("window 2", false), ("window 1", true)]
); );
cx.simulate_window_activation(Some(window_3.window_id())); cx.simulate_window_activation(Some(window_3.id()));
assert_eq!( assert_eq!(
mem::take(&mut *events.borrow_mut()), mem::take(&mut *events.borrow_mut()),
[("window 1", false), ("window 3", true)] [("window 1", false), ("window 3", true)]
); );
cx.simulate_window_activation(Some(window_3.window_id())); cx.simulate_window_activation(Some(window_3.id()));
assert_eq!(mem::take(&mut *events.borrow_mut()), []); assert_eq!(mem::take(&mut *events.borrow_mut()), []);
} }

View File

@ -4,9 +4,9 @@ use crate::{
keymap_matcher::{Binding, Keystroke}, keymap_matcher::{Binding, Keystroke},
platform, platform,
platform::{Event, InputHandler, KeyDownEvent, Platform}, platform::{Event, InputHandler, KeyDownEvent, Platform},
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle, Action, AnyWindowHandle, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache,
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle, Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
WindowContext, WindowHandle, WeakHandle, WindowContext, WindowHandle,
}; };
use collections::BTreeMap; use collections::BTreeMap;
use futures::Future; use futures::Future;
@ -124,6 +124,13 @@ impl TestAppContext {
} }
} }
pub fn window<V: View>(&self, window_id: usize) -> Option<WindowHandle<V>> {
self.cx
.borrow()
.read_window(window_id, |cx| cx.window())
.flatten()
}
pub fn read_window<T, F: FnOnce(&WindowContext) -> T>( pub fn read_window<T, F: FnOnce(&WindowContext) -> T>(
&self, &self,
window_id: usize, window_id: usize,
@ -157,9 +164,9 @@ impl TestAppContext {
.cx .cx
.borrow_mut() .borrow_mut()
.add_window(Default::default(), build_root_view); .add_window(Default::default(), build_root_view);
self.simulate_window_activation(Some(window.window_id())); self.simulate_window_activation(Some(window.id()));
WindowHandle::new(window.window_id()) WindowHandle::new(window.id())
} }
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T> pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
@ -191,10 +198,14 @@ impl TestAppContext {
self.cx.borrow_mut().subscribe_global(callback) self.cx.borrow_mut().subscribe_global(callback)
} }
pub fn window_ids(&self) -> Vec<usize> { pub fn windows(&self) -> impl Iterator<Item = AnyWindowHandle> {
self.cx.borrow().windows.keys().copied().collect() self.cx.borrow().windows()
} }
// pub fn window_ids(&self) -> Vec<usize> {
// self.cx.borrow().windows.keys().copied().collect()
// }
pub fn remove_all_windows(&mut self) { pub fn remove_all_windows(&mut self) {
self.update(|cx| cx.windows.clear()); self.update(|cx| cx.windows.clear());
} }
@ -311,15 +322,15 @@ impl TestAppContext {
pub fn simulate_window_activation(&self, to_activate: Option<usize>) { pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
self.cx.borrow_mut().update(|cx| { self.cx.borrow_mut().update(|cx| {
let other_window_ids = cx let other_windows = cx
.windows .windows
.keys() .keys()
.filter(|window_id| Some(**window_id) != to_activate) .filter(|window| Some(window.id()) != to_activate)
.copied() .copied()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for window_id in other_window_ids { for window in other_windows {
cx.window_changed_active_status(window_id, false) cx.window_changed_active_status(window.id(), false)
} }
if let Some(to_activate) = to_activate { if let Some(to_activate) = to_activate {

View File

@ -196,6 +196,16 @@ impl<'a> WindowContext<'a> {
self.window_id self.window_id
} }
pub fn window<V: View>(&self) -> Option<WindowHandle<V>> {
self.window.root_view.as_ref().and_then(|root_view| {
if root_view.is::<V>() {
Some(WindowHandle::new(self.window_id))
} else {
None
}
})
}
pub fn app_context(&mut self) -> &mut AppContext { pub fn app_context(&mut self) -> &mut AppContext {
&mut self.app_context &mut self.app_context
} }
@ -1157,17 +1167,6 @@ impl<'a> WindowContext<'a> {
self.window.platform_window.prompt(level, msg, answers) self.window.platform_window.prompt(level, msg, answers)
} }
pub(crate) fn replace_root_view<V, F>(&mut self, build_root_view: F) -> WindowHandle<V>
where
V: View,
F: FnOnce(&mut ViewContext<V>) -> V,
{
let root_view = self.add_view(|cx| build_root_view(cx));
self.window.focused_view_id = Some(root_view.id());
self.window.root_view = Some(root_view.into_any());
WindowHandle::new(self.window_id)
}
pub fn add_view<T, F>(&mut self, build_view: F) -> ViewHandle<T> pub fn add_view<T, F>(&mut self, build_view: F) -> ViewHandle<T>
where where
T: View, T: View,

View File

@ -1872,7 +1872,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
select_path(&panel, "root1", cx); select_path(&panel, "root1", cx);
@ -2225,7 +2225,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
select_path(&panel, "root1", cx); select_path(&panel, "root1", cx);
@ -2402,7 +2402,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
toggle_expand_dir(&panel, "src/test", cx); toggle_expand_dir(&panel, "src/test", cx);
@ -2493,7 +2493,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx)); let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
select_path(&panel, "src/", cx); select_path(&panel, "src/", cx);

View File

@ -328,7 +328,7 @@ mod tests {
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
// Create the project symbols view. // Create the project symbols view.
let symbols = cx.add_view(window_id, |cx| { let symbols = cx.add_view(window_id, |cx| {

View File

@ -851,11 +851,11 @@ mod tests {
}); });
let window = cx.add_window(|_| EmptyView); let window = cx.add_window(|_| EmptyView);
let editor = cx.add_view(window.window_id(), |cx| { let editor = cx.add_view(window.id(), |cx| {
Editor::for_buffer(buffer.clone(), None, cx) Editor::for_buffer(buffer.clone(), None, cx)
}); });
let search_bar = cx.add_view(window.window_id(), |cx| { let search_bar = cx.add_view(window.id(), |cx| {
let mut search_bar = BufferSearchBar::new(cx); let mut search_bar = BufferSearchBar::new(cx);
search_bar.set_active_pane_item(Some(&editor), cx); search_bar.set_active_pane_item(Some(&editor), cx);
search_bar.show(cx); search_bar.show(cx);
@ -1232,7 +1232,7 @@ mod tests {
); );
let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx));
let window = cx.add_window(|_| EmptyView); let window = cx.add_window(|_| EmptyView);
let window_id = window.window_id(); let window_id = window.id();
let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx)); let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx));
@ -1421,11 +1421,11 @@ mod tests {
let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx)); let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx));
let window = cx.add_window(|_| EmptyView); let window = cx.add_window(|_| EmptyView);
let editor = cx.add_view(window.window_id(), |cx| { let editor = cx.add_view(window.id(), |cx| {
Editor::for_buffer(buffer.clone(), None, cx) Editor::for_buffer(buffer.clone(), None, cx)
}); });
let search_bar = cx.add_view(window.window_id(), |cx| { let search_bar = cx.add_view(window.id(), |cx| {
let mut search_bar = BufferSearchBar::new(cx); let mut search_bar = BufferSearchBar::new(cx);
search_bar.set_active_pane_item(Some(&editor), cx); search_bar.set_active_pane_item(Some(&editor), cx);
search_bar.show(cx); search_bar.show(cx);

View File

@ -1568,7 +1568,7 @@ pub mod tests {
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let active_item = cx.read(|cx| { let active_item = cx.read(|cx| {
workspace workspace
@ -1874,7 +1874,7 @@ pub mod tests {
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
workspace.update(cx, |workspace, cx| { workspace.update(cx, |workspace, cx| {
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx) ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
}); });

View File

@ -37,7 +37,7 @@ use gpui::{
}, },
AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
WeakViewHandle, WindowContext, WeakViewHandle, WindowContext, WindowHandle,
}; };
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
use itertools::Itertools; use itertools::Itertools;
@ -749,7 +749,7 @@ impl Workspace {
fn new_local( fn new_local(
abs_paths: Vec<PathBuf>, abs_paths: Vec<PathBuf>,
app_state: Arc<AppState>, app_state: Arc<AppState>,
requesting_window_id: Option<usize>, requesting_window: Option<WindowHandle<Workspace>>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Task<( ) -> Task<(
WeakViewHandle<Workspace>, WeakViewHandle<Workspace>,
@ -793,55 +793,60 @@ impl Workspace {
DB.next_id().await.unwrap_or(0) DB.next_id().await.unwrap_or(0)
}; };
let window = requesting_window_id.and_then(|window_id| { let window = if let Some(window) = requesting_window {
cx.update(|cx| { window.replace_root(&mut cx, |cx| {
cx.replace_root_view(window_id, |cx| { Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) });
}) window
}) } else {
}); {
let window = window.unwrap_or_else(|| { let window_bounds_override = window_bounds_env_override(&cx);
let window_bounds_override = window_bounds_env_override(&cx); let (bounds, display) = if let Some(bounds) = window_bounds_override {
let (bounds, display) = if let Some(bounds) = window_bounds_override { (Some(bounds), None)
(Some(bounds), None) } else {
} else { serialized_workspace
serialized_workspace .as_ref()
.as_ref() .and_then(|serialized_workspace| {
.and_then(|serialized_workspace| { let display = serialized_workspace.display?;
let display = serialized_workspace.display?; let mut bounds = serialized_workspace.bounds?;
let mut bounds = serialized_workspace.bounds?;
// Stored bounds are relative to the containing display. // Stored bounds are relative to the containing display.
// So convert back to global coordinates if that screen still exists // So convert back to global coordinates if that screen still exists
if let WindowBounds::Fixed(mut window_bounds) = bounds { if let WindowBounds::Fixed(mut window_bounds) = bounds {
if let Some(screen) = cx.platform().screen_by_id(display) { if let Some(screen) = cx.platform().screen_by_id(display) {
let screen_bounds = screen.bounds(); let screen_bounds = screen.bounds();
window_bounds.set_origin_x( window_bounds.set_origin_x(
window_bounds.origin_x() + screen_bounds.origin_x(), window_bounds.origin_x() + screen_bounds.origin_x(),
); );
window_bounds.set_origin_y( window_bounds.set_origin_y(
window_bounds.origin_y() + screen_bounds.origin_y(), window_bounds.origin_y() + screen_bounds.origin_y(),
); );
bounds = WindowBounds::Fixed(window_bounds); bounds = WindowBounds::Fixed(window_bounds);
} else { } else {
// Screen no longer exists. Return none here. // Screen no longer exists. Return none here.
return None; return None;
}
} }
}
Some((bounds, display)) Some((bounds, display))
}) })
.unzip() .unzip()
}; };
// Use the serialized workspace to construct the new window // Use the serialized workspace to construct the new window
cx.add_window( cx.add_window(
(app_state.build_window_options)(bounds, display, cx.platform().as_ref()), (app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
|cx| { |cx| {
Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) Workspace::new(
}, workspace_id,
) project_handle.clone(),
}); app_state.clone(),
cx,
)
},
)
}
};
// We haven't yielded the main thread since obtaining the window handle, // We haven't yielded the main thread since obtaining the window handle,
// so the window exists. // so the window exists.
@ -1227,14 +1232,14 @@ impl Workspace {
pub fn close_global(_: &CloseWindow, cx: &mut AppContext) { pub fn close_global(_: &CloseWindow, cx: &mut AppContext) {
cx.spawn(|mut cx| async move { cx.spawn(|mut cx| async move {
let id = cx let window = cx
.window_ids() .windows()
.into_iter() .into_iter()
.find(|&id| cx.window_is_active(id)); .find(|window| window.is_active(&cx).unwrap_or(false));
if let Some(id) = id { if let Some(window) = window {
//This can only get called when the window's project connection has been lost //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 //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(); .detach();
@ -1265,9 +1270,9 @@ impl Workspace {
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
let workspace_count = cx let workspace_count = cx
.window_ids() .windows()
.into_iter() .into_iter()
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>()) .filter(|window| window.root_is::<Workspace>())
.count(); .count();
if let Some(active_call) = active_call { if let Some(active_call) = active_call {
@ -1385,7 +1390,7 @@ impl Workspace {
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Task<Result<()>> { ) -> Task<Result<()>> {
let window_id = cx.window_id(); let window = cx.window::<Self>();
let is_remote = self.project.read(cx).is_remote(); let is_remote = self.project.read(cx).is_remote();
let has_worktree = self.project.read(cx).worktrees(cx).next().is_some(); 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)); let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx));
@ -1397,15 +1402,15 @@ impl Workspace {
let app_state = self.app_state.clone(); let app_state = self.app_state.clone();
cx.spawn(|_, mut cx| async move { 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? { if !close_task.await? {
return Ok(()); return Ok(());
} }
Some(window_id) window
} else { } else {
None 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?; .await?;
Ok(()) Ok(())
}) })
@ -3851,7 +3856,7 @@ pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
pub fn open_paths( pub fn open_paths(
abs_paths: &[PathBuf], abs_paths: &[PathBuf],
app_state: &Arc<AppState>, app_state: &Arc<AppState>,
requesting_window_id: Option<usize>, requesting_window: Option<WindowHandle<Workspace>>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Task< ) -> Task<
Result<( Result<(
@ -3879,7 +3884,7 @@ pub fn open_paths(
} else { } else {
Ok(cx Ok(cx
.update(|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) .await)
} }
@ -4196,14 +4201,14 @@ mod tests {
); );
}); });
assert_eq!( assert_eq!(
cx.current_window_title(window.window_id()).as_deref(), cx.current_window_title(window.id()).as_deref(),
Some("one.txt — root1") Some("one.txt — root1")
); );
// Add a second item to a non-empty pane // Add a second item to a non-empty pane
workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx)); workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
assert_eq!( assert_eq!(
cx.current_window_title(window.window_id()).as_deref(), cx.current_window_title(window.id()).as_deref(),
Some("two.txt — root1") Some("two.txt — root1")
); );
project.read_with(cx, |project, cx| { project.read_with(cx, |project, cx| {
@ -4222,7 +4227,7 @@ mod tests {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
cx.current_window_title(window.window_id()).as_deref(), cx.current_window_title(window.id()).as_deref(),
Some("one.txt — root1") Some("one.txt — root1")
); );
project.read_with(cx, |project, cx| { project.read_with(cx, |project, cx| {
@ -4242,14 +4247,14 @@ mod tests {
.await .await
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
cx.current_window_title(window.window_id()).as_deref(), cx.current_window_title(window.id()).as_deref(),
Some("one.txt — root1, root2") Some("one.txt — root1, root2")
); );
// Remove a project folder // Remove a project folder
project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx)); project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
assert_eq!( assert_eq!(
cx.current_window_title(window.window_id()).as_deref(), cx.current_window_title(window.id()).as_deref(),
Some("one.txt — root2") Some("one.txt — root2")
); );
} }
@ -4285,9 +4290,9 @@ mod tests {
}); });
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx)); let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.simulate_prompt_answer(window.window_id(), 2 /* cancel */); cx.simulate_prompt_answer(window.id(), 2 /* cancel */);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
assert!(!cx.has_pending_prompt(window.window_id())); assert!(!cx.has_pending_prompt(window.id()));
assert!(!task.await.unwrap()); assert!(!task.await.unwrap());
} }
@ -4346,10 +4351,10 @@ mod tests {
assert_eq!(pane.items_len(), 4); assert_eq!(pane.items_len(), 4);
assert_eq!(pane.active_item().unwrap().id(), item1.id()); assert_eq!(pane.active_item().unwrap().id(), item1.id());
}); });
assert!(cx.has_pending_prompt(window.window_id())); assert!(cx.has_pending_prompt(window.id()));
// Confirm saving item 1. // Confirm saving item 1.
cx.simulate_prompt_answer(window.window_id(), 0); cx.simulate_prompt_answer(window.id(), 0);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
// Item 1 is saved. There's a prompt to save item 3. // Item 1 is saved. There's a prompt to save item 3.
@ -4360,10 +4365,10 @@ mod tests {
assert_eq!(pane.items_len(), 3); assert_eq!(pane.items_len(), 3);
assert_eq!(pane.active_item().unwrap().id(), item3.id()); assert_eq!(pane.active_item().unwrap().id(), item3.id());
}); });
assert!(cx.has_pending_prompt(window.window_id())); assert!(cx.has_pending_prompt(window.id()));
// Cancel saving item 3. // Cancel saving item 3.
cx.simulate_prompt_answer(window.window_id(), 1); cx.simulate_prompt_answer(window.id(), 1);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
// Item 3 is reloaded. There's a prompt to save item 4. // Item 3 is reloaded. There's a prompt to save item 4.
@ -4374,10 +4379,10 @@ mod tests {
assert_eq!(pane.items_len(), 2); assert_eq!(pane.items_len(), 2);
assert_eq!(pane.active_item().unwrap().id(), item4.id()); assert_eq!(pane.active_item().unwrap().id(), item4.id());
}); });
assert!(cx.has_pending_prompt(window.window_id())); assert!(cx.has_pending_prompt(window.id()));
// Confirm saving item 4. // Confirm saving item 4.
cx.simulate_prompt_answer(window.window_id(), 0); cx.simulate_prompt_answer(window.id(), 0);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
// There's a prompt for a path for item 4. // There's a prompt for a path for item 4.
@ -4480,7 +4485,7 @@ mod tests {
&[ProjectEntryId::from_proto(0)] &[ProjectEntryId::from_proto(0)]
); );
}); });
cx.simulate_prompt_answer(window.window_id(), 0); cx.simulate_prompt_answer(window.id(), 0);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
left_pane.read_with(cx, |pane, cx| { left_pane.read_with(cx, |pane, cx| {
@ -4489,7 +4494,7 @@ mod tests {
&[ProjectEntryId::from_proto(2)] &[ProjectEntryId::from_proto(2)]
); );
}); });
cx.simulate_prompt_answer(window.window_id(), 0); cx.simulate_prompt_answer(window.id(), 0);
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
close.await.unwrap(); close.await.unwrap();
@ -4549,7 +4554,7 @@ mod tests {
item.read_with(cx, |item, _| assert_eq!(item.save_count, 2)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
// Deactivating the window still saves the file. // Deactivating the window still saves the file.
cx.simulate_window_activation(Some(window.window_id())); cx.simulate_window_activation(Some(window.id()));
item.update(cx, |item, cx| { item.update(cx, |item, cx| {
cx.focus_self(); cx.focus_self();
item.is_dirty = true; item.is_dirty = true;
@ -4591,7 +4596,7 @@ mod tests {
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)) pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id))
.await .await
.unwrap(); .unwrap();
assert!(!cx.has_pending_prompt(window.window_id())); assert!(!cx.has_pending_prompt(window.id()));
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); 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. // Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
@ -4612,7 +4617,7 @@ mod tests {
let _close_items = let _close_items =
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id)); pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id));
deterministic.run_until_parked(); deterministic.run_until_parked();
assert!(cx.has_pending_prompt(window.window_id())); assert!(cx.has_pending_prompt(window.id()));
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5)); item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
} }

View File

@ -813,11 +813,12 @@ mod tests {
// Replace existing windows // Replace existing windows
let window_id = cx.window_ids()[0]; let window_id = cx.window_ids()[0];
let window = cx.read_window(window_id, |cx| cx.window()).flatten();
cx.update(|cx| { cx.update(|cx| {
open_paths( open_paths(
&[PathBuf::from("/root/c"), PathBuf::from("/root/d")], &[PathBuf::from("/root/c"), PathBuf::from("/root/d")],
&app_state, &app_state,
Some(window_id), window,
cx, cx,
) )
}) })
@ -982,9 +983,8 @@ mod tests {
.await; .await;
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let workspace = cx let window = cx.add_window(|cx| Workspace::test_new(project, cx));
.add_window(|cx| Workspace::test_new(project, cx)) let workspace = window.root(cx);
.root(cx);
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone(); let file1 = entries[0].clone();
@ -1298,7 +1298,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
// Open a file within an existing worktree. // Open a file within an existing worktree.
workspace workspace
@ -1341,7 +1341,7 @@ mod tests {
project.update(cx, |project, _| project.languages().add(rust_lang())); project.update(cx, |project, _| project.languages().add(rust_lang()));
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap()); let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
// Create a new untitled buffer // Create a new untitled buffer
@ -1436,7 +1436,7 @@ mod tests {
project.update(cx, |project, _| project.languages().add(rust_lang())); project.update(cx, |project, _| project.languages().add(rust_lang()));
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
// Create a new untitled buffer // Create a new untitled buffer
cx.dispatch_action(window_id, NewFile); cx.dispatch_action(window_id, NewFile);
@ -1489,7 +1489,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx)); let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx); let workspace = window.root(cx);
let window_id = window.window_id(); let window_id = window.id();
let entries = cx.read(|cx| workspace.file_project_paths(cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx));
let file1 = entries[0].clone(); let file1 = entries[0].clone();
@ -2087,7 +2087,7 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
let window = cx.add_window(|_| TestView); let window = cx.add_window(|_| TestView);
let window_id = window.window_id(); let window_id = window.id();
// Test loading the keymap base at all // Test loading the keymap base at all
assert_key_bindings_for( assert_key_bindings_for(
@ -2258,7 +2258,7 @@ mod tests {
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
let window = cx.add_window(|_| TestView); let window = cx.add_window(|_| TestView);
let window_id = window.window_id(); let window_id = window.id();
// Test loading the keymap base at all // Test loading the keymap base at all
assert_key_bindings_for( assert_key_bindings_for(