Merge pull request #2441 from zed-industries/implicit-ancestry

Determine view ancestry during layout
This commit is contained in:
Antonio Scandurra 2023-05-05 10:58:00 +02:00 committed by GitHub
commit 0296974ab1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 989 additions and 725 deletions

View file

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

View file

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

View file

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

View file

@ -147,7 +147,7 @@ impl Sidebar {
}
}),
];
cx.reparent(&view);
self.items.push(Item {
icon_path,
tooltip,

View file

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

View file

@ -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(&center_pane, move |this, _, event, cx| {
this.handle_pane_event(pane_id, event, cx)
})
.detach();
cx.subscribe(&center_pane, Self::handle_pane_event).detach();
cx.focus(&center_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());