Merge pull request #1740 from zed-industries/fix-dock-focus-issues

Fix Dock infinite loop
This commit is contained in:
Kay Simmons 2022-10-12 16:19:09 -07:00 committed by GitHub
commit d42bf8eebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 641 additions and 462 deletions

955
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -618,7 +618,6 @@ pub struct MutableAppContext {
HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
foreground: Rc<executor::Foreground>,
pending_effects: VecDeque<Effect>,
pending_focus_index: Option<usize>,
pending_notifications: HashSet<usize>,
pending_global_notifications: HashSet<TypeId>,
pending_flushes: usize,
@ -673,7 +672,6 @@ impl MutableAppContext {
presenters_and_platform_windows: Default::default(),
foreground,
pending_effects: VecDeque::new(),
pending_focus_index: None,
pending_notifications: Default::default(),
pending_global_notifications: Default::default(),
pending_flushes: 0,
@ -1876,9 +1874,6 @@ impl MutableAppContext {
let mut refreshing = false;
loop {
if let Some(effect) = self.pending_effects.pop_front() {
if let Some(pending_focus_index) = self.pending_focus_index.as_mut() {
*pending_focus_index = pending_focus_index.saturating_sub(1);
}
match effect {
Effect::Subscription {
entity_id,
@ -2259,8 +2254,6 @@ impl MutableAppContext {
}
fn handle_focus_effect(&mut self, window_id: usize, focused_id: Option<usize>) {
self.pending_focus_index.take();
if self
.cx
.windows
@ -2383,10 +2376,6 @@ impl MutableAppContext {
}
pub fn focus(&mut self, window_id: usize, view_id: Option<usize>) {
if let Some(pending_focus_index) = self.pending_focus_index {
self.pending_effects.remove(pending_focus_index);
}
self.pending_focus_index = Some(self.pending_effects.len());
self.pending_effects
.push_back(Effect::Focus { window_id, view_id });
}
@ -3465,6 +3454,15 @@ impl<'a, T: View> ViewContext<'a, T> {
self.app.focused_view_id(self.window_id) == Some(self.view_id)
}
pub fn is_child(&self, view: impl Into<AnyViewHandle>) -> bool {
let view = view.into();
if self.window_id != view.window_id {
return false;
}
self.parents(view.window_id, view.view_id)
.any(|parent| parent == self.view_id)
}
pub fn blur(&mut self) {
self.app.focus(self.window_id, None);
}
@ -6378,18 +6376,29 @@ mod tests {
assert_eq!(mem::take(&mut *observed_events.lock()), Vec::<&str>::new());
view_1.update(cx, |_, cx| {
// Ensure only the latest focus is honored.
// Ensure focus events are sent for all intermediate focuses
cx.focus(&view_2);
cx.focus(&view_1);
cx.focus(&view_2);
});
assert_eq!(
mem::take(&mut *view_events.lock()),
["view 1 blurred", "view 2 focused"],
[
"view 1 blurred",
"view 2 focused",
"view 2 blurred",
"view 1 focused",
"view 1 blurred",
"view 2 focused"
],
);
assert_eq!(
mem::take(&mut *observed_events.lock()),
[
"view 2 observed view 1's blur",
"view 1 observed view 2's focus",
"view 1 observed view 2's blur",
"view 2 observed view 1's focus",
"view 2 observed view 1's blur",
"view 1 observed view 2's focus"
]

View file

@ -200,6 +200,10 @@ impl View for ProjectSearchView {
.0
.insert(self.model.read(cx).project.downgrade(), handle)
});
if cx.is_self_focused() {
self.focus_query_editor(cx);
}
}
}

View file

@ -170,7 +170,11 @@ impl Dock {
} else {
cx.focus(pane);
}
} else if let Some(last_active_center_pane) = workspace.last_active_center_pane.clone() {
} else if let Some(last_active_center_pane) = workspace
.last_active_center_pane
.as_ref()
.and_then(|pane| pane.upgrade(cx))
{
cx.focus(last_active_center_pane);
}
cx.emit(crate::Event::DockAnchorChanged);
@ -583,10 +587,11 @@ mod tests {
}
pub fn center_pane_handle(&self) -> ViewHandle<Pane> {
self.workspace(|workspace, _| {
self.workspace(|workspace, cx| {
workspace
.last_active_center_pane
.clone()
.and_then(|pane| pane.upgrade(cx))
.unwrap_or_else(|| workspace.center.panes()[0].clone())
})
}
@ -597,6 +602,7 @@ mod tests {
let pane = workspace
.last_active_center_pane
.clone()
.and_then(|pane| pane.upgrade(cx))
.unwrap_or_else(|| workspace.center.panes()[0].clone());
Pane::add_item(
workspace,

View file

@ -112,10 +112,10 @@ pub fn init(cx: &mut MutableAppContext) {
pane.activate_item(pane.items.len() - 1, true, true, cx);
});
cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
pane.activate_prev_item(cx);
pane.activate_prev_item(true, cx);
});
cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| {
pane.activate_next_item(cx);
pane.activate_next_item(true, cx);
});
cx.add_async_action(Pane::close_active_item);
cx.add_async_action(Pane::close_inactive_items);
@ -189,7 +189,6 @@ pub fn init(cx: &mut MutableAppContext) {
#[derive(Debug)]
pub enum Event {
Focused,
ActivateItem { local: bool },
Remove,
RemoveItem { item_id: usize },
@ -201,7 +200,7 @@ pub struct Pane {
items: Vec<Box<dyn ItemHandle>>,
is_active: bool,
active_item_index: usize,
last_focused_view: Option<AnyWeakViewHandle>,
last_focused_view_by_item: HashMap<usize, AnyWeakViewHandle>,
autoscroll: bool,
nav_history: Rc<RefCell<NavHistory>>,
toolbar: ViewHandle<Toolbar>,
@ -263,7 +262,7 @@ impl Pane {
items: Vec::new(),
is_active: true,
active_item_index: 0,
last_focused_view: None,
last_focused_view_by_item: Default::default(),
autoscroll: false,
nav_history: Rc::new(RefCell::new(NavHistory {
mode: NavigationMode::Normal,
@ -632,32 +631,29 @@ impl Pane {
if focus_item {
self.focus_active_item(cx);
}
if activate_pane {
cx.emit(Event::Focused);
}
self.autoscroll = true;
cx.notify();
}
}
pub fn activate_prev_item(&mut self, cx: &mut ViewContext<Self>) {
pub fn activate_prev_item(&mut self, activate_pane: bool, cx: &mut ViewContext<Self>) {
let mut index = self.active_item_index;
if index > 0 {
index -= 1;
} else if !self.items.is_empty() {
index = self.items.len() - 1;
}
self.activate_item(index, true, true, cx);
self.activate_item(index, activate_pane, activate_pane, cx);
}
pub fn activate_next_item(&mut self, cx: &mut ViewContext<Self>) {
pub fn activate_next_item(&mut self, activate_pane: bool, cx: &mut ViewContext<Self>) {
let mut index = self.active_item_index;
if index + 1 < self.items.len() {
index += 1;
} else {
index = 0;
}
self.activate_item(index, true, true, cx);
self.activate_item(index, activate_pane, activate_pane, cx);
}
pub fn close_active_item(
@ -784,7 +780,7 @@ impl Pane {
// Remove the item from the pane.
pane.update(&mut cx, |pane, cx| {
if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) {
pane.remove_item(item_ix, cx);
pane.remove_item(item_ix, false, cx);
}
});
}
@ -794,15 +790,15 @@ impl Pane {
})
}
fn remove_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) {
fn remove_item(&mut self, item_ix: usize, activate_pane: bool, cx: &mut ViewContext<Self>) {
if item_ix == self.active_item_index {
// Activate the previous item if possible.
// This returns the user to the previously opened tab if they closed
// a new item they just navigated to.
if item_ix > 0 {
self.activate_prev_item(cx);
self.activate_prev_item(activate_pane, cx);
} else if item_ix + 1 < self.items.len() {
self.activate_next_item(cx);
self.activate_next_item(activate_pane, cx);
}
}
@ -965,26 +961,27 @@ impl Pane {
log::warn!("Tried to move item handle which was not in `from` pane. Maybe tab was closed during drop");
return;
}
let (item_ix, item_handle) = item_to_move.unwrap();
let item_handle = item_handle.clone();
if from != to {
// Close item from previous pane
from.update(cx, |from, cx| {
from.remove_item(item_ix, false, cx);
});
}
// This automatically removes duplicate items in the pane
Pane::add_item(
workspace,
&to,
item_handle.clone(),
item_handle,
true,
true,
Some(destination_index),
cx,
);
if from != to {
// Close item from previous pane
from.update(cx, |from, cx| {
from.remove_item(item_ix, cx);
});
}
cx.focus(to);
}
@ -1488,21 +1485,27 @@ impl View for Pane {
}
fn on_focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
if let Some(active_item) = self.active_item() {
if cx.is_self_focused() {
if let Some(last_focused_view) = self
.last_focused_view
.as_ref()
.and_then(|handle| handle.upgrade(cx))
.filter(|handle| handle.id() != self.tab_bar_context_menu.id())
// Pane was focused directly. We need to either focus a view inside the active item,
// or focus the active item itself
if let Some(weak_last_focused_view) =
self.last_focused_view_by_item.get(&active_item.id())
{
if let Some(last_focused_view) = weak_last_focused_view.upgrade(cx) {
cx.focus(last_focused_view);
return;
} else {
self.focus_active_item(cx);
self.last_focused_view_by_item.remove(&active_item.id());
}
}
cx.focus(active_item);
} else {
self.last_focused_view = Some(focused.downgrade());
self.last_focused_view_by_item
.insert(active_item.id(), focused.downgrade());
}
}
cx.emit(Event::Focused);
}
}

View file

@ -1,5 +1,4 @@
/// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in
/// which the workspace uses to change the activated pane.
/// NOTE: Focus only 'takes' after an update has flushed_effects.
///
/// This may cause issues when you're trying to write tests that use workspace focus to add items at
/// specific locations.
@ -971,7 +970,7 @@ pub struct Workspace {
panes: Vec<ViewHandle<Pane>>,
panes_by_item: HashMap<usize, WeakViewHandle<Pane>>,
active_pane: ViewHandle<Pane>,
last_active_center_pane: Option<ViewHandle<Pane>>,
last_active_center_pane: Option<WeakViewHandle<Pane>>,
status_bar: ViewHandle<StatusBar>,
titlebar_item: Option<AnyViewHandle>,
dock: Dock,
@ -1111,7 +1110,7 @@ impl Workspace {
panes: vec![dock_pane, center_pane.clone()],
panes_by_item: Default::default(),
active_pane: center_pane.clone(),
last_active_center_pane: Some(center_pane.clone()),
last_active_center_pane: Some(center_pane.downgrade()),
status_bar,
titlebar_item: None,
notifications: Default::default(),
@ -1850,7 +1849,7 @@ impl Workspace {
if &pane == self.dock_pane() {
Dock::show(self, cx);
} else {
self.last_active_center_pane = Some(pane.clone());
self.last_active_center_pane = Some(pane.downgrade());
if self.dock.is_anchored_at(DockAnchor::Expanded) {
Dock::hide(self, cx);
}
@ -1881,7 +1880,6 @@ impl Workspace {
}
pane::Event::Remove if !is_dock => self.remove_pane(pane, cx),
pane::Event::Remove if is_dock => Dock::hide(self, cx),
pane::Event::Focused => self.handle_pane_focused(pane, cx),
pane::Event::ActivateItem { local } => {
if *local {
self.unfollow(&pane, cx);
@ -1942,7 +1940,7 @@ impl Workspace {
for removed_item in pane.read(cx).items() {
self.panes_by_item.remove(&removed_item.id());
}
if self.last_active_center_pane == Some(pane) {
if self.last_active_center_pane == Some(pane.downgrade()) {
self.last_active_center_pane = None;
}
@ -2652,9 +2650,17 @@ impl View for Workspace {
.named("workspace")
}
fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
fn on_focus_in(&mut self, view: 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| cx.is_child(view)) {
self.handle_pane_focused(pane.clone(), cx);
break;
}
}
}
}