mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 06:22:42 +03:00
Merge branch 'main' into collab-panel
This commit is contained in:
commit
99daa73325
43
Cargo.lock
generated
43
Cargo.lock
generated
@ -1652,6 +1652,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 = "convert_case"
|
||||
version = "0.6.0"
|
||||
@ -2145,6 +2151,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 0.4.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version 0.4.0",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dhat"
|
||||
version = "0.3.2"
|
||||
@ -2326,7 +2345,7 @@ dependencies = [
|
||||
"clock",
|
||||
"collections",
|
||||
"context_menu",
|
||||
"convert_case",
|
||||
"convert_case 0.6.0",
|
||||
"copilot",
|
||||
"ctor",
|
||||
"db",
|
||||
@ -3109,6 +3128,7 @@ dependencies = [
|
||||
"core-graphics",
|
||||
"core-text",
|
||||
"ctor",
|
||||
"derive_more",
|
||||
"dhat",
|
||||
"env_logger 0.9.3",
|
||||
"etagere",
|
||||
@ -5119,7 +5139,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]]
|
||||
@ -6289,7 +6309,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]]
|
||||
@ -6729,6 +6758,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"
|
||||
@ -9828,7 +9863,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.99.0"
|
||||
version = "0.100.0"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"ai",
|
||||
|
@ -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" }
|
||||
|
BIN
assets/fonts/plex/IBMPlexSans-Bold.ttf
Normal file
BIN
assets/fonts/plex/IBMPlexSans-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/plex/IBMPlexSans-Italic.ttf
Normal file
BIN
assets/fonts/plex/IBMPlexSans-Italic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/plex/IBMPlexSans-Regular.ttf
Normal file
BIN
assets/fonts/plex/IBMPlexSans-Regular.ttf
Normal file
Binary file not shown.
93
assets/fonts/plex/LICENSE.txt
Normal file
93
assets/fonts/plex/LICENSE.txt
Normal file
@ -0,0 +1,93 @@
|
||||
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
|
||||
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
@ -541,45 +541,50 @@ impl TestClient {
|
||||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
cx: &mut TestAppContext,
|
||||
) -> ViewHandle<Workspace> {
|
||||
struct WorkspaceContainer {
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
}
|
||||
// <<<<<<< HEAD
|
||||
// ) -> ViewHandle<Workspace> {
|
||||
// struct WorkspaceContainer {
|
||||
// workspace: Option<WeakViewHandle<Workspace>>,
|
||||
// }
|
||||
|
||||
impl Entity for WorkspaceContainer {
|
||||
type Event = ();
|
||||
}
|
||||
// impl Entity for WorkspaceContainer {
|
||||
// type Event = ();
|
||||
// }
|
||||
|
||||
impl View for WorkspaceContainer {
|
||||
fn ui_name() -> &'static str {
|
||||
"WorkspaceContainer"
|
||||
}
|
||||
// impl View for WorkspaceContainer {
|
||||
// fn ui_name() -> &'static str {
|
||||
// "WorkspaceContainer"
|
||||
// }
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
if let Some(workspace) = self
|
||||
.workspace
|
||||
.as_ref()
|
||||
.and_then(|workspace| workspace.upgrade(cx))
|
||||
{
|
||||
ChildView::new(&workspace, cx).into_any()
|
||||
} else {
|
||||
Empty::new().into_any()
|
||||
}
|
||||
}
|
||||
}
|
||||
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
// if let Some(workspace) = self
|
||||
// .workspace
|
||||
// .as_ref()
|
||||
// .and_then(|workspace| workspace.upgrade(cx))
|
||||
// {
|
||||
// ChildView::new(&workspace, cx).into_any()
|
||||
// } else {
|
||||
// Empty::new().into_any()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// We use a workspace container so that we don't need to remove the window in order to
|
||||
// drop the workspace and we can use a ViewHandle instead.
|
||||
let window = cx.add_window(|_| WorkspaceContainer { workspace: None });
|
||||
let container = window.root(cx);
|
||||
let workspace = window.add_view(cx, |cx| {
|
||||
Workspace::new(0, project.clone(), self.app_state.clone(), cx)
|
||||
});
|
||||
container.update(cx, |container, cx| {
|
||||
container.workspace = Some(workspace.downgrade());
|
||||
cx.notify();
|
||||
});
|
||||
workspace
|
||||
// // We use a workspace container so that we don't need to remove the window in order to
|
||||
// // drop the workspace and we can use a ViewHandle instead.
|
||||
// let window = cx.add_window(|_| WorkspaceContainer { workspace: None });
|
||||
// let container = window.root(cx);
|
||||
// let workspace = window.add_view(cx, |cx| {
|
||||
// Workspace::new(0, project.clone(), self.app_state.clone(), cx)
|
||||
// });
|
||||
// container.update(cx, |container, cx| {
|
||||
// container.workspace = Some(workspace.downgrade());
|
||||
// cx.notify();
|
||||
// });
|
||||
// workspace
|
||||
// =======
|
||||
) -> WindowHandle<Workspace> {
|
||||
cx.add_window(|cx| Workspace::test_new(project.clone(), cx))
|
||||
// >>>>>>> main
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1511,7 +1511,7 @@ async fn test_host_disconnect(
|
||||
.unwrap();
|
||||
assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx)));
|
||||
editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
|
||||
assert!(cx_b.is_window_edited(workspace_b.window_id()));
|
||||
assert!(window_b.is_edited(cx_b));
|
||||
|
||||
// Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
|
||||
server.forbid_connections();
|
||||
@ -1526,7 +1526,7 @@ async fn test_host_disconnect(
|
||||
window_b.read_with(cx_b, |cx| {
|
||||
assert_eq!(cx.focused_view_id(), None);
|
||||
});
|
||||
assert!(!cx_b.is_window_edited(workspace_b.window_id()));
|
||||
assert!(!window_b.is_edited(cx_b));
|
||||
|
||||
// Ensure client B is not prompted to save edits when closing window after disconnecting.
|
||||
let can_close = workspace_b
|
||||
@ -3442,7 +3442,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: window_a.into(),
|
||||
editor: editor_a,
|
||||
};
|
||||
|
||||
@ -3455,7 +3455,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: window_b.into(),
|
||||
editor: editor_b,
|
||||
};
|
||||
|
||||
@ -6440,8 +6440,10 @@ async fn test_basic_following(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let window_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = window_a.root(cx_a);
|
||||
let window_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = window_b.root(cx_b);
|
||||
|
||||
// Client A opens some editors.
|
||||
let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||
@ -6524,7 +6526,8 @@ async fn test_basic_following(
|
||||
cx_c.foreground().run_until_parked();
|
||||
let active_call_c = cx_c.read(ActiveCall::global);
|
||||
let project_c = client_c.build_remote_project(project_id, cx_c).await;
|
||||
let workspace_c = client_c.build_workspace(&project_c, cx_c);
|
||||
let window_c = client_c.build_workspace(&project_c, cx_c);
|
||||
let workspace_c = window_c.root(cx_c);
|
||||
active_call_c
|
||||
.update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
|
||||
.await
|
||||
@ -6542,7 +6545,7 @@ async fn test_basic_following(
|
||||
cx_d.foreground().run_until_parked();
|
||||
let active_call_d = cx_d.read(ActiveCall::global);
|
||||
let project_d = client_d.build_remote_project(project_id, cx_d).await;
|
||||
let workspace_d = client_d.build_workspace(&project_d, cx_d);
|
||||
let workspace_d = client_d.build_workspace(&project_d, cx_d).root(cx_d);
|
||||
active_call_d
|
||||
.update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
|
||||
.await
|
||||
@ -6640,6 +6643,7 @@ async fn test_basic_following(
|
||||
}
|
||||
|
||||
// Client C closes the project.
|
||||
window_c.remove(cx_c);
|
||||
cx_c.drop_last(workspace_c);
|
||||
|
||||
// Clients A and B see that client B is following A, and client C is not present in the followers.
|
||||
@ -6869,9 +6873,7 @@ async fn test_basic_following(
|
||||
});
|
||||
|
||||
// Client B activates a panel, and the previously-opened screen-sharing item gets activated.
|
||||
let panel = cx_b.add_view(workspace_b.window_id(), |_| {
|
||||
TestPanel::new(DockPosition::Left)
|
||||
});
|
||||
let panel = window_b.add_view(cx_b, |_| TestPanel::new(DockPosition::Left));
|
||||
workspace_b.update(cx_b, |workspace, cx| {
|
||||
workspace.add_panel(panel, cx);
|
||||
workspace.toggle_panel_focus::<TestPanel>(cx);
|
||||
@ -6899,7 +6901,7 @@ async fn test_basic_following(
|
||||
|
||||
// Client B activates an item that doesn't implement following,
|
||||
// so the previously-opened screen-sharing item gets activated.
|
||||
let unfollowable_item = cx_b.add_view(workspace_b.window_id(), |_| TestItem::new());
|
||||
let unfollowable_item = window_b.add_view(cx_b, |_| TestItem::new());
|
||||
workspace_b.update(cx_b, |workspace, cx| {
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
|
||||
@ -7061,10 +7063,10 @@ async fn test_following_tab_order(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
let client_b_id = project_a.read_with(cx_a, |project, _| {
|
||||
@ -7187,7 +7189,7 @@ async fn test_peers_following_each_other(
|
||||
.unwrap();
|
||||
|
||||
// Client A opens some editors.
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||
let _editor_a1 = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
@ -7199,7 +7201,7 @@ async fn test_peers_following_each_other(
|
||||
.unwrap();
|
||||
|
||||
// Client B opens an editor.
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||
let _editor_b1 = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
@ -7358,7 +7360,7 @@ async fn test_auto_unfollowing(
|
||||
.unwrap();
|
||||
|
||||
// Client A opens some editors.
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let _editor_a1 = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
||||
@ -7369,7 +7371,7 @@ async fn test_auto_unfollowing(
|
||||
.unwrap();
|
||||
|
||||
// Client B starts following client A.
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||
let leader_id = project_b.read_with(cx_b, |project, _| {
|
||||
project.collaborators().values().next().unwrap().peer_id
|
||||
@ -7497,14 +7499,14 @@ async fn test_peers_simultaneously_following_each_other(
|
||||
|
||||
client_a.fs().insert_tree("/a", json!({})).await;
|
||||
let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
|
||||
deterministic.run_until_parked();
|
||||
let client_a_id = project_b.read_with(cx_b, |project, _| {
|
||||
@ -7886,7 +7888,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
cx_a.foreground().start_waiting();
|
||||
|
||||
let _buffer_a = project_a
|
||||
@ -7954,7 +7956,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
"Host editor update the cache version after every cache/view change",
|
||||
);
|
||||
});
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
let editor_b = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
@ -8193,8 +8195,8 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
cx_a.foreground().start_waiting();
|
||||
cx_b.foreground().start_waiting();
|
||||
|
||||
|
@ -1891,18 +1891,18 @@ impl CollabPanel {
|
||||
);
|
||||
let mut answer =
|
||||
cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]);
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
if answer.next().await == Some(0) {
|
||||
if let Err(e) = channel_store
|
||||
.update(&mut cx, |channels, _| channels.remove_channel(channel_id))
|
||||
.await
|
||||
{
|
||||
cx.prompt(
|
||||
window_id,
|
||||
window.prompt(
|
||||
PromptLevel::Info,
|
||||
&format!("Failed to remove channel: {}", e),
|
||||
&["Ok"],
|
||||
&mut cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1921,18 +1921,18 @@ impl CollabPanel {
|
||||
github_login
|
||||
);
|
||||
let mut answer = cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]);
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
if answer.next().await == Some(0) {
|
||||
if let Err(e) = user_store
|
||||
.update(&mut cx, |store, cx| store.remove_contact(user_id, cx))
|
||||
.await
|
||||
{
|
||||
cx.prompt(
|
||||
window_id,
|
||||
window.prompt(
|
||||
PromptLevel::Info,
|
||||
&format!("Failed to remove contact: {}", e),
|
||||
&["Ok"],
|
||||
&mut cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ use gpui::{
|
||||
geometry::{rect::RectF, vector::vec2f, PathBuilder},
|
||||
json::{self, ToJson},
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AppContext, Entity, ImageData, LayoutContext, ModelHandle, SceneBuilder, Subscription, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
AppContext, Entity, ImageData, LayoutContext, ModelHandle, PaintContext, SceneBuilder,
|
||||
Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use picker::PickerEvent;
|
||||
use project::{Project, RepositoryEntry};
|
||||
@ -1190,7 +1190,7 @@ impl Element<CollabTitlebarItem> for AvatarRibbon {
|
||||
_: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut CollabTitlebarItem,
|
||||
_: &mut ViewContext<CollabTitlebarItem>,
|
||||
_: &mut PaintContext<CollabTitlebarItem>,
|
||||
) -> Self::PaintState {
|
||||
let mut path = PathBuilder::new();
|
||||
path.reset(bounds.lower_left());
|
||||
|
@ -7,7 +7,7 @@ use gpui::{
|
||||
},
|
||||
json::ToJson,
|
||||
serde_json::{self, json},
|
||||
AnyElement, Axis, Element, LayoutContext, SceneBuilder, View, ViewContext,
|
||||
AnyElement, Axis, Element, LayoutContext, PaintContext, SceneBuilder, View, ViewContext,
|
||||
};
|
||||
|
||||
pub(crate) struct FacePile<V: View> {
|
||||
@ -58,7 +58,7 @@ impl<V: View> Element<V> for FacePile<V> {
|
||||
visible_bounds: RectF,
|
||||
_layout: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
||||
|
@ -7,7 +7,7 @@ use gpui::{
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions},
|
||||
AnyElement, AppContext, Entity, View, ViewContext,
|
||||
AnyElement, AppContext, Entity, View, ViewContext, WindowHandle,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use workspace::AppState;
|
||||
@ -16,10 +16,10 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
||||
let app_state = Arc::downgrade(app_state);
|
||||
let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut notification_windows = Vec::new();
|
||||
let mut notification_windows: Vec<WindowHandle<IncomingCallNotification>> = Vec::new();
|
||||
while let Some(incoming_call) = incoming_call.next().await {
|
||||
for window_id in notification_windows.drain(..) {
|
||||
cx.remove_window(window_id);
|
||||
for window in notification_windows.drain(..) {
|
||||
window.remove(&mut cx);
|
||||
}
|
||||
|
||||
if let Some(incoming_call) = incoming_call {
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,20 +52,20 @@ 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);
|
||||
}
|
||||
}
|
||||
room::Event::RemoteProjectUnshared { project_id } => {
|
||||
if let Some(window_ids) = notification_windows.remove(&project_id) {
|
||||
for window_id in window_ids {
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
if let Some(windows) = notification_windows.remove(&project_id) {
|
||||
for window in windows {
|
||||
window.remove(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
room::Event::Left => {
|
||||
for (_, window_ids) in notification_windows.drain() {
|
||||
for window_id in window_ids {
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
for (_, windows) in notification_windows.drain() {
|
||||
for window in windows {
|
||||
window.remove(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,11 @@ pub fn init(cx: &mut AppContext) {
|
||||
{
|
||||
status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator));
|
||||
}
|
||||
} else if let Some((window_id, _)) = status_indicator.take() {
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
} else if let Some(window) = status_indicator.take() {
|
||||
window.update(cx, |cx| cx.remove_window());
|
||||
}
|
||||
} else if let Some((window_id, _)) = status_indicator.take() {
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
} else if let Some(window) = status_indicator.take() {
|
||||
window.update(cx, |cx| cx.remove_window());
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
@ -1,8 +1,8 @@
|
||||
use collections::CommandPaletteFilter;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, elements::*, keymap_matcher::Keystroke, Action, AppContext, Element, MouseState,
|
||||
ViewContext,
|
||||
actions, anyhow::anyhow, elements::*, keymap_matcher::Keystroke, Action, AnyWindowHandle,
|
||||
AppContext, Element, MouseState, ViewContext,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||
use std::cmp;
|
||||
@ -28,7 +28,7 @@ pub struct CommandPaletteDelegate {
|
||||
pub enum Event {
|
||||
Dismissed,
|
||||
Confirmed {
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
focused_view_id: usize,
|
||||
action: Box<dyn Action>,
|
||||
},
|
||||
@ -80,12 +80,13 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||
query: String,
|
||||
cx: &mut ViewContext<Picker<Self>>,
|
||||
) -> gpui::Task<()> {
|
||||
let window_id = cx.window_id();
|
||||
let view_id = self.focused_view_id;
|
||||
let window = cx.window();
|
||||
cx.spawn(move |picker, mut cx| async move {
|
||||
let actions = cx
|
||||
.available_actions(window_id, view_id)
|
||||
let actions = window
|
||||
.available_actions(view_id, &cx)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|(name, action, bindings)| {
|
||||
let filtered = cx.read(|cx| {
|
||||
if cx.has_global::<CommandPaletteFilter>() {
|
||||
@ -162,13 +163,15 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||
|
||||
fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
|
||||
if !self.matches.is_empty() {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let focused_view_id = self.focused_view_id;
|
||||
let action_ix = self.matches[self.selected_ix].candidate_id;
|
||||
let action = self.actions.remove(action_ix).action;
|
||||
cx.app_context()
|
||||
.spawn(move |mut cx| async move {
|
||||
cx.dispatch_action(window_id, focused_view_id, action.as_ref())
|
||||
window
|
||||
.dispatch_action(focused_view_id, action.as_ref(), &mut cx)
|
||||
.ok_or_else(|| anyhow!("window was closed"))
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
@ -297,8 +300,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 editor = cx.add_view(window_id, |cx| {
|
||||
let editor = window.add_view(cx, |cx| {
|
||||
let mut editor = Editor::single_line(None, cx);
|
||||
editor.set_text("abc", cx);
|
||||
editor
|
||||
|
@ -1,5 +1,5 @@
|
||||
use gpui::{
|
||||
anyhow,
|
||||
anyhow::{self, anyhow},
|
||||
elements::*,
|
||||
geometry::vector::Vector2F,
|
||||
keymap_matcher::KeymapContext,
|
||||
@ -218,12 +218,14 @@ impl ContextMenu {
|
||||
if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) {
|
||||
match action {
|
||||
ContextMenuItemAction::Action(action) => {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let view_id = self.parent_view_id;
|
||||
let action = action.boxed_clone();
|
||||
cx.app_context()
|
||||
.spawn(|mut cx| async move {
|
||||
cx.dispatch_action(window_id, view_id, action.as_ref())
|
||||
window
|
||||
.dispatch_action(view_id, action.as_ref(), &mut cx)
|
||||
.ok_or_else(|| anyhow!("window was closed"))
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
@ -480,17 +482,19 @@ impl ContextMenu {
|
||||
.on_down(MouseButton::Left, |_, _, _| {}) // Capture these events
|
||||
.on_click(MouseButton::Left, move |_, menu, cx| {
|
||||
menu.cancel(&Default::default(), cx);
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
match &action {
|
||||
ContextMenuItemAction::Action(action) => {
|
||||
let action = action.boxed_clone();
|
||||
cx.app_context()
|
||||
.spawn(|mut cx| async move {
|
||||
cx.dispatch_action(
|
||||
window_id,
|
||||
view_id,
|
||||
action.as_ref(),
|
||||
)
|
||||
window
|
||||
.dispatch_action(
|
||||
view_id,
|
||||
action.as_ref(),
|
||||
&mut cx,
|
||||
)
|
||||
.ok_or_else(|| anyhow!("window was closed"))
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
@ -857,7 +857,6 @@ 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();
|
||||
|
||||
// Create some diagnostics
|
||||
project.update(cx, |project, cx| {
|
||||
@ -944,7 +943,7 @@ mod tests {
|
||||
});
|
||||
|
||||
// Open the project diagnostics view while there are already diagnostics.
|
||||
let view = cx.add_view(window_id, |cx| {
|
||||
let view = window.add_view(cx, |cx| {
|
||||
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
||||
});
|
||||
|
||||
@ -1252,9 +1251,8 @@ 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 view = cx.add_view(window_id, |cx| {
|
||||
let view = window.add_view(cx, |cx| {
|
||||
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
||||
});
|
||||
|
||||
|
@ -6,7 +6,7 @@ use gpui::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
platform::{CursorStyle, MouseButton},
|
||||
scene::{MouseDown, MouseDrag},
|
||||
AnyElement, Element, View, ViewContext, WeakViewHandle, WindowContext,
|
||||
AnyElement, AnyWindowHandle, Element, View, ViewContext, WeakViewHandle, WindowContext,
|
||||
};
|
||||
|
||||
const DEAD_ZONE: f32 = 4.;
|
||||
@ -21,7 +21,7 @@ enum State<V: View> {
|
||||
region: RectF,
|
||||
},
|
||||
Dragging {
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
position: Vector2F,
|
||||
region_offset: Vector2F,
|
||||
region: RectF,
|
||||
@ -49,14 +49,14 @@ impl<V: View> Clone for State<V> {
|
||||
region,
|
||||
},
|
||||
State::Dragging {
|
||||
window_id,
|
||||
window,
|
||||
position,
|
||||
region_offset,
|
||||
region,
|
||||
payload,
|
||||
render,
|
||||
} => Self::Dragging {
|
||||
window_id: window_id.clone(),
|
||||
window: window.clone(),
|
||||
position: position.clone(),
|
||||
region_offset: region_offset.clone(),
|
||||
region: region.clone(),
|
||||
@ -87,16 +87,16 @@ impl<V: View> DragAndDrop<V> {
|
||||
self.containers.insert(handle);
|
||||
}
|
||||
|
||||
pub fn currently_dragged<T: Any>(&self, window_id: usize) -> Option<(Vector2F, Rc<T>)> {
|
||||
pub fn currently_dragged<T: Any>(&self, window: AnyWindowHandle) -> Option<(Vector2F, Rc<T>)> {
|
||||
self.currently_dragged.as_ref().and_then(|state| {
|
||||
if let State::Dragging {
|
||||
position,
|
||||
payload,
|
||||
window_id: window_dragged_from,
|
||||
window: window_dragged_from,
|
||||
..
|
||||
} = state
|
||||
{
|
||||
if &window_id != window_dragged_from {
|
||||
if &window != window_dragged_from {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -126,9 +126,9 @@ impl<V: View> DragAndDrop<V> {
|
||||
cx: &mut WindowContext,
|
||||
render: Rc<impl 'static + Fn(&T, &mut ViewContext<V>) -> AnyElement<V>>,
|
||||
) {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
cx.update_global(|this: &mut Self, cx| {
|
||||
this.notify_containers_for_window(window_id, cx);
|
||||
this.notify_containers_for_window(window, cx);
|
||||
|
||||
match this.currently_dragged.as_ref() {
|
||||
Some(&State::Down {
|
||||
@ -141,7 +141,7 @@ impl<V: View> DragAndDrop<V> {
|
||||
}) => {
|
||||
if (event.position - (region.origin() + region_offset)).length() > DEAD_ZONE {
|
||||
this.currently_dragged = Some(State::Dragging {
|
||||
window_id,
|
||||
window,
|
||||
region_offset,
|
||||
region,
|
||||
position: event.position,
|
||||
@ -163,7 +163,7 @@ impl<V: View> DragAndDrop<V> {
|
||||
..
|
||||
}) => {
|
||||
this.currently_dragged = Some(State::Dragging {
|
||||
window_id,
|
||||
window,
|
||||
region_offset,
|
||||
region,
|
||||
position: event.position,
|
||||
@ -188,14 +188,14 @@ impl<V: View> DragAndDrop<V> {
|
||||
State::Down { .. } => None,
|
||||
State::DeadZone { .. } => None,
|
||||
State::Dragging {
|
||||
window_id,
|
||||
window,
|
||||
region_offset,
|
||||
position,
|
||||
region,
|
||||
payload,
|
||||
render,
|
||||
} => {
|
||||
if cx.window_id() != window_id {
|
||||
if cx.window() != window {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -260,27 +260,27 @@ impl<V: View> DragAndDrop<V> {
|
||||
|
||||
pub fn cancel_dragging<P: Any>(&mut self, cx: &mut WindowContext) {
|
||||
if let Some(State::Dragging {
|
||||
payload, window_id, ..
|
||||
payload, window, ..
|
||||
}) = &self.currently_dragged
|
||||
{
|
||||
if payload.is::<P>() {
|
||||
let window_id = *window_id;
|
||||
let window = *window;
|
||||
self.currently_dragged = Some(State::Canceled);
|
||||
self.notify_containers_for_window(window_id, cx);
|
||||
self.notify_containers_for_window(window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_dragging(&mut self, cx: &mut WindowContext) {
|
||||
if let Some(State::Dragging { window_id, .. }) = self.currently_dragged.take() {
|
||||
self.notify_containers_for_window(window_id, cx);
|
||||
if let Some(State::Dragging { window, .. }) = self.currently_dragged.take() {
|
||||
self.notify_containers_for_window(window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn notify_containers_for_window(&mut self, window_id: usize, cx: &mut WindowContext) {
|
||||
fn notify_containers_for_window(&mut self, window: AnyWindowHandle, cx: &mut WindowContext) {
|
||||
self.containers.retain(|container| {
|
||||
if let Some(container) = container.upgrade(cx) {
|
||||
if container.window_id() == window_id {
|
||||
if container.window() == window {
|
||||
container.update(cx, |_, cx| cx.notify());
|
||||
}
|
||||
true
|
||||
|
@ -90,7 +90,7 @@ use std::{
|
||||
cmp::{self, Ordering, Reverse},
|
||||
mem,
|
||||
num::NonZeroU32,
|
||||
ops::{ControlFlow, Deref, DerefMut, Range},
|
||||
ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
@ -7549,6 +7549,78 @@ impl Editor {
|
||||
results
|
||||
}
|
||||
|
||||
pub fn background_highlight_row_ranges<T: 'static>(
|
||||
&self,
|
||||
search_range: Range<Anchor>,
|
||||
display_snapshot: &DisplaySnapshot,
|
||||
count: usize,
|
||||
) -> Vec<RangeInclusive<DisplayPoint>> {
|
||||
let mut results = Vec::new();
|
||||
let buffer = &display_snapshot.buffer_snapshot;
|
||||
let Some((_, ranges)) = self.background_highlights
|
||||
.get(&TypeId::of::<T>()) else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
let start_ix = match ranges.binary_search_by(|probe| {
|
||||
let cmp = probe.end.cmp(&search_range.start, buffer);
|
||||
if cmp.is_gt() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
}) {
|
||||
Ok(i) | Err(i) => i,
|
||||
};
|
||||
let mut push_region = |start: Option<Point>, end: Option<Point>| {
|
||||
if let (Some(start_display), Some(end_display)) = (start, end) {
|
||||
results.push(
|
||||
start_display.to_display_point(display_snapshot)
|
||||
..=end_display.to_display_point(display_snapshot),
|
||||
);
|
||||
}
|
||||
};
|
||||
let mut start_row: Option<Point> = None;
|
||||
let mut end_row: Option<Point> = None;
|
||||
if ranges.len() > count {
|
||||
return vec![];
|
||||
}
|
||||
for range in &ranges[start_ix..] {
|
||||
if range.start.cmp(&search_range.end, buffer).is_ge() {
|
||||
break;
|
||||
}
|
||||
let end = range.end.to_point(buffer);
|
||||
if let Some(current_row) = &end_row {
|
||||
if end.row == current_row.row {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let start = range.start.to_point(buffer);
|
||||
|
||||
if start_row.is_none() {
|
||||
assert_eq!(end_row, None);
|
||||
start_row = Some(start);
|
||||
end_row = Some(end);
|
||||
continue;
|
||||
}
|
||||
if let Some(current_end) = end_row.as_mut() {
|
||||
if start.row > current_end.row + 1 {
|
||||
push_region(start_row, end_row);
|
||||
start_row = Some(start);
|
||||
end_row = Some(end);
|
||||
} else {
|
||||
// Merge two hunks.
|
||||
*current_end = end;
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
// We might still have a hunk that was not rendered (if there was a search hit on the last line)
|
||||
push_region(start_row, end_row);
|
||||
results
|
||||
}
|
||||
|
||||
pub fn highlight_text<T: 'static>(
|
||||
&mut self,
|
||||
ranges: Vec<Range<Anchor>>,
|
||||
|
@ -525,9 +525,8 @@ 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 pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
cx.add_view(window_id, |cx| {
|
||||
window.add_view(cx, |cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
||||
let mut editor = build_editor(buffer.clone(), cx);
|
||||
let handle = cx.handle();
|
||||
@ -1290,7 +1289,8 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppCon
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
|
||||
cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
|
||||
let window = cx.window;
|
||||
window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
|
||||
|
||||
cx.set_state(
|
||||
&r#"ˇone
|
||||
@ -1401,7 +1401,8 @@ async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
|
||||
cx.simulate_window_resize(cx.window_id, vec2f(1000., 4. * line_height + 0.5));
|
||||
let window = cx.window;
|
||||
window.simulate_resize(vec2f(1000., 4. * line_height + 0.5), &mut cx);
|
||||
|
||||
cx.set_state(
|
||||
&r#"ˇone
|
||||
@ -1439,7 +1440,8 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx).await;
|
||||
|
||||
let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
|
||||
cx.simulate_window_resize(cx.window_id, vec2f(100., 4. * line_height));
|
||||
let window = cx.window;
|
||||
window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
|
||||
|
||||
cx.set_state(
|
||||
&r#"
|
||||
|
@ -32,7 +32,7 @@ use gpui::{
|
||||
platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
|
||||
text_layout::{self, Line, RunStyle, TextLayoutCache},
|
||||
AnyElement, Axis, Border, CursorRegion, Element, EventContext, FontCache, LayoutContext,
|
||||
MouseRegion, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext,
|
||||
MouseRegion, PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use json::json;
|
||||
@ -1107,8 +1107,6 @@ impl EditorElement {
|
||||
if layout.is_singleton && scrollbar_settings.selections {
|
||||
let start_anchor = Anchor::min();
|
||||
let end_anchor = Anchor::max();
|
||||
let mut start_row = None;
|
||||
let mut end_row = None;
|
||||
let color = scrollbar_theme.selections;
|
||||
let border = Border {
|
||||
width: 1.,
|
||||
@ -1119,54 +1117,32 @@ impl EditorElement {
|
||||
bottom: false,
|
||||
left: true,
|
||||
};
|
||||
let mut push_region = |start, end| {
|
||||
if let (Some(start_display), Some(end_display)) = (start, end) {
|
||||
let start_y = y_for_row(start_display as f32);
|
||||
let mut end_y = y_for_row(end_display as f32);
|
||||
if end_y - start_y < 1. {
|
||||
end_y = start_y + 1.;
|
||||
}
|
||||
let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds,
|
||||
background: Some(color),
|
||||
border,
|
||||
corner_radius: style.thumb.corner_radius,
|
||||
})
|
||||
let mut push_region = |start: DisplayPoint, end: DisplayPoint| {
|
||||
let start_y = y_for_row(start.row() as f32);
|
||||
let mut end_y = y_for_row(end.row() as f32);
|
||||
if end_y - start_y < 1. {
|
||||
end_y = start_y + 1.;
|
||||
}
|
||||
let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds,
|
||||
background: Some(color),
|
||||
border,
|
||||
corner_radius: style.thumb.corner_radius,
|
||||
})
|
||||
};
|
||||
for (row, _) in &editor
|
||||
.background_highlights_in_range_for::<crate::items::BufferSearchHighlights>(
|
||||
let background_ranges = editor
|
||||
.background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
|
||||
start_anchor..end_anchor,
|
||||
&layout.position_map.snapshot,
|
||||
&theme,
|
||||
)
|
||||
{
|
||||
let start_display = row.start;
|
||||
let end_display = row.end;
|
||||
|
||||
if start_row.is_none() {
|
||||
assert_eq!(end_row, None);
|
||||
start_row = Some(start_display.row());
|
||||
end_row = Some(end_display.row());
|
||||
continue;
|
||||
}
|
||||
if let Some(current_end) = end_row.as_mut() {
|
||||
if start_display.row() > *current_end + 1 {
|
||||
push_region(start_row, end_row);
|
||||
start_row = Some(start_display.row());
|
||||
end_row = Some(end_display.row());
|
||||
} else {
|
||||
// Merge two hunks.
|
||||
*current_end = end_display.row();
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
50000,
|
||||
);
|
||||
for row in background_ranges {
|
||||
let start = row.start();
|
||||
let end = row.end();
|
||||
push_region(*start, *end);
|
||||
}
|
||||
// We might still have a hunk that was not rendered (if there was a search hit on the last line)
|
||||
push_region(start_row, end_row);
|
||||
}
|
||||
|
||||
if layout.is_singleton && scrollbar_settings.git_diff {
|
||||
@ -2479,7 +2455,7 @@ impl Element<Editor> for EditorElement {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
cx: &mut PaintContext<Editor>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
scene.push_layer(Some(visible_bounds));
|
||||
@ -3079,7 +3055,14 @@ mod tests {
|
||||
let mut scene = SceneBuilder::new(1.0);
|
||||
let bounds = RectF::new(Default::default(), size);
|
||||
editor.update(cx, |editor, cx| {
|
||||
element.paint(&mut scene, bounds, bounds, &mut state, editor, cx);
|
||||
element.paint(
|
||||
&mut scene,
|
||||
bounds,
|
||||
bounds,
|
||||
&mut state,
|
||||
editor,
|
||||
&mut PaintContext::new(cx),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ impl<'a> EditorLspTestContext<'a> {
|
||||
Self {
|
||||
cx: EditorTestContext {
|
||||
cx,
|
||||
window_id: window.window_id(),
|
||||
window: window.into(),
|
||||
editor,
|
||||
},
|
||||
lsp,
|
||||
|
@ -3,7 +3,8 @@ use crate::{
|
||||
};
|
||||
use futures::Future;
|
||||
use gpui::{
|
||||
keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle,
|
||||
keymap_matcher::Keystroke, AnyWindowHandle, AppContext, ContextHandle, ModelContext,
|
||||
ViewContext, ViewHandle,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use language::{Buffer, BufferSnapshot};
|
||||
@ -21,7 +22,7 @@ use super::build_editor;
|
||||
|
||||
pub struct EditorTestContext<'a> {
|
||||
pub cx: &'a mut gpui::TestAppContext,
|
||||
pub window_id: usize,
|
||||
pub window: AnyWindowHandle,
|
||||
pub editor: ViewHandle<Editor>,
|
||||
}
|
||||
|
||||
@ -39,7 +40,7 @@ impl<'a> EditorTestContext<'a> {
|
||||
let editor = window.root(cx);
|
||||
Self {
|
||||
cx,
|
||||
window_id: window.window_id(),
|
||||
window: window.into(),
|
||||
editor,
|
||||
}
|
||||
}
|
||||
@ -111,7 +112,8 @@ impl<'a> EditorTestContext<'a> {
|
||||
let keystroke_under_test_handle =
|
||||
self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
|
||||
let keystroke = Keystroke::parse(keystroke_text).unwrap();
|
||||
self.cx.dispatch_keystroke(self.window_id, keystroke, false);
|
||||
|
||||
self.cx.dispatch_keystroke(self.window, keystroke, false);
|
||||
keystroke_under_test_handle
|
||||
}
|
||||
|
||||
|
@ -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.into(), 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.into(), SelectNext);
|
||||
cx.dispatch_action(window.into(), 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.into(), 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.into(), SelectNext);
|
||||
cx.dispatch_action(window.into(), 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.into(), 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.into(), SelectNext);
|
||||
cx.dispatch_action(window.into(), Confirm);
|
||||
active_pane
|
||||
.condition(cx, |pane, _| pane.active_item().is_some())
|
||||
.await;
|
||||
@ -1168,7 +1168,6 @@ 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 worktree_id = cx.read(|cx| {
|
||||
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
|
||||
assert_eq!(worktrees.len(), 1);
|
||||
@ -1186,7 +1185,7 @@ mod tests {
|
||||
"fir",
|
||||
1,
|
||||
"first.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1201,7 +1200,7 @@ mod tests {
|
||||
"sec",
|
||||
1,
|
||||
"second.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1223,7 +1222,7 @@ mod tests {
|
||||
"thi",
|
||||
1,
|
||||
"third.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1255,7 +1254,7 @@ mod tests {
|
||||
"sec",
|
||||
1,
|
||||
"second.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1294,7 +1293,7 @@ mod tests {
|
||||
"thi",
|
||||
1,
|
||||
"third.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1376,7 +1375,6 @@ 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 worktree_id = cx.read(|cx| {
|
||||
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
|
||||
assert_eq!(worktrees.len(), 1,);
|
||||
@ -1411,7 +1409,7 @@ mod tests {
|
||||
"sec",
|
||||
1,
|
||||
"second.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1433,7 +1431,7 @@ mod tests {
|
||||
"fir",
|
||||
1,
|
||||
"first.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1465,12 +1463,12 @@ mod tests {
|
||||
input: &str,
|
||||
expected_matches: usize,
|
||||
expected_editor_title: &str,
|
||||
window_id: usize,
|
||||
window: gpui::AnyWindowHandle,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
deterministic: &gpui::executor::Deterministic,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) -> Vec<FoundPath> {
|
||||
cx.dispatch_action(window_id, Toggle);
|
||||
cx.dispatch_action(window, Toggle);
|
||||
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
|
||||
finder
|
||||
.update(cx, |finder, cx| {
|
||||
@ -1487,8 +1485,8 @@ mod tests {
|
||||
});
|
||||
|
||||
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||
cx.dispatch_action(window_id, SelectNext);
|
||||
cx.dispatch_action(window_id, Confirm);
|
||||
cx.dispatch_action(window, SelectNext);
|
||||
cx.dispatch_action(window, Confirm);
|
||||
deterministic.run_until_parked();
|
||||
active_pane
|
||||
.condition(cx, |pane, _| pane.active_item().is_some())
|
||||
|
@ -135,7 +135,7 @@ impl Entity for GoToLine {
|
||||
|
||||
fn release(&mut self, cx: &mut AppContext) {
|
||||
let scroll_position = self.prev_scroll_position.take();
|
||||
cx.update_window(self.active_editor.window_id(), |cx| {
|
||||
self.active_editor.window().update(cx, |cx| {
|
||||
self.active_editor.update(cx, |editor, cx| {
|
||||
editor.highlight_rows(None);
|
||||
if let Some(scroll_position) = scroll_position {
|
||||
|
@ -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"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -77,9 +77,9 @@ pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform,
|
||||
let cx = app.0.clone();
|
||||
move |action| {
|
||||
let mut cx = cx.borrow_mut();
|
||||
if let Some(main_window_id) = cx.platform.main_window_id() {
|
||||
let dispatched = cx
|
||||
.update_window(main_window_id, |cx| {
|
||||
if let Some(main_window) = cx.active_window() {
|
||||
let dispatched = main_window
|
||||
.update(&mut *cx, |cx| {
|
||||
if let Some(view_id) = cx.focused_view_id() {
|
||||
cx.dispatch_action(Some(view_id), action);
|
||||
true
|
||||
|
@ -9,7 +9,7 @@ use collections::{hash_map::Entry, HashMap, HashSet};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use crate::util::post_inc;
|
||||
use crate::ElementStateId;
|
||||
use crate::{AnyWindowHandle, ElementStateId};
|
||||
|
||||
lazy_static! {
|
||||
static ref LEAK_BACKTRACE: bool =
|
||||
@ -26,7 +26,7 @@ pub struct RefCounts {
|
||||
entity_counts: HashMap<usize, usize>,
|
||||
element_state_counts: HashMap<ElementStateId, ElementStateRefCount>,
|
||||
dropped_models: HashSet<usize>,
|
||||
dropped_views: HashSet<(usize, usize)>,
|
||||
dropped_views: HashSet<(AnyWindowHandle, usize)>,
|
||||
dropped_element_states: HashSet<ElementStateId>,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@ -55,12 +55,12 @@ impl RefCounts {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inc_view(&mut self, window_id: usize, view_id: usize) {
|
||||
pub fn inc_view(&mut self, window: AnyWindowHandle, view_id: usize) {
|
||||
match self.entity_counts.entry(view_id) {
|
||||
Entry::Occupied(mut entry) => *entry.get_mut() += 1,
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(1);
|
||||
self.dropped_views.remove(&(window_id, view_id));
|
||||
self.dropped_views.remove(&(window, view_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,12 +94,12 @@ impl RefCounts {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dec_view(&mut self, window_id: usize, view_id: usize) {
|
||||
pub fn dec_view(&mut self, window: AnyWindowHandle, view_id: usize) {
|
||||
let count = self.entity_counts.get_mut(&view_id).unwrap();
|
||||
*count -= 1;
|
||||
if *count == 0 {
|
||||
self.entity_counts.remove(&view_id);
|
||||
self.dropped_views.insert((window_id, view_id));
|
||||
self.dropped_views.insert((window, view_id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ impl RefCounts {
|
||||
&mut self,
|
||||
) -> (
|
||||
HashSet<usize>,
|
||||
HashSet<(usize, usize)>,
|
||||
HashSet<(AnyWindowHandle, usize)>,
|
||||
HashSet<ElementStateId>,
|
||||
) {
|
||||
(
|
||||
|
@ -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;
|
||||
@ -72,8 +72,8 @@ impl TestAppContext {
|
||||
cx
|
||||
}
|
||||
|
||||
pub fn dispatch_action<A: Action>(&mut self, window_id: usize, action: A) {
|
||||
self.update_window(window_id, |window| {
|
||||
pub fn dispatch_action<A: Action>(&mut self, window: AnyWindowHandle, action: A) {
|
||||
self.update_window(window, |window| {
|
||||
window.dispatch_action(window.focused_view_id(), &action);
|
||||
})
|
||||
.expect("window not found");
|
||||
@ -81,10 +81,10 @@ impl TestAppContext {
|
||||
|
||||
pub fn available_actions(
|
||||
&self,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
view_id: usize,
|
||||
) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
|
||||
self.read_window(window_id, |cx| cx.available_actions(view_id))
|
||||
self.read_window(window, |cx| cx.available_actions(view_id))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@ -92,33 +92,34 @@ impl TestAppContext {
|
||||
self.update(|cx| cx.dispatch_global_action_any(&action));
|
||||
}
|
||||
|
||||
pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) {
|
||||
let handled = self
|
||||
.cx
|
||||
.borrow_mut()
|
||||
.update_window(window_id, |cx| {
|
||||
if cx.dispatch_keystroke(&keystroke) {
|
||||
return true;
|
||||
}
|
||||
pub fn dispatch_keystroke(
|
||||
&mut self,
|
||||
window: AnyWindowHandle,
|
||||
keystroke: Keystroke,
|
||||
is_held: bool,
|
||||
) {
|
||||
let handled = window.update(self, |cx| {
|
||||
if cx.dispatch_keystroke(&keystroke) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if cx.dispatch_event(
|
||||
Event::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held,
|
||||
}),
|
||||
false,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if cx.dispatch_event(
|
||||
Event::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held,
|
||||
}),
|
||||
false,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
})
|
||||
.unwrap_or(false);
|
||||
false
|
||||
});
|
||||
|
||||
if !handled && !keystroke.cmd && !keystroke.ctrl {
|
||||
WindowInputHandler {
|
||||
app: self.cx.clone(),
|
||||
window_id,
|
||||
window,
|
||||
}
|
||||
.replace_text_in_range(None, &keystroke.key)
|
||||
}
|
||||
@ -126,18 +127,18 @@ impl TestAppContext {
|
||||
|
||||
pub fn read_window<T, F: FnOnce(&WindowContext) -> T>(
|
||||
&self,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
callback: F,
|
||||
) -> Option<T> {
|
||||
self.cx.borrow().read_window(window_id, callback)
|
||||
self.cx.borrow().read_window(window, callback)
|
||||
}
|
||||
|
||||
pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
callback: F,
|
||||
) -> Option<T> {
|
||||
self.cx.borrow_mut().update_window(window_id, callback)
|
||||
self.cx.borrow_mut().update_window(window, callback)
|
||||
}
|
||||
|
||||
pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
|
||||
@ -148,27 +149,17 @@ impl TestAppContext {
|
||||
self.cx.borrow_mut().add_model(build_model)
|
||||
}
|
||||
|
||||
pub fn add_window<T, F>(&mut self, build_root_view: F) -> WindowHandle<T>
|
||||
pub fn add_window<V, F>(&mut self, build_root_view: F) -> WindowHandle<V>
|
||||
where
|
||||
T: View,
|
||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||
V: View,
|
||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||
{
|
||||
let window = self
|
||||
.cx
|
||||
.borrow_mut()
|
||||
.add_window(Default::default(), build_root_view);
|
||||
self.simulate_window_activation(Some(window.window_id()));
|
||||
|
||||
WindowHandle::new(window.window_id())
|
||||
}
|
||||
|
||||
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
||||
where
|
||||
T: View,
|
||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||
{
|
||||
self.update_window(window_id, |cx| cx.add_view(build_view))
|
||||
.expect("window not found")
|
||||
window.simulate_activation(self);
|
||||
window
|
||||
}
|
||||
|
||||
pub fn observe_global<E, F>(&mut self, callback: F) -> Subscription
|
||||
@ -191,8 +182,8 @@ 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) -> Vec<AnyWindowHandle> {
|
||||
self.cx.borrow().windows().collect()
|
||||
}
|
||||
|
||||
pub fn remove_all_windows(&mut self) {
|
||||
@ -262,76 +253,6 @@ impl TestAppContext {
|
||||
self.foreground_platform.as_ref().did_prompt_for_new_path()
|
||||
}
|
||||
|
||||
pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) {
|
||||
use postage::prelude::Sink as _;
|
||||
|
||||
let mut done_tx = self
|
||||
.platform_window_mut(window_id)
|
||||
.pending_prompts
|
||||
.borrow_mut()
|
||||
.pop_front()
|
||||
.expect("prompt was not called");
|
||||
done_tx.try_send(answer).ok();
|
||||
}
|
||||
|
||||
pub fn has_pending_prompt(&self, window_id: usize) -> bool {
|
||||
let window = self.platform_window_mut(window_id);
|
||||
let prompts = window.pending_prompts.borrow_mut();
|
||||
!prompts.is_empty()
|
||||
}
|
||||
|
||||
pub fn current_window_title(&self, window_id: usize) -> Option<String> {
|
||||
self.platform_window_mut(window_id).title.clone()
|
||||
}
|
||||
|
||||
pub fn simulate_window_close(&self, window_id: usize) -> bool {
|
||||
let handler = self
|
||||
.platform_window_mut(window_id)
|
||||
.should_close_handler
|
||||
.take();
|
||||
if let Some(mut handler) = handler {
|
||||
let should_close = handler();
|
||||
self.platform_window_mut(window_id).should_close_handler = Some(handler);
|
||||
should_close
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulate_window_resize(&self, window_id: usize, size: Vector2F) {
|
||||
let mut window = self.platform_window_mut(window_id);
|
||||
window.size = size;
|
||||
let mut handlers = mem::take(&mut window.resize_handlers);
|
||||
drop(window);
|
||||
for handler in &mut handlers {
|
||||
handler();
|
||||
}
|
||||
self.platform_window_mut(window_id).resize_handlers = handlers;
|
||||
}
|
||||
|
||||
pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
|
||||
self.cx.borrow_mut().update(|cx| {
|
||||
let other_window_ids = cx
|
||||
.windows
|
||||
.keys()
|
||||
.filter(|window_id| Some(**window_id) != to_activate)
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for window_id in other_window_ids {
|
||||
cx.window_changed_active_status(window_id, false)
|
||||
}
|
||||
|
||||
if let Some(to_activate) = to_activate {
|
||||
cx.window_changed_active_status(to_activate, true)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn is_window_edited(&self, window_id: usize) -> bool {
|
||||
self.platform_window_mut(window_id).edited
|
||||
}
|
||||
|
||||
pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
|
||||
self.cx.borrow().leak_detector()
|
||||
}
|
||||
@ -352,18 +273,6 @@ impl TestAppContext {
|
||||
self.assert_dropped(weak);
|
||||
}
|
||||
|
||||
fn platform_window_mut(&self, window_id: usize) -> std::cell::RefMut<platform::test::Window> {
|
||||
std::cell::RefMut::map(self.cx.borrow_mut(), |state| {
|
||||
let window = state.windows.get_mut(&window_id).unwrap();
|
||||
let test_window = window
|
||||
.platform_window
|
||||
.as_any_mut()
|
||||
.downcast_mut::<platform::test::Window>()
|
||||
.unwrap();
|
||||
test_window
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_condition_duration(&mut self, duration: Option<Duration>) {
|
||||
self.condition_duration = duration;
|
||||
}
|
||||
@ -408,23 +317,37 @@ impl BorrowAppContext for TestAppContext {
|
||||
impl BorrowWindowContext for TestAppContext {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
|
||||
self.cx
|
||||
.borrow()
|
||||
.read_window(window_id, f)
|
||||
.read_window(window, f)
|
||||
.expect("window was closed")
|
||||
}
|
||||
|
||||
fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::read_window(self, window, f)
|
||||
}
|
||||
|
||||
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
f: F,
|
||||
) -> T {
|
||||
self.cx
|
||||
.borrow_mut()
|
||||
.update_window(window_id, f)
|
||||
.update_window(window, f)
|
||||
.expect("window was closed")
|
||||
}
|
||||
|
||||
fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::update_window(self, window, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Entity> ModelHandle<T> {
|
||||
@ -539,6 +462,71 @@ impl<T: Entity> ModelHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyWindowHandle {
|
||||
pub fn has_pending_prompt(&self, cx: &mut TestAppContext) -> bool {
|
||||
let window = self.platform_window_mut(cx);
|
||||
let prompts = window.pending_prompts.borrow_mut();
|
||||
!prompts.is_empty()
|
||||
}
|
||||
|
||||
pub fn current_title(&self, cx: &mut TestAppContext) -> Option<String> {
|
||||
self.platform_window_mut(cx).title.clone()
|
||||
}
|
||||
|
||||
pub fn simulate_close(&self, cx: &mut TestAppContext) -> bool {
|
||||
let handler = self.platform_window_mut(cx).should_close_handler.take();
|
||||
if let Some(mut handler) = handler {
|
||||
let should_close = handler();
|
||||
self.platform_window_mut(cx).should_close_handler = Some(handler);
|
||||
should_close
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulate_resize(&self, size: Vector2F, cx: &mut TestAppContext) {
|
||||
let mut window = self.platform_window_mut(cx);
|
||||
window.size = size;
|
||||
let mut handlers = mem::take(&mut window.resize_handlers);
|
||||
drop(window);
|
||||
for handler in &mut handlers {
|
||||
handler();
|
||||
}
|
||||
self.platform_window_mut(cx).resize_handlers = handlers;
|
||||
}
|
||||
|
||||
pub fn is_edited(&self, cx: &mut TestAppContext) -> bool {
|
||||
self.platform_window_mut(cx).edited
|
||||
}
|
||||
|
||||
pub fn simulate_prompt_answer(&self, answer: usize, cx: &mut TestAppContext) {
|
||||
use postage::prelude::Sink as _;
|
||||
|
||||
let mut done_tx = self
|
||||
.platform_window_mut(cx)
|
||||
.pending_prompts
|
||||
.borrow_mut()
|
||||
.pop_front()
|
||||
.expect("prompt was not called");
|
||||
done_tx.try_send(answer).ok();
|
||||
}
|
||||
|
||||
fn platform_window_mut<'a>(
|
||||
&self,
|
||||
cx: &'a mut TestAppContext,
|
||||
) -> std::cell::RefMut<'a, platform::test::Window> {
|
||||
std::cell::RefMut::map(cx.cx.borrow_mut(), |state| {
|
||||
let window = state.windows.get_mut(&self).unwrap();
|
||||
let test_window = window
|
||||
.platform_window
|
||||
.as_any_mut()
|
||||
.downcast_mut::<platform::test::Window>()
|
||||
.unwrap();
|
||||
test_window
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: View> ViewHandle<T> {
|
||||
pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
|
||||
use postage::prelude::{Sink as _, Stream as _};
|
||||
|
@ -13,9 +13,10 @@ use crate::{
|
||||
},
|
||||
text_layout::TextLayoutCache,
|
||||
util::post_inc,
|
||||
Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect,
|
||||
Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription,
|
||||
View, ViewContext, ViewHandle, WindowHandle, WindowInvalidation,
|
||||
Action, AnyView, AnyViewHandle, AnyWindowHandle, AppContext, BorrowAppContext,
|
||||
BorrowWindowContext, Effect, Element, Entity, Handle, LayoutContext, MouseRegion,
|
||||
MouseRegionId, PaintContext, SceneBuilder, Subscription, View, ViewContext, ViewHandle,
|
||||
WindowInvalidation,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use collections::{HashMap, HashSet};
|
||||
@ -51,8 +52,8 @@ pub struct Window {
|
||||
cursor_regions: Vec<CursorRegion>,
|
||||
mouse_regions: Vec<(MouseRegion, usize)>,
|
||||
last_mouse_moved_event: Option<Event>,
|
||||
pub(crate) hovered_region_ids: HashSet<MouseRegionId>,
|
||||
pub(crate) clicked_region_ids: HashSet<MouseRegionId>,
|
||||
pub(crate) hovered_region_ids: Vec<MouseRegionId>,
|
||||
pub(crate) clicked_region_ids: Vec<MouseRegionId>,
|
||||
pub(crate) clicked_region: Option<(MouseRegionId, MouseButton)>,
|
||||
mouse_position: Vector2F,
|
||||
text_layout_cache: TextLayoutCache,
|
||||
@ -60,7 +61,7 @@ pub struct Window {
|
||||
|
||||
impl Window {
|
||||
pub fn new<V, F>(
|
||||
window_id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
platform_window: Box<dyn platform::Window>,
|
||||
cx: &mut AppContext,
|
||||
build_view: F,
|
||||
@ -92,7 +93,7 @@ impl Window {
|
||||
appearance,
|
||||
};
|
||||
|
||||
let mut window_context = WindowContext::mutable(cx, &mut window, window_id);
|
||||
let mut window_context = WindowContext::mutable(cx, &mut window, handle);
|
||||
let root_view = window_context.add_view(|cx| build_view(cx));
|
||||
if let Some(invalidation) = window_context.window.invalidation.take() {
|
||||
window_context.invalidate(invalidation, appearance);
|
||||
@ -113,7 +114,7 @@ impl Window {
|
||||
pub struct WindowContext<'a> {
|
||||
pub(crate) app_context: Reference<'a, AppContext>,
|
||||
pub(crate) window: Reference<'a, Window>,
|
||||
pub(crate) window_id: usize,
|
||||
pub(crate) window_handle: AnyWindowHandle,
|
||||
pub(crate) removed: bool,
|
||||
}
|
||||
|
||||
@ -144,46 +145,64 @@ impl BorrowAppContext for WindowContext<'_> {
|
||||
impl BorrowWindowContext for WindowContext<'_> {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||
if self.window_id == window_id {
|
||||
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, handle: AnyWindowHandle, f: F) -> T {
|
||||
if self.window_handle == handle {
|
||||
f(self)
|
||||
} else {
|
||||
panic!("read_with called with id of window that does not belong to this context")
|
||||
}
|
||||
}
|
||||
|
||||
fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::read_window(self, window, f)
|
||||
}
|
||||
|
||||
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
f: F,
|
||||
) -> T {
|
||||
if self.window_id == window_id {
|
||||
if self.window_handle == handle {
|
||||
f(self)
|
||||
} else {
|
||||
panic!("update called with id of window that does not belong to this context")
|
||||
}
|
||||
}
|
||||
|
||||
fn update_window_optional<T, F>(&mut self, handle: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::update_window(self, handle, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WindowContext<'a> {
|
||||
pub fn mutable(
|
||||
app_context: &'a mut AppContext,
|
||||
window: &'a mut Window,
|
||||
window_id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
) -> Self {
|
||||
Self {
|
||||
app_context: Reference::Mutable(app_context),
|
||||
window: Reference::Mutable(window),
|
||||
window_id,
|
||||
window_handle: handle,
|
||||
removed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn immutable(app_context: &'a AppContext, window: &'a Window, window_id: usize) -> Self {
|
||||
pub fn immutable(
|
||||
app_context: &'a AppContext,
|
||||
window: &'a Window,
|
||||
handle: AnyWindowHandle,
|
||||
) -> Self {
|
||||
Self {
|
||||
app_context: Reference::Immutable(app_context),
|
||||
window: Reference::Immutable(window),
|
||||
window_id,
|
||||
window_handle: handle,
|
||||
removed: false,
|
||||
}
|
||||
}
|
||||
@ -192,8 +211,8 @@ impl<'a> WindowContext<'a> {
|
||||
self.removed = true;
|
||||
}
|
||||
|
||||
pub fn window_id(&self) -> usize {
|
||||
self.window_id
|
||||
pub fn window(&self) -> AnyWindowHandle {
|
||||
self.window_handle
|
||||
}
|
||||
|
||||
pub fn app_context(&mut self) -> &mut AppContext {
|
||||
@ -216,10 +235,10 @@ impl<'a> WindowContext<'a> {
|
||||
where
|
||||
F: FnOnce(&mut dyn AnyView, &mut Self) -> T,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let mut view = self.views.remove(&(window_id, view_id))?;
|
||||
let handle = self.window_handle;
|
||||
let mut view = self.views.remove(&(handle, view_id))?;
|
||||
let result = f(view.as_mut(), self);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
Some(result)
|
||||
}
|
||||
|
||||
@ -244,9 +263,9 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut WindowContext)) {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
self.app_context.defer(move |cx| {
|
||||
cx.update_window(window_id, |cx| callback(cx));
|
||||
cx.update_window(handle, |cx| callback(cx));
|
||||
})
|
||||
}
|
||||
|
||||
@ -286,10 +305,10 @@ impl<'a> WindowContext<'a> {
|
||||
H: Handle<E>,
|
||||
F: 'static + FnMut(H, &E::Event, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let window_handle = self.window_handle;
|
||||
self.app_context
|
||||
.subscribe_internal(handle, move |emitter, event, cx| {
|
||||
cx.update_window(window_id, |cx| callback(emitter, event, cx))
|
||||
cx.update_window(window_handle, |cx| callback(emitter, event, cx))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
@ -298,17 +317,17 @@ impl<'a> WindowContext<'a> {
|
||||
where
|
||||
F: 'static + FnMut(bool, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.pending_effects
|
||||
.push_back(Effect::WindowActivationObservation {
|
||||
window_id,
|
||||
window: handle,
|
||||
subscription_id,
|
||||
callback: Box::new(callback),
|
||||
});
|
||||
Subscription::WindowActivationObservation(
|
||||
self.window_activation_observations
|
||||
.subscribe(window_id, subscription_id),
|
||||
.subscribe(handle, subscription_id),
|
||||
)
|
||||
}
|
||||
|
||||
@ -316,17 +335,17 @@ impl<'a> WindowContext<'a> {
|
||||
where
|
||||
F: 'static + FnMut(bool, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let window = self.window_handle;
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.pending_effects
|
||||
.push_back(Effect::WindowFullscreenObservation {
|
||||
window_id,
|
||||
window,
|
||||
subscription_id,
|
||||
callback: Box::new(callback),
|
||||
});
|
||||
Subscription::WindowActivationObservation(
|
||||
self.window_activation_observations
|
||||
.subscribe(window_id, subscription_id),
|
||||
.subscribe(window, subscription_id),
|
||||
)
|
||||
}
|
||||
|
||||
@ -334,17 +353,17 @@ impl<'a> WindowContext<'a> {
|
||||
where
|
||||
F: 'static + FnMut(WindowBounds, Uuid, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let window = self.window_handle;
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.pending_effects
|
||||
.push_back(Effect::WindowBoundsObservation {
|
||||
window_id,
|
||||
window,
|
||||
subscription_id,
|
||||
callback: Box::new(callback),
|
||||
});
|
||||
Subscription::WindowBoundsObservation(
|
||||
self.window_bounds_observations
|
||||
.subscribe(window_id, subscription_id),
|
||||
.subscribe(window, subscription_id),
|
||||
)
|
||||
}
|
||||
|
||||
@ -353,13 +372,13 @@ impl<'a> WindowContext<'a> {
|
||||
F: 'static
|
||||
+ FnMut(&Keystroke, &MatchResult, Option<&Box<dyn Action>>, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let window = self.window_handle;
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.keystroke_observations
|
||||
.add_callback(window_id, subscription_id, Box::new(callback));
|
||||
.add_callback(window, subscription_id, Box::new(callback));
|
||||
Subscription::KeystrokeObservation(
|
||||
self.keystroke_observations
|
||||
.subscribe(window_id, subscription_id),
|
||||
.subscribe(window, subscription_id),
|
||||
)
|
||||
}
|
||||
|
||||
@ -367,11 +386,11 @@ impl<'a> WindowContext<'a> {
|
||||
&self,
|
||||
view_id: usize,
|
||||
) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
let mut contexts = Vec::new();
|
||||
let mut handler_depths_by_action_id = HashMap::<TypeId, usize>::default();
|
||||
for (depth, view_id) in self.ancestors(view_id).enumerate() {
|
||||
if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
|
||||
if let Some(view_metadata) = self.views_metadata.get(&(handle, view_id)) {
|
||||
contexts.push(view_metadata.keymap_context.clone());
|
||||
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
|
||||
handler_depths_by_action_id
|
||||
@ -416,13 +435,13 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.focused_view_id() {
|
||||
let dispatch_path = self
|
||||
.ancestors(focused_view_id)
|
||||
.filter_map(|view_id| {
|
||||
self.views_metadata
|
||||
.get(&(window_id, view_id))
|
||||
.get(&(handle, view_id))
|
||||
.map(|view| (view_id, view.keymap_context.clone()))
|
||||
})
|
||||
.collect();
|
||||
@ -447,15 +466,10 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
self.keystroke(
|
||||
window_id,
|
||||
keystroke.clone(),
|
||||
handled_by,
|
||||
match_result.clone(),
|
||||
);
|
||||
self.keystroke(handle, keystroke.clone(), handled_by, match_result.clone());
|
||||
keystroke_handled
|
||||
} else {
|
||||
self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
|
||||
self.keystroke(handle, keystroke.clone(), None, MatchResult::None);
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -463,7 +477,7 @@ impl<'a> WindowContext<'a> {
|
||||
pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
|
||||
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
||||
let mut notified_views: HashSet<usize> = Default::default();
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
|
||||
// 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events
|
||||
// get mapped into the mouse-specific MouseEvent type.
|
||||
@ -664,6 +678,7 @@ impl<'a> WindowContext<'a> {
|
||||
let mut highest_z_index = None;
|
||||
let mouse_position = self.window.mouse_position.clone();
|
||||
let window = &mut *self.window;
|
||||
let prev_hovered_regions = mem::take(&mut window.hovered_region_ids);
|
||||
for (region, z_index) in window.mouse_regions.iter().rev() {
|
||||
// Allow mouse regions to appear transparent to hovers
|
||||
if !region.hoverable {
|
||||
@ -682,7 +697,11 @@ impl<'a> WindowContext<'a> {
|
||||
// highest_z_index is set.
|
||||
if contains_mouse && z_index == highest_z_index.unwrap() {
|
||||
//Ensure that hover entrance events aren't sent twice
|
||||
if window.hovered_region_ids.insert(region.id()) {
|
||||
if let Err(ix) = window.hovered_region_ids.binary_search(®ion.id()) {
|
||||
window.hovered_region_ids.insert(ix, region.id());
|
||||
}
|
||||
// window.hovered_region_ids.insert(region.id());
|
||||
if !prev_hovered_regions.contains(®ion.id()) {
|
||||
valid_regions.push(region.clone());
|
||||
if region.notify_on_hover {
|
||||
notified_views.insert(region.id().view_id());
|
||||
@ -690,7 +709,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
} else {
|
||||
// Ensure that hover exit events aren't sent twice
|
||||
if window.hovered_region_ids.remove(®ion.id()) {
|
||||
if prev_hovered_regions.contains(®ion.id()) {
|
||||
valid_regions.push(region.clone());
|
||||
if region.notify_on_hover {
|
||||
notified_views.insert(region.id().view_id());
|
||||
@ -827,19 +846,19 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
for view_id in notified_views {
|
||||
self.notify_view(window_id, view_id);
|
||||
self.notify_view(handle, view_id);
|
||||
}
|
||||
|
||||
any_event_handled
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
|
||||
if let Some(mut view) = self.views.remove(&(handle, view_id)) {
|
||||
let handled = view.key_down(event, self, view_id);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
if handled {
|
||||
return true;
|
||||
}
|
||||
@ -853,12 +872,12 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_key_up(&mut self, event: &KeyUpEvent) -> bool {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
|
||||
if let Some(mut view) = self.views.remove(&(handle, view_id)) {
|
||||
let handled = view.key_up(event, self, view_id);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
if handled {
|
||||
return true;
|
||||
}
|
||||
@ -872,12 +891,12 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_modifiers_changed(&mut self, event: &ModifiersChangedEvent) -> bool {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
|
||||
if let Some(mut view) = self.views.remove(&(handle, view_id)) {
|
||||
let handled = view.modifiers_changed(event, self, view_id);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
if handled {
|
||||
return true;
|
||||
}
|
||||
@ -912,14 +931,14 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub fn render_view(&mut self, params: RenderParams) -> Result<Box<dyn AnyRootElement>> {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
let view_id = params.view_id;
|
||||
let mut view = self
|
||||
.views
|
||||
.remove(&(window_id, view_id))
|
||||
.remove(&(handle, view_id))
|
||||
.ok_or_else(|| anyhow!("view not found"))?;
|
||||
let element = view.render(self, view_id);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
Ok(element)
|
||||
}
|
||||
|
||||
@ -947,9 +966,9 @@ impl<'a> WindowContext<'a> {
|
||||
} else if old_parent_id == new_parent_id {
|
||||
current_view_id = *old_parent_id.unwrap();
|
||||
} else {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
for view_id_to_notify in view_ids_to_notify {
|
||||
self.notify_view(window_id, view_id_to_notify);
|
||||
self.notify_view(handle, view_id_to_notify);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1117,7 +1136,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub fn focus(&mut self, view_id: Option<usize>) {
|
||||
self.app_context.focus(self.window_id, view_id);
|
||||
self.app_context.focus(self.window_handle, view_id);
|
||||
}
|
||||
|
||||
pub fn window_bounds(&self) -> WindowBounds {
|
||||
@ -1157,17 +1176,6 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.platform_window.prompt(level, msg, answers)
|
||||
}
|
||||
|
||||
pub 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,
|
||||
@ -1181,26 +1189,26 @@ impl<'a> WindowContext<'a> {
|
||||
T: View,
|
||||
F: FnOnce(&mut ViewContext<T>) -> Option<T>,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
let view_id = post_inc(&mut self.next_id);
|
||||
let mut cx = ViewContext::mutable(self, view_id);
|
||||
let handle = if let Some(view) = build_view(&mut cx) {
|
||||
let mut keymap_context = KeymapContext::default();
|
||||
view.update_keymap_context(&mut keymap_context, cx.app_context());
|
||||
self.views_metadata.insert(
|
||||
(window_id, view_id),
|
||||
(handle, view_id),
|
||||
ViewMetadata {
|
||||
type_id: TypeId::of::<T>(),
|
||||
keymap_context,
|
||||
},
|
||||
);
|
||||
self.views.insert((window_id, view_id), Box::new(view));
|
||||
self.views.insert((handle, view_id), Box::new(view));
|
||||
self.window
|
||||
.invalidation
|
||||
.get_or_insert_with(Default::default)
|
||||
.updated
|
||||
.insert(view_id);
|
||||
Some(ViewHandle::new(window_id, view_id, &self.ref_counts))
|
||||
Some(ViewHandle::new(handle, view_id, &self.ref_counts))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -1377,7 +1385,7 @@ pub struct ChildView {
|
||||
|
||||
impl ChildView {
|
||||
pub fn new(view: &AnyViewHandle, cx: &AppContext) -> Self {
|
||||
let view_name = cx.view_ui_name(view.window_id(), view.id()).unwrap();
|
||||
let view_name = cx.view_ui_name(view.window, view.id()).unwrap();
|
||||
Self {
|
||||
view_id: view.id(),
|
||||
view_name,
|
||||
@ -1426,7 +1434,7 @@ impl<V: View> Element<V> for ChildView {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
|
||||
rendered_view
|
||||
|
@ -2,11 +2,11 @@ use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
|
||||
use crate::{platform::InputHandler, window::WindowContext, AnyView, AppContext};
|
||||
use crate::{platform::InputHandler, window::WindowContext, AnyView, AnyWindowHandle, AppContext};
|
||||
|
||||
pub struct WindowInputHandler {
|
||||
pub app: Rc<RefCell<AppContext>>,
|
||||
pub window_id: usize,
|
||||
pub window: AnyWindowHandle,
|
||||
}
|
||||
|
||||
impl WindowInputHandler {
|
||||
@ -21,13 +21,12 @@ impl WindowInputHandler {
|
||||
//
|
||||
// See https://github.com/zed-industries/community/issues/444
|
||||
let mut app = self.app.try_borrow_mut().ok()?;
|
||||
app.update_window(self.window_id, |cx| {
|
||||
self.window.update_optional(&mut *app, |cx| {
|
||||
let view_id = cx.window.focused_view_id?;
|
||||
let view = cx.views.get(&(self.window_id, view_id))?;
|
||||
let view = cx.views.get(&(self.window, view_id))?;
|
||||
let result = f(view.as_ref(), &cx);
|
||||
Some(result)
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn update_focused_view<T, F>(&mut self, f: F) -> Option<T>
|
||||
@ -35,11 +34,12 @@ impl WindowInputHandler {
|
||||
F: FnOnce(&mut dyn AnyView, &mut WindowContext, usize) -> T,
|
||||
{
|
||||
let mut app = self.app.try_borrow_mut().ok()?;
|
||||
app.update_window(self.window_id, |cx| {
|
||||
let view_id = cx.window.focused_view_id?;
|
||||
cx.update_any_view(view_id, |view, cx| f(view, cx, view_id))
|
||||
})
|
||||
.flatten()
|
||||
self.window
|
||||
.update(&mut *app, |cx| {
|
||||
let view_id = cx.window.focused_view_id?;
|
||||
cx.update_any_view(view_id, |view, cx| f(view, cx, view_id))
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,9 +83,8 @@ impl InputHandler for WindowInputHandler {
|
||||
}
|
||||
|
||||
fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
|
||||
self.app
|
||||
.borrow()
|
||||
.read_window(self.window_id, |cx| cx.rect_for_text_range(range_utf16))
|
||||
.flatten()
|
||||
self.window.read_optional_with(&*self.app.borrow(), |cx| {
|
||||
cx.rect_for_text_range(range_utf16)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ use crate::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json, Action, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle,
|
||||
WindowContext,
|
||||
json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
WeakViewHandle, WindowContext,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::HashMap;
|
||||
@ -65,7 +65,7 @@ pub trait Element<V: View>: 'static {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState;
|
||||
|
||||
fn rect_for_text_range(
|
||||
@ -310,7 +310,14 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||
mut layout,
|
||||
} => {
|
||||
let bounds = RectF::new(origin, size);
|
||||
let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
|
||||
let paint = element.paint(
|
||||
scene,
|
||||
bounds,
|
||||
visible_bounds,
|
||||
&mut layout,
|
||||
view,
|
||||
&mut PaintContext::new(cx),
|
||||
);
|
||||
ElementState::PostPaint {
|
||||
element,
|
||||
constraint,
|
||||
@ -328,7 +335,14 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||
..
|
||||
} => {
|
||||
let bounds = RectF::new(origin, bounds.size());
|
||||
let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
|
||||
let paint = element.paint(
|
||||
scene,
|
||||
bounds,
|
||||
visible_bounds,
|
||||
&mut layout,
|
||||
view,
|
||||
&mut PaintContext::new(cx),
|
||||
);
|
||||
ElementState::PostPaint {
|
||||
element,
|
||||
constraint,
|
||||
@ -525,7 +539,7 @@ impl<V: View> Element<V> for AnyElement<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
|
||||
@ -69,7 +70,7 @@ impl<V: View> Element<V> for Align<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let my_center = bounds.size() / 2.;
|
||||
let my_target = my_center + my_center * self.alignment;
|
||||
|
@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
||||
use super::Element;
|
||||
use crate::{
|
||||
json::{self, json},
|
||||
SceneBuilder, View, ViewContext,
|
||||
PaintContext, SceneBuilder, View, ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use pathfinder_geometry::{
|
||||
@ -56,7 +56,7 @@ where
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.0(scene, bounds, visible_bounds, view, cx)
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
pub struct Clipped<V: View> {
|
||||
@ -37,7 +38,7 @@ impl<V: View> Element<V> for Clipped<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.paint_layer(Some(bounds), |scene| {
|
||||
self.child
|
||||
|
@ -5,7 +5,8 @@ use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
pub struct ConstrainedBox<V: View> {
|
||||
@ -156,7 +157,7 @@ impl<V: View> Element<V> for ConstrainedBox<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.paint_layer(Some(visible_bounds), |scene| {
|
||||
self.child
|
||||
|
@ -10,7 +10,8 @@ use crate::{
|
||||
json::ToJson,
|
||||
platform::CursorStyle,
|
||||
scene::{self, Border, CursorRegion, Quad},
|
||||
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -214,7 +215,7 @@ impl<V: View> Element<V> for Container<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let quad_bounds = RectF::from_points(
|
||||
bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
LayoutContext, SceneBuilder, View, ViewContext,
|
||||
LayoutContext, PaintContext, SceneBuilder, View, ViewContext,
|
||||
};
|
||||
use crate::{Element, SizeConstraint};
|
||||
|
||||
@ -57,7 +57,7 @@ impl<V: View> Element<V> for Empty {
|
||||
_: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut ViewContext<V>,
|
||||
_: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,8 @@ use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@ -61,7 +62,7 @@ impl<V: View> Element<V> for Expanded<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
|
@ -2,8 +2,8 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
json::{self, ToJson, Value},
|
||||
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint,
|
||||
Vector2FExt, View, ViewContext,
|
||||
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder,
|
||||
SizeConstraint, Vector2FExt, View, ViewContext,
|
||||
};
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
@ -258,7 +258,7 @@ impl<V: View> Element<V> for Flex<V> {
|
||||
visible_bounds: RectF,
|
||||
remaining_space: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
||||
@ -449,7 +449,7 @@ impl<V: View> Element<V> for FlexItem<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx)
|
||||
|
@ -3,7 +3,8 @@ use std::ops::Range;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
pub struct Hook<V: View> {
|
||||
@ -52,7 +53,7 @@ impl<V: View> Element<V> for Hook<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
|
@ -5,8 +5,8 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
scene, Border, Element, ImageData, LayoutContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
scene, Border, Element, ImageData, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
View, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -97,7 +97,7 @@ impl<V: View> Element<V> for Image {
|
||||
_: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut ViewContext<V>,
|
||||
_: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
if let Some(data) = layout {
|
||||
scene.push_image(scene::Image {
|
||||
|
@ -66,7 +66,7 @@ impl<V: View> Element<V> for KeystrokeLabel {
|
||||
visible_bounds: RectF,
|
||||
element: &mut AnyElement<V>,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::{Line, RunStyle},
|
||||
Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -163,7 +163,7 @@ impl<V: View> Element<V> for Label {
|
||||
visible_bounds: RectF,
|
||||
line: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
line.paint(
|
||||
|
@ -4,8 +4,8 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::json,
|
||||
AnyElement, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, SizeConstraint,
|
||||
View, ViewContext,
|
||||
};
|
||||
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
|
||||
use sum_tree::{Bias, SumTree};
|
||||
@ -255,7 +255,7 @@ impl<V: View> Element<V> for List<V> {
|
||||
visible_bounds: RectF,
|
||||
scroll_top: &mut ListOffset,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
|
||||
scene.push_layer(Some(visible_bounds));
|
||||
@ -647,7 +647,7 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{elements::Empty, geometry::vector::vec2f, Entity};
|
||||
use crate::{elements::Empty, geometry::vector::vec2f, Entity, PaintContext};
|
||||
use rand::prelude::*;
|
||||
use std::env;
|
||||
|
||||
@ -988,7 +988,7 @@ mod tests {
|
||||
_: RectF,
|
||||
_: &mut (),
|
||||
_: &mut V,
|
||||
_: &mut ViewContext<V>,
|
||||
_: &mut PaintContext<V>,
|
||||
) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ use crate::{
|
||||
CursorRegion, HandlerSet, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
|
||||
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
||||
},
|
||||
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, SceneBuilder,
|
||||
SizeConstraint, View, ViewContext,
|
||||
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext,
|
||||
SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::{marker::PhantomData, ops::Range};
|
||||
@ -256,7 +256,7 @@ impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
if self.above {
|
||||
self.child
|
||||
|
@ -3,8 +3,8 @@ use std::ops::Range;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::ToJson,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, View, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@ -143,7 +143,7 @@ impl<V: View> Element<V> for Overlay<V> {
|
||||
_: RectF,
|
||||
size: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
let (anchor_position, mut bounds) = match self.position_mode {
|
||||
OverlayPositionMode::Window => {
|
||||
|
@ -7,8 +7,8 @@ use crate::{
|
||||
geometry::rect::RectF,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
scene::MouseDrag,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, View, ViewContext,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -125,7 +125,7 @@ impl<V: View> Element<V> for Resizable<V> {
|
||||
visible_bounds: pathfinder_geometry::rect::RectF,
|
||||
constraint: &mut SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.push_stacking_context(None, None);
|
||||
|
||||
|
@ -3,7 +3,8 @@ use std::ops::Range;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::{self, json, ToJson},
|
||||
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
/// Element which renders it's children in a stack on top of each other.
|
||||
@ -57,7 +58,7 @@ impl<V: View> Element<V> for Stack<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
for child in &mut self.children {
|
||||
scene.paint_layer(None, |scene| {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::constrain_size_preserving_aspect_ratio;
|
||||
use crate::json::ToJson;
|
||||
use crate::PaintContext;
|
||||
use crate::{
|
||||
color::Color,
|
||||
geometry::{
|
||||
@ -73,7 +74,7 @@ impl<V: View> Element<V> for Svg {
|
||||
_visible_bounds: RectF,
|
||||
svg: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut ViewContext<V>,
|
||||
_: &mut PaintContext<V>,
|
||||
) {
|
||||
if let Some(svg) = svg.clone() {
|
||||
scene.push_icon(scene::Icon {
|
||||
|
@ -7,8 +7,8 @@ use crate::{
|
||||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::{Line, RunStyle, ShapedBoundary},
|
||||
AppContext, Element, FontCache, LayoutContext, SceneBuilder, SizeConstraint, TextLayoutCache,
|
||||
View, ViewContext,
|
||||
AppContext, Element, FontCache, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
TextLayoutCache, View, ViewContext,
|
||||
};
|
||||
use log::warn;
|
||||
use serde_json::json;
|
||||
@ -171,7 +171,7 @@ impl<V: View> Element<V> for Text {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let mut origin = bounds.origin();
|
||||
let empty = Vec::new();
|
||||
|
@ -6,8 +6,8 @@ use crate::{
|
||||
fonts::TextStyle,
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
Action, Axis, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint, Task, View,
|
||||
ViewContext,
|
||||
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
Task, View, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -194,7 +194,7 @@ impl<V: View> Element<V> for Tooltip<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
},
|
||||
json::{self, json},
|
||||
platform::ScrollWheelEvent,
|
||||
AnyElement, LayoutContext, MouseRegion, SceneBuilder, View, ViewContext,
|
||||
AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, View, ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||
@ -278,7 +278,7 @@ impl<V: View> Element<V> for UniformList<V> {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
|
||||
|
||||
|
@ -71,6 +71,32 @@ pub struct TextStyle {
|
||||
pub underline: Underline,
|
||||
}
|
||||
|
||||
impl TextStyle {
|
||||
pub fn refine(self, refinement: TextStyleRefinement) -> TextStyle {
|
||||
TextStyle {
|
||||
color: refinement.color.unwrap_or(self.color),
|
||||
font_family_name: refinement
|
||||
.font_family_name
|
||||
.unwrap_or_else(|| self.font_family_name.clone()),
|
||||
font_family_id: refinement.font_family_id.unwrap_or(self.font_family_id),
|
||||
font_id: refinement.font_id.unwrap_or(self.font_id),
|
||||
font_size: refinement.font_size.unwrap_or(self.font_size),
|
||||
font_properties: refinement.font_properties.unwrap_or(self.font_properties),
|
||||
underline: refinement.underline.unwrap_or(self.underline),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextStyleRefinement {
|
||||
pub color: Option<Color>,
|
||||
pub font_family_name: Option<Arc<str>>,
|
||||
pub font_family_id: Option<FamilyId>,
|
||||
pub font_id: Option<FontId>,
|
||||
pub font_size: Option<f32>,
|
||||
pub font_properties: Option<Properties>,
|
||||
pub underline: Option<Underline>,
|
||||
}
|
||||
|
||||
#[derive(JsonSchema)]
|
||||
#[serde(remote = "Properties")]
|
||||
pub struct PropertiesDef {
|
||||
|
@ -19,7 +19,7 @@ use crate::{
|
||||
},
|
||||
keymap_matcher::KeymapMatcher,
|
||||
text_layout::{LineLayout, RunStyle},
|
||||
Action, ClipboardItem, Menu, Scene,
|
||||
Action, AnyWindowHandle, ClipboardItem, Menu, Scene,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use async_task::Runnable;
|
||||
@ -58,13 +58,13 @@ pub trait Platform: Send + Sync {
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
) -> Box<dyn Window>;
|
||||
fn main_window_id(&self) -> Option<usize>;
|
||||
fn main_window(&self) -> Option<AnyWindowHandle>;
|
||||
|
||||
fn add_status_item(&self, id: usize) -> Box<dyn Window>;
|
||||
fn add_status_item(&self, handle: AnyWindowHandle) -> Box<dyn Window>;
|
||||
|
||||
fn write_to_clipboard(&self, item: ClipboardItem);
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
||||
|
@ -21,7 +21,7 @@ pub use fonts::FontSystem;
|
||||
use platform::{MacForegroundPlatform, MacPlatform};
|
||||
pub use renderer::Surface;
|
||||
use std::{ops::Range, rc::Rc, sync::Arc};
|
||||
use window::Window;
|
||||
use window::MacWindow;
|
||||
|
||||
use crate::executor;
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
use super::{
|
||||
event::key_to_native, screen::Screen, status_item::StatusItem, BoolExt as _, Dispatcher,
|
||||
FontSystem, Window,
|
||||
FontSystem, MacWindow,
|
||||
};
|
||||
use crate::{
|
||||
executor,
|
||||
keymap_matcher::KeymapMatcher,
|
||||
platform::{self, AppVersion, CursorStyle, Event},
|
||||
Action, ClipboardItem, Menu, MenuItem,
|
||||
Action, AnyWindowHandle, ClipboardItem, Menu, MenuItem,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use block::ConcreteBlock;
|
||||
@ -590,18 +590,18 @@ impl platform::Platform for MacPlatform {
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
options: platform::WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
) -> Box<dyn platform::Window> {
|
||||
Box::new(Window::open(id, options, executor, self.fonts()))
|
||||
Box::new(MacWindow::open(handle, options, executor, self.fonts()))
|
||||
}
|
||||
|
||||
fn main_window_id(&self) -> Option<usize> {
|
||||
Window::main_window_id()
|
||||
fn main_window(&self) -> Option<AnyWindowHandle> {
|
||||
MacWindow::main_window()
|
||||
}
|
||||
|
||||
fn add_status_item(&self, _id: usize) -> Box<dyn platform::Window> {
|
||||
fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
|
||||
Box::new(StatusItem::add(self.fonts()))
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ use crate::{
|
||||
Event, InputHandler, KeyDownEvent, Modifiers, ModifiersChangedEvent, MouseButton,
|
||||
MouseButtonEvent, MouseMovedEvent, Scene, WindowBounds, WindowKind,
|
||||
},
|
||||
AnyWindowHandle,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
@ -282,7 +283,7 @@ struct InsertText {
|
||||
}
|
||||
|
||||
struct WindowState {
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
native_window: id,
|
||||
kind: WindowKind,
|
||||
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
|
||||
@ -422,11 +423,11 @@ impl WindowState {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Window(Rc<RefCell<WindowState>>);
|
||||
pub struct MacWindow(Rc<RefCell<WindowState>>);
|
||||
|
||||
impl Window {
|
||||
impl MacWindow {
|
||||
pub fn open(
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
options: platform::WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
fonts: Arc<dyn platform::FontSystem>,
|
||||
@ -504,7 +505,7 @@ impl Window {
|
||||
assert!(!native_view.is_null());
|
||||
|
||||
let window = Self(Rc::new(RefCell::new(WindowState {
|
||||
id,
|
||||
handle,
|
||||
native_window,
|
||||
kind: options.kind,
|
||||
event_callback: None,
|
||||
@ -621,13 +622,13 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_window_id() -> Option<usize> {
|
||||
pub fn main_window() -> Option<AnyWindowHandle> {
|
||||
unsafe {
|
||||
let app = NSApplication::sharedApplication(nil);
|
||||
let main_window: id = msg_send![app, mainWindow];
|
||||
if msg_send![main_window, isKindOfClass: WINDOW_CLASS] {
|
||||
let id = get_window_state(&*main_window).borrow().id;
|
||||
Some(id)
|
||||
let handle = get_window_state(&*main_window).borrow().handle;
|
||||
Some(handle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -635,7 +636,7 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
impl Drop for MacWindow {
|
||||
fn drop(&mut self) {
|
||||
let this = self.0.borrow();
|
||||
let window = this.native_window;
|
||||
@ -649,7 +650,7 @@ impl Drop for Window {
|
||||
}
|
||||
}
|
||||
|
||||
impl platform::Window for Window {
|
||||
impl platform::Window for MacWindow {
|
||||
fn bounds(&self) -> WindowBounds {
|
||||
self.0.as_ref().borrow().bounds()
|
||||
}
|
||||
@ -881,7 +882,7 @@ impl platform::Window for Window {
|
||||
|
||||
fn is_topmost_for_position(&self, position: Vector2F) -> bool {
|
||||
let self_borrow = self.0.borrow();
|
||||
let self_id = self_borrow.id;
|
||||
let self_handle = self_borrow.handle;
|
||||
|
||||
unsafe {
|
||||
let app = NSApplication::sharedApplication(nil);
|
||||
@ -898,8 +899,8 @@ impl platform::Window for Window {
|
||||
let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS];
|
||||
let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS];
|
||||
if is_panel == YES || is_window == YES {
|
||||
let topmost_window_id = get_window_state(&*top_most_window).borrow().id;
|
||||
topmost_window_id == self_id
|
||||
let topmost_window = get_window_state(&*top_most_window).borrow().handle;
|
||||
topmost_window == self_handle
|
||||
} else {
|
||||
// Someone else's window is on top
|
||||
false
|
||||
@ -1086,7 +1087,10 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||
button: MouseButton::Left,
|
||||
modifiers: Modifiers { ctrl: true, .. },
|
||||
..
|
||||
}) => return,
|
||||
}) => {
|
||||
window_state_borrow.synthetic_drag_counter += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
_ => None,
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
keymap_matcher::KeymapMatcher,
|
||||
Action, ClipboardItem, Menu,
|
||||
Action, AnyWindowHandle, ClipboardItem, Menu,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::VecDeque;
|
||||
@ -102,7 +102,7 @@ pub struct Platform {
|
||||
fonts: Arc<dyn super::FontSystem>,
|
||||
current_clipboard_item: Mutex<Option<ClipboardItem>>,
|
||||
cursor: Mutex<CursorStyle>,
|
||||
active_window_id: Arc<Mutex<Option<usize>>>,
|
||||
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
|
||||
}
|
||||
|
||||
impl Platform {
|
||||
@ -112,7 +112,7 @@ impl Platform {
|
||||
fonts: Arc::new(super::current::FontSystem::new()),
|
||||
current_clipboard_item: Default::default(),
|
||||
cursor: Mutex::new(CursorStyle::Arrow),
|
||||
active_window_id: Default::default(),
|
||||
active_window: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,30 +146,30 @@ impl super::Platform for Platform {
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
options: super::WindowOptions,
|
||||
_executor: Rc<super::executor::Foreground>,
|
||||
) -> Box<dyn super::Window> {
|
||||
*self.active_window_id.lock() = Some(id);
|
||||
*self.active_window.lock() = Some(handle);
|
||||
Box::new(Window::new(
|
||||
id,
|
||||
handle,
|
||||
match options.bounds {
|
||||
WindowBounds::Maximized | WindowBounds::Fullscreen => vec2f(1024., 768.),
|
||||
WindowBounds::Fixed(rect) => rect.size(),
|
||||
},
|
||||
self.active_window_id.clone(),
|
||||
self.active_window.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn main_window_id(&self) -> Option<usize> {
|
||||
self.active_window_id.lock().clone()
|
||||
fn main_window(&self) -> Option<AnyWindowHandle> {
|
||||
self.active_window.lock().clone()
|
||||
}
|
||||
|
||||
fn add_status_item(&self, id: usize) -> Box<dyn crate::platform::Window> {
|
||||
fn add_status_item(&self, handle: AnyWindowHandle) -> Box<dyn crate::platform::Window> {
|
||||
Box::new(Window::new(
|
||||
id,
|
||||
handle,
|
||||
vec2f(24., 24.),
|
||||
self.active_window_id.clone(),
|
||||
self.active_window.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
@ -256,7 +256,7 @@ impl super::Screen for Screen {
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
pub(crate) size: Vector2F,
|
||||
scale_factor: f32,
|
||||
current_scene: Option<crate::Scene>,
|
||||
@ -270,13 +270,17 @@ pub struct Window {
|
||||
pub(crate) title: Option<String>,
|
||||
pub(crate) edited: bool,
|
||||
pub(crate) pending_prompts: RefCell<VecDeque<oneshot::Sender<usize>>>,
|
||||
active_window_id: Arc<Mutex<Option<usize>>>,
|
||||
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(id: usize, size: Vector2F, active_window_id: Arc<Mutex<Option<usize>>>) -> Self {
|
||||
pub fn new(
|
||||
handle: AnyWindowHandle,
|
||||
size: Vector2F,
|
||||
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
handle,
|
||||
size,
|
||||
event_handlers: Default::default(),
|
||||
resize_handlers: Default::default(),
|
||||
@ -290,7 +294,7 @@ impl Window {
|
||||
title: None,
|
||||
edited: false,
|
||||
pending_prompts: Default::default(),
|
||||
active_window_id,
|
||||
active_window,
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,7 +346,7 @@ impl super::Window for Window {
|
||||
}
|
||||
|
||||
fn activate(&self) {
|
||||
*self.active_window_id.lock() = Some(self.id);
|
||||
*self.active_window.lock() = Some(self.handle);
|
||||
}
|
||||
|
||||
fn set_title(&mut self, title: &str) {
|
||||
|
@ -177,7 +177,7 @@ impl MouseRegion {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
|
||||
pub struct MouseRegionId {
|
||||
view_id: usize,
|
||||
tag: TypeId,
|
||||
|
@ -45,7 +45,7 @@ use syntax_map::SyntaxSnapshot;
|
||||
use theme::{SyntaxTheme, Theme};
|
||||
use tree_sitter::{self, Query};
|
||||
use unicase::UniCase;
|
||||
use util::http::HttpClient;
|
||||
use util::{http::HttpClient, paths::PathExt};
|
||||
use util::{merge_json_value_into, post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@ -182,8 +182,8 @@ impl CachedLspAdapter {
|
||||
self.adapter.workspace_configuration(cx)
|
||||
}
|
||||
|
||||
pub async fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
|
||||
self.adapter.process_diagnostics(params).await
|
||||
pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
|
||||
self.adapter.process_diagnostics(params)
|
||||
}
|
||||
|
||||
pub async fn process_completion(&self, completion_item: &mut lsp::CompletionItem) {
|
||||
@ -262,7 +262,7 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary>;
|
||||
|
||||
async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
|
||||
async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
|
||||
|
||||
@ -777,7 +777,7 @@ impl LanguageRegistry {
|
||||
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
|
||||
let path = path.as_ref();
|
||||
let filename = path.file_name().and_then(|name| name.to_str());
|
||||
let extension = path.extension().and_then(|name| name.to_str());
|
||||
let extension = path.extension_or_hidden_file_name();
|
||||
let path_suffixes = [extension, filename];
|
||||
self.get_or_load_language(|config| {
|
||||
let path_matches = config
|
||||
@ -1487,12 +1487,6 @@ impl Language {
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams) {
|
||||
for adapter in &self.adapters {
|
||||
adapter.process_diagnostics(diagnostics).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn process_completion(self: &Arc<Self>, completion: &mut lsp::CompletionItem) {
|
||||
for adapter in &self.adapters {
|
||||
adapter.process_completion(completion).await;
|
||||
@ -1756,7 +1750,7 @@ impl LspAdapter for Arc<FakeLspAdapter> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
|
||||
async fn disk_based_diagnostic_sources(&self) -> Vec<String> {
|
||||
self.disk_based_diagnostics_sources.clone()
|
||||
|
@ -2769,24 +2769,21 @@ impl Project {
|
||||
language_server
|
||||
.on_notification::<lsp::notification::PublishDiagnostics, _>({
|
||||
let adapter = adapter.clone();
|
||||
move |mut params, cx| {
|
||||
move |mut params, mut cx| {
|
||||
let this = this;
|
||||
let adapter = adapter.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
adapter.process_diagnostics(&mut params).await;
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.update_diagnostics(
|
||||
server_id,
|
||||
params,
|
||||
&adapter.disk_based_diagnostic_sources,
|
||||
cx,
|
||||
)
|
||||
.log_err();
|
||||
});
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
adapter.process_diagnostics(&mut params);
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.update_diagnostics(
|
||||
server_id,
|
||||
params,
|
||||
&adapter.disk_based_diagnostic_sources,
|
||||
cx,
|
||||
)
|
||||
.log_err();
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::Project;
|
||||
use gpui::{ModelContext, ModelHandle, WeakModelHandle};
|
||||
use gpui::{AnyWindowHandle, ModelContext, ModelHandle, WeakModelHandle};
|
||||
use std::path::PathBuf;
|
||||
use terminal::{Terminal, TerminalBuilder, TerminalSettings};
|
||||
|
||||
@ -11,7 +11,7 @@ impl Project {
|
||||
pub fn create_terminal(
|
||||
&mut self,
|
||||
working_directory: Option<PathBuf>,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> anyhow::Result<ModelHandle<Terminal>> {
|
||||
if self.is_remote() {
|
||||
@ -27,7 +27,7 @@ impl Project {
|
||||
settings.env.clone(),
|
||||
Some(settings.blinking.clone()),
|
||||
settings.alternate_scroll,
|
||||
window_id,
|
||||
window,
|
||||
)
|
||||
.map(|builder| {
|
||||
let terminal_handle = cx.add_model(|cx| builder.subscribe(cx));
|
||||
|
@ -1415,7 +1415,7 @@ impl ProjectPanel {
|
||||
|
||||
if cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
.is_some()
|
||||
&& dragged_entry_destination
|
||||
.as_ref()
|
||||
@ -1459,7 +1459,7 @@ impl ProjectPanel {
|
||||
.on_up(MouseButton::Left, move |_, this, cx| {
|
||||
if let Some((_, dragged_entry)) = cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
{
|
||||
this.move_entry(
|
||||
*dragged_entry,
|
||||
@ -1472,7 +1472,7 @@ impl ProjectPanel {
|
||||
.on_move(move |_, this, cx| {
|
||||
if cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
.is_some()
|
||||
{
|
||||
this.dragged_entry_destination = if matches!(kind, EntryKind::File(_)) {
|
||||
@ -1702,7 +1702,7 @@ impl ClipboardEntry {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::{TestAppContext, ViewHandle};
|
||||
use gpui::{AnyWindowHandle, TestAppContext, ViewHandle, WindowHandle};
|
||||
use pretty_assertions::assert_eq;
|
||||
use project::FakeFs;
|
||||
use serde_json::json;
|
||||
@ -1848,7 +1848,6 @@ 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 panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
select_path(&panel, "root1", cx);
|
||||
@ -1870,7 +1869,7 @@ mod tests {
|
||||
// Add a file with the root folder selected. The filename editor is placed
|
||||
// before the first file in the root folder.
|
||||
panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2201,7 +2200,6 @@ 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 panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
select_path(&panel, "root1", cx);
|
||||
@ -2223,7 +2221,7 @@ mod tests {
|
||||
// Add a file with the root folder selected. The filename editor is placed
|
||||
// before the first file in the root folder.
|
||||
panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2378,7 +2376,6 @@ 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 panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
toggle_expand_dir(&panel, "src/test", cx);
|
||||
@ -2395,9 +2392,9 @@ mod tests {
|
||||
" third.rs"
|
||||
]
|
||||
);
|
||||
ensure_single_file_is_opened(window_id, &workspace, "test/first.rs", cx);
|
||||
ensure_single_file_is_opened(window, "test/first.rs", cx);
|
||||
|
||||
submit_deletion(window_id, &panel, cx);
|
||||
submit_deletion(window.into(), &panel, cx);
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..10, cx),
|
||||
&[
|
||||
@ -2408,7 +2405,7 @@ mod tests {
|
||||
],
|
||||
"Project panel should have no deleted file, no other file is selected in it"
|
||||
);
|
||||
ensure_no_open_items_and_panes(window_id, &workspace, cx);
|
||||
ensure_no_open_items_and_panes(window.into(), &workspace, cx);
|
||||
|
||||
select_path(&panel, "src/test/second.rs", cx);
|
||||
panel.update(cx, |panel, cx| panel.open_file(&Open, cx));
|
||||
@ -2422,9 +2419,9 @@ mod tests {
|
||||
" third.rs"
|
||||
]
|
||||
);
|
||||
ensure_single_file_is_opened(window_id, &workspace, "test/second.rs", cx);
|
||||
ensure_single_file_is_opened(window, "test/second.rs", cx);
|
||||
|
||||
cx.update_window(window_id, |cx| {
|
||||
window.update(cx, |cx| {
|
||||
let active_items = workspace
|
||||
.read(cx)
|
||||
.panes()
|
||||
@ -2440,13 +2437,13 @@ mod tests {
|
||||
.expect("Open item should be an editor");
|
||||
open_editor.update(cx, |editor, cx| editor.set_text("Another text!", cx));
|
||||
});
|
||||
submit_deletion(window_id, &panel, cx);
|
||||
submit_deletion(window.into(), &panel, cx);
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..10, cx),
|
||||
&["v src", " v test", " third.rs"],
|
||||
"Project panel should have no deleted file, with one last file remaining"
|
||||
);
|
||||
ensure_no_open_items_and_panes(window_id, &workspace, cx);
|
||||
ensure_no_open_items_and_panes(window.into(), &workspace, cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@ -2469,7 +2466,6 @@ 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 panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
select_path(&panel, "src/", cx);
|
||||
@ -2480,7 +2476,7 @@ mod tests {
|
||||
&["v src <== selected", " > test"]
|
||||
);
|
||||
panel.update(cx, |panel, cx| panel.new_directory(&NewDirectory, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2511,7 +2507,7 @@ mod tests {
|
||||
&["v src", " > test <== selected"]
|
||||
);
|
||||
panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2561,7 +2557,7 @@ mod tests {
|
||||
],
|
||||
);
|
||||
panel.update(cx, |panel, cx| panel.rename(&Rename, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2858,13 +2854,11 @@ mod tests {
|
||||
}
|
||||
|
||||
fn ensure_single_file_is_opened(
|
||||
window_id: usize,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
window: WindowHandle<Workspace>,
|
||||
expected_path: &str,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
cx.read_window(window_id, |cx| {
|
||||
let workspace = workspace.read(cx);
|
||||
window.update_root(cx, |workspace, cx| {
|
||||
let worktrees = workspace.worktrees(cx).collect::<Vec<_>>();
|
||||
assert_eq!(worktrees.len(), 1);
|
||||
let worktree_id = WorktreeId::from_usize(worktrees[0].id());
|
||||
@ -2886,12 +2880,12 @@ mod tests {
|
||||
}
|
||||
|
||||
fn submit_deletion(
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
panel: &ViewHandle<ProjectPanel>,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
assert!(
|
||||
!cx.has_pending_prompt(window_id),
|
||||
!window.has_pending_prompt(cx),
|
||||
"Should have no prompts before the deletion"
|
||||
);
|
||||
panel.update(cx, |panel, cx| {
|
||||
@ -2901,27 +2895,27 @@ mod tests {
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
assert!(
|
||||
cx.has_pending_prompt(window_id),
|
||||
window.has_pending_prompt(cx),
|
||||
"Should have a prompt after the deletion"
|
||||
);
|
||||
cx.simulate_prompt_answer(window_id, 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
assert!(
|
||||
!cx.has_pending_prompt(window_id),
|
||||
!window.has_pending_prompt(cx),
|
||||
"Should have no prompts after prompt was replied to"
|
||||
);
|
||||
cx.foreground().run_until_parked();
|
||||
}
|
||||
|
||||
fn ensure_no_open_items_and_panes(
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
assert!(
|
||||
!cx.has_pending_prompt(window_id),
|
||||
!window.has_pending_prompt(cx),
|
||||
"Should have no prompts after deletion operation closes the file"
|
||||
);
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let open_project_paths = workspace
|
||||
.read(cx)
|
||||
.panes()
|
||||
|
@ -328,10 +328,9 @@ 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();
|
||||
|
||||
// Create the project symbols view.
|
||||
let symbols = cx.add_view(window_id, |cx| {
|
||||
let symbols = window.add_view(cx, |cx| {
|
||||
ProjectSymbols::new(
|
||||
ProjectSymbolsDelegate::new(workspace.downgrade(), project.clone()),
|
||||
cx,
|
||||
|
@ -850,12 +850,9 @@ mod tests {
|
||||
)
|
||||
});
|
||||
let window = cx.add_window(|_| EmptyView);
|
||||
let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||
|
||||
let editor = cx.add_view(window.window_id(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), None, cx)
|
||||
});
|
||||
|
||||
let search_bar = cx.add_view(window.window_id(), |cx| {
|
||||
let search_bar = window.add_view(cx, |cx| {
|
||||
let mut search_bar = BufferSearchBar::new(cx);
|
||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||
search_bar.show(cx);
|
||||
@ -1232,11 +1229,9 @@ 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 editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||
|
||||
let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||
|
||||
let search_bar = cx.add_view(window_id, |cx| {
|
||||
let search_bar = window.add_view(cx, |cx| {
|
||||
let mut search_bar = BufferSearchBar::new(cx);
|
||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||
search_bar.show(cx);
|
||||
@ -1252,12 +1247,13 @@ mod tests {
|
||||
search_bar.activate_current_match(cx);
|
||||
});
|
||||
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
!editor.is_focused(cx),
|
||||
"Initially, the editor should not be focused"
|
||||
);
|
||||
});
|
||||
|
||||
let initial_selections = editor.update(cx, |editor, cx| {
|
||||
let initial_selections = editor.selections.display_ranges(cx);
|
||||
assert_eq!(
|
||||
@ -1274,7 +1270,7 @@ mod tests {
|
||||
cx.focus(search_bar.query_editor.as_any());
|
||||
search_bar.select_all_matches(&SelectAllMatches, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
editor.is_focused(cx),
|
||||
"Should focus editor after successful SelectAllMatches"
|
||||
@ -1298,7 +1294,7 @@ mod tests {
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
editor.is_focused(cx),
|
||||
"Should still have editor focused after SelectNextMatch"
|
||||
@ -1327,7 +1323,7 @@ mod tests {
|
||||
cx.focus(search_bar.query_editor.as_any());
|
||||
search_bar.select_all_matches(&SelectAllMatches, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
editor.is_focused(cx),
|
||||
"Should focus editor after successful SelectAllMatches"
|
||||
@ -1351,7 +1347,7 @@ mod tests {
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
editor.is_focused(cx),
|
||||
"Should still have editor focused after SelectPrevMatch"
|
||||
@ -1387,7 +1383,7 @@ mod tests {
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_all_matches(&SelectAllMatches, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
!editor.is_focused(cx),
|
||||
"Should not switch focus to editor if SelectAllMatches does not find any matches"
|
||||
@ -1421,11 +1417,9 @@ 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| {
|
||||
Editor::for_buffer(buffer.clone(), None, cx)
|
||||
});
|
||||
let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||
|
||||
let search_bar = cx.add_view(window.window_id(), |cx| {
|
||||
let search_bar = window.add_view(cx, |cx| {
|
||||
let mut search_bar = BufferSearchBar::new(cx);
|
||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||
search_bar.show(cx);
|
||||
|
@ -1568,7 +1568,6 @@ 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 active_item = cx.read(|cx| {
|
||||
workspace
|
||||
@ -1599,9 +1598,9 @@ pub mod tests {
|
||||
};
|
||||
let search_view_id = search_view.id();
|
||||
|
||||
cx.spawn(
|
||||
|mut cx| async move { cx.dispatch_action(window_id, search_view_id, &ToggleFocus) },
|
||||
)
|
||||
cx.spawn(|mut cx| async move {
|
||||
window.dispatch_action(search_view_id, &ToggleFocus, &mut cx);
|
||||
})
|
||||
.detach();
|
||||
deterministic.run_until_parked();
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
@ -1652,7 +1651,7 @@ pub mod tests {
|
||||
);
|
||||
});
|
||||
cx.spawn(
|
||||
|mut cx| async move { cx.dispatch_action(window_id, search_view_id, &ToggleFocus) },
|
||||
|mut cx| async move { window.dispatch_action(search_view_id, &ToggleFocus, &mut cx) },
|
||||
)
|
||||
.detach();
|
||||
deterministic.run_until_parked();
|
||||
@ -1683,9 +1682,9 @@ pub mod tests {
|
||||
"Search view with mismatching query should be focused after search results are available",
|
||||
);
|
||||
});
|
||||
cx.spawn(
|
||||
|mut cx| async move { cx.dispatch_action(window_id, search_view_id, &ToggleFocus) },
|
||||
)
|
||||
cx.spawn(|mut cx| async move {
|
||||
window.dispatch_action(search_view_id, &ToggleFocus, &mut cx);
|
||||
})
|
||||
.detach();
|
||||
deterministic.run_until_parked();
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
@ -1713,9 +1712,9 @@ pub mod tests {
|
||||
);
|
||||
});
|
||||
|
||||
cx.spawn(
|
||||
|mut cx| async move { cx.dispatch_action(window_id, search_view_id, &ToggleFocus) },
|
||||
)
|
||||
cx.spawn(|mut cx| async move {
|
||||
window.dispatch_action(search_view_id, &ToggleFocus, &mut cx);
|
||||
})
|
||||
.detach();
|
||||
deterministic.run_until_parked();
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
@ -1874,7 +1873,6 @@ 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();
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
|
||||
});
|
||||
@ -1889,7 +1887,7 @@ pub mod tests {
|
||||
.expect("Search view expected to appear after new search event trigger")
|
||||
});
|
||||
|
||||
let search_bar = cx.add_view(window_id, |cx| {
|
||||
let search_bar = window.add_view(cx, |cx| {
|
||||
let mut search_bar = ProjectSearchBar::new();
|
||||
search_bar.set_active_pane_item(Some(&search_view), cx);
|
||||
// search_bar.show(cx);
|
||||
|
@ -53,7 +53,7 @@ use gpui::{
|
||||
keymap_matcher::Keystroke,
|
||||
platform::{Modifiers, MouseButton, MouseMovedEvent, TouchPhase},
|
||||
scene::{MouseDown, MouseDrag, MouseScrollWheel, MouseUp},
|
||||
AppContext, ClipboardItem, Entity, ModelContext, Task,
|
||||
AnyWindowHandle, AppContext, ClipboardItem, Entity, ModelContext, Task,
|
||||
};
|
||||
|
||||
use crate::mappings::{
|
||||
@ -404,7 +404,7 @@ impl TerminalBuilder {
|
||||
mut env: HashMap<String, String>,
|
||||
blink_settings: Option<TerminalBlink>,
|
||||
alternate_scroll: AlternateScroll,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
) -> Result<TerminalBuilder> {
|
||||
let pty_config = {
|
||||
let alac_shell = match shell.clone() {
|
||||
@ -462,7 +462,7 @@ impl TerminalBuilder {
|
||||
let pty = match tty::new(
|
||||
&pty_config,
|
||||
TerminalSize::default().into(),
|
||||
window_id as u64,
|
||||
window.id() as u64,
|
||||
) {
|
||||
Ok(pty) => pty,
|
||||
Err(error) => {
|
||||
|
@ -10,8 +10,9 @@ use gpui::{
|
||||
platform::{CursorStyle, MouseButton},
|
||||
serde_json::json,
|
||||
text_layout::{Line, RunStyle},
|
||||
AnyElement, Element, EventContext, FontCache, LayoutContext, ModelContext, MouseRegion, Quad,
|
||||
SceneBuilder, SizeConstraint, TextLayoutCache, ViewContext, WeakModelHandle,
|
||||
AnyElement, Element, EventContext, FontCache, LayoutContext, ModelContext, MouseRegion,
|
||||
PaintContext, Quad, SceneBuilder, SizeConstraint, TextLayoutCache, ViewContext,
|
||||
WeakModelHandle,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::CursorShape;
|
||||
@ -734,7 +735,7 @@ impl Element<TerminalView> for TerminalElement {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
view: &mut TerminalView,
|
||||
cx: &mut ViewContext<TerminalView>,
|
||||
cx: &mut PaintContext<TerminalView>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
||||
|
@ -48,7 +48,7 @@ impl TerminalPanel {
|
||||
fn new(workspace: &Workspace, cx: &mut ViewContext<Self>) -> Self {
|
||||
let weak_self = cx.weak_handle();
|
||||
let pane = cx.add_view(|cx| {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let mut pane = Pane::new(
|
||||
workspace.weak_handle(),
|
||||
workspace.project().clone(),
|
||||
@ -60,7 +60,7 @@ impl TerminalPanel {
|
||||
pane.set_can_navigate(false, cx);
|
||||
pane.on_can_drop(move |drag_and_drop, cx| {
|
||||
drag_and_drop
|
||||
.currently_dragged::<DraggedItem>(window_id)
|
||||
.currently_dragged::<DraggedItem>(window)
|
||||
.map_or(false, |(_, item)| {
|
||||
item.handle.act_as::<TerminalView>(cx).is_some()
|
||||
})
|
||||
@ -255,10 +255,10 @@ impl TerminalPanel {
|
||||
.clone();
|
||||
let working_directory =
|
||||
crate::get_working_directory(workspace, cx, working_directory_strategy);
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
if let Some(terminal) = workspace.project().update(cx, |project, cx| {
|
||||
project
|
||||
.create_terminal(working_directory, window_id, cx)
|
||||
.create_terminal(working_directory, window, cx)
|
||||
.log_err()
|
||||
}) {
|
||||
let terminal = Box::new(cx.add_view(|cx| {
|
||||
|
@ -112,11 +112,11 @@ impl TerminalView {
|
||||
let working_directory =
|
||||
get_working_directory(workspace, cx, strategy.working_directory.clone());
|
||||
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let terminal = workspace
|
||||
.project()
|
||||
.update(cx, |project, cx| {
|
||||
project.create_terminal(working_directory, window_id, cx)
|
||||
project.create_terminal(working_directory, window, cx)
|
||||
})
|
||||
.notify_err(workspace, cx);
|
||||
|
||||
@ -741,7 +741,7 @@ impl Item for TerminalView {
|
||||
item_id: workspace::ItemId,
|
||||
cx: &mut ViewContext<Pane>,
|
||||
) -> Task<anyhow::Result<ViewHandle<Self>>> {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
cx.spawn(|pane, mut cx| async move {
|
||||
let cwd = TERMINAL_DB
|
||||
.get_working_directory(item_id, workspace_id)
|
||||
@ -762,7 +762,7 @@ impl Item for TerminalView {
|
||||
});
|
||||
|
||||
let terminal = project.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(cwd, window_id, cx)
|
||||
project.create_terminal(cwd, window, cx)
|
||||
})?;
|
||||
Ok(pane.update(&mut cx, |_, cx| {
|
||||
cx.add_view(|cx| TerminalView::new(terminal, workspace, workspace_id, cx))
|
||||
|
@ -202,7 +202,6 @@ where
|
||||
F: FnOnce(&mut gpui::ViewContext<V>) -> D,
|
||||
{
|
||||
const TITLEBAR_HEIGHT: f32 = 28.;
|
||||
// let active = cx.window_is_active(cx.window_id());
|
||||
|
||||
Flex::column()
|
||||
.with_child(
|
||||
|
@ -33,6 +33,7 @@ pub mod legacy {
|
||||
pub trait PathExt {
|
||||
fn compact(&self) -> PathBuf;
|
||||
fn icon_suffix(&self) -> Option<&str>;
|
||||
fn extension_or_hidden_file_name(&self) -> Option<&str>;
|
||||
}
|
||||
|
||||
impl<T: AsRef<Path>> PathExt for T {
|
||||
@ -60,6 +61,7 @@ impl<T: AsRef<Path>> PathExt for T {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a suffix of the path that is used to determine which file icon to use
|
||||
fn icon_suffix(&self) -> Option<&str> {
|
||||
let file_name = self.as_ref().file_name()?.to_str()?;
|
||||
|
||||
@ -69,8 +71,16 @@ impl<T: AsRef<Path>> PathExt for T {
|
||||
|
||||
self.as_ref()
|
||||
.extension()
|
||||
.map(|extension| extension.to_str())
|
||||
.flatten()
|
||||
.and_then(|extension| extension.to_str())
|
||||
}
|
||||
|
||||
/// Returns a file's extension or, if the file is hidden, its name without the leading dot
|
||||
fn extension_or_hidden_file_name(&self) -> Option<&str> {
|
||||
if let Some(extension) = self.as_ref().extension() {
|
||||
return extension.to_str();
|
||||
}
|
||||
|
||||
self.as_ref().file_name()?.to_str()?.split('.').last()
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +304,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_suffix() {
|
||||
fn test_icon_suffix() {
|
||||
// No dots in name
|
||||
let path = Path::new("/a/b/c/file_name.rs");
|
||||
assert_eq!(path.icon_suffix(), Some("rs"));
|
||||
@ -315,4 +325,27 @@ mod tests {
|
||||
let path = Path::new("/a/b/c/.eslintrc.js");
|
||||
assert_eq!(path.icon_suffix(), Some("eslintrc.js"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extension_or_hidden_file_name() {
|
||||
// No dots in name
|
||||
let path = Path::new("/a/b/c/file_name.rs");
|
||||
assert_eq!(path.extension_or_hidden_file_name(), Some("rs"));
|
||||
|
||||
// Single dot in name
|
||||
let path = Path::new("/a/b/c/file.name.rs");
|
||||
assert_eq!(path.extension_or_hidden_file_name(), Some("rs"));
|
||||
|
||||
// Multiple dots in name
|
||||
let path = Path::new("/a/b/c/long.file.name.rs");
|
||||
assert_eq!(path.extension_or_hidden_file_name(), Some("rs"));
|
||||
|
||||
// Hidden file, no extension
|
||||
let path = Path::new("/a/b/c/.gitignore");
|
||||
assert_eq!(path.extension_or_hidden_file_name(), Some("gitignore"));
|
||||
|
||||
// Hidden file, with extension
|
||||
let path = Path::new("/a/b/c/.eslintrc.js");
|
||||
assert_eq!(path.extension_or_hidden_file_name(), Some("js"));
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ pub fn init(cx: &mut AppContext) {
|
||||
|
||||
fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) {
|
||||
if let Some(previously_active_editor) = Vim::read(cx).active_editor.clone() {
|
||||
cx.update_window(previously_active_editor.window_id(), |cx| {
|
||||
previously_active_editor.window().update(cx, |cx| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
vim.update_active_editor(cx, |previously_active_editor, cx| {
|
||||
vim.unhook_vim_settings(previously_active_editor, cx)
|
||||
@ -19,7 +19,7 @@ fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) {
|
||||
});
|
||||
}
|
||||
|
||||
cx.update_window(editor.window_id(), |cx| {
|
||||
editor.window().update(cx, |cx| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
vim.set_active_editor(editor.clone(), cx);
|
||||
});
|
||||
@ -27,7 +27,7 @@ fn focused(EditorFocused(editor): &EditorFocused, cx: &mut AppContext) {
|
||||
}
|
||||
|
||||
fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) {
|
||||
cx.update_window(editor.window_id(), |cx| {
|
||||
editor.window().update(cx, |cx| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
if let Some(previous_editor) = vim.active_editor.clone() {
|
||||
if previous_editor == editor.clone() {
|
||||
@ -41,7 +41,7 @@ fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) {
|
||||
}
|
||||
|
||||
fn released(EditorReleased(editor): &EditorReleased, cx: &mut AppContext) {
|
||||
cx.update_window(editor.window_id(), |cx| {
|
||||
editor.window().update(cx, |cx| {
|
||||
cx.update_default_global(|vim: &mut Vim, _| {
|
||||
if let Some(previous_editor) = vim.active_editor.clone() {
|
||||
if previous_editor == editor.clone() {
|
||||
|
@ -20,7 +20,7 @@ impl ModeIndicator {
|
||||
if let Some(mode_indicator) = handle.upgrade(cx) {
|
||||
match event {
|
||||
VimEvent::ModeChanged { mode } => {
|
||||
cx.update_window(mode_indicator.window_id(), |cx| {
|
||||
mode_indicator.window().update(cx, |cx| {
|
||||
mode_indicator.update(cx, move |mode_indicator, cx| {
|
||||
mode_indicator.set_mode(mode, cx);
|
||||
})
|
||||
|
@ -85,8 +85,8 @@ impl<'a> VimTestContext<'a> {
|
||||
}
|
||||
|
||||
pub fn set_state(&mut self, text: &str, mode: Mode) -> ContextHandle {
|
||||
let window_id = self.window_id;
|
||||
self.update_window(window_id, |cx| {
|
||||
let window = self.window;
|
||||
window.update(self.cx.cx.cx, |cx| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
vim.switch_mode(mode, false, cx);
|
||||
})
|
||||
|
@ -213,7 +213,7 @@ impl Dock {
|
||||
pub fn panel_index_for_ui_name(&self, ui_name: &str, cx: &AppContext) -> Option<usize> {
|
||||
self.panel_entries.iter().position(|entry| {
|
||||
let panel = entry.panel.as_any();
|
||||
cx.view_ui_name(panel.window_id(), panel.id()) == Some(ui_name)
|
||||
cx.view_ui_name(panel.window(), panel.id()) == Some(ui_name)
|
||||
})
|
||||
}
|
||||
|
||||
@ -542,16 +542,15 @@ impl View for PanelButtons {
|
||||
tooltip_action.as_ref().map(|action| action.boxed_clone());
|
||||
move |_, this, cx| {
|
||||
if let Some(tooltip_action) = &tooltip_action {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let view_id = this.workspace.id();
|
||||
let tooltip_action = tooltip_action.boxed_clone();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
cx.dispatch_action(
|
||||
window_id,
|
||||
window.dispatch_action(
|
||||
view_id,
|
||||
&*tooltip_action,
|
||||
)
|
||||
.ok();
|
||||
&mut cx,
|
||||
);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings};
|
||||
use anyhow::Result;
|
||||
use client::{proto, Client};
|
||||
use gpui::geometry::vector::Vector2F;
|
||||
use gpui::AnyWindowHandle;
|
||||
use gpui::{
|
||||
fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, ModelHandle, Task, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle, WindowContext,
|
||||
@ -250,7 +251,7 @@ pub trait ItemHandle: 'static + fmt::Debug {
|
||||
fn workspace_deactivated(&self, cx: &mut WindowContext);
|
||||
fn navigate(&self, data: Box<dyn Any>, cx: &mut WindowContext) -> bool;
|
||||
fn id(&self) -> usize;
|
||||
fn window_id(&self) -> usize;
|
||||
fn window(&self) -> AnyWindowHandle;
|
||||
fn as_any(&self) -> &AnyViewHandle;
|
||||
fn is_dirty(&self, cx: &AppContext) -> bool;
|
||||
fn has_conflict(&self, cx: &AppContext) -> bool;
|
||||
@ -280,7 +281,7 @@ pub trait ItemHandle: 'static + fmt::Debug {
|
||||
|
||||
pub trait WeakItemHandle {
|
||||
fn id(&self) -> usize;
|
||||
fn window_id(&self) -> usize;
|
||||
fn window(&self) -> AnyWindowHandle;
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>>;
|
||||
}
|
||||
|
||||
@ -542,8 +543,8 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
|
||||
self.id()
|
||||
}
|
||||
|
||||
fn window_id(&self) -> usize {
|
||||
self.window_id()
|
||||
fn window(&self) -> AnyWindowHandle {
|
||||
AnyViewHandle::window(self)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &AnyViewHandle {
|
||||
@ -649,8 +650,8 @@ impl<T: Item> WeakItemHandle for WeakViewHandle<T> {
|
||||
self.id()
|
||||
}
|
||||
|
||||
fn window_id(&self) -> usize {
|
||||
self.window_id()
|
||||
fn window(&self) -> AnyWindowHandle {
|
||||
self.window()
|
||||
}
|
||||
|
||||
fn upgrade(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
|
||||
|
@ -25,8 +25,8 @@ use gpui::{
|
||||
keymap_matcher::KeymapContext,
|
||||
platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel},
|
||||
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
||||
LayoutContext, ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle,
|
||||
WeakViewHandle, WindowContext,
|
||||
LayoutContext, ModelHandle, MouseRegion, PaintContext, Quad, Task, View, ViewContext,
|
||||
ViewHandle, WeakViewHandle, WindowContext,
|
||||
};
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
use serde::Deserialize;
|
||||
@ -1900,7 +1900,7 @@ impl<V: View> Element<V> for PaneBackdrop<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let background = theme::current(cx).editor.background;
|
||||
|
||||
@ -1917,8 +1917,8 @@ impl<V: View> Element<V> for PaneBackdrop<V> {
|
||||
MouseRegion::new::<Self>(child_view_id, 0, visible_bounds).on_down(
|
||||
gpui::platform::MouseButton::Left,
|
||||
move |_, _: &mut V, cx| {
|
||||
let window_id = cx.window_id();
|
||||
cx.app_context().focus(window_id, Some(child_view_id))
|
||||
let window = cx.window();
|
||||
cx.app_context().focus(window, Some(child_view_id))
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -28,11 +28,11 @@ where
|
||||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
let drag_position = if (pane.can_drop)(drag_and_drop, cx) {
|
||||
drag_and_drop
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.currently_dragged::<DraggedItem>(cx.window())
|
||||
.map(|(drag_position, _)| drag_position)
|
||||
.or_else(|| {
|
||||
drag_and_drop
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
.map(|(drag_position, _)| drag_position)
|
||||
})
|
||||
} else {
|
||||
@ -91,10 +91,10 @@ where
|
||||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
|
||||
if drag_and_drop
|
||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
.currently_dragged::<DraggedItem>(cx.window())
|
||||
.is_some()
|
||||
|| drag_and_drop
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
.is_some()
|
||||
{
|
||||
cx.notify();
|
||||
@ -122,11 +122,11 @@ pub fn handle_dropped_item<V: View>(
|
||||
}
|
||||
let drag_and_drop = cx.global::<DragAndDrop<Workspace>>();
|
||||
let action = if let Some((_, dragged_item)) =
|
||||
drag_and_drop.currently_dragged::<DraggedItem>(cx.window_id())
|
||||
drag_and_drop.currently_dragged::<DraggedItem>(cx.window())
|
||||
{
|
||||
Action::Move(dragged_item.pane.clone(), dragged_item.handle.id())
|
||||
} else if let Some((_, project_entry)) =
|
||||
drag_and_drop.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
drag_and_drop.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
{
|
||||
Action::Open(*project_entry)
|
||||
} else {
|
||||
|
@ -595,7 +595,7 @@ mod element {
|
||||
platform::{CursorStyle, MouseButton},
|
||||
scene::MouseDrag,
|
||||
AnyElement, Axis, CursorRegion, Element, EventContext, LayoutContext, MouseRegion,
|
||||
RectFExt, SceneBuilder, SizeConstraint, Vector2FExt, ViewContext,
|
||||
PaintContext, RectFExt, SceneBuilder, SizeConstraint, Vector2FExt, ViewContext,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -856,7 +856,7 @@ mod element {
|
||||
visible_bounds: RectF,
|
||||
remaining_space: &mut Self::LayoutState,
|
||||
view: &mut Workspace,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
cx: &mut PaintContext<Workspace>,
|
||||
) -> Self::PaintState {
|
||||
let can_resize = settings::get::<WorkspaceSettings>(cx).active_pane_magnification == 1.;
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
@ -235,7 +235,7 @@ impl From<&Box<dyn SearchableItemHandle>> for AnyViewHandle {
|
||||
|
||||
impl PartialEq for Box<dyn SearchableItemHandle> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id() && self.window_id() == other.window_id()
|
||||
self.id() == other.id() && self.window() == other.window()
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ impl<T: SearchableItem> WeakSearchableItemHandle for WeakViewHandle<T> {
|
||||
|
||||
impl PartialEq for Box<dyn WeakSearchableItemHandle> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id() && self.window_id() == other.window_id()
|
||||
self.id() == other.id() && self.window() == other.window()
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,6 +267,6 @@ impl Eq for Box<dyn WeakSearchableItemHandle> {}
|
||||
|
||||
impl std::hash::Hash for Box<dyn WeakSearchableItemHandle> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(self.id(), self.window_id()).hash(state)
|
||||
(self.id(), self.window().id()).hash(state)
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ use gpui::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
AnyElement, AnyViewHandle, Entity, LayoutContext, SceneBuilder, SizeConstraint, Subscription,
|
||||
View, ViewContext, ViewHandle, WindowContext,
|
||||
AnyElement, AnyViewHandle, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
Subscription, View, ViewContext, ViewHandle, WindowContext,
|
||||
};
|
||||
|
||||
pub trait StatusItemView: View {
|
||||
@ -231,7 +231,7 @@ impl Element<StatusBar> for StatusBarElement {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut StatusBar,
|
||||
cx: &mut ViewContext<StatusBar>,
|
||||
cx: &mut PaintContext<StatusBar>,
|
||||
) -> Self::PaintState {
|
||||
let origin_y = bounds.upper_right().y();
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
@ -37,7 +37,7 @@ use gpui::{
|
||||
},
|
||||
AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
|
||||
ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
|
||||
WeakViewHandle, WindowContext,
|
||||
WeakViewHandle, WindowContext, WindowHandle,
|
||||
};
|
||||
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
|
||||
use itertools::Itertools;
|
||||
@ -753,7 +753,7 @@ impl Workspace {
|
||||
fn new_local(
|
||||
abs_paths: Vec<PathBuf>,
|
||||
app_state: Arc<AppState>,
|
||||
requesting_window_id: Option<usize>,
|
||||
requesting_window: Option<WindowHandle<Workspace>>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<(
|
||||
WeakViewHandle<Workspace>,
|
||||
@ -797,55 +797,60 @@ impl Workspace {
|
||||
DB.next_id().await.unwrap_or(0)
|
||||
};
|
||||
|
||||
let window = requesting_window_id.and_then(|window_id| {
|
||||
cx.update(|cx| {
|
||||
cx.replace_root_view(window_id, |cx| {
|
||||
Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
|
||||
})
|
||||
})
|
||||
});
|
||||
let window = window.unwrap_or_else(|| {
|
||||
let window_bounds_override = window_bounds_env_override(&cx);
|
||||
let (bounds, display) = if let Some(bounds) = window_bounds_override {
|
||||
(Some(bounds), None)
|
||||
} else {
|
||||
serialized_workspace
|
||||
.as_ref()
|
||||
.and_then(|serialized_workspace| {
|
||||
let display = serialized_workspace.display?;
|
||||
let mut bounds = serialized_workspace.bounds?;
|
||||
let window = if let Some(window) = requesting_window {
|
||||
window.replace_root(&mut cx, |cx| {
|
||||
Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
|
||||
});
|
||||
window
|
||||
} else {
|
||||
{
|
||||
let window_bounds_override = window_bounds_env_override(&cx);
|
||||
let (bounds, display) = if let Some(bounds) = window_bounds_override {
|
||||
(Some(bounds), None)
|
||||
} else {
|
||||
serialized_workspace
|
||||
.as_ref()
|
||||
.and_then(|serialized_workspace| {
|
||||
let display = serialized_workspace.display?;
|
||||
let mut bounds = serialized_workspace.bounds?;
|
||||
|
||||
// Stored bounds are relative to the containing display.
|
||||
// So convert back to global coordinates if that screen still exists
|
||||
if let WindowBounds::Fixed(mut window_bounds) = bounds {
|
||||
if let Some(screen) = cx.platform().screen_by_id(display) {
|
||||
let screen_bounds = screen.bounds();
|
||||
window_bounds.set_origin_x(
|
||||
window_bounds.origin_x() + screen_bounds.origin_x(),
|
||||
);
|
||||
window_bounds.set_origin_y(
|
||||
window_bounds.origin_y() + screen_bounds.origin_y(),
|
||||
);
|
||||
bounds = WindowBounds::Fixed(window_bounds);
|
||||
} else {
|
||||
// Screen no longer exists. Return none here.
|
||||
return None;
|
||||
// Stored bounds are relative to the containing display.
|
||||
// So convert back to global coordinates if that screen still exists
|
||||
if let WindowBounds::Fixed(mut window_bounds) = bounds {
|
||||
if let Some(screen) = cx.platform().screen_by_id(display) {
|
||||
let screen_bounds = screen.bounds();
|
||||
window_bounds.set_origin_x(
|
||||
window_bounds.origin_x() + screen_bounds.origin_x(),
|
||||
);
|
||||
window_bounds.set_origin_y(
|
||||
window_bounds.origin_y() + screen_bounds.origin_y(),
|
||||
);
|
||||
bounds = WindowBounds::Fixed(window_bounds);
|
||||
} else {
|
||||
// Screen no longer exists. Return none here.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some((bounds, display))
|
||||
})
|
||||
.unzip()
|
||||
};
|
||||
Some((bounds, display))
|
||||
})
|
||||
.unzip()
|
||||
};
|
||||
|
||||
// Use the serialized workspace to construct the new window
|
||||
cx.add_window(
|
||||
(app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
|
||||
|cx| {
|
||||
Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx)
|
||||
},
|
||||
)
|
||||
});
|
||||
// Use the serialized workspace to construct the new window
|
||||
cx.add_window(
|
||||
(app_state.build_window_options)(bounds, display, cx.platform().as_ref()),
|
||||
|cx| {
|
||||
Workspace::new(
|
||||
workspace_id,
|
||||
project_handle.clone(),
|
||||
app_state.clone(),
|
||||
cx,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
// We haven't yielded the main thread since obtaining the window handle,
|
||||
// so the window exists.
|
||||
@ -1231,14 +1236,14 @@ impl Workspace {
|
||||
|
||||
pub fn close_global(_: &CloseWindow, cx: &mut AppContext) {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let id = cx
|
||||
.window_ids()
|
||||
let window = cx
|
||||
.windows()
|
||||
.into_iter()
|
||||
.find(|&id| cx.window_is_active(id));
|
||||
if let Some(id) = id {
|
||||
.find(|window| window.is_active(&cx).unwrap_or(false));
|
||||
if let Some(window) = window {
|
||||
//This can only get called when the window's project connection has been lost
|
||||
//so we don't need to prompt the user for anything and instead just close the window
|
||||
cx.remove_window(id);
|
||||
window.remove(&mut cx);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
@ -1249,11 +1254,11 @@ impl Workspace {
|
||||
_: &CloseWindow,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let prepare = self.prepare_to_close(false, cx);
|
||||
Some(cx.spawn(|_, mut cx| async move {
|
||||
if prepare.await? {
|
||||
cx.remove_window(window_id);
|
||||
window.remove(&mut cx);
|
||||
}
|
||||
Ok(())
|
||||
}))
|
||||
@ -1265,13 +1270,13 @@ impl Workspace {
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<bool>> {
|
||||
let active_call = self.active_call().cloned();
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let workspace_count = cx
|
||||
.window_ids()
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
|
||||
.filter(|window| window.root_is::<Workspace>())
|
||||
.count();
|
||||
|
||||
if let Some(active_call) = active_call {
|
||||
@ -1279,11 +1284,11 @@ impl Workspace {
|
||||
&& workspace_count == 1
|
||||
&& active_call.read_with(&cx, |call, _| call.room().is_some())
|
||||
{
|
||||
let answer = cx.prompt(
|
||||
window_id,
|
||||
let answer = window.prompt(
|
||||
PromptLevel::Warning,
|
||||
"Do you want to leave the current call?",
|
||||
&["Close window and hang up", "Cancel"],
|
||||
&mut cx,
|
||||
);
|
||||
|
||||
if let Some(mut answer) = answer {
|
||||
@ -1389,7 +1394,7 @@ impl Workspace {
|
||||
paths: Vec<PathBuf>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window().downcast::<Self>();
|
||||
let is_remote = self.project.read(cx).is_remote();
|
||||
let has_worktree = self.project.read(cx).worktrees(cx).next().is_some();
|
||||
let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx));
|
||||
@ -1401,15 +1406,15 @@ impl Workspace {
|
||||
let app_state = self.app_state.clone();
|
||||
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let window_id_to_replace = if let Some(close_task) = close_task {
|
||||
let window_to_replace = if let Some(close_task) = close_task {
|
||||
if !close_task.await? {
|
||||
return Ok(());
|
||||
}
|
||||
Some(window_id)
|
||||
window
|
||||
} else {
|
||||
None
|
||||
};
|
||||
cx.update(|cx| open_paths(&paths, &app_state, window_id_to_replace, cx))
|
||||
cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx))
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
@ -3180,7 +3185,7 @@ impl Workspace {
|
||||
let left_visible = left_dock.is_open();
|
||||
let left_active_panel = left_dock.visible_panel().and_then(|panel| {
|
||||
Some(
|
||||
cx.view_ui_name(panel.as_any().window_id(), panel.id())?
|
||||
cx.view_ui_name(panel.as_any().window(), panel.id())?
|
||||
.to_string(),
|
||||
)
|
||||
});
|
||||
@ -3193,7 +3198,7 @@ impl Workspace {
|
||||
let right_visible = right_dock.is_open();
|
||||
let right_active_panel = right_dock.visible_panel().and_then(|panel| {
|
||||
Some(
|
||||
cx.view_ui_name(panel.as_any().window_id(), panel.id())?
|
||||
cx.view_ui_name(panel.as_any().window(), panel.id())?
|
||||
.to_string(),
|
||||
)
|
||||
});
|
||||
@ -3206,7 +3211,7 @@ impl Workspace {
|
||||
let bottom_visible = bottom_dock.is_open();
|
||||
let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| {
|
||||
Some(
|
||||
cx.view_ui_name(panel.as_any().window_id(), panel.id())?
|
||||
cx.view_ui_name(panel.as_any().window(), panel.id())?
|
||||
.to_string(),
|
||||
)
|
||||
});
|
||||
@ -3841,9 +3846,9 @@ pub fn activate_workspace_for_project(
|
||||
cx: &mut AsyncAppContext,
|
||||
predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
|
||||
) -> Option<WeakViewHandle<Workspace>> {
|
||||
for window_id in cx.window_ids() {
|
||||
let handle = cx
|
||||
.update_window(window_id, |cx| {
|
||||
for window in cx.windows() {
|
||||
let handle = window
|
||||
.update(cx, |cx| {
|
||||
if let Some(workspace_handle) = cx.root_view().clone().downcast::<Workspace>() {
|
||||
let project = workspace_handle.read(cx).project.clone();
|
||||
if project.update(cx, &predicate) {
|
||||
@ -3870,7 +3875,7 @@ pub async fn last_opened_workspace_paths() -> Option<WorkspaceLocation> {
|
||||
pub fn open_paths(
|
||||
abs_paths: &[PathBuf],
|
||||
app_state: &Arc<AppState>,
|
||||
requesting_window_id: Option<usize>,
|
||||
requesting_window: Option<WindowHandle<Workspace>>,
|
||||
cx: &mut AppContext,
|
||||
) -> Task<
|
||||
Result<(
|
||||
@ -3898,7 +3903,7 @@ pub fn open_paths(
|
||||
} else {
|
||||
Ok(cx
|
||||
.update(|cx| {
|
||||
Workspace::new_local(abs_paths, app_state.clone(), requesting_window_id, cx)
|
||||
Workspace::new_local(abs_paths, app_state.clone(), requesting_window, cx)
|
||||
})
|
||||
.await)
|
||||
}
|
||||
@ -3959,18 +3964,23 @@ pub fn join_remote_project(
|
||||
) -> Task<Result<()>> {
|
||||
cx.spawn(|mut cx| async move {
|
||||
let existing_workspace = cx
|
||||
.window_ids()
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
|
||||
.find(|workspace| {
|
||||
cx.read_window(workspace.window_id(), |cx| {
|
||||
workspace.read(cx).project().read(cx).remote_id() == Some(project_id)
|
||||
.find_map(|window| {
|
||||
window.downcast::<Workspace>().and_then(|window| {
|
||||
window.read_root_with(&cx, |workspace, cx| {
|
||||
if workspace.project().read(cx).remote_id() == Some(project_id) {
|
||||
Some(cx.handle().downgrade())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or(false)
|
||||
});
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let workspace = if let Some(existing_workspace) = existing_workspace {
|
||||
existing_workspace.downgrade()
|
||||
existing_workspace
|
||||
} else {
|
||||
let active_call = cx.read(ActiveCall::global);
|
||||
let room = active_call
|
||||
@ -4009,7 +4019,7 @@ pub fn join_remote_project(
|
||||
workspace.downgrade()
|
||||
};
|
||||
|
||||
cx.activate_window(workspace.window_id());
|
||||
workspace.window().activate(&mut cx);
|
||||
cx.platform().activate(true);
|
||||
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
@ -4048,29 +4058,22 @@ pub fn join_remote_project(
|
||||
pub fn restart(_: &Restart, cx: &mut AppContext) {
|
||||
let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
let mut workspace_windows = cx
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.filter_map(|window| window.downcast::<Workspace>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// If multiple windows have unsaved changes, and need a save prompt,
|
||||
// prompt in the active window before switching to a different window.
|
||||
workspaces.sort_by_key(|workspace| !cx.window_is_active(workspace.window_id()));
|
||||
workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
|
||||
|
||||
if let (true, Some(workspace)) = (should_confirm, workspaces.first()) {
|
||||
let answer = cx.prompt(
|
||||
workspace.window_id(),
|
||||
if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
|
||||
let answer = window.prompt(
|
||||
PromptLevel::Info,
|
||||
"Are you sure you want to restart?",
|
||||
&["Restart", "Cancel"],
|
||||
&mut cx,
|
||||
);
|
||||
|
||||
if let Some(mut answer) = answer {
|
||||
@ -4082,14 +4085,13 @@ pub fn restart(_: &Restart, cx: &mut AppContext) {
|
||||
}
|
||||
|
||||
// If the user cancels any save prompt, then keep the app open.
|
||||
for workspace in workspaces {
|
||||
if !workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(true, cx)
|
||||
})?
|
||||
.await?
|
||||
{
|
||||
return Ok(());
|
||||
for window in workspace_windows {
|
||||
if let Some(close) = window.update_root(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(true, cx)
|
||||
}) {
|
||||
if !close.await? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
cx.platform().restart();
|
||||
@ -4214,17 +4216,11 @@ mod tests {
|
||||
.map(|e| e.id)
|
||||
);
|
||||
});
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
Some("one.txt — root1")
|
||||
);
|
||||
assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root1"));
|
||||
|
||||
// Add a second item to a non-empty pane
|
||||
workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
Some("two.txt — root1")
|
||||
);
|
||||
assert_eq!(window.current_title(cx).as_deref(), Some("two.txt — root1"));
|
||||
project.read_with(cx, |project, cx| {
|
||||
assert_eq!(
|
||||
project.active_entry(),
|
||||
@ -4240,10 +4236,7 @@ mod tests {
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
Some("one.txt — root1")
|
||||
);
|
||||
assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root1"));
|
||||
project.read_with(cx, |project, cx| {
|
||||
assert_eq!(
|
||||
project.active_entry(),
|
||||
@ -4261,16 +4254,13 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
window.current_title(cx).as_deref(),
|
||||
Some("one.txt — root1, root2")
|
||||
);
|
||||
|
||||
// Remove a project folder
|
||||
project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
|
||||
assert_eq!(
|
||||
cx.current_window_title(window.window_id()).as_deref(),
|
||||
Some("one.txt — root2")
|
||||
);
|
||||
assert_eq!(window.current_title(cx).as_deref(), Some("one.txt — root2"));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@ -4304,9 +4294,9 @@ mod tests {
|
||||
});
|
||||
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
|
||||
cx.foreground().run_until_parked();
|
||||
cx.simulate_prompt_answer(window.window_id(), 2 /* cancel */);
|
||||
window.simulate_prompt_answer(2, cx); // cancel
|
||||
cx.foreground().run_until_parked();
|
||||
assert!(!cx.has_pending_prompt(window.window_id()));
|
||||
assert!(!window.has_pending_prompt(cx));
|
||||
assert!(!task.await.unwrap());
|
||||
}
|
||||
|
||||
@ -4365,10 +4355,10 @@ mod tests {
|
||||
assert_eq!(pane.items_len(), 4);
|
||||
assert_eq!(pane.active_item().unwrap().id(), item1.id());
|
||||
});
|
||||
assert!(cx.has_pending_prompt(window.window_id()));
|
||||
assert!(window.has_pending_prompt(cx));
|
||||
|
||||
// Confirm saving item 1.
|
||||
cx.simulate_prompt_answer(window.window_id(), 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
// Item 1 is saved. There's a prompt to save item 3.
|
||||
@ -4379,10 +4369,10 @@ mod tests {
|
||||
assert_eq!(pane.items_len(), 3);
|
||||
assert_eq!(pane.active_item().unwrap().id(), item3.id());
|
||||
});
|
||||
assert!(cx.has_pending_prompt(window.window_id()));
|
||||
assert!(window.has_pending_prompt(cx));
|
||||
|
||||
// Cancel saving item 3.
|
||||
cx.simulate_prompt_answer(window.window_id(), 1);
|
||||
window.simulate_prompt_answer(1, cx);
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
// Item 3 is reloaded. There's a prompt to save item 4.
|
||||
@ -4393,10 +4383,10 @@ mod tests {
|
||||
assert_eq!(pane.items_len(), 2);
|
||||
assert_eq!(pane.active_item().unwrap().id(), item4.id());
|
||||
});
|
||||
assert!(cx.has_pending_prompt(window.window_id()));
|
||||
assert!(window.has_pending_prompt(cx));
|
||||
|
||||
// Confirm saving item 4.
|
||||
cx.simulate_prompt_answer(window.window_id(), 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
// There's a prompt for a path for item 4.
|
||||
@ -4499,7 +4489,7 @@ mod tests {
|
||||
&[ProjectEntryId::from_proto(0)]
|
||||
);
|
||||
});
|
||||
cx.simulate_prompt_answer(window.window_id(), 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
left_pane.read_with(cx, |pane, cx| {
|
||||
@ -4508,7 +4498,7 @@ mod tests {
|
||||
&[ProjectEntryId::from_proto(2)]
|
||||
);
|
||||
});
|
||||
cx.simulate_prompt_answer(window.window_id(), 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
close.await.unwrap();
|
||||
@ -4547,7 +4537,7 @@ mod tests {
|
||||
});
|
||||
|
||||
// Deactivating the window saves the file.
|
||||
cx.simulate_window_activation(None);
|
||||
window.simulate_deactivation(cx);
|
||||
deterministic.run_until_parked();
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 1));
|
||||
|
||||
@ -4568,12 +4558,12 @@ mod tests {
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
|
||||
|
||||
// Deactivating the window still saves the file.
|
||||
cx.simulate_window_activation(Some(window.window_id()));
|
||||
window.simulate_activation(cx);
|
||||
item.update(cx, |item, cx| {
|
||||
cx.focus_self();
|
||||
item.is_dirty = true;
|
||||
});
|
||||
cx.simulate_window_activation(None);
|
||||
window.simulate_deactivation(cx);
|
||||
|
||||
deterministic.run_until_parked();
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
|
||||
@ -4610,7 +4600,7 @@ mod tests {
|
||||
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id))
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!cx.has_pending_prompt(window.window_id()));
|
||||
assert!(!window.has_pending_prompt(cx));
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
|
||||
|
||||
// Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
|
||||
@ -4631,7 +4621,7 @@ mod tests {
|
||||
let _close_items =
|
||||
pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id));
|
||||
deterministic.run_until_parked();
|
||||
assert!(cx.has_pending_prompt(window.window_id()));
|
||||
assert!(window.has_pending_prompt(cx));
|
||||
item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.99.0"
|
||||
version = "0.100.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
|
@ -1,5 +1,5 @@
|
||||
name = "Shell Script"
|
||||
path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin"]
|
||||
path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin", "zprofile"]
|
||||
line_comment = "# "
|
||||
first_line_pattern = "^#!.*\\b(?:ba|z)?sh\\b"
|
||||
brackets = [
|
||||
|
@ -102,7 +102,7 @@ impl LspAdapter for RustLspAdapter {
|
||||
Some("rust-analyzer/flycheck".into())
|
||||
}
|
||||
|
||||
async fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
|
||||
fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
|
||||
lazy_static! {
|
||||
static ref REGEX: Regex = Regex::new("(?m)`([^`]+)\n`$").unwrap();
|
||||
}
|
||||
@ -310,7 +310,7 @@ mod tests {
|
||||
},
|
||||
],
|
||||
};
|
||||
RustLspAdapter.process_diagnostics(&mut params).await;
|
||||
RustLspAdapter.process_diagnostics(&mut params);
|
||||
|
||||
assert_eq!(params.diagnostics[0].message, "use of moved value `a`");
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
name = "TOML"
|
||||
path_suffixes = ["toml"]
|
||||
path_suffixes = ["Cargo.lock", "toml"]
|
||||
line_comment = "# "
|
||||
autoclose_before = ",]}"
|
||||
brackets = [
|
||||
|
@ -16,7 +16,7 @@ use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
FutureExt, SinkExt, StreamExt,
|
||||
};
|
||||
use gpui::{Action, App, AppContext, AssetSource, AsyncAppContext, Task, ViewContext};
|
||||
use gpui::{Action, App, AppContext, AssetSource, AsyncAppContext, Task};
|
||||
use isahc::{config::Configurable, Request};
|
||||
use language::{LanguageRegistry, Point};
|
||||
use log::LevelFilter;
|
||||
@ -45,7 +45,6 @@ use std::{
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use sum_tree::Bias;
|
||||
use terminal_view::{get_working_directory, TerminalSettings, TerminalView};
|
||||
use util::{
|
||||
channel::ReleaseChannel,
|
||||
http::{self, HttpClient},
|
||||
@ -58,7 +57,7 @@ use fs::RealFs;
|
||||
#[cfg(debug_assertions)]
|
||||
use staff_mode::StaffMode;
|
||||
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
|
||||
use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace};
|
||||
use workspace::AppState;
|
||||
use zed::{
|
||||
assets::Assets,
|
||||
build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
|
||||
@ -660,6 +659,10 @@ fn load_embedded_fonts(app: &App) {
|
||||
let embedded_fonts = Mutex::new(Vec::new());
|
||||
smol::block_on(app.background().scoped(|scope| {
|
||||
for font_path in &font_paths {
|
||||
if !font_path.ends_with(".ttf") {
|
||||
continue;
|
||||
}
|
||||
|
||||
scope.spawn(async {
|
||||
let font_path = &*font_path;
|
||||
let font_bytes = Assets.load(font_path).unwrap().to_vec();
|
||||
@ -927,35 +930,6 @@ async fn handle_cli_connection(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dock_default_item_factory(
|
||||
workspace: &mut Workspace,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Option<Box<dyn ItemHandle>> {
|
||||
let strategy = settings::get::<TerminalSettings>(cx)
|
||||
.working_directory
|
||||
.clone();
|
||||
let working_directory = get_working_directory(workspace, cx, strategy);
|
||||
|
||||
let window_id = cx.window_id();
|
||||
let terminal = workspace
|
||||
.project()
|
||||
.update(cx, |project, cx| {
|
||||
project.create_terminal(working_directory, window_id, cx)
|
||||
})
|
||||
.notify_err(workspace, cx)?;
|
||||
|
||||
let terminal_view = cx.add_view(|cx| {
|
||||
TerminalView::new(
|
||||
terminal,
|
||||
workspace.weak_handle(),
|
||||
workspace.database_id(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
Some(Box::new(terminal_view))
|
||||
}
|
||||
|
||||
pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] {
|
||||
&[
|
||||
("Go to file", &file_finder::Toggle),
|
||||
|
@ -165,13 +165,12 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
||||
move |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext<Workspace>| {
|
||||
let app_state = workspace.app_state().clone();
|
||||
let markdown = app_state.languages.language_for_name("JSON");
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
let markdown = markdown.await.log_err();
|
||||
let content = to_string_pretty(
|
||||
&cx.debug_elements(window_id)
|
||||
.ok_or_else(|| anyhow!("could not debug elements for {window_id}"))?,
|
||||
)
|
||||
let content = to_string_pretty(&window.debug_elements(&cx).ok_or_else(|| {
|
||||
anyhow!("could not debug elements for window {}", window.id())
|
||||
})?)
|
||||
.unwrap();
|
||||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
@ -405,29 +404,22 @@ pub fn build_window_options(
|
||||
fn quit(_: &Quit, cx: &mut gpui::AppContext) {
|
||||
let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
let mut workspace_windows = cx
|
||||
.windows()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.filter_map(|window| window.downcast::<Workspace>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// If multiple windows have unsaved changes, and need a save prompt,
|
||||
// prompt in the active window before switching to a different window.
|
||||
workspaces.sort_by_key(|workspace| !cx.window_is_active(workspace.window_id()));
|
||||
workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
|
||||
|
||||
if let (true, Some(workspace)) = (should_confirm, workspaces.first()) {
|
||||
let answer = cx.prompt(
|
||||
workspace.window_id(),
|
||||
if let (true, Some(window)) = (should_confirm, workspace_windows.first().copied()) {
|
||||
let answer = window.prompt(
|
||||
PromptLevel::Info,
|
||||
"Are you sure you want to quit?",
|
||||
&["Quit", "Cancel"],
|
||||
&mut cx,
|
||||
);
|
||||
|
||||
if let Some(mut answer) = answer {
|
||||
@ -439,14 +431,13 @@ fn quit(_: &Quit, cx: &mut gpui::AppContext) {
|
||||
}
|
||||
|
||||
// If the user cancels any save prompt, then keep the app open.
|
||||
for workspace in workspaces {
|
||||
if !workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(true, cx)
|
||||
})?
|
||||
.await?
|
||||
{
|
||||
return Ok(());
|
||||
for window in workspace_windows {
|
||||
if let Some(close) = window.update_root(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(false, cx)
|
||||
}) {
|
||||
if close.await? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
cx.platform().quit();
|
||||
@ -723,8 +714,8 @@ mod tests {
|
||||
use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor};
|
||||
use fs::{FakeFs, Fs};
|
||||
use gpui::{
|
||||
actions, elements::Empty, executor::Deterministic, Action, AnyElement, AppContext,
|
||||
AssetSource, Element, Entity, TestAppContext, View, ViewHandle,
|
||||
actions, elements::Empty, executor::Deterministic, Action, AnyElement, AnyWindowHandle,
|
||||
AppContext, AssetSource, Element, Entity, TestAppContext, View, ViewHandle,
|
||||
};
|
||||
use language::LanguageRegistry;
|
||||
use node_runtime::NodeRuntime;
|
||||
@ -781,17 +772,13 @@ mod tests {
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
let workspace_1 = cx
|
||||
.read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
|
||||
.unwrap()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
let workspace_1 = cx.windows()[0].downcast::<Workspace>().unwrap().root(cx);
|
||||
workspace_1.update(cx, |workspace, cx| {
|
||||
assert_eq!(workspace.worktrees(cx).count(), 2);
|
||||
assert!(workspace.left_dock().read(cx).is_open());
|
||||
@ -808,27 +795,22 @@ mod tests {
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 2);
|
||||
assert_eq!(cx.windows().len(), 2);
|
||||
|
||||
// Replace existing windows
|
||||
let window_id = cx.window_ids()[0];
|
||||
let window = cx.windows()[0].downcast::<Workspace>().unwrap();
|
||||
cx.update(|cx| {
|
||||
open_paths(
|
||||
&[PathBuf::from("/root/c"), PathBuf::from("/root/d")],
|
||||
&app_state,
|
||||
Some(window_id),
|
||||
Some(window),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 2);
|
||||
let workspace_1 = cx
|
||||
.read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
|
||||
.unwrap()
|
||||
.clone()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
assert_eq!(cx.windows().len(), 2);
|
||||
let workspace_1 = cx.windows()[0].downcast::<Workspace>().unwrap().root(cx);
|
||||
workspace_1.update(cx, |workspace, cx| {
|
||||
assert_eq!(
|
||||
workspace
|
||||
@ -854,14 +836,11 @@ mod tests {
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
|
||||
// When opening the workspace, the window is not in a edited state.
|
||||
let workspace = cx
|
||||
.read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
|
||||
.unwrap()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
let window = cx.windows()[0].downcast::<Workspace>().unwrap();
|
||||
let workspace = window.root(cx);
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
let editor = workspace.read_with(cx, |workspace, cx| {
|
||||
workspace
|
||||
@ -870,19 +849,19 @@ mod tests {
|
||||
.downcast::<Editor>()
|
||||
.unwrap()
|
||||
});
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
assert!(!window.is_edited(cx));
|
||||
|
||||
// Editing a buffer marks the window as edited.
|
||||
editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
|
||||
assert!(cx.is_window_edited(workspace.window_id()));
|
||||
assert!(window.is_edited(cx));
|
||||
|
||||
// Undoing the edit restores the window's edited state.
|
||||
editor.update(cx, |editor, cx| editor.undo(&Default::default(), cx));
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
assert!(!window.is_edited(cx));
|
||||
|
||||
// Redoing the edit marks the window as edited again.
|
||||
editor.update(cx, |editor, cx| editor.redo(&Default::default(), cx));
|
||||
assert!(cx.is_window_edited(workspace.window_id()));
|
||||
assert!(window.is_edited(cx));
|
||||
|
||||
// Closing the item restores the window's edited state.
|
||||
let close = pane.update(cx, |pane, cx| {
|
||||
@ -890,9 +869,10 @@ mod tests {
|
||||
pane.close_active_item(&Default::default(), cx).unwrap()
|
||||
});
|
||||
executor.run_until_parked();
|
||||
cx.simulate_prompt_answer(workspace.window_id(), 1);
|
||||
|
||||
window.simulate_prompt_answer(1, cx);
|
||||
close.await.unwrap();
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
assert!(!window.is_edited(cx));
|
||||
|
||||
// Opening the buffer again doesn't impact the window's edited state.
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
|
||||
@ -905,22 +885,22 @@ mod tests {
|
||||
.downcast::<Editor>()
|
||||
.unwrap()
|
||||
});
|
||||
assert!(!cx.is_window_edited(workspace.window_id()));
|
||||
assert!(!window.is_edited(cx));
|
||||
|
||||
// Editing the buffer marks the window as edited.
|
||||
editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
|
||||
assert!(cx.is_window_edited(workspace.window_id()));
|
||||
assert!(window.is_edited(cx));
|
||||
|
||||
// Ensure closing the window via the mouse gets preempted due to the
|
||||
// buffer having unsaved changes.
|
||||
assert!(!cx.simulate_window_close(workspace.window_id()));
|
||||
assert!(!window.simulate_close(cx));
|
||||
executor.run_until_parked();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
|
||||
// The window is successfully closed after the user dismisses the prompt.
|
||||
cx.simulate_prompt_answer(workspace.window_id(), 1);
|
||||
window.simulate_prompt_answer(1, cx);
|
||||
executor.run_until_parked();
|
||||
assert_eq!(cx.window_ids().len(), 0);
|
||||
assert_eq!(cx.windows().len(), 0);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@ -933,12 +913,13 @@ mod tests {
|
||||
})
|
||||
.await;
|
||||
|
||||
let window_id = *cx.window_ids().first().unwrap();
|
||||
let workspace = cx
|
||||
.read_window(window_id, |cx| cx.root_view().clone())
|
||||
let window = cx
|
||||
.windows()
|
||||
.first()
|
||||
.unwrap()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
let workspace = window.root(cx);
|
||||
|
||||
let editor = workspace.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
@ -981,9 +962,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();
|
||||
@ -1104,12 +1084,8 @@ mod tests {
|
||||
cx.update(|cx| open_paths(&[PathBuf::from("/dir1/")], &app_state, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(cx.window_ids().len(), 1);
|
||||
let workspace = cx
|
||||
.read_window(cx.window_ids()[0], |cx| cx.root_view().clone())
|
||||
.unwrap()
|
||||
.downcast::<Workspace>()
|
||||
.unwrap();
|
||||
assert_eq!(cx.windows().len(), 1);
|
||||
let workspace = cx.windows()[0].downcast::<Workspace>().unwrap().root(cx);
|
||||
|
||||
#[track_caller]
|
||||
fn assert_project_panel_selection(
|
||||
@ -1297,7 +1273,6 @@ 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();
|
||||
|
||||
// Open a file within an existing worktree.
|
||||
workspace
|
||||
@ -1323,7 +1298,7 @@ mod tests {
|
||||
cx.read(|cx| assert!(editor.is_dirty(cx)));
|
||||
|
||||
let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx));
|
||||
cx.simulate_prompt_answer(window_id, 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
save_task.await.unwrap();
|
||||
editor.read_with(cx, |editor, cx| {
|
||||
assert!(!editor.is_dirty(cx));
|
||||
@ -1340,11 +1315,10 @@ 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 worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap());
|
||||
|
||||
// Create a new untitled buffer
|
||||
cx.dispatch_action(window_id, NewFile);
|
||||
cx.dispatch_action(window.into(), NewFile);
|
||||
let editor = workspace.read_with(cx, |workspace, cx| {
|
||||
workspace
|
||||
.active_item(cx)
|
||||
@ -1399,7 +1373,7 @@ mod tests {
|
||||
|
||||
// Open the same newly-created file in another pane item. The new editor should reuse
|
||||
// the same buffer.
|
||||
cx.dispatch_action(window_id, NewFile);
|
||||
cx.dispatch_action(window.into(), NewFile);
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.split_and_clone(
|
||||
@ -1435,10 +1409,9 @@ 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();
|
||||
|
||||
// Create a new untitled buffer
|
||||
cx.dispatch_action(window_id, NewFile);
|
||||
cx.dispatch_action(window.into(), NewFile);
|
||||
let editor = workspace.read_with(cx, |workspace, cx| {
|
||||
workspace
|
||||
.active_item(cx)
|
||||
@ -1488,7 +1461,6 @@ 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 entries = cx.read(|cx| workspace.file_project_paths(cx));
|
||||
let file1 = entries[0].clone();
|
||||
@ -1510,7 +1482,7 @@ mod tests {
|
||||
(editor.downgrade(), buffer)
|
||||
});
|
||||
|
||||
cx.dispatch_action(window_id, pane::SplitRight);
|
||||
cx.dispatch_action(window.into(), pane::SplitRight);
|
||||
let editor_2 = cx.update(|cx| {
|
||||
let pane_2 = workspace.read(cx).active_pane().clone();
|
||||
assert_ne!(pane_1, pane_2);
|
||||
@ -1520,7 +1492,7 @@ mod tests {
|
||||
|
||||
pane2_item.downcast::<Editor>().unwrap().downgrade()
|
||||
});
|
||||
cx.dispatch_action(window_id, workspace::CloseActiveItem);
|
||||
cx.dispatch_action(window.into(), workspace::CloseActiveItem);
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
workspace.read_with(cx, |workspace, _| {
|
||||
@ -1528,9 +1500,9 @@ mod tests {
|
||||
assert_eq!(workspace.active_pane(), &pane_1);
|
||||
});
|
||||
|
||||
cx.dispatch_action(window_id, workspace::CloseActiveItem);
|
||||
cx.dispatch_action(window.into(), workspace::CloseActiveItem);
|
||||
cx.foreground().run_until_parked();
|
||||
cx.simulate_prompt_answer(window_id, 1);
|
||||
window.simulate_prompt_answer(1, cx);
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
workspace.read_with(cx, |workspace, cx| {
|
||||
@ -2086,11 +2058,10 @@ mod tests {
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
let window = cx.add_window(|_| TestView);
|
||||
let window_id = window.window_id();
|
||||
|
||||
// Test loading the keymap base at all
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
@ -2117,7 +2088,7 @@ mod tests {
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("backspace", &B), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
@ -2140,7 +2111,7 @@ mod tests {
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("backspace", &B), ("[", &ActivatePrevItem)],
|
||||
line!(),
|
||||
@ -2148,7 +2119,7 @@ mod tests {
|
||||
|
||||
#[track_caller]
|
||||
fn assert_key_bindings_for<'a>(
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
cx: &TestAppContext,
|
||||
actions: Vec<(&'static str, &'a dyn Action)>,
|
||||
line: u32,
|
||||
@ -2156,7 +2127,7 @@ mod tests {
|
||||
for (key, action) in actions {
|
||||
// assert that...
|
||||
assert!(
|
||||
cx.available_actions(window_id, 0)
|
||||
cx.available_actions(window, 0)
|
||||
.into_iter()
|
||||
.any(|(_, bound_action, b)| {
|
||||
// action names match...
|
||||
@ -2257,11 +2228,10 @@ mod tests {
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
let window = cx.add_window(|_| TestView);
|
||||
let window_id = window.window_id();
|
||||
|
||||
// Test loading the keymap base at all
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
@ -2287,7 +2257,12 @@ mod tests {
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(window_id, cx, vec![("k", &ActivatePreviousPane)], line!());
|
||||
assert_key_bindings_for(
|
||||
window.into(),
|
||||
cx,
|
||||
vec![("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
);
|
||||
|
||||
// Test modifying the base, while retaining the users keymap
|
||||
fs.save(
|
||||
@ -2305,11 +2280,11 @@ mod tests {
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(window_id, cx, vec![("[", &ActivatePrevItem)], line!());
|
||||
assert_key_bindings_for(window.into(), cx, vec![("[", &ActivatePrevItem)], line!());
|
||||
|
||||
#[track_caller]
|
||||
fn assert_key_bindings_for<'a>(
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
cx: &TestAppContext,
|
||||
actions: Vec<(&'static str, &'a dyn Action)>,
|
||||
line: u32,
|
||||
@ -2317,7 +2292,7 @@ mod tests {
|
||||
for (key, action) in actions {
|
||||
// assert that...
|
||||
assert!(
|
||||
cx.available_actions(window_id, 0)
|
||||
cx.available_actions(window, 0)
|
||||
.into_iter()
|
||||
.any(|(_, bound_action, b)| {
|
||||
// action names match...
|
||||
@ -2351,6 +2326,11 @@ mod tests {
|
||||
.unwrap()
|
||||
.to_vec()
|
||||
.into(),
|
||||
Assets
|
||||
.load("fonts/plex/IBMPlexSans-Regular.ttf")
|
||||
.unwrap()
|
||||
.to_vec()
|
||||
.into(),
|
||||
])
|
||||
.unwrap();
|
||||
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
|
||||
|
@ -4,6 +4,7 @@ export * from "./theme/theme_config"
|
||||
export { chroma }
|
||||
|
||||
export const font_families = {
|
||||
ui_sans: "IBM Plex Sans",
|
||||
sans: "Zed Sans",
|
||||
mono: "Zed Mono",
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Scale, Color } from "chroma-js"
|
||||
import chroma, { Scale, Color } from "chroma-js"
|
||||
import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax"
|
||||
export { Syntax, ThemeSyntax, SyntaxHighlightStyle }
|
||||
import {
|
||||
@ -32,6 +32,7 @@ export interface Theme {
|
||||
|
||||
players: Players
|
||||
syntax?: Partial<ThemeSyntax>
|
||||
color_family: ColorFamily
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
@ -69,6 +70,15 @@ export interface Players {
|
||||
"7": Player
|
||||
}
|
||||
|
||||
export type ColorFamily = Partial<{ [K in keyof RampSet]: ColorFamilyRange }>
|
||||
|
||||
export interface ColorFamilyRange {
|
||||
low: number
|
||||
high: number
|
||||
range: number
|
||||
scaling_value: number
|
||||
}
|
||||
|
||||
export interface Shadow {
|
||||
blur: number
|
||||
color: string
|
||||
@ -162,6 +172,8 @@ export function create_theme(theme: ThemeConfig): Theme {
|
||||
"7": player(ramps.yellow),
|
||||
}
|
||||
|
||||
const color_family = build_color_family(ramps)
|
||||
|
||||
return {
|
||||
name,
|
||||
is_light,
|
||||
@ -177,6 +189,7 @@ export function create_theme(theme: ThemeConfig): Theme {
|
||||
|
||||
players,
|
||||
syntax,
|
||||
color_family,
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +200,28 @@ function player(ramp: Scale): Player {
|
||||
}
|
||||
}
|
||||
|
||||
function build_color_family(ramps: RampSet): ColorFamily {
|
||||
const color_family: ColorFamily = {}
|
||||
|
||||
for (const ramp in ramps) {
|
||||
const ramp_value = ramps[ramp as keyof RampSet]
|
||||
|
||||
const lightnessValues = [ramp_value(0).get('hsl.l') * 100, ramp_value(1).get('hsl.l') * 100]
|
||||
const low = Math.min(...lightnessValues)
|
||||
const high = Math.max(...lightnessValues)
|
||||
const range = high - low
|
||||
|
||||
color_family[ramp as keyof RampSet] = {
|
||||
low,
|
||||
high,
|
||||
range,
|
||||
scaling_value: 100 / range,
|
||||
}
|
||||
}
|
||||
|
||||
return color_family
|
||||
}
|
||||
|
||||
function lowest_layer(ramps: RampSet): Layer {
|
||||
return {
|
||||
base: build_style_set(ramps.neutral, 0.2, 1),
|
||||
|
Loading…
Reference in New Issue
Block a user