Allow dragging tabs (#3616)
This commit is contained in:
commit
e09b07ddae
10 changed files with 154 additions and 587 deletions
|
@ -288,7 +288,11 @@ impl ChatPanel {
|
|||
),
|
||||
),
|
||||
)
|
||||
.child(div().grow().child(self.render_active_channel_messages(cx)))
|
||||
.child(
|
||||
div()
|
||||
.flex_grow()
|
||||
.child(self.render_active_channel_messages(cx)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.z_index(1)
|
||||
|
|
|
@ -29,7 +29,7 @@ pub struct GroupStyle {
|
|||
pub style: Box<StyleRefinement>,
|
||||
}
|
||||
|
||||
pub trait InteractiveElement: Sized + Element {
|
||||
pub trait InteractiveElement: Sized {
|
||||
fn interactivity(&mut self) -> &mut Interactivity;
|
||||
|
||||
fn group(mut self, group: impl Into<SharedString>) -> Self {
|
||||
|
@ -61,6 +61,10 @@ pub trait InteractiveElement: Sized + Element {
|
|||
}
|
||||
|
||||
fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
|
||||
debug_assert!(
|
||||
self.interactivity().hover_style.is_none(),
|
||||
"hover style already set"
|
||||
);
|
||||
self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
|
||||
self
|
||||
}
|
||||
|
@ -436,7 +440,6 @@ pub trait StatefulInteractiveElement: InteractiveElement {
|
|||
"calling tooltip more than once on the same element is not supported"
|
||||
);
|
||||
self.interactivity().tooltip_builder = Some(Rc::new(build_tooltip));
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -1013,6 +1016,10 @@ impl Interactivity {
|
|||
|
||||
let overflow = style.overflow;
|
||||
if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
|
||||
if let Some(scroll_handle) = &self.scroll_handle {
|
||||
scroll_handle.0.borrow_mut().overflow = overflow;
|
||||
}
|
||||
|
||||
let scroll_offset = element_state
|
||||
.scroll_offset
|
||||
.get_or_insert_with(Rc::default)
|
||||
|
@ -1314,16 +1321,16 @@ where
|
|||
|
||||
impl<E> IntoElement for Focusable<E>
|
||||
where
|
||||
E: Element,
|
||||
E: IntoElement,
|
||||
{
|
||||
type Element = E;
|
||||
type Element = E::Element;
|
||||
|
||||
fn element_id(&self) -> Option<ElementId> {
|
||||
self.element.element_id()
|
||||
}
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
self.element
|
||||
self.element.into_element()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1417,6 +1424,7 @@ struct ScrollHandleState {
|
|||
bounds: Bounds<Pixels>,
|
||||
child_bounds: Vec<Bounds<Pixels>>,
|
||||
requested_scroll_top: Option<(usize, Pixels)>,
|
||||
overflow: Point<Overflow>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -1462,12 +1470,22 @@ impl ScrollHandle {
|
|||
return;
|
||||
};
|
||||
|
||||
let scroll_offset = state.offset.borrow().y;
|
||||
let mut scroll_offset = state.offset.borrow_mut();
|
||||
|
||||
if bounds.top() + scroll_offset < state.bounds.top() {
|
||||
state.offset.borrow_mut().y = state.bounds.top() - bounds.top();
|
||||
} else if bounds.bottom() + scroll_offset > state.bounds.bottom() {
|
||||
state.offset.borrow_mut().y = state.bounds.bottom() - bounds.bottom();
|
||||
if state.overflow.y == Overflow::Scroll {
|
||||
if bounds.top() + scroll_offset.y < state.bounds.top() {
|
||||
scroll_offset.y = state.bounds.top() - bounds.top();
|
||||
} else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
|
||||
scroll_offset.y = state.bounds.bottom() - bounds.bottom();
|
||||
}
|
||||
}
|
||||
|
||||
if state.overflow.x == Overflow::Scroll {
|
||||
if bounds.left() + scroll_offset.x < state.bounds.left() {
|
||||
scroll_offset.x = state.bounds.left() - bounds.left();
|
||||
} else if bounds.right() + scroll_offset.x > state.bounds.right() {
|
||||
scroll_offset.x = state.bounds.right() - bounds.right();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -303,7 +303,7 @@ pub trait Styled: Sized {
|
|||
|
||||
/// Sets the element to allow a flex item to grow to fill any available space.
|
||||
/// [Docs](https://tailwindcss.com/docs/flex-grow)
|
||||
fn grow(mut self) -> Self {
|
||||
fn flex_grow(mut self) -> Self {
|
||||
self.style().flex_grow = Some(1.);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ impl<D: PickerDelegate> Render for Picker<D> {
|
|||
.when(self.delegate.match_count() > 0, |el| {
|
||||
el.child(
|
||||
v_stack()
|
||||
.grow()
|
||||
.flex_grow()
|
||||
.child(
|
||||
uniform_list(
|
||||
cx.view().clone(),
|
||||
|
|
|
@ -61,7 +61,7 @@ impl ComponentStory {
|
|||
Self::Scroll => ScrollStory::view(cx).into(),
|
||||
Self::Text => TextStory::view(cx).into(),
|
||||
Self::Tab => cx.build_view(|_| ui::TabStory).into(),
|
||||
Self::TabBar => cx.build_view(|cx| ui::TabBarStory::new(cx)).into(),
|
||||
Self::TabBar => cx.build_view(|_| ui::TabBarStory).into(),
|
||||
Self::ViewportUnits => cx.build_view(|_| crate::stories::ViewportUnitsStory).into(),
|
||||
Self::ZIndex => cx.build_view(|_| ZIndexStory).into(),
|
||||
Self::Picker => PickerStory::new(cx).into(),
|
||||
|
|
|
@ -54,14 +54,6 @@ impl ListItem {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn on_drag(
|
||||
mut self,
|
||||
handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.on_secondary_mouse_down = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.tooltip = Some(Box::new(tooltip));
|
||||
self
|
||||
|
|
|
@ -1,19 +1,9 @@
|
|||
use gpui::{Div, FocusHandle, Render};
|
||||
use gpui::{Div, Render};
|
||||
use story::Story;
|
||||
|
||||
use crate::{prelude::*, Tab, TabBar, TabPosition};
|
||||
|
||||
pub struct TabBarStory {
|
||||
tab_bar_focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl TabBarStory {
|
||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||
Self {
|
||||
tab_bar_focus_handle: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct TabBarStory;
|
||||
|
||||
impl Render for TabBarStory {
|
||||
type Element = Div;
|
||||
|
@ -48,7 +38,7 @@ impl Render for TabBarStory {
|
|||
.child(Story::label("Default"))
|
||||
.child(
|
||||
h_stack().child(
|
||||
TabBar::new("tab_bar_1", self.tab_bar_focus_handle.clone())
|
||||
TabBar::new("tab_bar_1")
|
||||
.start_child(
|
||||
IconButton::new("navigate_backward", Icon::ArrowLeft)
|
||||
.icon_size(IconSize::Small),
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{AnyElement, AnyView, ClickEvent, IntoElement, MouseButton};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
use gpui::{AnyElement, IntoElement, Stateful};
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// The position of a [`Tab`] within a list of tabs.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
|
@ -29,12 +26,10 @@ pub enum TabCloseSide {
|
|||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Tab {
|
||||
id: ElementId,
|
||||
div: Stateful<Div>,
|
||||
selected: bool,
|
||||
position: TabPosition,
|
||||
close_side: TabCloseSide,
|
||||
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
|
||||
start_slot: Option<AnyElement>,
|
||||
end_slot: Option<AnyElement>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
|
@ -43,12 +38,10 @@ pub struct Tab {
|
|||
impl Tab {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
div: div().id(id),
|
||||
selected: false,
|
||||
position: TabPosition::First,
|
||||
close_side: TabCloseSide::End,
|
||||
on_click: None,
|
||||
tooltip: None,
|
||||
start_slot: None,
|
||||
end_slot: None,
|
||||
children: SmallVec::new(),
|
||||
|
@ -65,16 +58,6 @@ impl Tab {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
||||
self.on_click = Some(Rc::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.tooltip = Some(Box::new(tooltip));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn start_slot<E: IntoElement>(mut self, element: impl Into<Option<E>>) -> Self {
|
||||
self.start_slot = element.into().map(IntoElement::into_any_element);
|
||||
self
|
||||
|
@ -86,6 +69,14 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
|
||||
impl InteractiveElement for Tab {
|
||||
fn interactivity(&mut self) -> &mut gpui::Interactivity {
|
||||
self.div.interactivity()
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulInteractiveElement for Tab {}
|
||||
|
||||
impl Selectable for Tab {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
|
@ -100,7 +91,7 @@ impl ParentElement for Tab {
|
|||
}
|
||||
|
||||
impl RenderOnce for Tab {
|
||||
type Rendered = Div;
|
||||
type Rendered = Stateful<Div>;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
const HEIGHT_IN_REMS: f32 = 30. / 16.;
|
||||
|
@ -120,7 +111,7 @@ impl RenderOnce for Tab {
|
|||
),
|
||||
};
|
||||
|
||||
div()
|
||||
self.div
|
||||
.h(rems(HEIGHT_IN_REMS))
|
||||
.bg(tab_bg)
|
||||
.border_color(cx.theme().colors().border)
|
||||
|
@ -146,7 +137,6 @@ impl RenderOnce for Tab {
|
|||
.child(
|
||||
h_stack()
|
||||
.group("")
|
||||
.id(self.id)
|
||||
.relative()
|
||||
.h_full()
|
||||
.px_5()
|
||||
|
@ -154,18 +144,6 @@ impl RenderOnce for Tab {
|
|||
.text_color(text_color)
|
||||
// .hover(|style| style.bg(tab_hover_bg))
|
||||
// .active(|style| style.bg(tab_active_bg))
|
||||
.when_some(self.on_click, |tab, on_click| {
|
||||
tab.cursor_pointer().on_click(move |event, cx| {
|
||||
// HACK: GPUI currently fires `on_click` with any mouse button,
|
||||
// but we only care about the left button.
|
||||
if event.down.button == MouseButton::Left {
|
||||
(on_click)(event, cx)
|
||||
}
|
||||
})
|
||||
})
|
||||
.when_some(self.tooltip, |tab, tooltip| {
|
||||
tab.tooltip(move |cx| tooltip(cx))
|
||||
})
|
||||
.child(
|
||||
h_stack()
|
||||
.w_3()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use gpui::{AnyElement, FocusHandle, Focusable, Stateful};
|
||||
use gpui::{AnyElement, ScrollHandle, Stateful};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
@ -6,23 +6,28 @@ use crate::prelude::*;
|
|||
#[derive(IntoElement)]
|
||||
pub struct TabBar {
|
||||
id: ElementId,
|
||||
focus_handle: FocusHandle,
|
||||
start_children: SmallVec<[AnyElement; 2]>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
end_children: SmallVec<[AnyElement; 2]>,
|
||||
scroll_handle: Option<ScrollHandle>,
|
||||
}
|
||||
|
||||
impl TabBar {
|
||||
pub fn new(id: impl Into<ElementId>, focus_handle: FocusHandle) -> Self {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
focus_handle,
|
||||
start_children: SmallVec::new(),
|
||||
children: SmallVec::new(),
|
||||
end_children: SmallVec::new(),
|
||||
scroll_handle: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn track_scroll(mut self, scroll_handle: ScrollHandle) -> Self {
|
||||
self.scroll_handle = Some(scroll_handle);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn start_children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||
&mut self.start_children
|
||||
}
|
||||
|
@ -84,7 +89,7 @@ impl ParentElement for TabBar {
|
|||
}
|
||||
|
||||
impl RenderOnce for TabBar {
|
||||
type Rendered = Focusable<Stateful<Div>>;
|
||||
type Rendered = Stateful<Div>;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
const HEIGHT_IN_REMS: f32 = 30. / 16.;
|
||||
|
@ -92,7 +97,6 @@ impl RenderOnce for TabBar {
|
|||
div()
|
||||
.id(self.id)
|
||||
.group("tab_bar")
|
||||
.track_focus(&self.focus_handle)
|
||||
.flex()
|
||||
.flex_none()
|
||||
.w_full()
|
||||
|
@ -128,7 +132,11 @@ impl RenderOnce for TabBar {
|
|||
h_stack()
|
||||
.id("tabs")
|
||||
.z_index(2)
|
||||
.flex_grow()
|
||||
.overflow_x_scroll()
|
||||
.when_some(self.scroll_handle, |cx, scroll_handle| {
|
||||
cx.track_scroll(&scroll_handle)
|
||||
})
|
||||
.children(self.children),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -10,7 +10,7 @@ use gpui::{
|
|||
actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext,
|
||||
AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
|
||||
FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel, Render,
|
||||
Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use project::{Project, ProjectEntryId, ProjectPath};
|
||||
|
@ -25,13 +25,14 @@ use std::{
|
|||
Arc,
|
||||
},
|
||||
};
|
||||
use theme::ThemeSettings;
|
||||
|
||||
use ui::{
|
||||
h_stack, prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize,
|
||||
Indicator, Label, Tab, TabBar, TabPosition, Tooltip,
|
||||
};
|
||||
use ui::{v_stack, ContextMenu};
|
||||
use util::{maybe, truncate_and_remove_front};
|
||||
use util::{maybe, truncate_and_remove_front, ResultExt};
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -176,10 +177,8 @@ pub struct Pane {
|
|||
was_focused: bool,
|
||||
active_item_index: usize,
|
||||
last_focused_view_by_item: HashMap<EntityId, FocusHandle>,
|
||||
autoscroll: bool,
|
||||
nav_history: NavHistory,
|
||||
toolbar: View<Toolbar>,
|
||||
tab_bar_focus_handle: FocusHandle,
|
||||
new_item_menu: Option<View<ContextMenu>>,
|
||||
split_item_menu: Option<View<ContextMenu>>,
|
||||
// tab_context_menu: ViewHandle<ContextMenu>,
|
||||
|
@ -189,6 +188,7 @@ pub struct Pane {
|
|||
can_split: bool,
|
||||
// render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>,
|
||||
subscriptions: Vec<Subscription>,
|
||||
tab_bar_scroll_handle: ScrollHandle,
|
||||
}
|
||||
|
||||
pub struct ItemNavHistory {
|
||||
|
@ -231,6 +231,14 @@ pub struct NavigationEntry {
|
|||
pub timestamp: usize,
|
||||
}
|
||||
|
||||
struct DraggedTab {
|
||||
pub pane: View<Pane>,
|
||||
pub ix: usize,
|
||||
pub item_id: EntityId,
|
||||
pub detail: usize,
|
||||
pub is_active: bool,
|
||||
}
|
||||
|
||||
// pub struct DraggedItem {
|
||||
// pub handle: Box<dyn ItemHandle>,
|
||||
// pub pane: WeakView<Pane>,
|
||||
|
@ -344,7 +352,6 @@ impl Pane {
|
|||
zoomed: false,
|
||||
active_item_index: 0,
|
||||
last_focused_view_by_item: Default::default(),
|
||||
autoscroll: false,
|
||||
nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState {
|
||||
mode: NavigationMode::Normal,
|
||||
backward_stack: Default::default(),
|
||||
|
@ -355,9 +362,9 @@ impl Pane {
|
|||
next_timestamp,
|
||||
}))),
|
||||
toolbar: cx.build_view(|_| Toolbar::new()),
|
||||
tab_bar_focus_handle: cx.focus_handle(),
|
||||
new_item_menu: None,
|
||||
split_item_menu: None,
|
||||
tab_bar_scroll_handle: ScrollHandle::new(),
|
||||
// tab_bar_context_menu: TabBarContextMenu {
|
||||
// kind: TabBarContextMenuKind::New,
|
||||
// handle: context_menu,
|
||||
|
@ -460,8 +467,8 @@ impl Pane {
|
|||
}
|
||||
|
||||
active_item.focus_handle(cx).focus(cx);
|
||||
} else if !self.tab_bar_focus_handle.contains_focused(cx) {
|
||||
if let Some(focused) = cx.focused() {
|
||||
} else if let Some(focused) = cx.focused() {
|
||||
if !self.context_menu_focused(cx) {
|
||||
self.last_focused_view_by_item
|
||||
.insert(active_item.item_id(), focused);
|
||||
}
|
||||
|
@ -469,6 +476,13 @@ impl Pane {
|
|||
}
|
||||
}
|
||||
|
||||
fn context_menu_focused(&self, cx: &mut ViewContext<Self>) -> bool {
|
||||
self.new_item_menu
|
||||
.as_ref()
|
||||
.or(self.split_item_menu.as_ref())
|
||||
.map_or(false, |menu| menu.focus_handle(cx).is_focused(cx))
|
||||
}
|
||||
|
||||
fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.was_focused = false;
|
||||
self.toolbar.update(cx, |toolbar, cx| {
|
||||
|
@ -785,7 +799,7 @@ impl Pane {
|
|||
self.focus_active_item(cx);
|
||||
}
|
||||
|
||||
self.autoscroll = true;
|
||||
self.tab_bar_scroll_handle.scroll_to_item(index);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
@ -1478,8 +1492,7 @@ impl Pane {
|
|||
Some(Indicator::dot().color(indicator_color))
|
||||
});
|
||||
|
||||
let id = item.item_id();
|
||||
|
||||
let item_id = item.item_id();
|
||||
let is_first_item = ix == 0;
|
||||
let is_last_item = ix == self.items.len() - 1;
|
||||
let position_relative_to_active_item = ix.cmp(&self.active_item_index);
|
||||
|
@ -1501,11 +1514,24 @@ impl Pane {
|
|||
.on_click(cx.listener(move |pane: &mut Self, event, cx| {
|
||||
pane.activate_item(ix, true, true, cx)
|
||||
}))
|
||||
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
|
||||
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
|
||||
// .on_drop(|_view, state: View<DraggedTab>, cx| {
|
||||
// eprintln!("{:?}", state.read(cx));
|
||||
// })
|
||||
.on_drag({
|
||||
let pane = cx.view().clone();
|
||||
move |cx| {
|
||||
cx.build_view(|cx| DraggedTab {
|
||||
pane: pane.clone(),
|
||||
detail,
|
||||
item_id,
|
||||
is_active,
|
||||
ix,
|
||||
})
|
||||
}
|
||||
})
|
||||
.drag_over::<DraggedTab>(|tab| tab.bg(cx.theme().colors().tab_active_background))
|
||||
.on_drop(
|
||||
cx.listener(move |this, dragged_tab: &View<DraggedTab>, cx| {
|
||||
this.handle_tab_drop(dragged_tab, ix, cx)
|
||||
}),
|
||||
)
|
||||
.when_some(item.tab_tooltip_text(cx), |tab, text| {
|
||||
tab.tooltip(move |cx| Tooltip::text(text.clone(), cx))
|
||||
})
|
||||
|
@ -1516,7 +1542,7 @@ impl Pane {
|
|||
.size(ButtonSize::None)
|
||||
.icon_size(IconSize::XSmall)
|
||||
.on_click(cx.listener(move |pane, _, cx| {
|
||||
pane.close_item_by_id(id, SaveIntent::Close, cx)
|
||||
pane.close_item_by_id(item_id, SaveIntent::Close, cx)
|
||||
.detach_and_log_err(cx);
|
||||
})),
|
||||
)
|
||||
|
@ -1562,7 +1588,8 @@ impl Pane {
|
|||
}
|
||||
|
||||
fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
|
||||
TabBar::new("tab_bar", self.tab_bar_focus_handle.clone())
|
||||
TabBar::new("tab_bar")
|
||||
.track_scroll(self.tab_bar_scroll_handle.clone())
|
||||
.start_child(
|
||||
IconButton::new("navigate_backward", Icon::ArrowLeft)
|
||||
.icon_size(IconSize::Small)
|
||||
|
@ -1635,6 +1662,19 @@ impl Pane {
|
|||
.zip(self.tab_details(cx))
|
||||
.map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.h_full()
|
||||
.flex_grow()
|
||||
.drag_over::<DraggedTab>(|bar| {
|
||||
bar.bg(cx.theme().colors().tab_active_background)
|
||||
})
|
||||
.on_drop(
|
||||
cx.listener(move |this, dragged_tab: &View<DraggedTab>, cx| {
|
||||
this.handle_tab_drop(dragged_tab, this.items.len(), cx)
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
|
||||
|
@ -1647,161 +1687,6 @@ impl Pane {
|
|||
.child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone()))
|
||||
}
|
||||
|
||||
// fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
|
||||
// let theme = theme::current(cx).clone();
|
||||
|
||||
// let pane = cx.handle().downgrade();
|
||||
// let autoscroll = if mem::take(&mut self.autoscroll) {
|
||||
// Some(self.active_item_index)
|
||||
// } else {
|
||||
// None
|
||||
// };
|
||||
|
||||
// let pane_active = self.has_focus;
|
||||
|
||||
// enum Tabs {}
|
||||
// let mut row = Flex::row().scrollable::<Tabs>(1, autoscroll, cx);
|
||||
// for (ix, (item, detail)) in self
|
||||
// .items
|
||||
// .iter()
|
||||
// .cloned()
|
||||
// .zip(self.tab_details(cx))
|
||||
// .enumerate()
|
||||
// {
|
||||
// let git_status = item
|
||||
// .project_path(cx)
|
||||
// .and_then(|path| self.project.read(cx).entry_for_path(&path, cx))
|
||||
// .and_then(|entry| entry.git_status());
|
||||
|
||||
// let detail = if detail == 0 { None } else { Some(detail) };
|
||||
// let tab_active = ix == self.active_item_index;
|
||||
|
||||
// row.add_child({
|
||||
// enum TabDragReceiver {}
|
||||
// let mut receiver =
|
||||
// dragged_item_receiver::<TabDragReceiver, _, _>(self, ix, ix, true, None, cx, {
|
||||
// let item = item.clone();
|
||||
// let pane = pane.clone();
|
||||
// let detail = detail.clone();
|
||||
|
||||
// let theme = theme::current(cx).clone();
|
||||
// let mut tooltip_theme = theme.tooltip.clone();
|
||||
// tooltip_theme.max_text_width = None;
|
||||
// let tab_tooltip_text =
|
||||
// item.tab_tooltip_text(cx).map(|text| text.into_owned());
|
||||
|
||||
// let mut tab_style = theme
|
||||
// .workspace
|
||||
// .tab_bar
|
||||
// .tab_style(pane_active, tab_active)
|
||||
// .clone();
|
||||
// let should_show_status = settings::get::<ItemSettings>(cx).git_status;
|
||||
// if should_show_status && git_status != None {
|
||||
// tab_style.label.text.color = match git_status.unwrap() {
|
||||
// GitFileStatus::Added => tab_style.git.inserted,
|
||||
// GitFileStatus::Modified => tab_style.git.modified,
|
||||
// GitFileStatus::Conflict => tab_style.git.conflict,
|
||||
// };
|
||||
// }
|
||||
|
||||
// move |mouse_state, cx| {
|
||||
// let hovered = mouse_state.hovered();
|
||||
|
||||
// enum Tab {}
|
||||
// let mouse_event_handler =
|
||||
// MouseEventHandler::new::<Tab, _>(ix, cx, |_, cx| {
|
||||
// Self::render_tab(
|
||||
// &item,
|
||||
// pane.clone(),
|
||||
// ix == 0,
|
||||
// detail,
|
||||
// hovered,
|
||||
// &tab_style,
|
||||
// cx,
|
||||
// )
|
||||
// })
|
||||
// .on_down(MouseButton::Left, move |_, this, cx| {
|
||||
// this.activate_item(ix, true, true, cx);
|
||||
// })
|
||||
// .on_click(MouseButton::Middle, {
|
||||
// let item_id = item.id();
|
||||
// move |_, pane, cx| {
|
||||
// pane.close_item_by_id(item_id, SaveIntent::Close, cx)
|
||||
// .detach_and_log_err(cx);
|
||||
// }
|
||||
// })
|
||||
// .on_down(
|
||||
// MouseButton::Right,
|
||||
// move |event, pane, cx| {
|
||||
// pane.deploy_tab_context_menu(event.position, item.id(), cx);
|
||||
// },
|
||||
// );
|
||||
|
||||
// if let Some(tab_tooltip_text) = tab_tooltip_text {
|
||||
// mouse_event_handler
|
||||
// .with_tooltip::<Self>(
|
||||
// ix,
|
||||
// tab_tooltip_text,
|
||||
// None,
|
||||
// tooltip_theme,
|
||||
// cx,
|
||||
// )
|
||||
// .into_any()
|
||||
// } else {
|
||||
// mouse_event_handler.into_any()
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// if !pane_active || !tab_active {
|
||||
// receiver = receiver.with_cursor_style(CursorStyle::PointingHand);
|
||||
// }
|
||||
|
||||
// receiver.as_draggable(
|
||||
// DraggedItem {
|
||||
// handle: item,
|
||||
// pane: pane.clone(),
|
||||
// },
|
||||
// {
|
||||
// let theme = theme::current(cx).clone();
|
||||
|
||||
// let detail = detail.clone();
|
||||
// move |_, dragged_item: &DraggedItem, cx: &mut ViewContext<Workspace>| {
|
||||
// let tab_style = &theme.workspace.tab_bar.dragged_tab;
|
||||
// Self::render_dragged_tab(
|
||||
// &dragged_item.handle,
|
||||
// dragged_item.pane.clone(),
|
||||
// false,
|
||||
// detail,
|
||||
// false,
|
||||
// &tab_style,
|
||||
// cx,
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
// })
|
||||
// }
|
||||
|
||||
// // Use the inactive tab style along with the current pane's active status to decide how to render
|
||||
// // the filler
|
||||
// let filler_index = self.items.len();
|
||||
// let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false);
|
||||
// enum Filler {}
|
||||
// row.add_child(
|
||||
// dragged_item_receiver::<Filler, _, _>(self, 0, filler_index, true, None, cx, |_, _| {
|
||||
// Empty::new()
|
||||
// .contained()
|
||||
// .with_style(filler_style.container)
|
||||
// .with_border(filler_style.container.border)
|
||||
// })
|
||||
// .flex(1., true)
|
||||
// .into_any_named("filler"),
|
||||
// );
|
||||
|
||||
// row
|
||||
// }
|
||||
|
||||
fn tab_details(&self, cx: &AppContext) -> Vec<usize> {
|
||||
let mut tab_details = self.items.iter().map(|_| 0).collect::<Vec<_>>();
|
||||
|
||||
|
@ -1839,192 +1724,6 @@ impl Pane {
|
|||
tab_details
|
||||
}
|
||||
|
||||
// fn render_tab(
|
||||
// item: &Box<dyn ItemHandle>,
|
||||
// pane: WeakView<Pane>,
|
||||
// first: bool,
|
||||
// detail: Option<usize>,
|
||||
// hovered: bool,
|
||||
// tab_style: &theme::Tab,
|
||||
// cx: &mut ViewContext<Self>,
|
||||
// ) -> AnyElement<Self> {
|
||||
// let title = item.tab_content(detail, &tab_style, cx);
|
||||
// Self::render_tab_with_title(title, item, pane, first, hovered, tab_style, cx)
|
||||
// }
|
||||
|
||||
// fn render_dragged_tab(
|
||||
// item: &Box<dyn ItemHandle>,
|
||||
// pane: WeakView<Pane>,
|
||||
// first: bool,
|
||||
// detail: Option<usize>,
|
||||
// hovered: bool,
|
||||
// tab_style: &theme::Tab,
|
||||
// cx: &mut ViewContext<Workspace>,
|
||||
// ) -> AnyElement<Workspace> {
|
||||
// let title = item.dragged_tab_content(detail, &tab_style, cx);
|
||||
// Self::render_tab_with_title(title, item, pane, first, hovered, tab_style, cx)
|
||||
// }
|
||||
|
||||
// fn render_tab_with_title<T: View>(
|
||||
// title: AnyElement<T>,
|
||||
// item: &Box<dyn ItemHandle>,
|
||||
// pane: WeakView<Pane>,
|
||||
// first: bool,
|
||||
// hovered: bool,
|
||||
// tab_style: &theme::Tab,
|
||||
// cx: &mut ViewContext<T>,
|
||||
// ) -> AnyElement<T> {
|
||||
// let mut container = tab_style.container.clone();
|
||||
// if first {
|
||||
// container.border.left = false;
|
||||
// }
|
||||
|
||||
// let buffer_jewel_element = {
|
||||
// let diameter = 7.0;
|
||||
// let icon_color = if item.has_conflict(cx) {
|
||||
// Some(tab_style.icon_conflict)
|
||||
// } else if item.is_dirty(cx) {
|
||||
// Some(tab_style.icon_dirty)
|
||||
// } else {
|
||||
// None
|
||||
// };
|
||||
|
||||
// Canvas::new(move |bounds, _, _, cx| {
|
||||
// if let Some(color) = icon_color {
|
||||
// let square = RectF::new(bounds.origin(), vec2f(diameter, diameter));
|
||||
// cx.scene().push_quad(Quad {
|
||||
// bounds: square,
|
||||
// background: Some(color),
|
||||
// border: Default::default(),
|
||||
// corner_radii: (diameter / 2.).into(),
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
// .constrained()
|
||||
// .with_width(diameter)
|
||||
// .with_height(diameter)
|
||||
// .aligned()
|
||||
// };
|
||||
|
||||
// let title_element = title.aligned().contained().with_style(ContainerStyle {
|
||||
// margin: Margin {
|
||||
// left: tab_style.spacing,
|
||||
// right: tab_style.spacing,
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// });
|
||||
|
||||
// let close_element = if hovered {
|
||||
// let item_id = item.id();
|
||||
// enum TabCloseButton {}
|
||||
// let icon = Svg::new("icons/x.svg");
|
||||
// MouseEventHandler::new::<TabCloseButton, _>(item_id, cx, |mouse_state, _| {
|
||||
// if mouse_state.hovered() {
|
||||
// icon.with_color(tab_style.icon_close_active)
|
||||
// } else {
|
||||
// icon.with_color(tab_style.icon_close)
|
||||
// }
|
||||
// })
|
||||
// .with_padding(Padding::uniform(4.))
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .on_click(MouseButton::Left, {
|
||||
// let pane = pane.clone();
|
||||
// move |_, _, cx| {
|
||||
// let pane = pane.clone();
|
||||
// cx.window_context().defer(move |cx| {
|
||||
// if let Some(pane) = pane.upgrade(cx) {
|
||||
// pane.update(cx, |pane, cx| {
|
||||
// pane.close_item_by_id(item_id, SaveIntent::Close, cx)
|
||||
// .detach_and_log_err(cx);
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// })
|
||||
// .into_any_named("close-tab-icon")
|
||||
// .constrained()
|
||||
// } else {
|
||||
// Empty::new().constrained()
|
||||
// }
|
||||
// .with_width(tab_style.close_icon_width)
|
||||
// .aligned();
|
||||
|
||||
// let close_right = settings::get::<ItemSettings>(cx).close_position.right();
|
||||
|
||||
// if close_right {
|
||||
// Flex::row()
|
||||
// .with_child(buffer_jewel_element)
|
||||
// .with_child(title_element)
|
||||
// .with_child(close_element)
|
||||
// } else {
|
||||
// Flex::row()
|
||||
// .with_child(close_element)
|
||||
// .with_child(title_element)
|
||||
// .with_child(buffer_jewel_element)
|
||||
// }
|
||||
// .contained()
|
||||
// .with_style(container)
|
||||
// .constrained()
|
||||
// .with_height(tab_style.height)
|
||||
// .into_any()
|
||||
// }
|
||||
|
||||
// pub fn render_tab_bar_button<
|
||||
// F1: 'static + Fn(&mut Pane, &mut EventContext<Pane>),
|
||||
// F2: 'static + Fn(&mut Pane, &mut EventContext<Pane>),
|
||||
// >(
|
||||
// index: usize,
|
||||
// icon: &'static str,
|
||||
// is_active: bool,
|
||||
// tooltip: Option<(&'static str, Option<Box<dyn Action>>)>,
|
||||
// cx: &mut ViewContext<Pane>,
|
||||
// on_click: F1,
|
||||
// on_down: F2,
|
||||
// context_menu: Option<ViewHandle<ContextMenu>>,
|
||||
// ) -> AnyElement<Pane> {
|
||||
// enum TabBarButton {}
|
||||
|
||||
// let mut button = MouseEventHandler::new::<TabBarButton, _>(index, cx, |mouse_state, cx| {
|
||||
// let theme = &settings2::get::<ThemeSettings>(cx).theme.workspace.tab_bar;
|
||||
// let style = theme.pane_button.in_state(is_active).style_for(mouse_state);
|
||||
// Svg::new(icon)
|
||||
// .with_color(style.color)
|
||||
// .constrained()
|
||||
// .with_width(style.icon_width)
|
||||
// .aligned()
|
||||
// .constrained()
|
||||
// .with_width(style.button_width)
|
||||
// .with_height(style.button_width)
|
||||
// })
|
||||
// .with_cursor_style(CursorStyle::PointingHand)
|
||||
// .on_down(MouseButton::Left, move |_, pane, cx| on_down(pane, cx))
|
||||
// .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx))
|
||||
// .into_any();
|
||||
// if let Some((tooltip, action)) = tooltip {
|
||||
// let tooltip_style = settings::get::<ThemeSettings>(cx).theme.tooltip.clone();
|
||||
// button = button
|
||||
// .with_tooltip::<TabBarButton>(index, tooltip, action, tooltip_style, cx)
|
||||
// .into_any();
|
||||
// }
|
||||
|
||||
// Stack::new()
|
||||
// .with_child(button)
|
||||
// .with_children(
|
||||
// context_menu.map(|menu| ChildView::new(&menu, cx).aligned().bottom().right()),
|
||||
// )
|
||||
// .flex(1., false)
|
||||
// .into_any_named("tab bar button")
|
||||
// }
|
||||
|
||||
// fn render_blank_pane(&self, theme: &Theme, _cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
// let background = theme.workspace.background;
|
||||
// Empty::new()
|
||||
// .contained()
|
||||
// .with_background_color(background)
|
||||
// .into_any()
|
||||
// }
|
||||
|
||||
pub fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) {
|
||||
self.zoomed = zoomed;
|
||||
cx.notify();
|
||||
|
@ -2033,6 +1732,25 @@ impl Pane {
|
|||
pub fn is_zoomed(&self) -> bool {
|
||||
self.zoomed
|
||||
}
|
||||
|
||||
fn handle_tab_drop(
|
||||
&mut self,
|
||||
dragged_tab: &View<DraggedTab>,
|
||||
ix: usize,
|
||||
cx: &mut ViewContext<'_, Pane>,
|
||||
) {
|
||||
let dragged_tab = dragged_tab.read(cx);
|
||||
let item_id = dragged_tab.item_id;
|
||||
let from_pane = dragged_tab.pane.clone();
|
||||
let to_pane = cx.view().clone();
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
cx.defer(move |workspace, cx| {
|
||||
workspace.move_item(from_pane, to_pane, item_id, ix, cx);
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
impl FocusableView for Pane {
|
||||
|
@ -2134,148 +1852,6 @@ impl Render for Pane {
|
|||
.justify_center()
|
||||
.child(Label::new("Open a file or project to get started.").color(Color::Muted))
|
||||
})
|
||||
// enum MouseNavigationHandler {}
|
||||
// MouseEventHandler::new::<MouseNavigationHandler, _>(0, cx, |_, cx| {
|
||||
// let active_item_index = self.active_item_index;
|
||||
// if let Some(active_item) = self.active_item() {
|
||||
// Flex::column()
|
||||
// .with_child({
|
||||
// let theme = theme::current(cx).clone();
|
||||
// let mut stack = Stack::new();
|
||||
// enum TabBarEventHandler {}
|
||||
// stack.add_child(
|
||||
// MouseEventHandler::new::<TabBarEventHandler, _>(0, cx, |_, _| {
|
||||
// Empty::new()
|
||||
// .contained()
|
||||
// .with_style(theme.workspace.tab_bar.container)
|
||||
// })
|
||||
// .on_down(
|
||||
// MouseButton::Left,
|
||||
// move |_, this, cx| {
|
||||
// this.activate_item(active_item_index, true, true, cx);
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// let tooltip_style = theme.tooltip.clone();
|
||||
// let tab_bar_theme = theme.workspace.tab_bar.clone();
|
||||
// let nav_button_height = tab_bar_theme.height;
|
||||
// let button_style = tab_bar_theme.nav_button;
|
||||
// let border_for_nav_buttons = tab_bar_theme
|
||||
// .tab_style(false, false)
|
||||
// .container
|
||||
// .border
|
||||
// .clone();
|
||||
// let mut tab_row = Flex::row()
|
||||
// .with_child(nav_button(
|
||||
// "icons/arrow_left.svg",
|
||||
// button_style.clone(),
|
||||
// nav_button_height,
|
||||
// tooltip_style.clone(),
|
||||
// self.can_navigate_backward(),
|
||||
// {
|
||||
// move |pane, cx| {
|
||||
// if let Some(workspace) = pane.workspace.upgrade(cx) {
|
||||
// let pane = cx.weak_handle();
|
||||
// cx.window_context().defer(move |cx| {
|
||||
// workspace.update(cx, |workspace, cx| {
|
||||
// workspace
|
||||
// .go_back(pane, cx)
|
||||
// .detach_and_log_err(cx)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// super::GoBack,
|
||||
// "Go Back",
|
||||
// cx,
|
||||
// ))
|
||||
// .with_child(
|
||||
// nav_button(
|
||||
// "icons/arrow_right.svg",
|
||||
// button_style.clone(),
|
||||
// nav_button_height,
|
||||
// tooltip_style,
|
||||
// self.can_navigate_forward(),
|
||||
// {
|
||||
// move |pane, cx| {
|
||||
// if let Some(workspace) = pane.workspace.upgrade(cx) {
|
||||
// let pane = cx.weak_handle();
|
||||
// cx.window_context().defer(move |cx| {
|
||||
// workspace.update(cx, |workspace, cx| {
|
||||
// workspace
|
||||
// .go_forward(pane, cx)
|
||||
// .detach_and_log_err(cx)
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// super::GoForward,
|
||||
// "Go Forward",
|
||||
// cx,
|
||||
// )
|
||||
// .contained()
|
||||
// .with_border(border_for_nav_buttons),
|
||||
// )
|
||||
// .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs"));
|
||||
// if self.has_focus {
|
||||
// let render_tab_bar_buttons = self.render_tab_bar_buttons.clone();
|
||||
// tab_row.add_child(
|
||||
// (render_tab_bar_buttons)(self, cx)
|
||||
// .contained()
|
||||
// .with_style(theme.workspace.tab_bar.pane_button_container)
|
||||
// .flex(1., false)
|
||||
// .into_any(),
|
||||
// )
|
||||
// }
|
||||
// stack.add_child(tab_row);
|
||||
// stack
|
||||
// .constrained()
|
||||
// .with_height(theme.workspace.tab_bar.height)
|
||||
// .flex(1., false)
|
||||
// .into_any_named("tab bar")
|
||||
// })
|
||||
// .with_child({
|
||||
// enum PaneContentTabDropTarget {}
|
||||
// dragged_item_receiver::<PaneContentTabDropTarget, _, _>(
|
||||
// self,
|
||||
// 0,
|
||||
// self.active_item_index + 1,
|
||||
// !self.can_split,
|
||||
// if self.can_split { Some(100.) } else { None },
|
||||
// cx,
|
||||
// {
|
||||
// let toolbar = self.toolbar.clone();
|
||||
// let toolbar_hidden = toolbar.read(cx).hidden();
|
||||
// move |_, cx| {
|
||||
// Flex::column()
|
||||
// .with_children(
|
||||
// (!toolbar_hidden)
|
||||
// .then(|| ChildView::new(&toolbar, cx).expanded()),
|
||||
// )
|
||||
// .with_child(
|
||||
// ChildView::new(active_item.as_any(), cx).flex(1., true),
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
// .flex(1., true)
|
||||
// })
|
||||
// .with_child(ChildView::new(&self.tab_context_menu, cx))
|
||||
// .into_any()
|
||||
// } else {
|
||||
// enum EmptyPane {}
|
||||
// let theme = theme::current(cx).clone();
|
||||
// dragged_item_receiver::<EmptyPane, _, _>(self, 0, 0, false, None, cx, |_, cx| {
|
||||
// self.render_blank_pane(&theme, cx)
|
||||
// })
|
||||
// .on_down(MouseButton::Left, |_, _, cx| {
|
||||
// cx.focus_parent();
|
||||
// })
|
||||
// .into_any()
|
||||
// }
|
||||
// })
|
||||
.on_mouse_down(
|
||||
MouseButton::Navigate(NavigationDirection::Back),
|
||||
cx.listener(|pane, _, cx| {
|
||||
|
@ -2302,7 +1878,6 @@ impl Render for Pane {
|
|||
}
|
||||
}),
|
||||
)
|
||||
// .into_any_named("pane")
|
||||
}
|
||||
|
||||
// fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
|
@ -3131,15 +2706,17 @@ fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String {
|
|||
// }
|
||||
// }
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct DraggedTab {
|
||||
title: String,
|
||||
}
|
||||
|
||||
impl Render for DraggedTab {
|
||||
type Element = Div;
|
||||
type Element = <Tab as RenderOnce>::Rendered;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
div().w_8().h_4().bg(gpui::red())
|
||||
let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
|
||||
let item = &self.pane.read(cx).items[self.ix];
|
||||
let label = item.tab_content(Some(self.detail), false, cx);
|
||||
Tab::new("")
|
||||
.selected(self.is_active)
|
||||
.child(label)
|
||||
.render(cx)
|
||||
.font(ui_font)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue