Detailed changes
@@ -502,5 +502,18 @@
"enter": "vim::SearchSubmit",
"escape": "buffer_search::Dismiss"
}
+ },
+ {
+ "context": "Dock",
+ "bindings": {
+ "ctrl-w h": ["workspace::ActivatePaneInDirection", "Left"],
+ "ctrl-w l": ["workspace::ActivatePaneInDirection", "Right"],
+ "ctrl-w k": ["workspace::ActivatePaneInDirection", "Up"],
+ "ctrl-w j": ["workspace::ActivatePaneInDirection", "Down"],
+ "ctrl-w ctrl-h": ["workspace::ActivatePaneInDirection", "Left"],
+ "ctrl-w ctrl-l": ["workspace::ActivatePaneInDirection", "Right"],
+ "ctrl-w ctrl-k": ["workspace::ActivatePaneInDirection", "Up"],
+ "ctrl-w ctrl-j": ["workspace::ActivatePaneInDirection", "Down"]
+ }
}
]
@@ -3,8 +3,9 @@ use crate::DraggedDock;
use crate::{status_bar::StatusItemView, Workspace};
use gpui::{
div, px, Action, AnchorCorner, AnyView, AppContext, Axis, ClickEvent, Entity, EntityId,
- EventEmitter, FocusHandle, FocusableView, IntoElement, MouseButton, ParentElement, Render,
- SharedString, Styled, Subscription, View, ViewContext, VisualContext, WeakView, WindowContext,
+ EventEmitter, FocusHandle, FocusableView, IntoElement, KeyContext, MouseButton, ParentElement,
+ Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WeakView,
+ WindowContext,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -534,10 +535,18 @@ impl Dock {
DockPosition::Right => crate::ToggleRightDock.boxed_clone(),
}
}
+
+ fn dispatch_context() -> KeyContext {
+ let mut dispatch_context = KeyContext::default();
+ dispatch_context.add("Dock");
+
+ dispatch_context
+ }
}
impl Render for Dock {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
+ let dispatch_context = Self::dispatch_context();
if let Some(entry) = self.visible_entry() {
let size = entry.panel.size(cx);
@@ -588,6 +597,7 @@ impl Render for Dock {
}
div()
+ .key_context(dispatch_context)
.track_focus(&self.focus_handle)
.flex()
.bg(cx.theme().colors().panel_background)
@@ -612,7 +622,9 @@ impl Render for Dock {
)
.child(handle)
} else {
- div().track_focus(&self.focus_handle)
+ div()
+ .key_context(dispatch_context)
+ .track_focus(&self.focus_handle)
}
}
}
@@ -2075,30 +2075,93 @@ impl Workspace {
direction: SplitDirection,
cx: &mut WindowContext,
) {
- if let Some(pane) = self.find_pane_in_direction(direction, cx) {
- cx.focus_view(pane);
+ use ActivateInDirectionTarget as Target;
+ enum Origin {
+ LeftDock,
+ RightDock,
+ BottomDock,
+ Center,
}
- }
- pub fn swap_pane_in_direction(
- &mut self,
- direction: SplitDirection,
- cx: &mut ViewContext<Self>,
- ) {
- if let Some(to) = self
- .find_pane_in_direction(direction, cx)
- .map(|pane| pane.clone())
- {
- self.center.swap(&self.active_pane.clone(), &to);
- cx.notify();
+ let origin: Origin = [
+ (&self.left_dock, Origin::LeftDock),
+ (&self.right_dock, Origin::RightDock),
+ (&self.bottom_dock, Origin::BottomDock),
+ ]
+ .into_iter()
+ .find_map(|(dock, origin)| {
+ if dock.focus_handle(cx).contains_focused(cx) && dock.read(cx).is_open() {
+ Some(origin)
+ } else {
+ None
+ }
+ })
+ .unwrap_or(Origin::Center);
+
+ let get_last_active_pane = || {
+ self.last_active_center_pane.as_ref().and_then(|p| {
+ let p = p.upgrade()?;
+ (p.read(cx).items_len() != 0).then_some(p)
+ })
+ };
+
+ let try_dock =
+ |dock: &View<Dock>| dock.read(cx).is_open().then(|| Target::Dock(dock.clone()));
+
+ let target = match (origin, direction) {
+ // We're in the center, so we first try to go to a different pane,
+ // otherwise try to go to a dock.
+ (Origin::Center, direction) => {
+ if let Some(pane) = self.find_pane_in_direction(direction, cx) {
+ Some(Target::Pane(pane))
+ } else {
+ match direction {
+ SplitDirection::Up => None,
+ SplitDirection::Down => try_dock(&self.bottom_dock),
+ SplitDirection::Left => try_dock(&self.left_dock),
+ SplitDirection::Right => try_dock(&self.right_dock),
+ }
+ }
+ }
+
+ (Origin::LeftDock, SplitDirection::Right) => {
+ if let Some(last_active_pane) = get_last_active_pane() {
+ Some(Target::Pane(last_active_pane))
+ } else {
+ try_dock(&self.bottom_dock).or_else(|| try_dock(&self.right_dock))
+ }
+ }
+
+ (Origin::LeftDock, SplitDirection::Down)
+ | (Origin::RightDock, SplitDirection::Down) => try_dock(&self.bottom_dock),
+
+ (Origin::BottomDock, SplitDirection::Up) => get_last_active_pane().map(Target::Pane),
+ (Origin::BottomDock, SplitDirection::Left) => try_dock(&self.left_dock),
+ (Origin::BottomDock, SplitDirection::Right) => try_dock(&self.right_dock),
+
+ (Origin::RightDock, SplitDirection::Left) => {
+ if let Some(last_active_pane) = get_last_active_pane() {
+ Some(Target::Pane(last_active_pane))
+ } else {
+ try_dock(&self.bottom_dock).or_else(|| try_dock(&self.left_dock))
+ }
+ }
+
+ _ => None,
+ };
+
+ match target {
+ Some(ActivateInDirectionTarget::Pane(pane)) => cx.focus_view(&pane),
+ Some(ActivateInDirectionTarget::Dock(dock)) => cx.focus_view(&dock),
+ None => {}
}
}
fn find_pane_in_direction(
&mut self,
direction: SplitDirection,
- cx: &AppContext,
- ) -> Option<&View<Pane>> {
+ cx: &WindowContext,
+ ) -> Option<View<Pane>> {
let Some(bounding_box) = self.center.bounding_box_for_pane(&self.active_pane) else {
return None;
};
@@ -2124,7 +2187,21 @@ impl Workspace {
Point::new(center.x, bounding_box.bottom() + distance_to_next.into())
}
};
- self.center.pane_at_pixel_position(target)
+ self.center.pane_at_pixel_position(target).cloned()
+ }
+
+ pub fn swap_pane_in_direction(
+ &mut self,
+ direction: SplitDirection,
+ cx: &mut ViewContext<Self>,
+ ) {
+ if let Some(to) = self
+ .find_pane_in_direction(direction, cx)
+ .map(|pane| pane.clone())
+ {
+ self.center.swap(&self.active_pane.clone(), &to);
+ cx.notify();
+ }
}
fn handle_pane_focused(&mut self, pane: View<Pane>, cx: &mut ViewContext<Self>) {
@@ -3488,6 +3565,11 @@ fn open_items(
})
}
+enum ActivateInDirectionTarget {
+ Pane(View<Pane>),
+ Dock(View<Dock>),
+}
+
fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncAppContext) {
const REPORT_ISSUE_URL: &str ="https://github.com/zed-industries/zed/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml";