Merge branch 'main' into collab-panel

This commit is contained in:
Mikayla 2023-08-09 10:37:22 -07:00
commit 99daa73325
No known key found for this signature in database
93 changed files with 1908 additions and 1442 deletions

43
Cargo.lock generated
View File

@ -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",

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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.

View File

@ -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
}
}

View File

@ -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();

View File

@ -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,
);
}
}

View File

@ -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());

View File

@ -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();

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();

View File

@ -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

View File

@ -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);
}

View File

@ -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)
});

View File

@ -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

View File

@ -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>>,

View File

@ -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#"

View File

@ -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),
);
});
}

View File

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

View File

@ -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
}

View File

@ -619,7 +619,7 @@ mod tests {
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
let workspace = window.root(cx);
cx.dispatch_action(window.window_id(), Toggle);
cx.dispatch_action(window.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())

View File

@ -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 {

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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>,
) {
(

View File

@ -4,9 +4,9 @@ use crate::{
keymap_matcher::{Binding, Keystroke},
platform,
platform::{Event, InputHandler, KeyDownEvent, Platform},
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle,
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle,
WindowContext, WindowHandle,
Action, AnyWindowHandle, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache,
Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
WeakHandle, WindowContext, WindowHandle,
};
use collections::BTreeMap;
use futures::Future;
@ -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 _};

View File

@ -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(&region.id()) {
window.hovered_region_ids.insert(ix, region.id());
}
// window.hovered_region_ids.insert(region.id());
if !prev_hovered_regions.contains(&region.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(&region.id()) {
if prev_hovered_regions.contains(&region.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

View File

@ -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)
})
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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),

View File

@ -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 {
}

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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 {

View File

@ -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);
}

View File

@ -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(

View File

@ -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!()
}

View File

@ -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

View File

@ -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 => {

View File

@ -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);

View File

@ -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| {

View File

@ -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 {

View File

@ -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();

View File

@ -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);

View File

@ -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();

View File

@ -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 {

View File

@ -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>;

View File

@ -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;

View File

@ -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()))
}

View File

@ -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,
};

View File

@ -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) {

View File

@ -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,

View File

@ -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()

View File

@ -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();

View File

@ -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));

View File

@ -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()

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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) => {

View File

@ -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();

View File

@ -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| {

View File

@ -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))

View File

@ -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(

View File

@ -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"));
}
}

View File

@ -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() {

View File

@ -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);
})

View File

@ -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);
})

View File

@ -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();
}

View File

@ -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>> {

View File

@ -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))
},
),
);

View File

@ -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 {

View File

@ -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();

View File

@ -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)
}
}

View File

@ -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();

View File

@ -37,7 +37,7 @@ use gpui::{
},
AnyModelHandle, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity,
ModelContext, ModelHandle, SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle,
WeakViewHandle, WindowContext,
WeakViewHandle, WindowContext, WindowHandle,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem};
use itertools::Itertools;
@ -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));
}

View File

@ -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]

View File

@ -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 = [

View File

@ -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`");

View File

@ -1,5 +1,5 @@
name = "TOML"
path_suffixes = ["toml"]
path_suffixes = ["Cargo.lock", "toml"]
line_comment = "# "
autoclose_before = ",]}"
brackets = [

View File

@ -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),

View File

@ -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());

View File

@ -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",
}

View File

@ -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),