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",
]
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "copilot"
version = "0.1.0"
@ -2133,6 +2139,19 @@ dependencies = [
"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]]
name = "dhat"
version = "0.3.2"
@ -3096,6 +3115,7 @@ dependencies = [
"core-graphics",
"core-text",
"ctor",
"derive_more",
"dhat",
"env_logger 0.9.3",
"etagere",
@ -5106,7 +5126,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff"
dependencies = [
"rustc_version",
"rustc_version 0.3.3",
]
[[package]]
@ -6276,7 +6296,16 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
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]]
@ -6716,6 +6745,12 @@ dependencies = [
"semver-parser",
]
[[package]]
name = "semver"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
[[package]]
name = "semver-parser"
version = "0.10.2"

View File

@ -79,6 +79,7 @@ resolver = "2"
anyhow = { version = "1.0.57" }
async-trait = { version = "0.1" }
ctor = { version = "0.1" }
derive_more = { version = "0.99.17" }
env_logger = { version = "0.9" }
futures = { version = "0.3" }
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 mut editor_cx_a = EditorTestContext {
cx: cx_a,
window_id: window_a.window_id(),
window_id: window_a.id(),
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 mut editor_cx_b = EditorTestContext {
cx: cx_b,
window_id: window_b.window_id(),
window_id: window_b.id(),
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()),
);
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
.entry(*project_id)
.or_insert(Vec::new())
.push(window.window_id());
.push(window.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 window = cx.add_window(|cx| Workspace::test_new(project.clone(), 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 mut editor = Editor::single_line(None, 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 window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let window_id = window.id();
// Create some diagnostics
project.update(cx, |project, cx| {
@ -1252,7 +1252,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), 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| {
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 window = cx.add_window(|cx| Workspace::test_new(project, 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());
cx.add_view(window_id, |cx| {
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@ use std::{
};
use anyhow::{anyhow, Context, Result};
use derive_more::Deref;
use parking_lot::Mutex;
use postage::oneshot;
use smallvec::SmallVec;
@ -132,11 +133,13 @@ pub trait BorrowAppContext {
pub trait BorrowWindowContext {
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
H: Into<AnyWindowHandle>,
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
H: Into<AnyWindowHandle>,
F: FnOnce(&mut WindowContext) -> T;
}
@ -300,13 +303,13 @@ impl App {
result
}
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
callback: F,
) -> Option<T> {
fn update_window<H, T, F>(&mut self, handle: H, callback: F) -> Option<T>
where
H: Into<AnyWindowHandle>,
F: FnOnce(&mut WindowContext) -> T,
{
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();
result
}
@ -333,6 +336,10 @@ impl AsyncAppContext {
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>(
&self,
window_id: usize,
@ -343,10 +350,10 @@ impl AsyncAppContext {
pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
&mut self,
window_id: usize,
handle: AnyWindowHandle,
callback: F,
) -> 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> {
@ -359,14 +366,14 @@ impl AsyncAppContext {
pub fn dispatch_action(
&mut self,
window_id: usize,
window: impl Into<AnyWindowHandle>,
view_id: usize,
action: &dyn Action,
) -> Result<()> {
self.0
.borrow_mut()
.update_window(window_id, |window| {
window.dispatch_action(Some(view_id), action);
.update_window(window, |cx| {
cx.dispatch_action(Some(view_id), action);
})
.ok_or_else(|| anyhow!("window not found"))
}
@ -380,22 +387,6 @@ impl AsyncAppContext {
.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>
where
T: Entity,
@ -501,7 +492,7 @@ pub struct AppContext {
models: HashMap<usize, Box<dyn AnyModel>>,
views: HashMap<(usize, usize), Box<dyn AnyView>>,
views_metadata: HashMap<(usize, usize), ViewMetadata>,
windows: HashMap<usize, Window>,
windows: HashMap<AnyWindowHandle, Window>,
globals: HashMap<TypeId, Box<dyn Any>>,
element_states: HashMap<ElementStateId, Box<dyn Any>>,
background: Arc<executor::Background>,
@ -818,22 +809,6 @@ impl AppContext {
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>(
&mut self,
callback: F,
@ -1331,43 +1306,40 @@ impl AppContext {
F: FnOnce(&mut ViewContext<V>) -> V,
{
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 =
this.platform
.open_window(window_id, window_options, this.foreground.clone());
let window = this.build_window(window_id, platform_window, build_root_view);
this.windows.insert(window_id, window);
WindowHandle::new(window_id)
.open_window(window, window_options, this.foreground.clone());
let window = this.build_window(window, platform_window, build_root_view);
this.windows.insert(window.into(), window);
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
V: View,
F: FnOnce(&mut ViewContext<V>) -> V,
{
self.update(|this| {
let window_id = post_inc(&mut this.next_id);
let platform_window = this.platform.add_status_item(window_id);
let window = this.build_window(window_id, platform_window, build_root_view);
let root_view = window.root_view().clone().downcast::<V>().unwrap();
let handle = WindowHandle::<V>::new(post_inc(&mut this.next_id));
let platform_window = this.platform.add_status_item(handle.id());
let window = this.build_window(handle, platform_window, build_root_view);
this.windows.insert(window_id, window);
this.update_window(window_id, |cx| {
root_view.update(cx, |view, cx| view.focus_in(cx.handle().into_any(), cx))
});
(window_id, root_view)
this.windows.insert(handle.into(), window);
handle.update_root(this, |view, cx| view.focus_in(cx.handle().into_any(), cx));
handle
})
}
pub fn build_window<V, F>(
pub fn build_window<H, V, F>(
&mut self,
window_id: usize,
handle: H,
mut platform_window: Box<dyn platform::Window>,
build_root_view: F,
) -> Window
where
H: Into<AnyWindowHandle>,
V: View,
F: FnOnce(&mut ViewContext<V>) -> V,
{
@ -1375,7 +1347,7 @@ impl AppContext {
let mut app = self.upgrade();
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 cx.dispatch_keystroke(keystroke) {
return true;
@ -1391,35 +1363,35 @@ impl AppContext {
{
let mut app = self.upgrade();
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();
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();
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();
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();
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 {
app: self.upgrade().0,
window_id,
window_id: handle,
}));
let mut window = Window::new(window_id, platform_window, self, build_root_view);
let mut cx = WindowContext::mutable(self, &mut window, window_id);
let mut window = Window::new(handle, platform_window, self, build_root_view);
let mut cx = WindowContext::mutable(self, &mut window, handle);
cx.layout(false).expect("initial layout should not error");
let scene = cx.paint().expect("initial paint should not error");
window.platform_window.present_scene(scene);
window
}
pub(crate) fn replace_root_view<V, F>(
&mut self,
window_id: usize,
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 windows(&self) -> impl Iterator<Item = AnyWindowHandle> {
todo!();
None.into_iter()
}
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)
}
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
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> {}
#[derive(Deref, Copy, Clone)]
pub struct WindowHandle<T> {
window_id: usize,
#[deref]
any_handle: AnyWindowHandle,
root_view_type: PhantomData<T>,
}
#[allow(dead_code)]
impl<V: View> WindowHandle<V> {
fn new(window_id: usize) -> Self {
WindowHandle {
any_handle: AnyWindowHandle {
window_id,
root_view_type: TypeId::of::<V>(),
},
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>> {
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>
where
C: BorrowWindowContext,
@ -3918,7 +3876,7 @@ impl<V: View> WindowHandle<V> {
C: BorrowWindowContext,
F: FnOnce(&mut V, &mut ViewContext<V>) -> R,
{
cx.update_window(self.window_id, |cx| {
cx.update_window(*self, |cx| {
cx.root_view()
.clone()
.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>>
where
C: BorrowWindowContext,
@ -3936,21 +3941,16 @@ impl<V: View> WindowHandle<V> {
self.update(cx, |cx| cx.add_view(build_view))
}
pub(crate) fn replace_root_view<C, F>(
&self,
cx: &mut C,
build_root_view: F,
) -> C::Result<ViewHandle<V>>
where
C: BorrowWindowContext,
F: FnOnce(&mut ViewContext<V>) -> V,
{
cx.update_window(self.window_id, |cx| {
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
})
pub fn root_is<V: View>(&self) -> bool {
self.root_view_type == TypeId::of::<V>()
}
pub fn is_active<C: BorrowWindowContext>(&self, cx: &C) -> C::Result<bool> {
self.read_with(cx, |cx| cx.window.is_active)
}
pub fn remove<C: BorrowWindowContext>(&self, cx: &mut C) -> C::Result<()> {
self.update(cx, |cx| cx.remove_window())
}
}
@ -5390,7 +5390,7 @@ mod tests {
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)]
@ -6227,7 +6227,7 @@ mod tests {
// Check that global actions do not have a binding, even if a binding does exist in another view
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::GlobalAction", vec![])
@ -6236,7 +6236,7 @@ mod tests {
// Check that view 1 actions and bindings are available even when called from view 2
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::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!(
actions[0].1.as_any().downcast_ref::<ActionWithArg>(),
Some(&ActionWithArg { arg: false })
@ -6585,25 +6585,25 @@ mod tests {
[("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!(
mem::take(&mut *events.borrow_mut()),
[("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!(
mem::take(&mut *events.borrow_mut()),
[("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!(
mem::take(&mut *events.borrow_mut()),
[("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()), []);
}

View File

@ -4,9 +4,9 @@ use crate::{
keymap_matcher::{Binding, Keystroke},
platform,
platform::{Event, InputHandler, KeyDownEvent, Platform},
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle,
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle,
WindowContext, WindowHandle,
Action, AnyWindowHandle, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache,
Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
WeakHandle, WindowContext, WindowHandle,
};
use collections::BTreeMap;
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>(
&self,
window_id: usize,
@ -157,9 +164,9 @@ impl TestAppContext {
.cx
.borrow_mut()
.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>
@ -191,10 +198,14 @@ impl TestAppContext {
self.cx.borrow_mut().subscribe_global(callback)
}
pub fn window_ids(&self) -> Vec<usize> {
self.cx.borrow().windows.keys().copied().collect()
pub fn windows(&self) -> impl Iterator<Item = AnyWindowHandle> {
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) {
self.update(|cx| cx.windows.clear());
}
@ -311,15 +322,15 @@ impl TestAppContext {
pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
self.cx.borrow_mut().update(|cx| {
let other_window_ids = cx
let other_windows = cx
.windows
.keys()
.filter(|window_id| Some(**window_id) != to_activate)
.filter(|window| Some(window.id()) != to_activate)
.copied()
.collect::<Vec<_>>();
for window_id in other_window_ids {
cx.window_changed_active_status(window_id, false)
for window in other_windows {
cx.window_changed_active_status(window.id(), false)
}
if let Some(to_activate) = to_activate {

View File

@ -196,6 +196,16 @@ impl<'a> WindowContext<'a> {
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 {
&mut self.app_context
}
@ -1157,17 +1167,6 @@ impl<'a> WindowContext<'a> {
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>
where
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 window = cx.add_window(|cx| Workspace::test_new(project.clone(), 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));
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 window = cx.add_window(|cx| Workspace::test_new(project.clone(), 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));
select_path(&panel, "root1", cx);
@ -2402,7 +2402,7 @@ mod tests {
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), 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));
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 window = cx.add_window(|cx| Workspace::test_new(project.clone(), 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));
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 workspace = window.root(cx);
let window_id = window.window_id();
let window_id = window.id();
// Create the project symbols view.
let symbols = cx.add_view(window_id, |cx| {

View File

@ -851,11 +851,11 @@ mod tests {
});
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)
});
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);
search_bar.set_active_pane_item(Some(&editor), cx);
search_bar.show(cx);
@ -1232,7 +1232,7 @@ mod tests {
);
let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx));
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));
@ -1421,11 +1421,11 @@ mod tests {
let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx));
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)
});
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);
search_bar.set_active_pane_item(Some(&editor), 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 window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let window_id = window.id();
let active_item = cx.read(|cx| {
workspace
@ -1874,7 +1874,7 @@ pub mod tests {
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
let window_id = window.window_id();
let window_id = window.id();
workspace.update(cx, |workspace, cx| {
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
});

View File

@ -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;
@ -749,7 +749,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>,
@ -793,14 +793,13 @@ 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| {
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)
})
})
});
let window = window.unwrap_or_else(|| {
window
} else {
{
let window_bounds_override = window_bounds_env_override(&cx);
let (bounds, display) = if let Some(bounds) = window_bounds_override {
(Some(bounds), None)
@ -838,10 +837,16 @@ impl Workspace {
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)
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.
@ -1227,14 +1232,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();
@ -1265,9 +1270,9 @@ impl Workspace {
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 {
@ -1385,7 +1390,7 @@ impl Workspace {
paths: Vec<PathBuf>,
cx: &mut ViewContext<Self>,
) -> Task<Result<()>> {
let window_id = cx.window_id();
let window = cx.window::<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));
@ -1397,15 +1402,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(())
})
@ -3851,7 +3856,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<(
@ -3879,7 +3884,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)
}
@ -4196,14 +4201,14 @@ mod tests {
);
});
assert_eq!(
cx.current_window_title(window.window_id()).as_deref(),
cx.current_window_title(window.id()).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(),
cx.current_window_title(window.id()).as_deref(),
Some("two.txt — root1")
);
project.read_with(cx, |project, cx| {
@ -4222,7 +4227,7 @@ mod tests {
.await
.unwrap();
assert_eq!(
cx.current_window_title(window.window_id()).as_deref(),
cx.current_window_title(window.id()).as_deref(),
Some("one.txt — root1")
);
project.read_with(cx, |project, cx| {
@ -4242,14 +4247,14 @@ mod tests {
.await
.unwrap();
assert_eq!(
cx.current_window_title(window.window_id()).as_deref(),
cx.current_window_title(window.id()).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(),
cx.current_window_title(window.id()).as_deref(),
Some("one.txt — root2")
);
}
@ -4285,9 +4290,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 */);
cx.simulate_prompt_answer(window.id(), 2 /* cancel */);
cx.foreground().run_until_parked();
assert!(!cx.has_pending_prompt(window.window_id()));
assert!(!cx.has_pending_prompt(window.id()));
assert!(!task.await.unwrap());
}
@ -4346,10 +4351,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!(cx.has_pending_prompt(window.id()));
// Confirm saving item 1.
cx.simulate_prompt_answer(window.window_id(), 0);
cx.simulate_prompt_answer(window.id(), 0);
cx.foreground().run_until_parked();
// 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.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.
cx.simulate_prompt_answer(window.window_id(), 1);
cx.simulate_prompt_answer(window.id(), 1);
cx.foreground().run_until_parked();
// 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.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.
cx.simulate_prompt_answer(window.window_id(), 0);
cx.simulate_prompt_answer(window.id(), 0);
cx.foreground().run_until_parked();
// There's a prompt for a path for item 4.
@ -4480,7 +4485,7 @@ mod tests {
&[ProjectEntryId::from_proto(0)]
);
});
cx.simulate_prompt_answer(window.window_id(), 0);
cx.simulate_prompt_answer(window.id(), 0);
cx.foreground().run_until_parked();
left_pane.read_with(cx, |pane, cx| {
@ -4489,7 +4494,7 @@ mod tests {
&[ProjectEntryId::from_proto(2)]
);
});
cx.simulate_prompt_answer(window.window_id(), 0);
cx.simulate_prompt_answer(window.id(), 0);
cx.foreground().run_until_parked();
close.await.unwrap();
@ -4549,7 +4554,7 @@ 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()));
cx.simulate_window_activation(Some(window.id()));
item.update(cx, |item, cx| {
cx.focus_self();
item.is_dirty = true;
@ -4591,7 +4596,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!(!cx.has_pending_prompt(window.id()));
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.
@ -4612,7 +4617,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!(cx.has_pending_prompt(window.id()));
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
}

View File

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