Add pane splitting by dragged item. Works, but the overlay doesn't clear quite right
This commit is contained in:
parent
70e2951e35
commit
cfde3e348c
12 changed files with 308 additions and 280 deletions
|
@ -21,6 +21,7 @@ use std::{
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
use postage::oneshot;
|
use postage::oneshot;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use smol::prelude::*;
|
use smol::prelude::*;
|
||||||
|
@ -939,6 +940,7 @@ impl MutableAppContext {
|
||||||
window_id,
|
window_id,
|
||||||
view_id,
|
view_id,
|
||||||
titlebar_height,
|
titlebar_height,
|
||||||
|
mouse_position: Default::default(),
|
||||||
hovered_region_ids: Default::default(),
|
hovered_region_ids: Default::default(),
|
||||||
clicked_region_ids: None,
|
clicked_region_ids: None,
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
|
@ -3895,6 +3897,7 @@ pub struct RenderParams {
|
||||||
pub window_id: usize,
|
pub window_id: usize,
|
||||||
pub view_id: usize,
|
pub view_id: usize,
|
||||||
pub titlebar_height: f32,
|
pub titlebar_height: f32,
|
||||||
|
pub mouse_position: Vector2F,
|
||||||
pub hovered_region_ids: HashSet<MouseRegionId>,
|
pub hovered_region_ids: HashSet<MouseRegionId>,
|
||||||
pub clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
pub clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
||||||
pub refreshing: bool,
|
pub refreshing: bool,
|
||||||
|
@ -3905,6 +3908,7 @@ pub struct RenderContext<'a, T: View> {
|
||||||
pub(crate) window_id: usize,
|
pub(crate) window_id: usize,
|
||||||
pub(crate) view_id: usize,
|
pub(crate) view_id: usize,
|
||||||
pub(crate) view_type: PhantomData<T>,
|
pub(crate) view_type: PhantomData<T>,
|
||||||
|
pub(crate) mouse_position: Vector2F,
|
||||||
pub(crate) hovered_region_ids: HashSet<MouseRegionId>,
|
pub(crate) hovered_region_ids: HashSet<MouseRegionId>,
|
||||||
pub(crate) clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
pub(crate) clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
||||||
pub app: &'a mut MutableAppContext,
|
pub app: &'a mut MutableAppContext,
|
||||||
|
@ -3916,12 +3920,19 @@ pub struct RenderContext<'a, T: View> {
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct MouseState {
|
pub struct MouseState {
|
||||||
hovered: bool,
|
hovered: bool,
|
||||||
|
mouse_position: Vector2F,
|
||||||
clicked: Option<MouseButton>,
|
clicked: Option<MouseButton>,
|
||||||
|
accessed_mouse_position: bool,
|
||||||
accessed_hovered: bool,
|
accessed_hovered: bool,
|
||||||
accessed_clicked: bool,
|
accessed_clicked: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MouseState {
|
impl MouseState {
|
||||||
|
pub fn mouse_position(&mut self) -> Vector2F {
|
||||||
|
self.accessed_mouse_position = true;
|
||||||
|
self.mouse_position
|
||||||
|
}
|
||||||
|
|
||||||
pub fn hovered(&mut self) -> bool {
|
pub fn hovered(&mut self) -> bool {
|
||||||
self.accessed_hovered = true;
|
self.accessed_hovered = true;
|
||||||
self.hovered
|
self.hovered
|
||||||
|
@ -3932,6 +3943,10 @@ impl MouseState {
|
||||||
self.clicked
|
self.clicked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn accessed_mouse_position(&self) -> bool {
|
||||||
|
self.accessed_mouse_position
|
||||||
|
}
|
||||||
|
|
||||||
pub fn accessed_hovered(&self) -> bool {
|
pub fn accessed_hovered(&self) -> bool {
|
||||||
self.accessed_hovered
|
self.accessed_hovered
|
||||||
}
|
}
|
||||||
|
@ -3949,6 +3964,7 @@ impl<'a, V: View> RenderContext<'a, V> {
|
||||||
view_id: params.view_id,
|
view_id: params.view_id,
|
||||||
view_type: PhantomData,
|
view_type: PhantomData,
|
||||||
titlebar_height: params.titlebar_height,
|
titlebar_height: params.titlebar_height,
|
||||||
|
mouse_position: params.mouse_position,
|
||||||
hovered_region_ids: params.hovered_region_ids.clone(),
|
hovered_region_ids: params.hovered_region_ids.clone(),
|
||||||
clicked_region_ids: params.clicked_region_ids.clone(),
|
clicked_region_ids: params.clicked_region_ids.clone(),
|
||||||
refreshing: params.refreshing,
|
refreshing: params.refreshing,
|
||||||
|
@ -3971,6 +3987,7 @@ impl<'a, V: View> RenderContext<'a, V> {
|
||||||
pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
|
pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
|
||||||
let region_id = MouseRegionId::new::<Tag>(self.view_id, region_id);
|
let region_id = MouseRegionId::new::<Tag>(self.view_id, region_id);
|
||||||
MouseState {
|
MouseState {
|
||||||
|
mouse_position: self.mouse_position.clone(),
|
||||||
hovered: self.hovered_region_ids.contains(®ion_id),
|
hovered: self.hovered_region_ids.contains(®ion_id),
|
||||||
clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| {
|
clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| {
|
||||||
if ids.contains(®ion_id) {
|
if ids.contains(®ion_id) {
|
||||||
|
@ -3979,6 +3996,7 @@ impl<'a, V: View> RenderContext<'a, V> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
accessed_mouse_position: false,
|
||||||
accessed_hovered: false,
|
accessed_hovered: false,
|
||||||
accessed_clicked: false,
|
accessed_clicked: false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,6 +186,7 @@ impl TestAppContext {
|
||||||
view_id: handle.id(),
|
view_id: handle.id(),
|
||||||
view_type: PhantomData,
|
view_type: PhantomData,
|
||||||
titlebar_height: 0.,
|
titlebar_height: 0.,
|
||||||
|
mouse_position: Default::default(),
|
||||||
hovered_region_ids: Default::default(),
|
hovered_region_ids: Default::default(),
|
||||||
clicked_region_ids: None,
|
clicked_region_ids: None,
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub struct MouseEventHandler<Tag: 'static> {
|
||||||
cursor_style: Option<CursorStyle>,
|
cursor_style: Option<CursorStyle>,
|
||||||
handlers: HandlerSet,
|
handlers: HandlerSet,
|
||||||
hoverable: bool,
|
hoverable: bool,
|
||||||
|
notify_on_move: bool,
|
||||||
notify_on_hover: bool,
|
notify_on_hover: bool,
|
||||||
notify_on_click: bool,
|
notify_on_click: bool,
|
||||||
above: bool,
|
above: bool,
|
||||||
|
@ -28,9 +29,8 @@ pub struct MouseEventHandler<Tag: 'static> {
|
||||||
_tag: PhantomData<Tag>,
|
_tag: PhantomData<Tag>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// MouseEventHandler::new
|
/// Element which provides a render_child callback with a MouseState and paints a mouse
|
||||||
// MouseEventHandler::above
|
/// region under (or above) it for easy mouse event handling.
|
||||||
|
|
||||||
impl<Tag> MouseEventHandler<Tag> {
|
impl<Tag> MouseEventHandler<Tag> {
|
||||||
pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
|
pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
|
||||||
where
|
where
|
||||||
|
@ -39,6 +39,7 @@ impl<Tag> MouseEventHandler<Tag> {
|
||||||
{
|
{
|
||||||
let mut mouse_state = cx.mouse_state::<Tag>(region_id);
|
let mut mouse_state = cx.mouse_state::<Tag>(region_id);
|
||||||
let child = render_child(&mut mouse_state, cx);
|
let child = render_child(&mut mouse_state, cx);
|
||||||
|
let notify_on_move = mouse_state.accessed_mouse_position();
|
||||||
let notify_on_hover = mouse_state.accessed_hovered();
|
let notify_on_hover = mouse_state.accessed_hovered();
|
||||||
let notify_on_click = mouse_state.accessed_clicked();
|
let notify_on_click = mouse_state.accessed_clicked();
|
||||||
Self {
|
Self {
|
||||||
|
@ -46,6 +47,7 @@ impl<Tag> MouseEventHandler<Tag> {
|
||||||
region_id,
|
region_id,
|
||||||
cursor_style: None,
|
cursor_style: None,
|
||||||
handlers: Default::default(),
|
handlers: Default::default(),
|
||||||
|
notify_on_move,
|
||||||
notify_on_hover,
|
notify_on_hover,
|
||||||
notify_on_click,
|
notify_on_click,
|
||||||
hoverable: true,
|
hoverable: true,
|
||||||
|
@ -55,6 +57,9 @@ impl<Tag> MouseEventHandler<Tag> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful
|
||||||
|
/// for drag and drop handling and similar events which should be captured before the child
|
||||||
|
/// gets the opportunity
|
||||||
pub fn above<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
|
pub fn above<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
|
||||||
where
|
where
|
||||||
V: View,
|
V: View,
|
||||||
|
@ -183,6 +188,7 @@ impl<Tag> MouseEventHandler<Tag> {
|
||||||
self.handlers.clone(),
|
self.handlers.clone(),
|
||||||
)
|
)
|
||||||
.with_hoverable(self.hoverable)
|
.with_hoverable(self.hoverable)
|
||||||
|
.with_notify_on_move(self.notify_on_move)
|
||||||
.with_notify_on_hover(self.notify_on_hover)
|
.with_notify_on_hover(self.notify_on_hover)
|
||||||
.with_notify_on_click(self.notify_on_click),
|
.with_notify_on_click(self.notify_on_click),
|
||||||
);
|
);
|
||||||
|
|
|
@ -90,6 +90,7 @@ impl Presenter {
|
||||||
window_id: self.window_id,
|
window_id: self.window_id,
|
||||||
view_id: *view_id,
|
view_id: *view_id,
|
||||||
titlebar_height: self.titlebar_height,
|
titlebar_height: self.titlebar_height,
|
||||||
|
mouse_position: self.mouse_position.clone(),
|
||||||
hovered_region_ids: self.hovered_region_ids.clone(),
|
hovered_region_ids: self.hovered_region_ids.clone(),
|
||||||
clicked_region_ids: self
|
clicked_region_ids: self
|
||||||
.clicked_button
|
.clicked_button
|
||||||
|
@ -116,6 +117,7 @@ impl Presenter {
|
||||||
window_id: self.window_id,
|
window_id: self.window_id,
|
||||||
view_id: *view_id,
|
view_id: *view_id,
|
||||||
titlebar_height: self.titlebar_height,
|
titlebar_height: self.titlebar_height,
|
||||||
|
mouse_position: self.mouse_position.clone(),
|
||||||
hovered_region_ids: self.hovered_region_ids.clone(),
|
hovered_region_ids: self.hovered_region_ids.clone(),
|
||||||
clicked_region_ids: self
|
clicked_region_ids: self
|
||||||
.clicked_button
|
.clicked_button
|
||||||
|
@ -183,6 +185,7 @@ impl Presenter {
|
||||||
asset_cache: &self.asset_cache,
|
asset_cache: &self.asset_cache,
|
||||||
view_stack: Vec::new(),
|
view_stack: Vec::new(),
|
||||||
refreshing,
|
refreshing,
|
||||||
|
mouse_position: self.mouse_position.clone(),
|
||||||
hovered_region_ids: self.hovered_region_ids.clone(),
|
hovered_region_ids: self.hovered_region_ids.clone(),
|
||||||
clicked_region_ids: self
|
clicked_region_ids: self
|
||||||
.clicked_button
|
.clicked_button
|
||||||
|
@ -231,6 +234,10 @@ impl Presenter {
|
||||||
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
||||||
let mut notified_views: HashSet<usize> = Default::default();
|
let mut notified_views: HashSet<usize> = Default::default();
|
||||||
|
|
||||||
|
if let Some(mouse_position) = event.position() {
|
||||||
|
self.mouse_position = mouse_position;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events
|
// 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events
|
||||||
// get mapped into the mouse-specific MouseEvent type.
|
// get mapped into the mouse-specific MouseEvent type.
|
||||||
// -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?]
|
// -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?]
|
||||||
|
@ -402,10 +409,10 @@ impl Presenter {
|
||||||
MouseEvent::Down(_) | MouseEvent::Up(_) => {
|
MouseEvent::Down(_) | MouseEvent::Up(_) => {
|
||||||
for (region, _) in self.mouse_regions.iter().rev() {
|
for (region, _) in self.mouse_regions.iter().rev() {
|
||||||
if region.bounds.contains_point(self.mouse_position) {
|
if region.bounds.contains_point(self.mouse_position) {
|
||||||
|
valid_regions.push(region.clone());
|
||||||
if region.notify_on_click {
|
if region.notify_on_click {
|
||||||
notified_views.insert(region.id().view_id());
|
notified_views.insert(region.id().view_id());
|
||||||
}
|
}
|
||||||
valid_regions.push(region.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -447,6 +454,16 @@ impl Presenter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MouseEvent::Move(_) => {
|
||||||
|
for (mouse_region, _) in self.mouse_regions.iter().rev() {
|
||||||
|
if mouse_region.bounds.contains_point(self.mouse_position) {
|
||||||
|
valid_regions.push(mouse_region.clone());
|
||||||
|
if mouse_region.notify_on_move {
|
||||||
|
notified_views.insert(mouse_region.id().view_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
for (mouse_region, _) in self.mouse_regions.iter().rev() {
|
for (mouse_region, _) in self.mouse_regions.iter().rev() {
|
||||||
// Contains
|
// Contains
|
||||||
|
@ -551,6 +568,7 @@ pub struct LayoutContext<'a> {
|
||||||
pub window_size: Vector2F,
|
pub window_size: Vector2F,
|
||||||
titlebar_height: f32,
|
titlebar_height: f32,
|
||||||
appearance: Appearance,
|
appearance: Appearance,
|
||||||
|
mouse_position: Vector2F,
|
||||||
hovered_region_ids: HashSet<MouseRegionId>,
|
hovered_region_ids: HashSet<MouseRegionId>,
|
||||||
clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
clicked_region_ids: Option<(HashSet<MouseRegionId>, MouseButton)>,
|
||||||
}
|
}
|
||||||
|
@ -622,6 +640,7 @@ impl<'a> LayoutContext<'a> {
|
||||||
view_id: handle.id(),
|
view_id: handle.id(),
|
||||||
view_type: PhantomData,
|
view_type: PhantomData,
|
||||||
titlebar_height: self.titlebar_height,
|
titlebar_height: self.titlebar_height,
|
||||||
|
mouse_position: self.mouse_position.clone(),
|
||||||
hovered_region_ids: self.hovered_region_ids.clone(),
|
hovered_region_ids: self.hovered_region_ids.clone(),
|
||||||
clicked_region_ids: self.clicked_region_ids.clone(),
|
clicked_region_ids: self.clicked_region_ids.clone(),
|
||||||
refreshing: self.refreshing,
|
refreshing: self.refreshing,
|
||||||
|
@ -861,6 +880,13 @@ impl Axis {
|
||||||
Self::Vertical => Self::Horizontal,
|
Self::Vertical => Self::Horizontal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn component(&self, point: Vector2F) -> f32 {
|
||||||
|
match self {
|
||||||
|
Self::Horizontal => point.x(),
|
||||||
|
Self::Vertical => point.y(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToJson for Axis {
|
impl ToJson for Axis {
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub struct MouseRegion {
|
||||||
pub bounds: RectF,
|
pub bounds: RectF,
|
||||||
pub handlers: HandlerSet,
|
pub handlers: HandlerSet,
|
||||||
pub hoverable: bool,
|
pub hoverable: bool,
|
||||||
|
pub notify_on_move: bool,
|
||||||
pub notify_on_hover: bool,
|
pub notify_on_hover: bool,
|
||||||
pub notify_on_click: bool,
|
pub notify_on_click: bool,
|
||||||
}
|
}
|
||||||
|
@ -54,6 +55,7 @@ impl MouseRegion {
|
||||||
bounds,
|
bounds,
|
||||||
handlers,
|
handlers,
|
||||||
hoverable: true,
|
hoverable: true,
|
||||||
|
notify_on_move: false,
|
||||||
notify_on_hover: false,
|
notify_on_hover: false,
|
||||||
notify_on_click: false,
|
notify_on_click: false,
|
||||||
}
|
}
|
||||||
|
@ -136,6 +138,11 @@ impl MouseRegion {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_notify_on_move(mut self, notify: bool) -> Self {
|
||||||
|
self.notify_on_move = notify;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
|
pub fn with_notify_on_hover(mut self, notify: bool) -> Self {
|
||||||
self.notify_on_hover = notify;
|
self.notify_on_hover = notify;
|
||||||
self
|
self
|
||||||
|
|
|
@ -62,6 +62,7 @@ pub struct Workspace {
|
||||||
pub joining_project_message: ContainedText,
|
pub joining_project_message: ContainedText,
|
||||||
pub external_location_message: ContainedText,
|
pub external_location_message: ContainedText,
|
||||||
pub dock: Dock,
|
pub dock: Dock,
|
||||||
|
pub drop_target_overlay_color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
@ -150,7 +151,6 @@ pub struct TabBar {
|
||||||
pub inactive_pane: TabStyles,
|
pub inactive_pane: TabStyles,
|
||||||
pub dragged_tab: Tab,
|
pub dragged_tab: Tab,
|
||||||
pub height: f32,
|
pub height: f32,
|
||||||
pub drop_target_overlay_color: Color,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TabBar {
|
impl TabBar {
|
||||||
|
|
|
@ -397,10 +397,10 @@ impl View for ToggleDockButton {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.with_cursor_style(CursorStyle::PointingHand)
|
.with_cursor_style(CursorStyle::PointingHand)
|
||||||
.on_up(MouseButton::Left, move |_, cx| {
|
.on_up(MouseButton::Left, move |event, cx| {
|
||||||
let dock_pane = workspace.read(cx.app).dock_pane();
|
let dock_pane = workspace.read(cx.app).dock_pane();
|
||||||
let drop_index = dock_pane.read(cx.app).items_len() + 1;
|
let drop_index = dock_pane.read(cx.app).items_len() + 1;
|
||||||
Pane::handle_dropped_item(&dock_pane.downgrade(), drop_index, false, cx);
|
Pane::handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
if dock_position.is_visible() {
|
if dock_position.is_visible() {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::{ItemHandle, SplitDirection};
|
||||||
use crate::{
|
use crate::{
|
||||||
dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, ExpandDock, HideDock},
|
dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, ExpandDock, HideDock},
|
||||||
toolbar::Toolbar,
|
toolbar::Toolbar,
|
||||||
Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace,
|
Item, NewFile, NewSearch, NewTerminal, SplitWithItem, WeakItemHandle, Workspace,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, HashSet, VecDeque};
|
use collections::{HashMap, HashSet, VecDeque};
|
||||||
|
@ -19,6 +19,7 @@ use gpui::{
|
||||||
},
|
},
|
||||||
impl_actions, impl_internal_actions,
|
impl_actions, impl_internal_actions,
|
||||||
platform::{CursorStyle, NavigationDirection},
|
platform::{CursorStyle, NavigationDirection},
|
||||||
|
scene::MouseUp,
|
||||||
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
||||||
ModelHandle, MouseButton, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
|
ModelHandle, MouseButton, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View,
|
||||||
ViewContext, ViewHandle, WeakViewHandle,
|
ViewContext, ViewHandle, WeakViewHandle,
|
||||||
|
@ -98,7 +99,7 @@ impl_internal_actions!(
|
||||||
DeploySplitMenu,
|
DeploySplitMenu,
|
||||||
DeployNewMenu,
|
DeployNewMenu,
|
||||||
DeployDockMenu,
|
DeployDockMenu,
|
||||||
MoveItem
|
MoveItem,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1097,7 +1098,7 @@ impl Pane {
|
||||||
ix == 0,
|
ix == 0,
|
||||||
detail,
|
detail,
|
||||||
hovered,
|
hovered,
|
||||||
Self::tab_overlay_color(hovered, theme.as_ref(), cx),
|
Self::tab_overlay_color(hovered, cx),
|
||||||
tab_style,
|
tab_style,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -1124,7 +1125,7 @@ impl Pane {
|
||||||
})
|
})
|
||||||
.on_up(MouseButton::Left, {
|
.on_up(MouseButton::Left, {
|
||||||
let pane = pane.clone();
|
let pane = pane.clone();
|
||||||
move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, ix, true, cx)
|
move |event, cx| Pane::handle_dropped_item(event, &pane, ix, true, None, cx)
|
||||||
})
|
})
|
||||||
.as_draggable(
|
.as_draggable(
|
||||||
DraggedItem {
|
DraggedItem {
|
||||||
|
@ -1164,14 +1165,14 @@ impl Pane {
|
||||||
.with_style(filler_style.container)
|
.with_style(filler_style.container)
|
||||||
.with_border(filler_style.container.border);
|
.with_border(filler_style.container.border);
|
||||||
|
|
||||||
if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered(), &theme, cx) {
|
if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered(), cx) {
|
||||||
filler = filler.with_overlay_color(overlay);
|
filler = filler.with_overlay_color(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
filler.boxed()
|
filler.boxed()
|
||||||
})
|
})
|
||||||
.on_up(MouseButton::Left, move |_, cx| {
|
.on_up(MouseButton::Left, move |event, cx| {
|
||||||
Pane::handle_dropped_item(&pane, filler_index, true, cx)
|
Pane::handle_dropped_item(event, &pane, filler_index, true, None, cx)
|
||||||
})
|
})
|
||||||
.flex(1., true)
|
.flex(1., true)
|
||||||
.named("filler"),
|
.named("filler"),
|
||||||
|
@ -1320,17 +1321,64 @@ impl Pane {
|
||||||
tab.constrained().with_height(tab_style.height).boxed()
|
tab.constrained().with_height(tab_style.height).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_tab_bar_buttons(
|
||||||
|
&mut self,
|
||||||
|
theme: &Theme,
|
||||||
|
cx: &mut RenderContext<Self>,
|
||||||
|
) -> ElementBox {
|
||||||
|
Flex::row()
|
||||||
|
// New menu
|
||||||
|
.with_child(tab_bar_button(0, "icons/plus_12.svg", cx, |position| {
|
||||||
|
DeployNewMenu { position }
|
||||||
|
}))
|
||||||
|
.with_child(
|
||||||
|
self.docked
|
||||||
|
.map(|anchor| {
|
||||||
|
// Add the dock menu button if this pane is a dock
|
||||||
|
let dock_icon = icon_for_dock_anchor(anchor);
|
||||||
|
|
||||||
|
tab_bar_button(1, dock_icon, cx, |position| DeployDockMenu { position })
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
// Add the split menu if this pane is not a dock
|
||||||
|
tab_bar_button(2, "icons/split_12.svg", cx, |position| DeploySplitMenu {
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
// Add the close dock button if this pane is a dock
|
||||||
|
.with_children(
|
||||||
|
self.docked
|
||||||
|
.map(|_| tab_bar_button(3, "icons/x_mark_thin_8.svg", cx, |_| HideDock)),
|
||||||
|
)
|
||||||
|
.contained()
|
||||||
|
.with_style(theme.workspace.tab_bar.pane_button_container)
|
||||||
|
.flex(1., false)
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_dropped_item(
|
pub fn handle_dropped_item(
|
||||||
|
event: MouseUp,
|
||||||
pane: &WeakViewHandle<Pane>,
|
pane: &WeakViewHandle<Pane>,
|
||||||
index: usize,
|
index: usize,
|
||||||
allow_same_pane: bool,
|
allow_same_pane: bool,
|
||||||
|
split_margin: Option<f32>,
|
||||||
cx: &mut EventContext,
|
cx: &mut EventContext,
|
||||||
) {
|
) {
|
||||||
if let Some((_, dragged_item)) = cx
|
if let Some((_, dragged_item)) = cx
|
||||||
.global::<DragAndDrop<Workspace>>()
|
.global::<DragAndDrop<Workspace>>()
|
||||||
.currently_dragged::<DraggedItem>(cx.window_id)
|
.currently_dragged::<DraggedItem>(cx.window_id)
|
||||||
{
|
{
|
||||||
if pane != &dragged_item.pane || allow_same_pane {
|
if let Some(split_direction) = split_margin
|
||||||
|
.and_then(|margin| Self::drop_split_direction(event.position, event.region, margin))
|
||||||
|
{
|
||||||
|
cx.dispatch_action(SplitWithItem {
|
||||||
|
item_id_to_move: dragged_item.item.id(),
|
||||||
|
pane_to_split: pane.clone(),
|
||||||
|
split_direction,
|
||||||
|
});
|
||||||
|
} else if pane != &dragged_item.pane || allow_same_pane {
|
||||||
|
// If no split margin or not close enough to the edge, just move the item
|
||||||
cx.dispatch_action(MoveItem {
|
cx.dispatch_action(MoveItem {
|
||||||
item_id: dragged_item.item.id(),
|
item_id: dragged_item.item.id(),
|
||||||
from: dragged_item.pane.clone(),
|
from: dragged_item.pane.clone(),
|
||||||
|
@ -1343,18 +1391,39 @@ impl Pane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tab_overlay_color(
|
fn drop_split_direction(
|
||||||
hovered: bool,
|
position: Vector2F,
|
||||||
theme: &Theme,
|
region: RectF,
|
||||||
cx: &mut RenderContext<Self>,
|
split_margin: f32,
|
||||||
) -> Option<Color> {
|
) -> Option<SplitDirection> {
|
||||||
|
let mut min_direction = None;
|
||||||
|
let mut min_distance = split_margin;
|
||||||
|
for direction in SplitDirection::all() {
|
||||||
|
let edge_distance =
|
||||||
|
(direction.edge(region) - direction.axis().component(position)).abs();
|
||||||
|
|
||||||
|
if edge_distance < min_distance {
|
||||||
|
min_direction = Some(direction);
|
||||||
|
min_distance = edge_distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
min_direction
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_overlay_color(hovered: bool, cx: &mut RenderContext<Self>) -> Option<Color> {
|
||||||
if hovered
|
if hovered
|
||||||
&& cx
|
&& cx
|
||||||
.global::<DragAndDrop<Workspace>>()
|
.global::<DragAndDrop<Workspace>>()
|
||||||
.currently_dragged::<DraggedItem>(cx.window_id())
|
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
Some(theme.workspace.tab_bar.drop_target_overlay_color)
|
Some(
|
||||||
|
cx.global::<Settings>()
|
||||||
|
.theme
|
||||||
|
.workspace
|
||||||
|
.drop_target_overlay_color,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1389,55 +1458,7 @@ impl View for Pane {
|
||||||
// Render pane buttons
|
// Render pane buttons
|
||||||
let theme = cx.global::<Settings>().theme.clone();
|
let theme = cx.global::<Settings>().theme.clone();
|
||||||
if self.is_active {
|
if self.is_active {
|
||||||
tab_row.add_child(
|
tab_row.add_child(self.render_tab_bar_buttons(&theme, cx))
|
||||||
Flex::row()
|
|
||||||
// New menu
|
|
||||||
.with_child(tab_bar_button(
|
|
||||||
0,
|
|
||||||
"icons/plus_12.svg",
|
|
||||||
cx,
|
|
||||||
|position| DeployNewMenu { position },
|
|
||||||
))
|
|
||||||
.with_child(
|
|
||||||
self.docked
|
|
||||||
.map(|anchor| {
|
|
||||||
// Add the dock menu button if this pane is a dock
|
|
||||||
let dock_icon =
|
|
||||||
icon_for_dock_anchor(anchor);
|
|
||||||
|
|
||||||
tab_bar_button(
|
|
||||||
1,
|
|
||||||
dock_icon,
|
|
||||||
cx,
|
|
||||||
|position| DeployDockMenu { position },
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
// Add the split menu if this pane is not a dock
|
|
||||||
tab_bar_button(
|
|
||||||
2,
|
|
||||||
"icons/split_12.svg",
|
|
||||||
cx,
|
|
||||||
|position| DeploySplitMenu { position },
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
// Add the close dock button if this pane is a dock
|
|
||||||
.with_children(self.docked.map(|_| {
|
|
||||||
tab_bar_button(
|
|
||||||
3,
|
|
||||||
"icons/x_mark_thin_8.svg",
|
|
||||||
cx,
|
|
||||||
|_| HideDock,
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
.contained()
|
|
||||||
.with_style(
|
|
||||||
theme.workspace.tab_bar.pane_button_container,
|
|
||||||
)
|
|
||||||
.flex(1., false)
|
|
||||||
.boxed(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tab_row
|
tab_row
|
||||||
|
@ -1453,25 +1474,66 @@ impl View for Pane {
|
||||||
MouseEventHandler::<PaneContentTabDropTarget>::above(
|
MouseEventHandler::<PaneContentTabDropTarget>::above(
|
||||||
0,
|
0,
|
||||||
cx,
|
cx,
|
||||||
|_, cx| {
|
|state, cx| {
|
||||||
Flex::column()
|
let overlay_color = Self::tab_overlay_color(true, cx);
|
||||||
|
let drag_position = cx
|
||||||
|
.global::<DragAndDrop<Workspace>>()
|
||||||
|
.currently_dragged::<DraggedItem>(cx.window_id())
|
||||||
|
.map(|_| state.mouse_position());
|
||||||
|
|
||||||
|
Stack::new()
|
||||||
.with_child(
|
.with_child(
|
||||||
ChildView::new(&self.toolbar, cx)
|
Flex::column()
|
||||||
.expanded()
|
.with_child(
|
||||||
.boxed(),
|
ChildView::new(&self.toolbar, cx)
|
||||||
)
|
.expanded()
|
||||||
.with_child(
|
.boxed(),
|
||||||
ChildView::new(active_item, cx)
|
)
|
||||||
.flex(1., true)
|
.with_child(
|
||||||
|
ChildView::new(active_item, cx)
|
||||||
|
.flex(1., true)
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
|
.with_children(drag_position.map(|drag_position| {
|
||||||
|
Canvas::new(move |region, _, cx| {
|
||||||
|
let overlay_region =
|
||||||
|
if let Some(split_direction) =
|
||||||
|
Self::drop_split_direction(
|
||||||
|
drag_position,
|
||||||
|
region,
|
||||||
|
100., /* Replace with theme value */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
split_direction.along_edge(region, 100.)
|
||||||
|
} else {
|
||||||
|
region
|
||||||
|
};
|
||||||
|
|
||||||
|
cx.scene.push_quad(Quad {
|
||||||
|
bounds: overlay_region,
|
||||||
|
background: overlay_color,
|
||||||
|
border: Default::default(),
|
||||||
|
corner_radius: 0.,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}))
|
||||||
.boxed()
|
.boxed()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.on_up(MouseButton::Left, {
|
.on_up(MouseButton::Left, {
|
||||||
let pane = cx.handle();
|
let pane = cx.handle();
|
||||||
move |_, cx: &mut EventContext| {
|
move |event, cx| {
|
||||||
Pane::handle_dropped_item(&pane, drop_index, false, cx)
|
Pane::handle_dropped_item(
|
||||||
|
event,
|
||||||
|
&pane,
|
||||||
|
drop_index,
|
||||||
|
false,
|
||||||
|
Some(100.), /* Use theme value */
|
||||||
|
cx,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.flex(1., true)
|
.flex(1., true)
|
||||||
|
@ -1493,8 +1555,8 @@ impl View for Pane {
|
||||||
})
|
})
|
||||||
.on_up(MouseButton::Left, {
|
.on_up(MouseButton::Left, {
|
||||||
let pane = this.clone();
|
let pane = this.clone();
|
||||||
move |_, cx: &mut EventContext| {
|
move |event, cx| {
|
||||||
Pane::handle_dropped_item(&pane, 0, true, cx)
|
Pane::handle_dropped_item(event, &pane, 0, true, None, cx)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.boxed()
|
.boxed()
|
||||||
|
|
|
@ -2,7 +2,9 @@ use crate::{FollowerStatesByLeader, JoinProject, Pane, Workspace};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use call::{ActiveCall, ParticipantLocation};
|
use call::{ActiveCall, ParticipantLocation};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*, Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle,
|
elements::*,
|
||||||
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
|
Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -263,9 +265,7 @@ impl PaneAxis {
|
||||||
new_pane: &ViewHandle<Pane>,
|
new_pane: &ViewHandle<Pane>,
|
||||||
direction: SplitDirection,
|
direction: SplitDirection,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use SplitDirection::*;
|
for (mut idx, member) in self.members.iter_mut().enumerate() {
|
||||||
|
|
||||||
for (idx, member) in self.members.iter_mut().enumerate() {
|
|
||||||
match member {
|
match member {
|
||||||
Member::Axis(axis) => {
|
Member::Axis(axis) => {
|
||||||
if axis.split(old_pane, new_pane, direction).is_ok() {
|
if axis.split(old_pane, new_pane, direction).is_ok() {
|
||||||
|
@ -274,15 +274,12 @@ impl PaneAxis {
|
||||||
}
|
}
|
||||||
Member::Pane(pane) => {
|
Member::Pane(pane) => {
|
||||||
if pane == old_pane {
|
if pane == old_pane {
|
||||||
if direction.matches_axis(self.axis) {
|
if direction.axis() == self.axis {
|
||||||
match direction {
|
if direction.increasing() {
|
||||||
Up | Left => {
|
idx += 1;
|
||||||
self.members.insert(idx, Member::Pane(new_pane.clone()));
|
|
||||||
}
|
|
||||||
Down | Right => {
|
|
||||||
self.members.insert(idx + 1, Member::Pane(new_pane.clone()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.members.insert(idx, Member::Pane(new_pane.clone()));
|
||||||
} else {
|
} else {
|
||||||
*member =
|
*member =
|
||||||
Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
|
Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
|
||||||
|
@ -374,187 +371,46 @@ pub enum SplitDirection {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SplitDirection {
|
impl SplitDirection {
|
||||||
fn matches_axis(self, orientation: Axis) -> bool {
|
pub fn all() -> [Self; 4] {
|
||||||
use Axis::*;
|
[Self::Up, Self::Down, Self::Left, Self::Right]
|
||||||
use SplitDirection::*;
|
}
|
||||||
|
|
||||||
|
pub fn edge(&self, rect: RectF) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Up | Down => match orientation {
|
Self::Up => rect.min_y(),
|
||||||
Vertical => true,
|
Self::Down => rect.max_y(),
|
||||||
Horizontal => false,
|
Self::Left => rect.min_x(),
|
||||||
},
|
Self::Right => rect.max_x(),
|
||||||
Left | Right => match orientation {
|
}
|
||||||
Vertical => false,
|
}
|
||||||
Horizontal => true,
|
|
||||||
},
|
// Returns a new rectangle which shares an edge in SplitDirection and has `size` along SplitDirection
|
||||||
|
pub fn along_edge(&self, rect: RectF, size: f32) -> RectF {
|
||||||
|
match self {
|
||||||
|
Self::Up => RectF::new(rect.origin(), Vector2F::new(rect.width(), size)),
|
||||||
|
Self::Down => RectF::new(
|
||||||
|
rect.lower_left() - Vector2F::new(0., size),
|
||||||
|
Vector2F::new(rect.width(), size),
|
||||||
|
),
|
||||||
|
Self::Left => RectF::new(rect.origin(), Vector2F::new(size, rect.height())),
|
||||||
|
Self::Right => RectF::new(
|
||||||
|
rect.upper_right() - Vector2F::new(size, 0.),
|
||||||
|
Vector2F::new(size, rect.height()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn axis(&self) -> Axis {
|
||||||
|
match self {
|
||||||
|
Self::Up | Self::Down => Axis::Vertical,
|
||||||
|
Self::Left | Self::Right => Axis::Horizontal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increasing(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Left | Self::Up => false,
|
||||||
|
Self::Down | Self::Right => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
// use super::*;
|
|
||||||
// use serde_json::json;
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn test_split_and_remove() -> Result<()> {
|
|
||||||
// let mut group = PaneGroup::new(1);
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "pane",
|
|
||||||
// "paneId": 1,
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// group.split(1, 2, SplitDirection::Right)?;
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "horizontal",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 1},
|
|
||||||
// {"type": "pane", "paneId": 2},
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// group.split(2, 3, SplitDirection::Up)?;
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "horizontal",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 1},
|
|
||||||
// {
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "vertical",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 3},
|
|
||||||
// {"type": "pane", "paneId": 2},
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// group.split(1, 4, SplitDirection::Right)?;
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "horizontal",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 1},
|
|
||||||
// {"type": "pane", "paneId": 4},
|
|
||||||
// {
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "vertical",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 3},
|
|
||||||
// {"type": "pane", "paneId": 2},
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// group.split(2, 5, SplitDirection::Up)?;
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "horizontal",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 1},
|
|
||||||
// {"type": "pane", "paneId": 4},
|
|
||||||
// {
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "vertical",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 3},
|
|
||||||
// {"type": "pane", "paneId": 5},
|
|
||||||
// {"type": "pane", "paneId": 2},
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_eq!(true, group.remove(5)?);
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "horizontal",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 1},
|
|
||||||
// {"type": "pane", "paneId": 4},
|
|
||||||
// {
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "vertical",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 3},
|
|
||||||
// {"type": "pane", "paneId": 2},
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_eq!(true, group.remove(4)?);
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "horizontal",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 1},
|
|
||||||
// {
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "vertical",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 3},
|
|
||||||
// {"type": "pane", "paneId": 2},
|
|
||||||
// ]
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_eq!(true, group.remove(3)?);
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "axis",
|
|
||||||
// "orientation": "horizontal",
|
|
||||||
// "members": [
|
|
||||||
// {"type": "pane", "paneId": 1},
|
|
||||||
// {"type": "pane", "paneId": 2},
|
|
||||||
// ]
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_eq!(true, group.remove(2)?);
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "pane",
|
|
||||||
// "paneId": 1,
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// assert_eq!(false, group.remove(1)?);
|
|
||||||
// assert_eq!(
|
|
||||||
// serde_json::to_value(&group)?,
|
|
||||||
// json!({
|
|
||||||
// "type": "pane",
|
|
||||||
// "paneId": 1,
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ actions!(
|
||||||
ToggleLeftSidebar,
|
ToggleLeftSidebar,
|
||||||
ToggleRightSidebar,
|
ToggleRightSidebar,
|
||||||
NewTerminal,
|
NewTerminal,
|
||||||
NewSearch
|
NewSearch,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -126,6 +126,12 @@ pub struct OpenSharedScreen {
|
||||||
pub peer_id: PeerId,
|
pub peer_id: PeerId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SplitWithItem {
|
||||||
|
pane_to_split: WeakViewHandle<Pane>,
|
||||||
|
split_direction: SplitDirection,
|
||||||
|
item_id_to_move: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl_internal_actions!(
|
impl_internal_actions!(
|
||||||
workspace,
|
workspace,
|
||||||
[
|
[
|
||||||
|
@ -133,7 +139,8 @@ impl_internal_actions!(
|
||||||
ToggleFollow,
|
ToggleFollow,
|
||||||
JoinProject,
|
JoinProject,
|
||||||
OpenSharedScreen,
|
OpenSharedScreen,
|
||||||
RemoveWorktreeFromProject
|
RemoveWorktreeFromProject,
|
||||||
|
SplitWithItem,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
impl_actions!(workspace, [ActivatePane]);
|
impl_actions!(workspace, [ActivatePane]);
|
||||||
|
@ -206,6 +213,22 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
|
||||||
workspace.toggle_sidebar(SidebarSide::Right, cx);
|
workspace.toggle_sidebar(SidebarSide::Right, cx);
|
||||||
});
|
});
|
||||||
cx.add_action(Workspace::activate_pane_at_index);
|
cx.add_action(Workspace::activate_pane_at_index);
|
||||||
|
cx.add_action(
|
||||||
|
|workspace: &mut Workspace,
|
||||||
|
SplitWithItem {
|
||||||
|
pane_to_split,
|
||||||
|
item_id_to_move,
|
||||||
|
split_direction,
|
||||||
|
}: &_,
|
||||||
|
cx| {
|
||||||
|
workspace.split_pane_with_item(
|
||||||
|
pane_to_split.clone(),
|
||||||
|
*item_id_to_move,
|
||||||
|
*split_direction,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let client = &app_state.client;
|
let client = &app_state.client;
|
||||||
client.add_view_request_handler(Workspace::handle_follow);
|
client.add_view_request_handler(Workspace::handle_follow);
|
||||||
|
@ -1950,6 +1973,35 @@ impl Workspace {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn split_pane_with_item(
|
||||||
|
&mut self,
|
||||||
|
pane_to_split: WeakViewHandle<Pane>,
|
||||||
|
item_id_to_move: usize,
|
||||||
|
split_direction: SplitDirection,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
if let Some(pane_to_split) = pane_to_split.upgrade(cx) {
|
||||||
|
if &pane_to_split == self.dock_pane() {
|
||||||
|
warn!("Can't split dock pane.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_pane = self.add_pane(cx);
|
||||||
|
Pane::move_item(
|
||||||
|
self,
|
||||||
|
pane_to_split.clone(),
|
||||||
|
new_pane.clone(),
|
||||||
|
item_id_to_move,
|
||||||
|
0,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
self.center
|
||||||
|
.split(&pane_to_split, &new_pane, split_direction)
|
||||||
|
.unwrap();
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_pane(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
|
fn remove_pane(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
|
||||||
if self.center.remove(&pane).unwrap() {
|
if self.center.remove(&pane).unwrap() {
|
||||||
self.panes.retain(|p| p != &pane);
|
self.panes.retain(|p| p != &pane);
|
||||||
|
|
|
@ -75,10 +75,6 @@ export default function tabBar(colorScheme: ColorScheme) {
|
||||||
return {
|
return {
|
||||||
height,
|
height,
|
||||||
background: background(layer),
|
background: background(layer),
|
||||||
dropTargetOverlayColor: withOpacity(
|
|
||||||
foreground(layer),
|
|
||||||
0.6
|
|
||||||
),
|
|
||||||
activePane: {
|
activePane: {
|
||||||
activeTab: activePaneActiveTab,
|
activeTab: activePaneActiveTab,
|
||||||
inactiveTab: tab,
|
inactiveTab: tab,
|
||||||
|
|
|
@ -227,5 +227,9 @@ export default function workspace(colorScheme: ColorScheme) {
|
||||||
shadow: colorScheme.modalShadow,
|
shadow: colorScheme.modalShadow,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dropTargetOverlayColor: withOpacity(
|
||||||
|
foreground(layer),
|
||||||
|
0.6
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue