diff --git a/Cargo.lock b/Cargo.lock
index b6d8e621adb5b3255b5a943a2ea26fab85ac8c5c..6c87bb055f7fcbbbffaa3cbd0f5daa818d32f038 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6934,6 +6934,7 @@ dependencies = [
"client",
"clock",
"collections",
+ "context_menu",
"futures",
"gpui",
"language",
diff --git a/assets/icons/split.svg b/assets/icons/split.svg
new file mode 100644
index 0000000000000000000000000000000000000000..298de950926571bfd64319442c0646370ef150bf
--- /dev/null
+++ b/assets/icons/split.svg
@@ -0,0 +1,3 @@
+
diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs
index 2123aadf4c34e97a8f0348ead2e8f140c4ec7dc0..bb4350d5b66bb30b2caa14a57fdec0d3d45a8771 100644
--- a/crates/context_menu/src/context_menu.rs
+++ b/crates/context_menu/src/context_menu.rs
@@ -277,6 +277,8 @@ impl ContextMenu {
.boxed(),
}
}))
+ .contained()
+ .with_margin_left(style.keystroke_margin)
.boxed(),
)
.contained()
diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs
index c609dcc829b7920df018c5fcd3a38251a44ecea5..7936a9b6bb864f2f6cdc949fe2672af2069bde50 100644
--- a/crates/theme/src/theme.rs
+++ b/crates/theme/src/theme.rs
@@ -242,6 +242,7 @@ pub struct ContextMenu {
#[serde(flatten)]
pub container: ContainerStyle,
pub item: Interactive,
+ pub keystroke_margin: f32,
pub separator: ContainerStyle,
}
diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml
index 81dcad6a128da05357e2d673cae2626f65502422..3534e293f8d86f1ec1efface052e246e76bfc0f6 100644
--- a/crates/workspace/Cargo.toml
+++ b/crates/workspace/Cargo.toml
@@ -14,6 +14,7 @@ test-support = ["client/test-support", "project/test-support", "settings/test-su
client = { path = "../client" }
clock = { path = "../clock" }
collections = { path = "../collections" }
+context_menu = { path = "../context_menu" }
gpui = { path = "../gpui" }
language = { path = "../language" }
menu = { path = "../menu" }
diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs
index bbbaffc530b25cde7ffbbfe1427d8e7c9c034d7b..87b6ea7547d3223dd2669d17d298eb765235a7c1 100644
--- a/crates/workspace/src/pane.rs
+++ b/crates/workspace/src/pane.rs
@@ -2,11 +2,15 @@ use super::{ItemHandle, SplitDirection};
use crate::{toolbar::Toolbar, Item, WeakItemHandle, Workspace};
use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque};
+use context_menu::{ContextMenu, ContextMenuItem};
use futures::StreamExt;
use gpui::{
actions,
elements::*,
- geometry::{rect::RectF, vector::vec2f},
+ geometry::{
+ rect::RectF,
+ vector::{vec2f, Vector2F},
+ },
impl_actions, impl_internal_actions,
platform::{CursorStyle, NavigationDirection},
AppContext, AsyncAppContext, Entity, ModelHandle, MutableAppContext, PromptLevel, Quad,
@@ -55,8 +59,13 @@ pub struct GoForward {
pub pane: Option>,
}
+#[derive(Clone, PartialEq)]
+pub struct DeploySplitMenu {
+ position: Vector2F,
+}
+
impl_actions!(pane, [GoBack, GoForward, ActivateItem]);
-impl_internal_actions!(pane, [CloseItem]);
+impl_internal_actions!(pane, [CloseItem, DeploySplitMenu]);
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
@@ -87,6 +96,7 @@ pub fn init(cx: &mut MutableAppContext) {
cx.add_action(|pane: &mut Pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx));
cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx));
cx.add_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx));
+ cx.add_action(Pane::deploy_split_menu);
cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
Pane::reopen_closed_item(workspace, cx).detach();
});
@@ -129,6 +139,7 @@ pub struct Pane {
autoscroll: bool,
nav_history: Rc>,
toolbar: ViewHandle,
+ split_menu: ViewHandle,
}
pub struct ItemNavHistory {
@@ -169,6 +180,7 @@ pub struct NavigationEntry {
impl Pane {
pub fn new(cx: &mut ViewContext) -> Self {
let handle = cx.weak_handle();
+ let split_menu = cx.add_view(|cx| ContextMenu::new(cx));
Self {
items: Vec::new(),
active_item_index: 0,
@@ -182,6 +194,7 @@ impl Pane {
pane: handle.clone(),
})),
toolbar: cx.add_view(|_| Toolbar::new(handle)),
+ split_menu,
}
}
@@ -786,6 +799,21 @@ impl Pane {
cx.emit(Event::Split(direction));
}
+ fn deploy_split_menu(&mut self, action: &DeploySplitMenu, cx: &mut ViewContext) {
+ self.split_menu.update(cx, |menu, cx| {
+ menu.show(
+ action.position,
+ vec![
+ ContextMenuItem::item("Split Right", SplitRight),
+ ContextMenuItem::item("Split Left", SplitLeft),
+ ContextMenuItem::item("Split Up", SplitUp),
+ ContextMenuItem::item("Split Down", SplitDown),
+ ],
+ cx,
+ );
+ });
+ }
+
pub fn toolbar(&self) -> &ViewHandle {
&self.toolbar
}
@@ -959,51 +987,68 @@ impl View for Pane {
let this = cx.handle();
- EventHandler::new(if let Some(active_item) = self.active_item() {
- Flex::column()
- .with_child(
- Flex::row()
- .with_child(self.render_tabs(cx).flex(1., true).named("tabs"))
+ Stack::new()
+ .with_child(
+ EventHandler::new(if let Some(active_item) = self.active_item() {
+ Flex::column()
.with_child(
- MouseEventHandler::new::(0, cx, |mouse_state, cx| {
- let theme = &cx.global::().theme.workspace;
- let style = theme.pane_button.style_for(mouse_state, false);
- Svg::new("icons/split.svg")
- .with_color(style.color)
- .constrained()
- .with_width(style.icon_width)
- .aligned()
- .contained()
- .with_style(style.container)
- .constrained()
- .with_width(style.button_width)
- .with_height(style.button_width)
- .aligned()
- .boxed()
- })
- .with_cursor_style(CursorStyle::PointingHand)
- .boxed(),
+ Flex::row()
+ .with_child(self.render_tabs(cx).flex(1., true).named("tabs"))
+ .with_child(
+ MouseEventHandler::new::(
+ 0,
+ cx,
+ |mouse_state, cx| {
+ let theme = &cx.global::().theme.workspace;
+ let style =
+ theme.pane_button.style_for(mouse_state, false);
+ Svg::new("icons/split.svg")
+ .with_color(style.color)
+ .constrained()
+ .with_width(style.icon_width)
+ .aligned()
+ .contained()
+ .with_style(style.container)
+ .constrained()
+ .with_width(style.button_width)
+ .with_height(style.button_width)
+ .aligned()
+ .boxed()
+ },
+ )
+ .with_cursor_style(CursorStyle::PointingHand)
+ .on_mouse_down(|position, cx| {
+ cx.dispatch_action(DeploySplitMenu { position });
+ })
+ .boxed(),
+ )
+ .constrained()
+ .with_height(cx.global::().theme.workspace.tab.height)
+ .boxed(),
)
- .constrained()
- .with_height(cx.global::().theme.workspace.tab.height)
- .boxed(),
- )
- .with_child(ChildView::new(&self.toolbar).boxed())
- .with_child(ChildView::new(active_item).flex(1., true).boxed())
- .boxed()
- } else {
- Empty::new().boxed()
- })
- .on_navigate_mouse_down(move |direction, cx| {
- let this = this.clone();
- match direction {
- NavigationDirection::Back => cx.dispatch_action(GoBack { pane: Some(this) }),
- NavigationDirection::Forward => cx.dispatch_action(GoForward { pane: Some(this) }),
- }
+ .with_child(ChildView::new(&self.toolbar).boxed())
+ .with_child(ChildView::new(active_item).flex(1., true).boxed())
+ .boxed()
+ } else {
+ Empty::new().boxed()
+ })
+ .on_navigate_mouse_down(move |direction, cx| {
+ let this = this.clone();
+ match direction {
+ NavigationDirection::Back => {
+ cx.dispatch_action(GoBack { pane: Some(this) })
+ }
+ NavigationDirection::Forward => {
+ cx.dispatch_action(GoForward { pane: Some(this) })
+ }
+ }
- true
- })
- .named("pane")
+ true
+ })
+ .boxed(),
+ )
+ .with_child(ChildView::new(&self.split_menu).boxed())
+ .named("pane")
}
fn on_focus(&mut self, cx: &mut ViewContext) {
diff --git a/styles/src/styleTree/contextMenu.ts b/styles/src/styleTree/contextMenu.ts
index f6197930d49530e4dd463225f3c07a094a85368a..43b3a200b12c327f015b1c13aee70cffdc8da3fc 100644
--- a/styles/src/styleTree/contextMenu.ts
+++ b/styles/src/styleTree/contextMenu.ts
@@ -8,13 +8,14 @@ export default function contextMenu(theme: Theme) {
padding: 6,
shadow: popoverShadow(theme),
border: border(theme, "primary"),
+ keystrokeMargin: 30,
item: {
padding: { left: 4, right: 4, top: 2, bottom: 2 },
cornerRadius: 6,
label: text(theme, "sans", "secondary", { size: "sm" }),
keystroke: {
- margin: { left: 60 },
- ...text(theme, "sans", "muted", { size: "sm", weight: "bold" })
+ ...text(theme, "sans", "muted", { size: "sm", weight: "bold" }),
+ padding: { left: 3, right: 3 }
},
hover: {
background: backgroundColor(theme, 300, "hovered"),