Merge pull request #2441 from zed-industries/implicit-ancestry
Determine view ancestry during layout
This commit is contained in:
commit
0296974ab1
54 changed files with 989 additions and 725 deletions
|
@ -178,11 +178,7 @@ impl Dock {
|
|||
pane.update(cx, |pane, cx| {
|
||||
pane.set_active(false, cx);
|
||||
});
|
||||
let pane_id = pane.id();
|
||||
cx.subscribe(&pane, move |workspace, _, event, cx| {
|
||||
workspace.handle_pane_event(pane_id, event, cx);
|
||||
})
|
||||
.detach();
|
||||
cx.subscribe(&pane, Workspace::handle_pane_event).detach();
|
||||
|
||||
Self {
|
||||
pane,
|
||||
|
@ -730,7 +726,7 @@ mod tests {
|
|||
self.update_workspace(|workspace, cx| Dock::move_dock(workspace, anchor, true, cx));
|
||||
}
|
||||
|
||||
pub fn hide_dock(&self) {
|
||||
pub fn hide_dock(&mut self) {
|
||||
self.cx.dispatch_action(self.window_id, HideDock);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ use gpui::{
|
|||
keymap_matcher::KeymapContext,
|
||||
platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel},
|
||||
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
||||
ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
WindowContext,
|
||||
LayoutContext, ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle,
|
||||
WeakViewHandle, WindowContext,
|
||||
};
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
use serde::Deserialize;
|
||||
|
@ -134,6 +134,7 @@ pub enum Event {
|
|||
RemoveItem { item_id: usize },
|
||||
Split(SplitDirection),
|
||||
ChangeItemTitle,
|
||||
Focus,
|
||||
}
|
||||
|
||||
pub struct Pane {
|
||||
|
@ -150,6 +151,7 @@ pub struct Pane {
|
|||
docked: Option<DockAnchor>,
|
||||
_background_actions: BackgroundActions,
|
||||
workspace: WeakViewHandle<Workspace>,
|
||||
has_focus: bool,
|
||||
}
|
||||
|
||||
pub struct ItemNavHistory {
|
||||
|
@ -226,8 +228,9 @@ impl Pane {
|
|||
background_actions: BackgroundActions,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let pane_view_id = cx.view_id();
|
||||
let handle = cx.weak_handle();
|
||||
let context_menu = cx.add_view(ContextMenu::new);
|
||||
let context_menu = cx.add_view(|cx| ContextMenu::new(pane_view_id, cx));
|
||||
context_menu.update(cx, |menu, _| {
|
||||
menu.set_position_mode(OverlayPositionMode::Local)
|
||||
});
|
||||
|
@ -252,10 +255,11 @@ impl Pane {
|
|||
kind: TabBarContextMenuKind::New,
|
||||
handle: context_menu,
|
||||
},
|
||||
tab_context_menu: cx.add_view(ContextMenu::new),
|
||||
tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)),
|
||||
docked,
|
||||
_background_actions: background_actions,
|
||||
workspace,
|
||||
has_focus: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,6 +276,10 @@ impl Pane {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn has_focus(&self) -> bool {
|
||||
self.has_focus
|
||||
}
|
||||
|
||||
pub fn set_docked(&mut self, docked: Option<DockAnchor>, cx: &mut ViewContext<Self>) {
|
||||
self.docked = docked;
|
||||
cx.notify();
|
||||
|
@ -537,7 +545,6 @@ impl Pane {
|
|||
// If the item already exists, move it to the desired destination and activate it
|
||||
pane.update(cx, |pane, cx| {
|
||||
if existing_item_index != insertion_index {
|
||||
cx.reparent(item.as_any());
|
||||
let existing_item_is_active = existing_item_index == pane.active_item_index;
|
||||
|
||||
// If the caller didn't specify a destination and the added item is already
|
||||
|
@ -567,7 +574,6 @@ impl Pane {
|
|||
});
|
||||
} else {
|
||||
pane.update(cx, |pane, cx| {
|
||||
cx.reparent(item.as_any());
|
||||
pane.items.insert(insertion_index, item);
|
||||
if insertion_index <= pane.active_item_index {
|
||||
pane.active_item_index += 1;
|
||||
|
@ -1764,7 +1770,7 @@ impl View for Pane {
|
|||
self.render_blank_pane(&theme, cx)
|
||||
})
|
||||
.on_down(MouseButton::Left, |_, _, cx| {
|
||||
cx.focus_parent_view();
|
||||
cx.focus_parent();
|
||||
})
|
||||
.into_any()
|
||||
}
|
||||
|
@ -1798,6 +1804,7 @@ impl View for Pane {
|
|||
}
|
||||
|
||||
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
self.has_focus = true;
|
||||
self.toolbar.update(cx, |toolbar, cx| {
|
||||
toolbar.pane_focus_update(true, cx);
|
||||
});
|
||||
|
@ -1823,9 +1830,12 @@ impl View for Pane {
|
|||
.insert(active_item.id(), focused.downgrade());
|
||||
}
|
||||
}
|
||||
|
||||
cx.emit(Event::Focus);
|
||||
}
|
||||
|
||||
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
self.has_focus = false;
|
||||
self.toolbar.update(cx, |toolbar, cx| {
|
||||
toolbar.pane_focus_update(false, cx);
|
||||
});
|
||||
|
@ -1998,7 +2008,7 @@ impl<V: View> Element<V> for PaneBackdrop<V> {
|
|||
&mut self,
|
||||
constraint: gpui::SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut LayoutContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let size = self.child.layout(constraint, view, cx);
|
||||
(size, ())
|
||||
|
|
|
@ -90,7 +90,7 @@ impl View for SharedScreen {
|
|||
.contained()
|
||||
.with_style(cx.global::<Settings>().theme.shared_screen)
|
||||
})
|
||||
.on_down(MouseButton::Left, |_, _, cx| cx.focus_parent_view())
|
||||
.on_down(MouseButton::Left, |_, _, cx| cx.focus_parent())
|
||||
.into_any()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ impl Sidebar {
|
|||
}
|
||||
}),
|
||||
];
|
||||
cx.reparent(&view);
|
||||
|
||||
self.items.push(Item {
|
||||
icon_path,
|
||||
tooltip,
|
||||
|
|
|
@ -8,8 +8,8 @@ use gpui::{
|
|||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
AnyElement, AnyViewHandle, Entity, SceneBuilder, SizeConstraint, Subscription, View,
|
||||
ViewContext, ViewHandle, WindowContext,
|
||||
AnyElement, AnyViewHandle, Entity, LayoutContext, SceneBuilder, SizeConstraint, Subscription,
|
||||
View, ViewContext, ViewHandle, WindowContext,
|
||||
};
|
||||
use settings::Settings;
|
||||
|
||||
|
@ -93,7 +93,6 @@ impl StatusBar {
|
|||
where
|
||||
T: 'static + StatusItemView,
|
||||
{
|
||||
cx.reparent(item.as_any());
|
||||
self.left_items.push(Box::new(item));
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -102,7 +101,6 @@ impl StatusBar {
|
|||
where
|
||||
T: 'static + StatusItemView,
|
||||
{
|
||||
cx.reparent(item.as_any());
|
||||
self.right_items.push(Box::new(item));
|
||||
cx.notify();
|
||||
}
|
||||
|
@ -157,7 +155,7 @@ impl Element<StatusBar> for StatusBarElement {
|
|||
&mut self,
|
||||
mut constraint: SizeConstraint,
|
||||
view: &mut StatusBar,
|
||||
cx: &mut ViewContext<StatusBar>,
|
||||
cx: &mut LayoutContext<StatusBar>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let max_width = constraint.max.x();
|
||||
constraint.min = vec2f(0., constraint.min.y());
|
||||
|
|
|
@ -63,7 +63,7 @@ use crate::{
|
|||
persistence::model::{SerializedPane, SerializedPaneGroup, SerializedWorkspace},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use log::{error, warn};
|
||||
use log::warn;
|
||||
use notifications::{NotificationHandle, NotifyResultExt};
|
||||
pub use pane::*;
|
||||
pub use pane_group::*;
|
||||
|
@ -536,11 +536,7 @@ impl Workspace {
|
|||
|
||||
let center_pane = cx
|
||||
.add_view(|cx| Pane::new(weak_handle.clone(), None, app_state.background_actions, cx));
|
||||
let pane_id = center_pane.id();
|
||||
cx.subscribe(¢er_pane, move |this, _, event, cx| {
|
||||
this.handle_pane_event(pane_id, event, cx)
|
||||
})
|
||||
.detach();
|
||||
cx.subscribe(¢er_pane, Self::handle_pane_event).detach();
|
||||
cx.focus(¢er_pane);
|
||||
cx.emit(Event::PaneAdded(center_pane.clone()));
|
||||
let dock = Dock::new(
|
||||
|
@ -1433,11 +1429,7 @@ impl Workspace {
|
|||
cx,
|
||||
)
|
||||
});
|
||||
let pane_id = pane.id();
|
||||
cx.subscribe(&pane, move |this, _, event, cx| {
|
||||
this.handle_pane_event(pane_id, event, cx)
|
||||
})
|
||||
.detach();
|
||||
cx.subscribe(&pane, Self::handle_pane_event).detach();
|
||||
self.panes.push(pane.clone());
|
||||
cx.focus(&pane);
|
||||
cx.emit(Event::PaneAdded(pane.clone()));
|
||||
|
@ -1634,47 +1626,46 @@ impl Workspace {
|
|||
|
||||
fn handle_pane_event(
|
||||
&mut self,
|
||||
pane_id: usize,
|
||||
pane: ViewHandle<Pane>,
|
||||
event: &pane::Event,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if let Some(pane) = self.pane(pane_id) {
|
||||
let is_dock = &pane == self.dock.pane();
|
||||
match event {
|
||||
pane::Event::Split(direction) if !is_dock => {
|
||||
self.split_pane(pane, *direction, cx);
|
||||
}
|
||||
pane::Event::Remove if !is_dock => self.remove_pane(pane, cx),
|
||||
pane::Event::Remove if is_dock => Dock::hide(self, cx),
|
||||
pane::Event::ActivateItem { local } => {
|
||||
if *local {
|
||||
self.unfollow(&pane, cx);
|
||||
}
|
||||
if &pane == self.active_pane() {
|
||||
self.active_item_path_changed(cx);
|
||||
}
|
||||
}
|
||||
pane::Event::ChangeItemTitle => {
|
||||
if pane == self.active_pane {
|
||||
self.active_item_path_changed(cx);
|
||||
}
|
||||
self.update_window_edited(cx);
|
||||
}
|
||||
pane::Event::RemoveItem { item_id } => {
|
||||
self.update_window_edited(cx);
|
||||
if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) {
|
||||
if entry.get().id() == pane.id() {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
let is_dock = &pane == self.dock.pane();
|
||||
match event {
|
||||
pane::Event::Split(direction) if !is_dock => {
|
||||
self.split_pane(pane, *direction, cx);
|
||||
}
|
||||
|
||||
self.serialize_workspace(cx);
|
||||
} else if self.dock.visible_pane().is_none() {
|
||||
error!("pane {} not found", pane_id);
|
||||
pane::Event::Remove if !is_dock => self.remove_pane(pane, cx),
|
||||
pane::Event::Remove if is_dock => Dock::hide(self, cx),
|
||||
pane::Event::ActivateItem { local } => {
|
||||
if *local {
|
||||
self.unfollow(&pane, cx);
|
||||
}
|
||||
if &pane == self.active_pane() {
|
||||
self.active_item_path_changed(cx);
|
||||
}
|
||||
}
|
||||
pane::Event::ChangeItemTitle => {
|
||||
if pane == self.active_pane {
|
||||
self.active_item_path_changed(cx);
|
||||
}
|
||||
self.update_window_edited(cx);
|
||||
}
|
||||
pane::Event::RemoveItem { item_id } => {
|
||||
self.update_window_edited(cx);
|
||||
if let hash_map::Entry::Occupied(entry) = self.panes_by_item.entry(*item_id) {
|
||||
if entry.get().id() == pane.id() {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
pane::Event::Focus => {
|
||||
self.handle_pane_focused(pane.clone(), cx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.serialize_workspace(cx);
|
||||
}
|
||||
|
||||
pub fn split_pane(
|
||||
|
@ -1773,10 +1764,6 @@ impl Workspace {
|
|||
&self.panes
|
||||
}
|
||||
|
||||
fn pane(&self, pane_id: usize) -> Option<ViewHandle<Pane>> {
|
||||
self.panes.iter().find(|pane| pane.id() == pane_id).cloned()
|
||||
}
|
||||
|
||||
pub fn active_pane(&self) -> &ViewHandle<Pane> {
|
||||
&self.active_pane
|
||||
}
|
||||
|
@ -2365,19 +2352,14 @@ impl Workspace {
|
|||
}
|
||||
|
||||
for (pane, item) in items_to_activate {
|
||||
let active_item_was_focused = pane
|
||||
.read(cx)
|
||||
.active_item()
|
||||
.map(|active_item| cx.is_child_focused(active_item.as_any()))
|
||||
.unwrap_or_default();
|
||||
|
||||
let pane_was_focused = pane.read(cx).has_focus();
|
||||
if let Some(index) = pane.update(cx, |pane, _| pane.index_for_item(item.as_ref())) {
|
||||
pane.update(cx, |pane, cx| pane.activate_item(index, false, false, cx));
|
||||
} else {
|
||||
Pane::add_item(self, &pane, item.boxed_clone(), false, false, None, cx);
|
||||
}
|
||||
|
||||
if active_item_was_focused {
|
||||
if pane_was_focused {
|
||||
pane.update(cx, |pane, cx| pane.focus_active_item(cx));
|
||||
}
|
||||
}
|
||||
|
@ -2796,17 +2778,9 @@ impl View for Workspace {
|
|||
.into_any_named("workspace")
|
||||
}
|
||||
|
||||
fn focus_in(&mut self, view: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
if cx.is_self_focused() {
|
||||
cx.focus(&self.active_pane);
|
||||
} else {
|
||||
for pane in self.panes() {
|
||||
let view = view.clone();
|
||||
if pane.update(cx, |_, cx| view.id() == cx.view_id() || cx.is_child(view)) {
|
||||
self.handle_pane_focused(pane.clone(), cx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3154,10 +3128,10 @@ mod tests {
|
|||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
let project = Project::test(fs, [], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
|
||||
// Adding an item with no ambiguity renders the tab without detail.
|
||||
let item1 = cx.add_view(&workspace, |_| {
|
||||
let item1 = cx.add_view(window_id, |_| {
|
||||
let mut item = TestItem::new();
|
||||
item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
|
||||
item
|
||||
|
@ -3169,7 +3143,7 @@ mod tests {
|
|||
|
||||
// Adding an item that creates ambiguity increases the level of detail on
|
||||
// both tabs.
|
||||
let item2 = cx.add_view(&workspace, |_| {
|
||||
let item2 = cx.add_view(window_id, |_| {
|
||||
let mut item = TestItem::new();
|
||||
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
||||
item
|
||||
|
@ -3183,7 +3157,7 @@ mod tests {
|
|||
// Adding an item that creates ambiguity increases the level of detail only
|
||||
// on the ambiguous tabs. In this case, the ambiguity can't be resolved so
|
||||
// we stop at the highest detail available.
|
||||
let item3 = cx.add_view(&workspace, |_| {
|
||||
let item3 = cx.add_view(window_id, |_| {
|
||||
let mut item = TestItem::new();
|
||||
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
||||
item
|
||||
|
@ -3223,10 +3197,10 @@ mod tests {
|
|||
project.worktrees(cx).next().unwrap().read(cx).id()
|
||||
});
|
||||
|
||||
let item1 = cx.add_view(&workspace, |cx| {
|
||||
let item1 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
|
||||
});
|
||||
let item2 = cx.add_view(&workspace, |cx| {
|
||||
let item2 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)])
|
||||
});
|
||||
|
||||
|
@ -3311,15 +3285,15 @@ mod tests {
|
|||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
|
||||
// When there are no dirty items, there's nothing to do.
|
||||
let item1 = cx.add_view(&workspace, |_| TestItem::new());
|
||||
let item1 = cx.add_view(window_id, |_| TestItem::new());
|
||||
workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
|
||||
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
|
||||
assert!(task.await.unwrap());
|
||||
|
||||
// When there are dirty untitled items, prompt to save each one. If the user
|
||||
// cancels any prompt, then abort.
|
||||
let item2 = cx.add_view(&workspace, |_| TestItem::new().with_dirty(true));
|
||||
let item3 = cx.add_view(&workspace, |cx| {
|
||||
let item2 = cx.add_view(window_id, |_| TestItem::new().with_dirty(true));
|
||||
let item3 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new()
|
||||
.with_dirty(true)
|
||||
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||
|
@ -3345,24 +3319,24 @@ mod tests {
|
|||
let project = Project::test(fs, None, cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
|
||||
let item1 = cx.add_view(&workspace, |cx| {
|
||||
let item1 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new()
|
||||
.with_dirty(true)
|
||||
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||
});
|
||||
let item2 = cx.add_view(&workspace, |cx| {
|
||||
let item2 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new()
|
||||
.with_dirty(true)
|
||||
.with_conflict(true)
|
||||
.with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
|
||||
});
|
||||
let item3 = cx.add_view(&workspace, |cx| {
|
||||
let item3 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new()
|
||||
.with_dirty(true)
|
||||
.with_conflict(true)
|
||||
.with_project_items(&[TestProjectItem::new(3, "3.txt", cx)])
|
||||
});
|
||||
let item4 = cx.add_view(&workspace, |cx| {
|
||||
let item4 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new()
|
||||
.with_dirty(true)
|
||||
.with_project_items(&[TestProjectItem::new_untitled(cx)])
|
||||
|
@ -3456,7 +3430,7 @@ mod tests {
|
|||
// workspace items with multiple project entries.
|
||||
let single_entry_items = (0..=4)
|
||||
.map(|project_entry_id| {
|
||||
cx.add_view(&workspace, |cx| {
|
||||
cx.add_view(window_id, |cx| {
|
||||
TestItem::new()
|
||||
.with_dirty(true)
|
||||
.with_project_items(&[TestProjectItem::new(
|
||||
|
@ -3467,7 +3441,7 @@ mod tests {
|
|||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let item_2_3 = cx.add_view(&workspace, |cx| {
|
||||
let item_2_3 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new()
|
||||
.with_dirty(true)
|
||||
.with_singleton(false)
|
||||
|
@ -3476,7 +3450,7 @@ mod tests {
|
|||
single_entry_items[3].read(cx).project_items[0].clone(),
|
||||
])
|
||||
});
|
||||
let item_3_4 = cx.add_view(&workspace, |cx| {
|
||||
let item_3_4 = cx.add_view(window_id, |cx| {
|
||||
TestItem::new()
|
||||
.with_dirty(true)
|
||||
.with_singleton(false)
|
||||
|
@ -3559,7 +3533,7 @@ mod tests {
|
|||
let project = Project::test(fs, [], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
|
||||
let item = cx.add_view(&workspace, |cx| {
|
||||
let item = cx.add_view(window_id, |cx| {
|
||||
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||
});
|
||||
let item_id = item.id();
|
||||
|
@ -3674,9 +3648,9 @@ mod tests {
|
|||
let fs = FakeFs::new(cx.background());
|
||||
|
||||
let project = Project::test(fs, [], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
|
||||
let item = cx.add_view(&workspace, |cx| {
|
||||
let item = cx.add_view(window_id, |cx| {
|
||||
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||
});
|
||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue