Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ce18bed403 | ||
![]() |
5fcb296434 | ||
![]() |
83ef9ebce8 | ||
![]() |
53c78ed80c | ||
![]() |
5273cce0bd | ||
![]() |
9e50435e7c | ||
![]() |
e724d7fae9 | ||
![]() |
7ad90fd0a7 | ||
![]() |
4df6b75ba3 | ||
![]() |
1908d3cf43 | ||
![]() |
b4531235ef | ||
![]() |
c3212e559f | ||
![]() |
1c7fb0f0cb | ||
![]() |
df26ef9770 | ||
![]() |
74d5e22cec | ||
![]() |
80efdb13be | ||
![]() |
3f8415b62e |
29 changed files with 391 additions and 405 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -8539,7 +8539,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
|||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.84.0"
|
||||
version = "0.84.5"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
|||
ToggleScreenSharing,
|
||||
};
|
||||
use call::{ActiveCall, ParticipantLocation, Room};
|
||||
use client::{proto::PeerId, ContactEventKind, SignIn, SignOut, User, UserStore};
|
||||
use client::{proto::PeerId, Client, ContactEventKind, SignIn, SignOut, User, UserStore};
|
||||
use clock::ReplicaId;
|
||||
use contacts_popover::ContactsPopover;
|
||||
use context_menu::{ContextMenu, ContextMenuItem};
|
||||
|
@ -19,6 +19,7 @@ use gpui::{
|
|||
AppContext, Entity, ImageData, ModelHandle, SceneBuilder, Subscription, View, ViewContext,
|
||||
ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use project::Project;
|
||||
use settings::Settings;
|
||||
use std::{ops::Range, sync::Arc};
|
||||
use theme::{AvatarStyle, Theme};
|
||||
|
@ -51,8 +52,10 @@ pub fn init(cx: &mut AppContext) {
|
|||
}
|
||||
|
||||
pub struct CollabTitlebarItem {
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
project: ModelHandle<Project>,
|
||||
user_store: ModelHandle<UserStore>,
|
||||
client: Arc<Client>,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
contacts_popover: Option<ViewHandle<ContactsPopover>>,
|
||||
user_menu: ViewHandle<ContextMenu>,
|
||||
collaborator_list_popover: Option<ViewHandle<CollaboratorListPopover>>,
|
||||
|
@ -75,7 +78,7 @@ impl View for CollabTitlebarItem {
|
|||
return Empty::new().into_any();
|
||||
};
|
||||
|
||||
let project = workspace.read(cx).project().read(cx);
|
||||
let project = self.project.read(cx);
|
||||
let mut project_title = String::new();
|
||||
for (i, name) in project.worktree_root_names(cx).enumerate() {
|
||||
if i > 0 {
|
||||
|
@ -100,8 +103,8 @@ impl View for CollabTitlebarItem {
|
|||
.left(),
|
||||
);
|
||||
|
||||
let user = workspace.read(cx).user_store().read(cx).current_user();
|
||||
let peer_id = workspace.read(cx).client().peer_id();
|
||||
let user = self.user_store.read(cx).current_user();
|
||||
let peer_id = self.client.peer_id();
|
||||
if let Some(((user, peer_id), room)) = user
|
||||
.zip(peer_id)
|
||||
.zip(ActiveCall::global(cx).read(cx).room().cloned())
|
||||
|
@ -135,20 +138,23 @@ impl View for CollabTitlebarItem {
|
|||
|
||||
impl CollabTitlebarItem {
|
||||
pub fn new(
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
user_store: &ModelHandle<UserStore>,
|
||||
workspace: &Workspace,
|
||||
workspace_handle: &ViewHandle<Workspace>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let project = workspace.project().clone();
|
||||
let user_store = workspace.user_store().clone();
|
||||
let client = workspace.client().clone();
|
||||
let active_call = ActiveCall::global(cx);
|
||||
let mut subscriptions = Vec::new();
|
||||
subscriptions.push(cx.observe(workspace, |_, _, cx| cx.notify()));
|
||||
subscriptions.push(cx.observe(workspace_handle, |_, _, cx| cx.notify()));
|
||||
subscriptions.push(cx.observe(&active_call, |this, _, cx| this.active_call_changed(cx)));
|
||||
subscriptions.push(cx.observe_window_activation(|this, active, cx| {
|
||||
this.window_activation_changed(active, cx)
|
||||
}));
|
||||
subscriptions.push(cx.observe(user_store, |_, _, cx| cx.notify()));
|
||||
subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
|
||||
subscriptions.push(
|
||||
cx.subscribe(user_store, move |this, user_store, event, cx| {
|
||||
cx.subscribe(&user_store, move |this, user_store, event, cx| {
|
||||
if let Some(workspace) = this.workspace.upgrade(cx) {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
if let client::Event::Contact { user, kind } = event {
|
||||
|
@ -171,8 +177,10 @@ impl CollabTitlebarItem {
|
|||
);
|
||||
|
||||
Self {
|
||||
workspace: workspace.downgrade(),
|
||||
user_store: user_store.clone(),
|
||||
workspace: workspace.weak_handle(),
|
||||
project,
|
||||
user_store,
|
||||
client,
|
||||
contacts_popover: None,
|
||||
user_menu: cx.add_view(|cx| {
|
||||
let mut menu = ContextMenu::new(cx);
|
||||
|
@ -185,16 +193,14 @@ impl CollabTitlebarItem {
|
|||
}
|
||||
|
||||
fn window_activation_changed(&mut self, active: bool, cx: &mut ViewContext<Self>) {
|
||||
if let Some(workspace) = self.workspace.upgrade(cx) {
|
||||
let project = if active {
|
||||
Some(workspace.read(cx).project().clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ActiveCall::global(cx)
|
||||
.update(cx, |call, cx| call.set_location(project.as_ref(), cx))
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
let project = if active {
|
||||
Some(self.project.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ActiveCall::global(cx)
|
||||
.update(cx, |call, cx| call.set_location(project.as_ref(), cx))
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn active_call_changed(&mut self, cx: &mut ViewContext<Self>) {
|
||||
|
@ -205,23 +211,19 @@ impl CollabTitlebarItem {
|
|||
}
|
||||
|
||||
fn share_project(&mut self, _: &ShareProject, cx: &mut ViewContext<Self>) {
|
||||
if let Some(workspace) = self.workspace.upgrade(cx) {
|
||||
let active_call = ActiveCall::global(cx);
|
||||
let project = workspace.read(cx).project().clone();
|
||||
active_call
|
||||
.update(cx, |call, cx| call.share_project(project, cx))
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
let active_call = ActiveCall::global(cx);
|
||||
let project = self.project.clone();
|
||||
active_call
|
||||
.update(cx, |call, cx| call.share_project(project, cx))
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn unshare_project(&mut self, _: &UnshareProject, cx: &mut ViewContext<Self>) {
|
||||
if let Some(workspace) = self.workspace.upgrade(cx) {
|
||||
let active_call = ActiveCall::global(cx);
|
||||
let project = workspace.read(cx).project().clone();
|
||||
active_call
|
||||
.update(cx, |call, cx| call.unshare_project(project, cx))
|
||||
.log_err();
|
||||
}
|
||||
let active_call = ActiveCall::global(cx);
|
||||
let project = self.project.clone();
|
||||
active_call
|
||||
.update(cx, |call, cx| call.unshare_project(project, cx))
|
||||
.log_err();
|
||||
}
|
||||
|
||||
pub fn toggle_collaborator_list_popover(
|
||||
|
@ -256,22 +258,20 @@ impl CollabTitlebarItem {
|
|||
|
||||
pub fn toggle_contacts_popover(&mut self, _: &ToggleContactsMenu, cx: &mut ViewContext<Self>) {
|
||||
if self.contacts_popover.take().is_none() {
|
||||
if let Some(workspace) = self.workspace.upgrade(cx) {
|
||||
let project = workspace.read(cx).project().clone();
|
||||
let user_store = workspace.read(cx).user_store().clone();
|
||||
let view = cx.add_view(|cx| ContactsPopover::new(project, user_store, cx));
|
||||
cx.subscribe(&view, |this, _, event, cx| {
|
||||
match event {
|
||||
contacts_popover::Event::Dismissed => {
|
||||
this.contacts_popover = None;
|
||||
}
|
||||
let view = cx.add_view(|cx| {
|
||||
ContactsPopover::new(self.project.clone(), self.user_store.clone(), cx)
|
||||
});
|
||||
cx.subscribe(&view, |this, _, event, cx| {
|
||||
match event {
|
||||
contacts_popover::Event::Dismissed => {
|
||||
this.contacts_popover = None;
|
||||
}
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
})
|
||||
.detach();
|
||||
self.contacts_popover = Some(view);
|
||||
}
|
||||
cx.notify();
|
||||
})
|
||||
.detach();
|
||||
self.contacts_popover = Some(view);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
|
|
|
@ -52,13 +52,16 @@ fn join_project(action: &JoinProject, app_state: Arc<AppState>, cx: &mut AppCont
|
|||
let project_id = action.project_id;
|
||||
let follow_user_id = action.follow_user_id;
|
||||
cx.spawn(|mut cx| async move {
|
||||
let existing_workspace = cx.update(|cx| {
|
||||
cx.window_ids()
|
||||
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
|
||||
.find(|workspace| {
|
||||
let existing_workspace = cx
|
||||
.window_ids()
|
||||
.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)
|
||||
})
|
||||
});
|
||||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
let workspace = if let Some(existing_workspace) = existing_workspace {
|
||||
existing_workspace.downgrade()
|
||||
|
|
|
@ -23,6 +23,7 @@ pub fn build_contact_finder(
|
|||
},
|
||||
cx,
|
||||
)
|
||||
.with_theme(|theme| theme.contact_finder.picker.clone())
|
||||
}
|
||||
|
||||
pub struct ContactFinderDelegate {
|
||||
|
|
|
@ -1289,10 +1289,9 @@ impl View for ContactList {
|
|||
"ContactList"
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||
let mut cx = Self::default_keymap_context();
|
||||
cx.add_identifier("menu");
|
||||
cx
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
|
||||
Self::reset_to_default_keymap_context(keymap);
|
||||
keymap.add_identifier("menu");
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
|
|
|
@ -62,14 +62,14 @@ pub fn init(cx: &mut AppContext) {
|
|||
room::Event::RemoteProjectUnshared { project_id } => {
|
||||
if let Some(window_ids) = notification_windows.remove(&project_id) {
|
||||
for window_id in window_ids {
|
||||
cx.remove_window(window_id);
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
}
|
||||
}
|
||||
}
|
||||
room::Event::Left => {
|
||||
for (_, window_ids) in notification_windows.drain() {
|
||||
for window_id in window_ids {
|
||||
cx.remove_window(window_id);
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,10 @@ 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.remove_status_bar_item(window_id);
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
}
|
||||
} else if let Some((window_id, _)) = status_indicator.take() {
|
||||
cx.remove_status_bar_item(window_id);
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
|
|
@ -137,10 +137,9 @@ impl View for ContextMenu {
|
|||
"ContextMenu"
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||
let mut cx = Self::default_keymap_context();
|
||||
cx.add_identifier("menu");
|
||||
cx
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
|
||||
Self::reset_to_default_keymap_context(keymap);
|
||||
keymap.add_identifier("menu");
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
|
|
|
@ -33,14 +33,13 @@ pub fn init(cx: &mut AppContext) {
|
|||
crate::Status::SigningIn { prompt } => {
|
||||
if let Some(code_verification_handle) = code_verification.as_mut() {
|
||||
let window_id = code_verification_handle.window_id();
|
||||
if cx.has_window(window_id) {
|
||||
cx.update_window(window_id, |cx| {
|
||||
code_verification_handle.update(cx, |code_verification, cx| {
|
||||
code_verification.set_status(status, cx)
|
||||
});
|
||||
cx.activate_window();
|
||||
let updated = cx.update_window(window_id, |cx| {
|
||||
code_verification_handle.update(cx, |code_verification, cx| {
|
||||
code_verification.set_status(status.clone(), cx)
|
||||
});
|
||||
} else {
|
||||
cx.activate_window();
|
||||
});
|
||||
if updated.is_none() {
|
||||
code_verification = Some(create_copilot_auth_window(cx, &status));
|
||||
}
|
||||
} else if let Some(_prompt) = prompt {
|
||||
|
@ -62,7 +61,7 @@ pub fn init(cx: &mut AppContext) {
|
|||
}
|
||||
_ => {
|
||||
if let Some(code_verification) = code_verification.take() {
|
||||
cx.remove_window(code_verification.window_id());
|
||||
cx.update_window(code_verification.window_id(), |cx| cx.remove_window());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1414,13 +1414,19 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_keymap_context_layer<Tag: 'static>(&mut self, context: KeymapContext) {
|
||||
pub fn set_keymap_context_layer<Tag: 'static>(
|
||||
&mut self,
|
||||
context: KeymapContext,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.keymap_context_layers
|
||||
.insert(TypeId::of::<Tag>(), context);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn remove_keymap_context_layer<Tag: 'static>(&mut self) {
|
||||
pub fn remove_keymap_context_layer<Tag: 'static>(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.keymap_context_layers.remove(&TypeId::of::<Tag>());
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn set_input_enabled(&mut self, input_enabled: bool) {
|
||||
|
@ -7116,28 +7122,26 @@ impl View for Editor {
|
|||
false
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||
let mut context = Self::default_keymap_context();
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
|
||||
Self::reset_to_default_keymap_context(keymap);
|
||||
let mode = match self.mode {
|
||||
EditorMode::SingleLine => "single_line",
|
||||
EditorMode::AutoHeight { .. } => "auto_height",
|
||||
EditorMode::Full => "full",
|
||||
};
|
||||
context.add_key("mode", mode);
|
||||
keymap.add_key("mode", mode);
|
||||
if self.pending_rename.is_some() {
|
||||
context.add_identifier("renaming");
|
||||
keymap.add_identifier("renaming");
|
||||
}
|
||||
match self.context_menu.as_ref() {
|
||||
Some(ContextMenu::Completions(_)) => context.add_identifier("showing_completions"),
|
||||
Some(ContextMenu::CodeActions(_)) => context.add_identifier("showing_code_actions"),
|
||||
Some(ContextMenu::Completions(_)) => keymap.add_identifier("showing_completions"),
|
||||
Some(ContextMenu::CodeActions(_)) => keymap.add_identifier("showing_code_actions"),
|
||||
None => {}
|
||||
}
|
||||
|
||||
for layer in self.keymap_context_layers.values() {
|
||||
context.extend(layer);
|
||||
keymap.extend(layer);
|
||||
}
|
||||
|
||||
context
|
||||
}
|
||||
|
||||
fn text_for_range(&self, range_utf16: Range<usize>, cx: &AppContext) -> Option<String> {
|
||||
|
|
|
@ -43,6 +43,7 @@ use window_input_handler::WindowInputHandler;
|
|||
use crate::{
|
||||
elements::{AnyElement, AnyRootElement, RootElement},
|
||||
executor::{self, Task},
|
||||
json,
|
||||
keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
|
||||
platform::{
|
||||
self, FontSystem, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent, MouseButton,
|
||||
|
@ -82,14 +83,15 @@ pub trait View: Entity + Sized {
|
|||
false
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> keymap_matcher::KeymapContext {
|
||||
Self::default_keymap_context()
|
||||
fn update_keymap_context(&self, keymap: &mut keymap_matcher::KeymapContext, _: &AppContext) {
|
||||
Self::reset_to_default_keymap_context(keymap);
|
||||
}
|
||||
fn default_keymap_context() -> keymap_matcher::KeymapContext {
|
||||
let mut cx = keymap_matcher::KeymapContext::default();
|
||||
cx.add_identifier(Self::ui_name());
|
||||
cx
|
||||
|
||||
fn reset_to_default_keymap_context(keymap: &mut keymap_matcher::KeymapContext) {
|
||||
keymap.clear();
|
||||
keymap.add_identifier(Self::ui_name());
|
||||
}
|
||||
|
||||
fn debug_json(&self, _: &AppContext) -> serde_json::Value {
|
||||
serde_json::Value::Null
|
||||
}
|
||||
|
@ -301,6 +303,14 @@ impl AsyncAppContext {
|
|||
self.0.borrow_mut().update(callback)
|
||||
}
|
||||
|
||||
pub fn read_window<T, F: FnOnce(&WindowContext) -> T>(
|
||||
&self,
|
||||
window_id: usize,
|
||||
callback: F,
|
||||
) -> Option<T> {
|
||||
self.0.borrow_mut().read_window(window_id, callback)
|
||||
}
|
||||
|
||||
pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
|
@ -309,6 +319,30 @@ impl AsyncAppContext {
|
|||
self.0.borrow_mut().update_window(window_id, callback)
|
||||
}
|
||||
|
||||
pub fn debug_elements(&self, window_id: usize) -> Option<json::Value> {
|
||||
self.0.borrow().read_window(window_id, |cx| {
|
||||
let root_view = cx.window.root_view();
|
||||
let root_element = cx.window.rendered_views.get(&root_view.id())?;
|
||||
root_element.debug(cx).log_err()
|
||||
})?
|
||||
}
|
||||
|
||||
pub fn has_window(&self, window_id: usize) -> bool {
|
||||
self.read(|cx| cx.windows.contains_key(&window_id))
|
||||
}
|
||||
|
||||
pub fn window_is_active(&self, window_id: usize) -> bool {
|
||||
self.read(|cx| cx.windows.get(&window_id).map_or(false, |w| w.is_active))
|
||||
}
|
||||
|
||||
pub fn root_view(&self, window_id: usize) -> Option<AnyViewHandle> {
|
||||
self.read(|cx| cx.windows.get(&window_id).map(|w| w.root_view().clone()))
|
||||
}
|
||||
|
||||
pub fn window_ids(&self) -> Vec<usize> {
|
||||
self.read(|cx| cx.windows.keys().copied().collect())
|
||||
}
|
||||
|
||||
pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
|
||||
where
|
||||
T: Entity,
|
||||
|
@ -330,7 +364,7 @@ impl AsyncAppContext {
|
|||
}
|
||||
|
||||
pub fn remove_window(&mut self, window_id: usize) {
|
||||
self.update(|cx| cx.remove_window(window_id))
|
||||
self.update_window(window_id, |cx| cx.remove_window());
|
||||
}
|
||||
|
||||
pub fn activate_window(&mut self, window_id: usize) {
|
||||
|
@ -393,6 +427,7 @@ type WindowShouldCloseSubscriptionCallback = Box<dyn FnMut(&mut AppContext) -> b
|
|||
pub struct AppContext {
|
||||
models: HashMap<usize, Box<dyn AnyModel>>,
|
||||
views: HashMap<(usize, usize), Box<dyn AnyView>>,
|
||||
views_metadata: HashMap<(usize, usize), ViewMetadata>,
|
||||
pub(crate) parents: HashMap<(usize, usize), ParentId>,
|
||||
windows: HashMap<usize, Window>,
|
||||
globals: HashMap<TypeId, Box<dyn Any>>,
|
||||
|
@ -455,6 +490,7 @@ impl AppContext {
|
|||
Self {
|
||||
models: Default::default(),
|
||||
views: Default::default(),
|
||||
views_metadata: Default::default(),
|
||||
parents: Default::default(),
|
||||
windows: Default::default(),
|
||||
globals: Default::default(),
|
||||
|
@ -529,7 +565,7 @@ impl AppContext {
|
|||
App(self.weak_self.as_ref().unwrap().upgrade().unwrap())
|
||||
}
|
||||
|
||||
pub fn quit(&mut self) {
|
||||
fn quit(&mut self) {
|
||||
let mut futures = Vec::new();
|
||||
|
||||
self.update(|cx| {
|
||||
|
@ -546,7 +582,8 @@ impl AppContext {
|
|||
}
|
||||
});
|
||||
|
||||
self.remove_all_windows();
|
||||
self.windows.clear();
|
||||
self.flush_effects();
|
||||
|
||||
let futures = futures::future::join_all(futures);
|
||||
if self
|
||||
|
@ -558,11 +595,6 @@ impl AppContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn remove_all_windows(&mut self) {
|
||||
self.windows.clear();
|
||||
self.flush_effects();
|
||||
}
|
||||
|
||||
pub fn foreground(&self) -> &Rc<executor::Foreground> {
|
||||
&self.foreground
|
||||
}
|
||||
|
@ -679,32 +711,14 @@ impl AppContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn has_window(&self, window_id: usize) -> bool {
|
||||
self.window_ids()
|
||||
.find(|window| window == &window_id)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
pub fn window_is_active(&self, window_id: usize) -> bool {
|
||||
self.windows.get(&window_id).map_or(false, |w| w.is_active)
|
||||
}
|
||||
|
||||
pub fn root_view(&self, window_id: usize) -> Option<&AnyViewHandle> {
|
||||
self.windows.get(&window_id).map(|w| w.root_view())
|
||||
}
|
||||
|
||||
pub fn window_ids(&self) -> impl Iterator<Item = usize> + '_ {
|
||||
self.windows.keys().copied()
|
||||
}
|
||||
|
||||
pub fn view_ui_name(&self, window_id: usize, view_id: usize) -> Option<&'static str> {
|
||||
Some(self.views.get(&(window_id, view_id))?.ui_name())
|
||||
}
|
||||
|
||||
pub fn view_type_id(&self, window_id: usize, view_id: usize) -> Option<TypeId> {
|
||||
self.views
|
||||
self.views_metadata
|
||||
.get(&(window_id, view_id))
|
||||
.map(|view| view.as_any().type_id())
|
||||
.map(|metadata| metadata.type_id)
|
||||
}
|
||||
|
||||
pub fn active_labeled_tasks<'a>(
|
||||
|
@ -1020,9 +1034,10 @@ impl AppContext {
|
|||
.read_window(window_id, |cx| {
|
||||
if let Some(focused_view_id) = cx.focused_view_id() {
|
||||
for view_id in cx.ancestors(focused_view_id) {
|
||||
if let Some(view) = cx.views.get(&(window_id, view_id)) {
|
||||
let view_type = view.as_any().type_id();
|
||||
if let Some(actions) = cx.actions.get(&view_type) {
|
||||
if let Some(view_metadata) =
|
||||
cx.views_metadata.get(&(window_id, view_id))
|
||||
{
|
||||
if let Some(actions) = cx.actions.get(&view_metadata.type_id) {
|
||||
if actions.contains_key(&action_type) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1266,15 +1281,6 @@ impl AppContext {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn remove_status_bar_item(&mut self, id: usize) {
|
||||
self.remove_window(id);
|
||||
}
|
||||
|
||||
pub fn remove_window(&mut self, window_id: usize) {
|
||||
self.windows.remove(&window_id);
|
||||
self.flush_effects();
|
||||
}
|
||||
|
||||
pub fn build_window<V, F>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
|
@ -1333,7 +1339,7 @@ impl AppContext {
|
|||
{
|
||||
let mut app = self.upgrade();
|
||||
platform_window.on_close(Box::new(move || {
|
||||
app.update(|cx| cx.remove_window(window_id));
|
||||
app.update(|cx| cx.update_window(window_id, |cx| cx.remove_window()));
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -1436,6 +1442,7 @@ impl AppContext {
|
|||
for (window_id, view_id) in dropped_views {
|
||||
self.subscriptions.remove(view_id);
|
||||
self.observations.remove(view_id);
|
||||
self.views_metadata.remove(&(window_id, view_id));
|
||||
let mut view = self.views.remove(&(window_id, view_id)).unwrap();
|
||||
view.release(self);
|
||||
let change_focus_to = self.windows.get_mut(&window_id).and_then(|window| {
|
||||
|
@ -1794,9 +1801,11 @@ impl AppContext {
|
|||
observed_window_id: usize,
|
||||
observed_view_id: usize,
|
||||
) {
|
||||
if self
|
||||
let view_key = (observed_window_id, observed_view_id);
|
||||
if let Some((view, mut view_metadata)) = self
|
||||
.views
|
||||
.contains_key(&(observed_window_id, observed_view_id))
|
||||
.remove(&view_key)
|
||||
.zip(self.views_metadata.remove(&view_key))
|
||||
{
|
||||
if let Some(window) = self.windows.get_mut(&observed_window_id) {
|
||||
window
|
||||
|
@ -1806,6 +1815,10 @@ impl AppContext {
|
|||
.insert(observed_view_id);
|
||||
}
|
||||
|
||||
view.update_keymap_context(&mut view_metadata.keymap_context, self);
|
||||
self.views.insert(view_key, view);
|
||||
self.views_metadata.insert(view_key, view_metadata);
|
||||
|
||||
let mut observations = self.observations.clone();
|
||||
observations.emit(observed_view_id, |callback| callback(self));
|
||||
}
|
||||
|
@ -2063,6 +2076,11 @@ pub enum ParentId {
|
|||
Root,
|
||||
}
|
||||
|
||||
struct ViewMetadata {
|
||||
type_id: TypeId,
|
||||
keymap_context: KeymapContext,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct WindowInvalidation {
|
||||
pub updated: HashSet<usize>,
|
||||
|
@ -2403,7 +2421,7 @@ pub trait AnyView {
|
|||
cx: &mut WindowContext,
|
||||
view_id: usize,
|
||||
) -> bool;
|
||||
fn keymap_context(&self, cx: &AppContext) -> KeymapContext;
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext);
|
||||
fn debug_json(&self, cx: &WindowContext) -> serde_json::Value;
|
||||
|
||||
fn text_for_range(&self, range: Range<usize>, cx: &WindowContext) -> Option<String>;
|
||||
|
@ -2475,11 +2493,10 @@ where
|
|||
cx.handle().into_any()
|
||||
} else {
|
||||
let focused_type = cx
|
||||
.views
|
||||
.views_metadata
|
||||
.get(&(cx.window_id, focused_id))
|
||||
.unwrap()
|
||||
.as_any()
|
||||
.type_id();
|
||||
.type_id;
|
||||
AnyViewHandle::new(
|
||||
cx.window_id,
|
||||
focused_id,
|
||||
|
@ -2496,11 +2513,10 @@ where
|
|||
cx.handle().into_any()
|
||||
} else {
|
||||
let blurred_type = cx
|
||||
.views
|
||||
.views_metadata
|
||||
.get(&(cx.window_id, blurred_id))
|
||||
.unwrap()
|
||||
.as_any()
|
||||
.type_id();
|
||||
.type_id;
|
||||
AnyViewHandle::new(
|
||||
cx.window_id,
|
||||
blurred_id,
|
||||
|
@ -2531,8 +2547,8 @@ where
|
|||
View::modifiers_changed(self, event, &mut cx)
|
||||
}
|
||||
|
||||
fn keymap_context(&self, cx: &AppContext) -> KeymapContext {
|
||||
View::keymap_context(self, cx)
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
|
||||
View::update_keymap_context(self, keymap, cx)
|
||||
}
|
||||
|
||||
fn debug_json(&self, cx: &WindowContext) -> serde_json::Value {
|
||||
|
@ -4708,7 +4724,7 @@ mod tests {
|
|||
assert!(model_release_observed.get());
|
||||
|
||||
drop(view);
|
||||
cx.remove_window(window_id);
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
assert!(view_released.get());
|
||||
assert!(view_release_observed.get());
|
||||
}
|
||||
|
@ -5611,8 +5627,8 @@ mod tests {
|
|||
"View"
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||
self.keymap_context.clone()
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
|
||||
*keymap = self.keymap_context.clone();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5716,7 +5732,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[crate::test(self)]
|
||||
fn test_keystrokes_for_action(cx: &mut AppContext) {
|
||||
fn test_keystrokes_for_action(cx: &mut TestAppContext) {
|
||||
actions!(test, [Action1, Action2, GlobalAction]);
|
||||
|
||||
struct View1 {}
|
||||
|
@ -5746,70 +5762,76 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
let (window_id, view_1) = cx.add_window(Default::default(), |_| View1 {});
|
||||
let (_, view_1) = cx.add_window(|_| View1 {});
|
||||
let view_2 = cx.add_view(&view_1, |cx| {
|
||||
cx.focus_self();
|
||||
View2 {}
|
||||
});
|
||||
|
||||
cx.add_action(|_: &mut View1, _: &Action1, _cx| {});
|
||||
cx.add_action(|_: &mut View2, _: &Action2, _cx| {});
|
||||
cx.add_global_action(|_: &GlobalAction, _| {});
|
||||
cx.update(|cx| {
|
||||
cx.add_action(|_: &mut View1, _: &Action1, _cx| {});
|
||||
cx.add_action(|_: &mut View2, _: &Action2, _cx| {});
|
||||
cx.add_global_action(|_: &GlobalAction, _| {});
|
||||
|
||||
cx.add_bindings(vec![
|
||||
Binding::new("a", Action1, Some("View1")),
|
||||
Binding::new("b", Action2, Some("View1 > View2")),
|
||||
Binding::new("c", GlobalAction, Some("View3")), // View 3 does not exist
|
||||
]);
|
||||
cx.add_bindings(vec![
|
||||
Binding::new("a", Action1, Some("View1")),
|
||||
Binding::new("b", Action2, Some("View1 > View2")),
|
||||
Binding::new("c", GlobalAction, Some("View3")), // View 3 does not exist
|
||||
]);
|
||||
});
|
||||
|
||||
cx.update_window(window_id, |cx| {
|
||||
// Sanity check
|
||||
assert_eq!(
|
||||
cx.keystrokes_for_action(view_1.id(), &Action1)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("a").unwrap()]
|
||||
);
|
||||
assert_eq!(
|
||||
cx.keystrokes_for_action(view_2.id(), &Action2)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("b").unwrap()]
|
||||
);
|
||||
// Here we update the views to ensure that, even if they are on the stack,
|
||||
// we can still retrieve keystrokes correctly.
|
||||
view_1.update(cx, |_, cx| {
|
||||
view_2.update(cx, |_, cx| {
|
||||
// Sanity check
|
||||
assert_eq!(
|
||||
cx.keystrokes_for_action(view_1.id(), &Action1)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("a").unwrap()]
|
||||
);
|
||||
assert_eq!(
|
||||
cx.keystrokes_for_action(view_2.id(), &Action2)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("b").unwrap()]
|
||||
);
|
||||
|
||||
// The 'a' keystroke propagates up the view tree from view_2
|
||||
// to view_1. The action, Action1, is handled by view_1.
|
||||
assert_eq!(
|
||||
cx.keystrokes_for_action(view_2.id(), &Action1)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("a").unwrap()]
|
||||
);
|
||||
// The 'a' keystroke propagates up the view tree from view_2
|
||||
// to view_1. The action, Action1, is handled by view_1.
|
||||
assert_eq!(
|
||||
cx.keystrokes_for_action(view_2.id(), &Action1)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("a").unwrap()]
|
||||
);
|
||||
|
||||
// Actions that are handled below the current view don't have bindings
|
||||
assert_eq!(cx.keystrokes_for_action(view_1.id(), &Action2), None);
|
||||
// Actions that are handled below the current view don't have bindings
|
||||
assert_eq!(cx.keystrokes_for_action(view_1.id(), &Action2), None);
|
||||
|
||||
// Actions that are handled in other branches of the tree should not have a binding
|
||||
assert_eq!(cx.keystrokes_for_action(view_2.id(), &GlobalAction), None);
|
||||
// Actions that are handled in other branches of the tree should not have a binding
|
||||
assert_eq!(cx.keystrokes_for_action(view_2.id(), &GlobalAction), None);
|
||||
|
||||
// Check that global actions do not have a binding, even if a binding does exist in another view
|
||||
assert_eq!(
|
||||
&available_actions(view_1.id(), cx),
|
||||
&[
|
||||
("test::Action1", vec![Keystroke::parse("a").unwrap()]),
|
||||
("test::GlobalAction", vec![])
|
||||
],
|
||||
);
|
||||
// Check that global actions do not have a binding, even if a binding does exist in another view
|
||||
assert_eq!(
|
||||
&available_actions(view_1.id(), cx),
|
||||
&[
|
||||
("test::Action1", vec![Keystroke::parse("a").unwrap()]),
|
||||
("test::GlobalAction", vec![])
|
||||
],
|
||||
);
|
||||
|
||||
// Check that view 1 actions and bindings are available even when called from view 2
|
||||
assert_eq!(
|
||||
&available_actions(view_2.id(), cx),
|
||||
&[
|
||||
("test::Action1", vec![Keystroke::parse("a").unwrap()]),
|
||||
("test::Action2", vec![Keystroke::parse("b").unwrap()]),
|
||||
("test::GlobalAction", vec![]),
|
||||
],
|
||||
);
|
||||
// Check that view 1 actions and bindings are available even when called from view 2
|
||||
assert_eq!(
|
||||
&available_actions(view_2.id(), cx),
|
||||
&[
|
||||
("test::Action1", vec![Keystroke::parse("a").unwrap()]),
|
||||
("test::Action2", vec![Keystroke::parse("b").unwrap()]),
|
||||
("test::GlobalAction", vec![]),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Produces a list of actions and key bindings
|
||||
|
|
|
@ -180,7 +180,11 @@ impl TestAppContext {
|
|||
}
|
||||
|
||||
pub fn window_ids(&self) -> Vec<usize> {
|
||||
self.cx.borrow().window_ids().collect()
|
||||
self.cx.borrow().windows.keys().copied().collect()
|
||||
}
|
||||
|
||||
pub fn remove_all_windows(&mut self) {
|
||||
self.update(|cx| cx.windows.clear());
|
||||
}
|
||||
|
||||
pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{
|
||||
elements::AnyRootElement,
|
||||
geometry::rect::RectF,
|
||||
json::{self, ToJson},
|
||||
keymap_matcher::{Binding, Keystroke, MatchResult},
|
||||
json::ToJson,
|
||||
keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
|
||||
platform::{
|
||||
self, Appearance, CursorStyle, Event, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent,
|
||||
MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
|
||||
|
@ -34,7 +34,7 @@ use std::{
|
|||
use util::ResultExt;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::Reference;
|
||||
use super::{Reference, ViewMetadata};
|
||||
|
||||
pub struct Window {
|
||||
pub(crate) root_view: Option<AnyViewHandle>,
|
||||
|
@ -369,13 +369,13 @@ impl<'a> WindowContext<'a> {
|
|||
let mut contexts = Vec::new();
|
||||
let mut handler_depth = None;
|
||||
for (i, view_id) in self.ancestors(view_id).enumerate() {
|
||||
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
||||
if let Some(actions) = self.actions.get(&view.as_any().type_id()) {
|
||||
if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
|
||||
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
|
||||
if actions.contains_key(&action.as_any().type_id()) {
|
||||
handler_depth = Some(i);
|
||||
}
|
||||
}
|
||||
contexts.push(view.keymap_context(self));
|
||||
contexts.push(view_metadata.keymap_context.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -406,10 +406,9 @@ impl<'a> WindowContext<'a> {
|
|||
let mut contexts = Vec::new();
|
||||
let mut handler_depths_by_action_type = HashMap::<TypeId, usize>::default();
|
||||
for (depth, view_id) in self.ancestors(view_id).enumerate() {
|
||||
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
||||
contexts.push(view.keymap_context(self));
|
||||
let view_type = view.as_any().type_id();
|
||||
if let Some(actions) = self.actions.get(&view_type) {
|
||||
if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
|
||||
contexts.push(view_metadata.keymap_context.clone());
|
||||
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
|
||||
handler_depths_by_action_type.extend(
|
||||
actions
|
||||
.keys()
|
||||
|
@ -458,9 +457,9 @@ impl<'a> WindowContext<'a> {
|
|||
let dispatch_path = self
|
||||
.ancestors(focused_view_id)
|
||||
.filter_map(|view_id| {
|
||||
self.views
|
||||
self.views_metadata
|
||||
.get(&(window_id, view_id))
|
||||
.map(|view| (view_id, view.keymap_context(self)))
|
||||
.map(|view| (view_id, view.keymap_context.clone()))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -975,17 +974,6 @@ impl<'a> WindowContext<'a> {
|
|||
.flatten()
|
||||
}
|
||||
|
||||
pub fn debug_elements(&self) -> Option<json::Value> {
|
||||
let view = self.window.root_view();
|
||||
Some(json!({
|
||||
"root_view": view.debug_json(self),
|
||||
"root_element": self.window.rendered_views.get(&view.id())
|
||||
.and_then(|root_element| {
|
||||
root_element.debug(self).log_err()
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn set_window_title(&mut self, title: &str) {
|
||||
self.window.platform_window.set_title(title);
|
||||
}
|
||||
|
@ -1188,6 +1176,15 @@ impl<'a> WindowContext<'a> {
|
|||
self.parents.insert((window_id, view_id), parent_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),
|
||||
ViewMetadata {
|
||||
type_id: TypeId::of::<T>(),
|
||||
keymap_context,
|
||||
},
|
||||
);
|
||||
self.views.insert((window_id, view_id), Box::new(view));
|
||||
self.window
|
||||
.invalidation
|
||||
|
@ -1454,13 +1451,7 @@ impl<V: View> Element<V> for ChildView {
|
|||
) -> serde_json::Value {
|
||||
json!({
|
||||
"type": "ChildView",
|
||||
"view_id": self.view_id,
|
||||
"bounds": bounds.to_json(),
|
||||
"view": if let Some(view) = cx.views.get(&(cx.window_id, self.view_id)) {
|
||||
view.debug_json(cx)
|
||||
} else {
|
||||
json!(null)
|
||||
},
|
||||
"child": if let Some(element) = cx.window.rendered_views.get(&self.view_id) {
|
||||
element.debug(&cx.window_context).log_err().unwrap_or_else(|| json!(null))
|
||||
} else {
|
||||
|
|
|
@ -45,7 +45,6 @@ use std::{
|
|||
mem,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
pub trait Element<V: View>: 'static {
|
||||
type LayoutState;
|
||||
|
@ -709,7 +708,12 @@ impl<V: View> AnyRootElement for RootElement<V> {
|
|||
.ok_or_else(|| anyhow!("debug called on a root element for a dropped view"))?;
|
||||
let view = view.read(cx);
|
||||
let view_context = ViewContext::immutable(cx, self.view.id());
|
||||
Ok(self.element.debug(view, &view_context))
|
||||
Ok(serde_json::json!({
|
||||
"view_id": self.view.id(),
|
||||
"view_name": V::ui_name(),
|
||||
"view": view.debug_json(cx),
|
||||
"element": self.element.debug(view, &view_context)
|
||||
}))
|
||||
}
|
||||
|
||||
fn name(&self) -> Option<&str> {
|
||||
|
@ -717,63 +721,6 @@ impl<V: View> AnyRootElement for RootElement<V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: View, R: View> Element<V> for RootElement<R> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
_view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, ()) {
|
||||
let size = AnyRootElement::layout(self, constraint, cx)
|
||||
.log_err()
|
||||
.unwrap_or_else(|| Vector2F::zero());
|
||||
(size, ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_layout: &mut Self::LayoutState,
|
||||
_view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
AnyRootElement::paint(self, scene, bounds.origin(), visible_bounds, cx).log_err();
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
&self,
|
||||
range_utf16: Range<usize>,
|
||||
_bounds: RectF,
|
||||
_visible_bounds: RectF,
|
||||
_layout: &Self::LayoutState,
|
||||
_paint: &Self::PaintState,
|
||||
_view: &V,
|
||||
cx: &ViewContext<V>,
|
||||
) -> Option<RectF> {
|
||||
AnyRootElement::rect_for_text_range(self, range_utf16, cx)
|
||||
.log_err()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_bounds: RectF,
|
||||
_layout: &Self::LayoutState,
|
||||
_paint: &Self::PaintState,
|
||||
_view: &V,
|
||||
cx: &ViewContext<V>,
|
||||
) -> serde_json::Value {
|
||||
AnyRootElement::debug(self, cx)
|
||||
.log_err()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
|
||||
fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
|
||||
self.extend(children.into_iter().map(|child| child.into_any()));
|
||||
|
|
|
@ -17,6 +17,11 @@ impl KeymapContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.set.clear();
|
||||
self.map.clear();
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, other: &Self) {
|
||||
for v in &other.set {
|
||||
self.set.insert(v.clone());
|
||||
|
|
|
@ -223,41 +223,41 @@ impl HandlerSet {
|
|||
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::move_disc(), None),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::hover_disc(), None),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
for button in MouseButton::all() {
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::drag_disc(), Some(button)),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::down_disc(), Some(button)),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::up_disc(), Some(button)),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::click_disc(), Some(button)),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::down_out_disc(), Some(button)),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::up_out_disc(), Some(button)),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
}
|
||||
set.insert(
|
||||
HandlerKey::new(MouseEvent::scroll_wheel_disc(), None),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| false)]),
|
||||
SmallVec::from_buf([Rc::new(|_, _, _, _| true)]),
|
||||
);
|
||||
|
||||
HandlerSet { set }
|
||||
|
|
|
@ -100,7 +100,7 @@ pub fn run_test(
|
|||
test_fn(cx, foreground_platform.clone(), deterministic.clone(), seed);
|
||||
});
|
||||
|
||||
cx.update(|cx| cx.remove_all_windows());
|
||||
cx.remove_all_windows();
|
||||
deterministic.run_until_parked();
|
||||
cx.update(|cx| cx.clear_globals());
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
|
|||
);
|
||||
));
|
||||
cx_teardowns.extend(quote!(
|
||||
#cx_varname.update(|cx| cx.remove_all_windows());
|
||||
#cx_varname.remove_all_windows();
|
||||
deterministic.run_until_parked();
|
||||
#cx_varname.update(|cx| cx.clear_globals());
|
||||
));
|
||||
|
@ -212,7 +212,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
|
|||
);
|
||||
));
|
||||
cx_teardowns.extend(quote!(
|
||||
#cx_varname.update(|cx| cx.remove_all_windows());
|
||||
#cx_varname.remove_all_windows();
|
||||
deterministic.run_until_parked();
|
||||
#cx_varname.update(|cx| cx.clear_globals());
|
||||
));
|
||||
|
|
|
@ -187,6 +187,7 @@ impl PickerDelegate for OutlineViewDelegate {
|
|||
active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
|
||||
s.select_ranges([position..position])
|
||||
});
|
||||
active_editor.highlight_rows(None);
|
||||
}
|
||||
});
|
||||
cx.emit(PickerEvent::Dismiss);
|
||||
|
|
|
@ -126,10 +126,9 @@ impl<D: PickerDelegate> View for Picker<D> {
|
|||
.into_any_named("picker")
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||
let mut cx = Self::default_keymap_context();
|
||||
cx.add_identifier("menu");
|
||||
cx
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
|
||||
Self::reset_to_default_keymap_context(keymap);
|
||||
keymap.add_identifier("menu");
|
||||
}
|
||||
|
||||
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
|
|
|
@ -1352,10 +1352,9 @@ impl View for ProjectPanel {
|
|||
}
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||
let mut cx = Self::default_keymap_context();
|
||||
cx.add_identifier("menu");
|
||||
cx
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
|
||||
Self::reset_to_default_keymap_context(keymap);
|
||||
keymap.add_identifier("menu");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -454,11 +454,11 @@ impl View for TerminalView {
|
|||
});
|
||||
}
|
||||
|
||||
fn keymap_context(&self, cx: &gpui::AppContext) -> KeymapContext {
|
||||
let mut context = Self::default_keymap_context();
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &gpui::AppContext) {
|
||||
Self::reset_to_default_keymap_context(keymap);
|
||||
|
||||
let mode = self.terminal.read(cx).last_content.mode;
|
||||
context.add_key(
|
||||
keymap.add_key(
|
||||
"screen",
|
||||
if mode.contains(TermMode::ALT_SCREEN) {
|
||||
"alt"
|
||||
|
@ -468,40 +468,40 @@ impl View for TerminalView {
|
|||
);
|
||||
|
||||
if mode.contains(TermMode::APP_CURSOR) {
|
||||
context.add_identifier("DECCKM");
|
||||
keymap.add_identifier("DECCKM");
|
||||
}
|
||||
if mode.contains(TermMode::APP_KEYPAD) {
|
||||
context.add_identifier("DECPAM");
|
||||
keymap.add_identifier("DECPAM");
|
||||
} else {
|
||||
context.add_identifier("DECPNM");
|
||||
keymap.add_identifier("DECPNM");
|
||||
}
|
||||
if mode.contains(TermMode::SHOW_CURSOR) {
|
||||
context.add_identifier("DECTCEM");
|
||||
keymap.add_identifier("DECTCEM");
|
||||
}
|
||||
if mode.contains(TermMode::LINE_WRAP) {
|
||||
context.add_identifier("DECAWM");
|
||||
keymap.add_identifier("DECAWM");
|
||||
}
|
||||
if mode.contains(TermMode::ORIGIN) {
|
||||
context.add_identifier("DECOM");
|
||||
keymap.add_identifier("DECOM");
|
||||
}
|
||||
if mode.contains(TermMode::INSERT) {
|
||||
context.add_identifier("IRM");
|
||||
keymap.add_identifier("IRM");
|
||||
}
|
||||
//LNM is apparently the name for this. https://vt100.net/docs/vt510-rm/LNM.html
|
||||
if mode.contains(TermMode::LINE_FEED_NEW_LINE) {
|
||||
context.add_identifier("LNM");
|
||||
keymap.add_identifier("LNM");
|
||||
}
|
||||
if mode.contains(TermMode::FOCUS_IN_OUT) {
|
||||
context.add_identifier("report_focus");
|
||||
keymap.add_identifier("report_focus");
|
||||
}
|
||||
if mode.contains(TermMode::ALTERNATE_SCROLL) {
|
||||
context.add_identifier("alternate_scroll");
|
||||
keymap.add_identifier("alternate_scroll");
|
||||
}
|
||||
if mode.contains(TermMode::BRACKETED_PASTE) {
|
||||
context.add_identifier("bracketed_paste");
|
||||
keymap.add_identifier("bracketed_paste");
|
||||
}
|
||||
if mode.intersects(TermMode::MOUSE_MODE) {
|
||||
context.add_identifier("any_mouse_reporting");
|
||||
keymap.add_identifier("any_mouse_reporting");
|
||||
}
|
||||
{
|
||||
let mouse_reporting = if mode.contains(TermMode::MOUSE_REPORT_CLICK) {
|
||||
|
@ -513,7 +513,7 @@ impl View for TerminalView {
|
|||
} else {
|
||||
"off"
|
||||
};
|
||||
context.add_key("mouse_reporting", mouse_reporting);
|
||||
keymap.add_key("mouse_reporting", mouse_reporting);
|
||||
}
|
||||
{
|
||||
let format = if mode.contains(TermMode::SGR_MOUSE) {
|
||||
|
@ -523,9 +523,8 @@ impl View for TerminalView {
|
|||
} else {
|
||||
"normal"
|
||||
};
|
||||
context.add_key("mouse_format", format);
|
||||
keymap.add_key("mouse_format", format);
|
||||
}
|
||||
context
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,18 @@ 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| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
vim.update_active_editor(cx, |previously_active_editor, cx| {
|
||||
Vim::unhook_vim_settings(previously_active_editor, cx);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
cx.update_window(editor.window_id(), |cx| {
|
||||
Vim::update(cx, |vim, cx| {
|
||||
vim.update_active_editor(cx, |previously_active_editor, cx| {
|
||||
Vim::unhook_vim_settings(previously_active_editor, cx);
|
||||
});
|
||||
vim.set_active_editor(editor.clone(), cx);
|
||||
});
|
||||
});
|
||||
|
@ -28,9 +35,7 @@ fn blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut AppContext) {
|
|||
}
|
||||
}
|
||||
|
||||
cx.update_window(editor.window_id(), |cx| {
|
||||
editor.update(cx, |editor, cx| Vim::unhook_vim_settings(editor, cx))
|
||||
});
|
||||
editor.update(cx, |editor, cx| Vim::unhook_vim_settings(editor, cx))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -84,7 +84,12 @@ pub fn init(cx: &mut AppContext) {
|
|||
Vim::active_editor_input_ignored("\n".into(), cx)
|
||||
});
|
||||
|
||||
// Any time settings change, update vim mode to match.
|
||||
// Any time settings change, update vim mode to match. The Vim struct
|
||||
// will be initialized as disabled by default, so we filter its commands
|
||||
// out when starting up.
|
||||
cx.update_default_global::<CommandPaletteFilter, _, _>(|filter, _| {
|
||||
filter.filtered_namespaces.insert("vim");
|
||||
});
|
||||
cx.update_default_global(|vim: &mut Vim, cx: &mut AppContext| {
|
||||
vim.set_enabled(cx.global::<Settings>().vim_mode, cx)
|
||||
});
|
||||
|
@ -309,7 +314,7 @@ impl Vim {
|
|||
editor.set_input_enabled(!state.vim_controlled());
|
||||
editor.selections.line_mode = matches!(state.mode, Mode::Visual { line: true });
|
||||
let context_layer = state.keymap_context_layer();
|
||||
editor.set_keymap_context_layer::<Self>(context_layer);
|
||||
editor.set_keymap_context_layer::<Self>(context_layer, cx);
|
||||
} else {
|
||||
Self::unhook_vim_settings(editor, cx);
|
||||
}
|
||||
|
@ -321,7 +326,7 @@ impl Vim {
|
|||
editor.set_clip_at_line_ends(false, cx);
|
||||
editor.set_input_enabled(true);
|
||||
editor.selections.line_mode = false;
|
||||
editor.remove_keymap_context_layer::<Self>();
|
||||
editor.remove_keymap_context_layer::<Self>(cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1854,12 +1854,11 @@ impl View for Pane {
|
|||
});
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||
let mut keymap = Self::default_keymap_context();
|
||||
fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
|
||||
Self::reset_to_default_keymap_context(keymap);
|
||||
if self.docked.is_some() {
|
||||
keymap.add_identifier("docked");
|
||||
}
|
||||
keymap
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ use gpui::{
|
|||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
impl_actions, impl_internal_actions,
|
||||
keymap_matcher::KeymapContext,
|
||||
platform::{
|
||||
CursorStyle, MouseButton, PathPromptOptions, Platform, PromptLevel, WindowBounds,
|
||||
WindowOptions,
|
||||
|
@ -1044,7 +1043,7 @@ impl Workspace {
|
|||
&self.project
|
||||
}
|
||||
|
||||
pub fn client(&self) -> &Client {
|
||||
pub fn client(&self) -> &Arc<Client> {
|
||||
&self.client
|
||||
}
|
||||
|
||||
|
@ -1110,12 +1109,18 @@ impl Workspace {
|
|||
}
|
||||
|
||||
pub fn close_global(_: &CloseWindow, cx: &mut AppContext) {
|
||||
let id = cx.window_ids().find(|&id| cx.window_is_active(id));
|
||||
if let Some(id) = id {
|
||||
//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);
|
||||
}
|
||||
cx.spawn(|mut cx| async move {
|
||||
let id = cx
|
||||
.window_ids()
|
||||
.into_iter()
|
||||
.find(|&id| cx.window_is_active(id));
|
||||
if let Some(id) = id {
|
||||
//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);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn close(
|
||||
|
@ -1140,19 +1145,14 @@ impl Workspace {
|
|||
) -> Task<Result<bool>> {
|
||||
let active_call = self.active_call().cloned();
|
||||
let window_id = cx.window_id();
|
||||
let workspace_count = cx
|
||||
.window_ids()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| {
|
||||
cx.app_context()
|
||||
.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()
|
||||
})
|
||||
.count();
|
||||
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
let workspace_count = cx
|
||||
.window_ids()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| cx.root_view(window_id)?.clone().downcast::<Workspace>())
|
||||
.count();
|
||||
|
||||
if let Some(active_call) = active_call {
|
||||
if !quitting
|
||||
&& workspace_count == 1
|
||||
|
@ -2925,10 +2925,6 @@ impl View for Workspace {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn keymap_context(&self, _: &AppContext) -> KeymapContext {
|
||||
Self::default_keymap_context()
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewId {
|
||||
|
@ -2979,10 +2975,10 @@ impl std::fmt::Debug for OpenPaths {
|
|||
pub struct WorkspaceCreated(WeakViewHandle<Workspace>);
|
||||
|
||||
pub fn activate_workspace_for_project(
|
||||
cx: &mut AppContext,
|
||||
cx: &mut AsyncAppContext,
|
||||
predicate: impl Fn(&mut Project, &mut ModelContext<Project>) -> bool,
|
||||
) -> Option<WeakViewHandle<Workspace>> {
|
||||
for window_id in cx.window_ids().collect::<Vec<_>>() {
|
||||
for window_id in cx.window_ids() {
|
||||
let handle = cx
|
||||
.update_window(window_id, |cx| {
|
||||
if let Some(workspace_handle) = cx.root_view().clone().downcast::<Workspace>() {
|
||||
|
@ -3021,13 +3017,14 @@ pub fn open_paths(
|
|||
> {
|
||||
log::info!("open paths {:?}", abs_paths);
|
||||
|
||||
// Open paths in existing workspace if possible
|
||||
let existing =
|
||||
activate_workspace_for_project(cx, |project, cx| project.contains_paths(abs_paths, cx));
|
||||
|
||||
let app_state = app_state.clone();
|
||||
let abs_paths = abs_paths.to_vec();
|
||||
cx.spawn(|mut cx| async move {
|
||||
// Open paths in existing workspace if possible
|
||||
let existing = activate_workspace_for_project(&mut cx, |project, cx| {
|
||||
project.contains_paths(&abs_paths, cx)
|
||||
});
|
||||
|
||||
if let Some(existing) = existing {
|
||||
Ok((
|
||||
existing.clone(),
|
||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
|||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.84.0"
|
||||
version = "0.84.5"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1 +1 @@
|
|||
dev
|
||||
stable
|
|
@ -11,6 +11,7 @@ use collections::VecDeque;
|
|||
pub use editor;
|
||||
use editor::{Editor, MultiBuffer};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use feedback::{
|
||||
feedback_info_text::FeedbackInfoText, submit_feedback_button::SubmitFeedbackButton,
|
||||
};
|
||||
|
@ -223,9 +224,14 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||
move |_: &mut Workspace, _: &DebugElements, cx: &mut ViewContext<Workspace>| {
|
||||
let app_state = app_state.clone();
|
||||
let markdown = app_state.languages.language_for_name("JSON");
|
||||
let content = to_string_pretty(&cx.debug_elements()).unwrap();
|
||||
let window_id = cx.window_id();
|
||||
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}"))?,
|
||||
)
|
||||
.unwrap();
|
||||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.with_local_workspace(&app_state, cx, move |workspace, cx| {
|
||||
|
@ -303,7 +309,7 @@ pub fn initialize_workspace(
|
|||
cx.emit(workspace::Event::PaneAdded(workspace.dock_pane().clone()));
|
||||
|
||||
let collab_titlebar_item =
|
||||
cx.add_view(|cx| CollabTitlebarItem::new(&workspace_handle, &app_state.user_store, cx));
|
||||
cx.add_view(|cx| CollabTitlebarItem::new(workspace, &workspace_handle, cx));
|
||||
workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx);
|
||||
|
||||
let project_panel = ProjectPanel::new(workspace.project().clone(), cx);
|
||||
|
@ -372,24 +378,25 @@ pub fn build_window_options(
|
|||
}
|
||||
|
||||
fn restart(_: &Restart, cx: &mut gpui::AppContext) {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.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()));
|
||||
|
||||
let should_confirm = cx.global::<Settings>().confirm_quit;
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.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()));
|
||||
|
||||
if let (true, Some(workspace)) = (should_confirm, workspaces.first()) {
|
||||
let answer = cx.prompt(
|
||||
workspace.window_id(),
|
||||
|
@ -424,24 +431,25 @@ fn restart(_: &Restart, cx: &mut gpui::AppContext) {
|
|||
}
|
||||
|
||||
fn quit(_: &Quit, cx: &mut gpui::AppContext) {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.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()));
|
||||
|
||||
let should_confirm = cx.global::<Settings>().confirm_quit;
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
.into_iter()
|
||||
.filter_map(|window_id| {
|
||||
Some(
|
||||
cx.root_view(window_id)?
|
||||
.clone()
|
||||
.downcast::<Workspace>()?
|
||||
.downgrade(),
|
||||
)
|
||||
})
|
||||
.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()));
|
||||
|
||||
if let (true, Some(workspace)) = (should_confirm, workspaces.first()) {
|
||||
let answer = cx.prompt(
|
||||
workspace.window_id(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue