History

Allow dragging tabs (#3616)

Max Brunsfeld committed 2 years ago

Commit e09b07d2 parents 2e00da5 636222110 files · +154 −587Browse files

Unified · Split

10 files
  1. crates/collab_ui2/src/chat_panel.rs +5 −1
  2. crates/gpui2/src/elements/div.rs +28 −10
  3. crates/gpui2/src/styled.rs +1 −1
  4. crates/picker2/src/picker2.rs +1 −1
  5. crates/storybook2/src/story_selector.rs +1 −1
  6. crates/ui2/src/components/list/list_item.rs +0 −8
  7. crates/ui2/src/components/stories/tab_bar.rs +3 −13
  8. crates/ui2/src/components/tab.rs +15 −37
  9. crates/ui2/src/components/tab_bar.rs +14 −6
  10. crates/workspace2/src/pane.rs +86 −509
crates/collab_ui2/src/chat_panel.rs+5−1
@@ -288,7 +288,11 @@ impl ChatPanel { · 287 unmodified lines
288288 ),
289289 ),
290290 )
291− .child(div().grow().child(self.render_active_channel_messages(cx)))
291+ .child(
292+ div()
293+ .flex_grow()
294+ .child(self.render_active_channel_messages(cx)),
295+ )
292296 .child(
293297 div()
294298 .z_index(1)
crates/gpui2/src/elements/div.rs+28−10
@@ -29,7 +29,7 @@ pub struct GroupStyle { · 28 unmodified lines
2929 pub style: Box<StyleRefinement>,
3030 }
3131
32−pub trait InteractiveElement: Sized + Element {
32+pub trait InteractiveElement: Sized {
3333 fn interactivity(&mut self) -> &mut Interactivity;
3434
3535 fn group(mut self, group: impl Into<SharedString>) -> Self {
@@ -61,6 +61,10 @@ pub trait InteractiveElement: Sized + Element { · 25 unmodified lines
6161 }
6262
6363 fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self {
64+ debug_assert!(
65+ self.interactivity().hover_style.is_none(),
66+ "hover style already set"
67+ );
6468 self.interactivity().hover_style = Some(Box::new(f(StyleRefinement::default())));
6569 self
6670 }
@@ -436,7 +440,6 @@ pub trait StatefulInteractiveElement: InteractiveElement { · 369 unmodified lines
436440 "calling tooltip more than once on the same element is not supported"
437441 );
438442 self.interactivity().tooltip_builder = Some(Rc::new(build_tooltip));
439−
440443 self
441444 }
442445 }
@@ -1013,6 +1016,10 @@ impl Interactivity { · 570 unmodified lines
10131016
10141017 let overflow = style.overflow;
10151018 if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll {
1019+ if let Some(scroll_handle) = &self.scroll_handle {
1020+ scroll_handle.0.borrow_mut().overflow = overflow;
1021+ }
1022+
10161023 let scroll_offset = element_state
10171024 .scroll_offset
10181025 .get_or_insert_with(Rc::default)
@@ -1314,16 +1321,16 @@ where · 295 unmodified lines
13141321
13151322 impl<E> IntoElement for Focusable<E>
13161323 where
1317− E: Element,
1324+ E: IntoElement,
13181325 {
1319− type Element = E;
1326+ type Element = E::Element;
13201327
13211328 fn element_id(&self) -> Option<ElementId> {
13221329 self.element.element_id()
13231330 }
13241331
13251332 fn into_element(self) -> Self::Element {
1326− self.element
1333+ self.element.into_element()
13271334 }
13281335 }
13291336
@@ -1417,6 +1424,7 @@ struct ScrollHandleState { · 87 unmodified lines
14171424 bounds: Bounds<Pixels>,
14181425 child_bounds: Vec<Bounds<Pixels>>,
14191426 requested_scroll_top: Option<(usize, Pixels)>,
1427+ overflow: Point<Overflow>,
14201428 }
14211429
14221430 #[derive(Clone)]
@@ -1462,12 +1470,22 @@ impl ScrollHandle { · 39 unmodified lines
14621470 return;
14631471 };
14641472
1465− let scroll_offset = state.offset.borrow().y;
1473+ let mut scroll_offset = state.offset.borrow_mut();
14661474
1467− if bounds.top() + scroll_offset < state.bounds.top() {
1468− state.offset.borrow_mut().y = state.bounds.top() - bounds.top();
1469− } else if bounds.bottom() + scroll_offset > state.bounds.bottom() {
1470− state.offset.borrow_mut().y = state.bounds.bottom() - bounds.bottom();
1475+ if state.overflow.y == Overflow::Scroll {
1476+ if bounds.top() + scroll_offset.y < state.bounds.top() {
1477+ scroll_offset.y = state.bounds.top() - bounds.top();
1478+ } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() {
1479+ scroll_offset.y = state.bounds.bottom() - bounds.bottom();
1480+ }
1481+ }
1482+
1483+ if state.overflow.x == Overflow::Scroll {
1484+ if bounds.left() + scroll_offset.x < state.bounds.left() {
1485+ scroll_offset.x = state.bounds.left() - bounds.left();
1486+ } else if bounds.right() + scroll_offset.x > state.bounds.right() {
1487+ scroll_offset.x = state.bounds.right() - bounds.right();
1488+ }
14711489 }
14721490 }
14731491
crates/gpui2/src/styled.rs+1−1
@@ -303,7 +303,7 @@ pub trait Styled: Sized { · 302 unmodified lines
303303
304304 /// Sets the element to allow a flex item to grow to fill any available space.
305305 /// [Docs](https://tailwindcss.com/docs/flex-grow)
306− fn grow(mut self) -> Self {
306+ fn flex_grow(mut self) -> Self {
307307 self.style().flex_grow = Some(1.);
308308 self
309309 }
crates/picker2/src/picker2.rs+1−1
@@ -228,7 +228,7 @@ impl<D: PickerDelegate> Render for Picker<D> { · 227 unmodified lines
228228 .when(self.delegate.match_count() > 0, |el| {
229229 el.child(
230230 v_stack()
231− .grow()
231+ .flex_grow()
232232 .child(
233233 uniform_list(
234234 cx.view().clone(),
crates/storybook2/src/story_selector.rs+1−1
@@ -61,7 +61,7 @@ impl ComponentStory { · 60 unmodified lines
6161 Self::Scroll => ScrollStory::view(cx).into(),
6262 Self::Text => TextStory::view(cx).into(),
6363 Self::Tab => cx.build_view(|_| ui::TabStory).into(),
64− Self::TabBar => cx.build_view(|cx| ui::TabBarStory::new(cx)).into(),
64+ Self::TabBar => cx.build_view(|_| ui::TabBarStory).into(),
6565 Self::ViewportUnits => cx.build_view(|_| crate::stories::ViewportUnitsStory).into(),
6666 Self::ZIndex => cx.build_view(|_| ZIndexStory).into(),
6767 Self::Picker => PickerStory::new(cx).into(),
crates/ui2/src/components/list/list_item.rs+0−8
@@ -54,14 +54,6 @@ impl ListItem { · 53 unmodified lines
5454 self
5555 }
5656
57− pub fn on_drag(
58− mut self,
59− handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
60− ) -> Self {
61− self.on_secondary_mouse_down = Some(Box::new(handler));
62− self
63− }
64−
6557 pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
6658 self.tooltip = Some(Box::new(tooltip));
6759 self
crates/ui2/src/components/stories/tab_bar.rs+3−13
@@ -1,19 +1,9 @@
1−use gpui::{Div, FocusHandle, Render};
1+use gpui::{Div, Render};
22 use story::Story;
33
44 use crate::{prelude::*, Tab, TabBar, TabPosition};
55
6−pub struct TabBarStory {
7− tab_bar_focus_handle: FocusHandle,
8−}
9−
10−impl TabBarStory {
11− pub fn new(cx: &mut ViewContext<Self>) -> Self {
12− Self {
13− tab_bar_focus_handle: cx.focus_handle(),
14− }
15− }
16−}
6+pub struct TabBarStory;
177
188 impl Render for TabBarStory {
199 type Element = Div;
@@ -48,7 +38,7 @@ impl Render for TabBarStory { · 28 unmodified lines
4838 .child(Story::label("Default"))
4939 .child(
5040 h_stack().child(
51− TabBar::new("tab_bar_1", self.tab_bar_focus_handle.clone())
41+ TabBar::new("tab_bar_1")
5242 .start_child(
5343 IconButton::new("navigate_backward", Icon::ArrowLeft)
5444 .icon_size(IconSize::Small),
crates/ui2/src/components/tab.rs+15−37
@@ -1,10 +1,7 @@
1−use std::cmp::Ordering;
2−use std::rc::Rc;
3−
4−use gpui::{AnyElement, AnyView, ClickEvent, IntoElement, MouseButton};
5−use smallvec::SmallVec;
6−
71 use crate::prelude::*;
2+use gpui::{AnyElement, IntoElement, Stateful};
3+use smallvec::SmallVec;
4+use std::cmp::Ordering;
85
96 /// The position of a [`Tab`] within a list of tabs.
107 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -29,12 +26,10 @@ pub enum TabCloseSide { · 18 unmodified lines
2926
3027 #[derive(IntoElement)]
3128 pub struct Tab {
32− id: ElementId,
29+ div: Stateful<Div>,
3330 selected: bool,
3431 position: TabPosition,
3532 close_side: TabCloseSide,
36− on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
37− tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>>,
3833 start_slot: Option<AnyElement>,
3934 end_slot: Option<AnyElement>,
4035 children: SmallVec<[AnyElement; 2]>,
@@ -43,12 +38,10 @@ pub struct Tab { · 2 unmodified lines
4338 impl Tab {
4439 pub fn new(id: impl Into<ElementId>) -> Self {
4540 Self {
46− id: id.into(),
41+ div: div().id(id),
4742 selected: false,
4843 position: TabPosition::First,
4944 close_side: TabCloseSide::End,
50− on_click: None,
51− tooltip: None,
5245 start_slot: None,
5346 end_slot: None,
5447 children: SmallVec::new(),
@@ -65,16 +58,6 @@ impl Tab { · 10 unmodified lines
6558 self
6659 }
6760
68− pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
69− self.on_click = Some(Rc::new(handler));
70− self
71− }
72−
73− pub fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
74− self.tooltip = Some(Box::new(tooltip));
75− self
76− }
77−
7861 pub fn start_slot<E: IntoElement>(mut self, element: impl Into<Option<E>>) -> Self {
7962 self.start_slot = element.into().map(IntoElement::into_any_element);
8063 self
@@ -86,6 +69,14 @@ impl Tab { · 5 unmodified lines
8669 }
8770 }
8871
72+impl InteractiveElement for Tab {
73+ fn interactivity(&mut self) -> &mut gpui::Interactivity {
74+ self.div.interactivity()
75+ }
76+}
77+
78+impl StatefulInteractiveElement for Tab {}
79+
8980 impl Selectable for Tab {
9081 fn selected(mut self, selected: bool) -> Self {
9182 self.selected = selected;
@@ -100,7 +91,7 @@ impl ParentElement for Tab { · 8 unmodified lines
10091 }
10192
10293 impl RenderOnce for Tab {
103− type Rendered = Div;
94+ type Rendered = Stateful<Div>;
10495
10596 fn render(self, cx: &mut WindowContext) -> Self::Rendered {
10697 const HEIGHT_IN_REMS: f32 = 30. / 16.;
@@ -120,7 +111,7 @@ impl RenderOnce for Tab { · 13 unmodified lines
120111 ),
121112 };
122113
123− div()
114+ self.div
124115 .h(rems(HEIGHT_IN_REMS))
125116 .bg(tab_bg)
126117 .border_color(cx.theme().colors().border)
@@ -146,7 +137,6 @@ impl RenderOnce for Tab { · 19 unmodified lines
146137 .child(
147138 h_stack()
148139 .group("")
149− .id(self.id)
150140 .relative()
151141 .h_full()
152142 .px_5()
@@ -154,18 +144,6 @@ impl RenderOnce for Tab { · 1 unmodified line
154144 .text_color(text_color)
155145 // .hover(|style| style.bg(tab_hover_bg))
156146 // .active(|style| style.bg(tab_active_bg))
157− .when_some(self.on_click, |tab, on_click| {
158− tab.cursor_pointer().on_click(move |event, cx| {
159− // HACK: GPUI currently fires `on_click` with any mouse button,
160− // but we only care about the left button.
161− if event.down.button == MouseButton::Left {
162− (on_click)(event, cx)
163− }
164− })
165− })
166− .when_some(self.tooltip, |tab, tooltip| {
167− tab.tooltip(move |cx| tooltip(cx))
168− })
169147 .child(
170148 h_stack()
171149 .w_3()
crates/ui2/src/components/tab_bar.rs+14−6
@@ -1,4 +1,4 @@
1−use gpui::{AnyElement, FocusHandle, Focusable, Stateful};
1+use gpui::{AnyElement, ScrollHandle, Stateful};
22 use smallvec::SmallVec;
33
44 use crate::prelude::*;
@@ -6,23 +6,28 @@ use crate::prelude::*; · 1 unmodified line
66 #[derive(IntoElement)]
77 pub struct TabBar {
88 id: ElementId,
9− focus_handle: FocusHandle,
109 start_children: SmallVec<[AnyElement; 2]>,
1110 children: SmallVec<[AnyElement; 2]>,
1211 end_children: SmallVec<[AnyElement; 2]>,
12+ scroll_handle: Option<ScrollHandle>,
1313 }
1414
1515 impl TabBar {
16− pub fn new(id: impl Into<ElementId>, focus_handle: FocusHandle) -> Self {
16+ pub fn new(id: impl Into<ElementId>) -> Self {
1717 Self {
1818 id: id.into(),
19− focus_handle,
2019 start_children: SmallVec::new(),
2120 children: SmallVec::new(),
2221 end_children: SmallVec::new(),
22+ scroll_handle: None,
2323 }
2424 }
2525
26+ pub fn track_scroll(mut self, scroll_handle: ScrollHandle) -> Self {
27+ self.scroll_handle = Some(scroll_handle);
28+ self
29+ }
30+
2631 pub fn start_children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
2732 &mut self.start_children
2833 }
@@ -84,7 +89,7 @@ impl ParentElement for TabBar { · 55 unmodified lines
8489 }
8590
8691 impl RenderOnce for TabBar {
87− type Rendered = Focusable<Stateful<Div>>;
92+ type Rendered = Stateful<Div>;
8893
8994 fn render(self, cx: &mut WindowContext) -> Self::Rendered {
9095 const HEIGHT_IN_REMS: f32 = 30. / 16.;
@@ -92,7 +97,6 @@ impl RenderOnce for TabBar { · 1 unmodified line
9297 div()
9398 .id(self.id)
9499 .group("tab_bar")
95− .track_focus(&self.focus_handle)
96100 .flex()
97101 .flex_none()
98102 .w_full()
@@ -128,7 +132,11 @@ impl RenderOnce for TabBar { · 29 unmodified lines
128132 h_stack()
129133 .id("tabs")
130134 .z_index(2)
135+ .flex_grow()
131136 .overflow_x_scroll()
137+ .when_some(self.scroll_handle, |cx, scroll_handle| {
138+ cx.track_scroll(&scroll_handle)
139+ })
132140 .children(self.children),
133141 ),
134142 )
crates/workspace2/src/pane.rs+86−509
@@ -10,7 +10,7 @@ use gpui::{ · 9 unmodified lines
1010 actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext,
1111 AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
1212 FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel, Render,
13− Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
13+ ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowContext,
1414 };
1515 use parking_lot::Mutex;
1616 use project::{Project, ProjectEntryId, ProjectPath};
@@ -25,13 +25,14 @@ use std::{ · 8 unmodified lines
2525 Arc,
2626 },
2727 };
28+use theme::ThemeSettings;
2829
2930 use ui::{
3031 h_stack, prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize,
3132 Indicator, Label, Tab, TabBar, TabPosition, Tooltip,
3233 };
3334 use ui::{v_stack, ContextMenu};
34−use util::{maybe, truncate_and_remove_front};
35+use util::{maybe, truncate_and_remove_front, ResultExt};
3536
3637 #[derive(PartialEq, Clone, Copy, Deserialize, Debug)]
3738 #[serde(rename_all = "camelCase")]
@@ -176,10 +177,8 @@ pub struct Pane { · 138 unmodified lines
176177 was_focused: bool,
177178 active_item_index: usize,
178179 last_focused_view_by_item: HashMap<EntityId, FocusHandle>,
179− autoscroll: bool,
180180 nav_history: NavHistory,
181181 toolbar: View<Toolbar>,
182− tab_bar_focus_handle: FocusHandle,
183182 new_item_menu: Option<View<ContextMenu>>,
184183 split_item_menu: Option<View<ContextMenu>>,
185184 // tab_context_menu: ViewHandle<ContextMenu>,
@@ -189,6 +188,7 @@ pub struct Pane { · 3 unmodified lines
189188 can_split: bool,
190189 // render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>,
191190 subscriptions: Vec<Subscription>,
191+ tab_bar_scroll_handle: ScrollHandle,
192192 }
193193
194194 pub struct ItemNavHistory {
@@ -231,6 +231,14 @@ pub struct NavigationEntry { · 36 unmodified lines
231231 pub timestamp: usize,
232232 }
233233
234+struct DraggedTab {
235+ pub pane: View<Pane>,
236+ pub ix: usize,
237+ pub item_id: EntityId,
238+ pub detail: usize,
239+ pub is_active: bool,
240+}
241+
234242 // pub struct DraggedItem {
235243 // pub handle: Box<dyn ItemHandle>,
236244 // pub pane: WeakView<Pane>,
@@ -344,7 +352,6 @@ impl Pane { · 107 unmodified lines
344352 zoomed: false,
345353 active_item_index: 0,
346354 last_focused_view_by_item: Default::default(),
347− autoscroll: false,
348355 nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState {
349356 mode: NavigationMode::Normal,
350357 backward_stack: Default::default(),
@@ -355,9 +362,9 @@ impl Pane { · 4 unmodified lines
355362 next_timestamp,
356363 }))),
357364 toolbar: cx.build_view(|_| Toolbar::new()),
358− tab_bar_focus_handle: cx.focus_handle(),
359365 new_item_menu: None,
360366 split_item_menu: None,
367+ tab_bar_scroll_handle: ScrollHandle::new(),
361368 // tab_bar_context_menu: TabBarContextMenu {
362369 // kind: TabBarContextMenuKind::New,
363370 // handle: context_menu,
@@ -460,8 +467,8 @@ impl Pane { · 96 unmodified lines
460467 }
461468
462469 active_item.focus_handle(cx).focus(cx);
463− } else if !self.tab_bar_focus_handle.contains_focused(cx) {
464− if let Some(focused) = cx.focused() {
470+ } else if let Some(focused) = cx.focused() {
471+ if !self.context_menu_focused(cx) {
465472 self.last_focused_view_by_item
466473 .insert(active_item.item_id(), focused);
467474 }
@@ -469,6 +476,13 @@ impl Pane { · 1 unmodified line
469476 }
470477 }
471478
479+ fn context_menu_focused(&self, cx: &mut ViewContext<Self>) -> bool {
480+ self.new_item_menu
481+ .as_ref()
482+ .or(self.split_item_menu.as_ref())
483+ .map_or(false, |menu| menu.focus_handle(cx).is_focused(cx))
484+ }
485+
472486 fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
473487 self.was_focused = false;
474488 self.toolbar.update(cx, |toolbar, cx| {
@@ -785,7 +799,7 @@ impl Pane { · 310 unmodified lines
785799 self.focus_active_item(cx);
786800 }
787801
788− self.autoscroll = true;
802+ self.tab_bar_scroll_handle.scroll_to_item(index);
789803 cx.notify();
790804 }
791805 }
@@ -1478,8 +1492,7 @@ impl Pane { · 686 unmodified lines
14781492 Some(Indicator::dot().color(indicator_color))
14791493 });
14801494
1481− let id = item.item_id();
1482−
1495+ let item_id = item.item_id();
14831496 let is_first_item = ix == 0;
14841497 let is_last_item = ix == self.items.len() - 1;
14851498 let position_relative_to_active_item = ix.cmp(&self.active_item_index);
@@ -1501,11 +1514,24 @@ impl Pane { · 15 unmodified lines
15011514 .on_click(cx.listener(move |pane: &mut Self, event, cx| {
15021515 pane.activate_item(ix, true, true, cx)
15031516 }))
1504− // .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
1505− // .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
1506− // .on_drop(|_view, state: View<DraggedTab>, cx| {
1507− // eprintln!("{:?}", state.read(cx));
1508− // })
1517+ .on_drag({
1518+ let pane = cx.view().clone();
1519+ move |cx| {
1520+ cx.build_view(|cx| DraggedTab {
1521+ pane: pane.clone(),
1522+ detail,
1523+ item_id,
1524+ is_active,
1525+ ix,
1526+ })
1527+ }
1528+ })
1529+ .drag_over::<DraggedTab>(|tab| tab.bg(cx.theme().colors().tab_active_background))
1530+ .on_drop(
1531+ cx.listener(move |this, dragged_tab: &View<DraggedTab>, cx| {
1532+ this.handle_tab_drop(dragged_tab, ix, cx)
1533+ }),
1534+ )
15091535 .when_some(item.tab_tooltip_text(cx), |tab, text| {
15101536 tab.tooltip(move |cx| Tooltip::text(text.clone(), cx))
15111537 })
@@ -1516,7 +1542,7 @@ impl Pane { · 4 unmodified lines
15161542 .size(ButtonSize::None)
15171543 .icon_size(IconSize::XSmall)
15181544 .on_click(cx.listener(move |pane, _, cx| {
1519− pane.close_item_by_id(id, SaveIntent::Close, cx)
1545+ pane.close_item_by_id(item_id, SaveIntent::Close, cx)
15201546 .detach_and_log_err(cx);
15211547 })),
15221548 )
@@ -1562,7 +1588,8 @@ impl Pane { · 39 unmodified lines
15621588 }
15631589
15641590 fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
1565− TabBar::new("tab_bar", self.tab_bar_focus_handle.clone())
1591+ TabBar::new("tab_bar")
1592+ .track_scroll(self.tab_bar_scroll_handle.clone())
15661593 .start_child(
15671594 IconButton::new("navigate_backward", Icon::ArrowLeft)
15681595 .icon_size(IconSize::Small)
@@ -1635,6 +1662,19 @@ impl Pane { · 66 unmodified lines
16351662 .zip(self.tab_details(cx))
16361663 .map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
16371664 )
1665+ .child(
1666+ div()
1667+ .h_full()
1668+ .flex_grow()
1669+ .drag_over::<DraggedTab>(|bar| {
1670+ bar.bg(cx.theme().colors().tab_active_background)
1671+ })
1672+ .on_drop(
1673+ cx.listener(move |this, dragged_tab: &View<DraggedTab>, cx| {
1674+ this.handle_tab_drop(dragged_tab, this.items.len(), cx)
1675+ }),
1676+ ),
1677+ )
16381678 }
16391679
16401680 fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {
@@ -1647,161 +1687,6 @@ impl Pane { · 6 unmodified lines
16471687 .child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone()))
16481688 }
16491689
1650− // fn render_tabs(&mut self, cx: &mut ViewContext<Self>) -> impl Element<Self> {
1651− // let theme = theme::current(cx).clone();
1652−
1653− // let pane = cx.handle().downgrade();
1654− // let autoscroll = if mem::take(&mut self.autoscroll) {
1655− // Some(self.active_item_index)
1656− // } else {
1657− // None
1658− // };
1659−
1660− // let pane_active = self.has_focus;
1661−
1662− // enum Tabs {}
1663− // let mut row = Flex::row().scrollable::<Tabs>(1, autoscroll, cx);
1664− // for (ix, (item, detail)) in self
1665− // .items
1666− // .iter()
1667− // .cloned()
1668− // .zip(self.tab_details(cx))
1669− // .enumerate()
1670− // {
1671− // let git_status = item
1672− // .project_path(cx)
1673− // .and_then(|path| self.project.read(cx).entry_for_path(&path, cx))
1674− // .and_then(|entry| entry.git_status());
1675−
1676− // let detail = if detail == 0 { None } else { Some(detail) };
1677− // let tab_active = ix == self.active_item_index;
1678−
1679− // row.add_child({
1680− // enum TabDragReceiver {}
1681− // let mut receiver =
1682− // dragged_item_receiver::<TabDragReceiver, _, _>(self, ix, ix, true, None, cx, {
1683− // let item = item.clone();
1684− // let pane = pane.clone();
1685− // let detail = detail.clone();
1686−
1687− // let theme = theme::current(cx).clone();
1688− // let mut tooltip_theme = theme.tooltip.clone();
1689− // tooltip_theme.max_text_width = None;
1690− // let tab_tooltip_text =
1691− // item.tab_tooltip_text(cx).map(|text| text.into_owned());
1692−
1693− // let mut tab_style = theme
1694− // .workspace
1695− // .tab_bar
1696− // .tab_style(pane_active, tab_active)
1697− // .clone();
1698− // let should_show_status = settings::get::<ItemSettings>(cx).git_status;
1699− // if should_show_status && git_status != None {
1700− // tab_style.label.text.color = match git_status.unwrap() {
1701− // GitFileStatus::Added => tab_style.git.inserted,
1702− // GitFileStatus::Modified => tab_style.git.modified,
1703− // GitFileStatus::Conflict => tab_style.git.conflict,
1704− // };
1705− // }
1706−
1707− // move |mouse_state, cx| {
1708− // let hovered = mouse_state.hovered();
1709−
1710− // enum Tab {}
1711− // let mouse_event_handler =
1712− // MouseEventHandler::new::<Tab, _>(ix, cx, |_, cx| {
1713− // Self::render_tab(
1714− // &item,
1715− // pane.clone(),
1716− // ix == 0,
1717− // detail,
1718− // hovered,
1719− // &tab_style,
1720− // cx,
1721− // )
1722− // })
1723− // .on_down(MouseButton::Left, move |_, this, cx| {
1724− // this.activate_item(ix, true, true, cx);
1725− // })
1726− // .on_click(MouseButton::Middle, {
1727− // let item_id = item.id();
1728− // move |_, pane, cx| {
1729− // pane.close_item_by_id(item_id, SaveIntent::Close, cx)
1730− // .detach_and_log_err(cx);
1731− // }
1732− // })
1733− // .on_down(
1734− // MouseButton::Right,
1735− // move |event, pane, cx| {
1736− // pane.deploy_tab_context_menu(event.position, item.id(), cx);
1737− // },
1738− // );
1739−
1740− // if let Some(tab_tooltip_text) = tab_tooltip_text {
1741− // mouse_event_handler
1742− // .with_tooltip::<Self>(
1743− // ix,
1744− // tab_tooltip_text,
1745− // None,
1746− // tooltip_theme,
1747− // cx,
1748− // )
1749− // .into_any()
1750− // } else {
1751− // mouse_event_handler.into_any()
1752− // }
1753− // }
1754− // });
1755−
1756− // if !pane_active || !tab_active {
1757− // receiver = receiver.with_cursor_style(CursorStyle::PointingHand);
1758− // }
1759−
1760− // receiver.as_draggable(
1761− // DraggedItem {
1762− // handle: item,
1763− // pane: pane.clone(),
1764− // },
1765− // {
1766− // let theme = theme::current(cx).clone();
1767−
1768− // let detail = detail.clone();
1769− // move |_, dragged_item: &DraggedItem, cx: &mut ViewContext<Workspace>| {
1770− // let tab_style = &theme.workspace.tab_bar.dragged_tab;
1771− // Self::render_dragged_tab(
1772− // &dragged_item.handle,
1773− // dragged_item.pane.clone(),
1774− // false,
1775− // detail,
1776− // false,
1777− // &tab_style,
1778− // cx,
1779− // )
1780− // }
1781− // },
1782− // )
1783− // })
1784− // }
1785−
1786− // // Use the inactive tab style along with the current pane's active status to decide how to render
1787− // // the filler
1788− // let filler_index = self.items.len();
1789− // let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false);
1790− // enum Filler {}
1791− // row.add_child(
1792− // dragged_item_receiver::<Filler, _, _>(self, 0, filler_index, true, None, cx, |_, _| {
1793− // Empty::new()
1794− // .contained()
1795− // .with_style(filler_style.container)
1796− // .with_border(filler_style.container.border)
1797− // })
1798− // .flex(1., true)
1799− // .into_any_named("filler"),
1800− // );
1801−
1802− // row
1803− // }
1804−
18051690 fn tab_details(&self, cx: &AppContext) -> Vec<usize> {
18061691 let mut tab_details = self.items.iter().map(|_| 0).collect::<Vec<_>>();
18071692
@@ -1839,192 +1724,6 @@ impl Pane { · 31 unmodified lines
18391724 tab_details
18401725 }
18411726
1842− // fn render_tab(
1843− // item: &Box<dyn ItemHandle>,
1844− // pane: WeakView<Pane>,
1845− // first: bool,
1846− // detail: Option<usize>,
1847− // hovered: bool,
1848− // tab_style: &theme::Tab,
1849− // cx: &mut ViewContext<Self>,
1850− // ) -> AnyElement<Self> {
1851− // let title = item.tab_content(detail, &tab_style, cx);
1852− // Self::render_tab_with_title(title, item, pane, first, hovered, tab_style, cx)
1853− // }
1854−
1855− // fn render_dragged_tab(
1856− // item: &Box<dyn ItemHandle>,
1857− // pane: WeakView<Pane>,
1858− // first: bool,
1859− // detail: Option<usize>,
1860− // hovered: bool,
1861− // tab_style: &theme::Tab,
1862− // cx: &mut ViewContext<Workspace>,
1863− // ) -> AnyElement<Workspace> {
1864− // let title = item.dragged_tab_content(detail, &tab_style, cx);
1865− // Self::render_tab_with_title(title, item, pane, first, hovered, tab_style, cx)
1866− // }
1867−
1868− // fn render_tab_with_title<T: View>(
1869− // title: AnyElement<T>,
1870− // item: &Box<dyn ItemHandle>,
1871− // pane: WeakView<Pane>,
1872− // first: bool,
1873− // hovered: bool,
1874− // tab_style: &theme::Tab,
1875− // cx: &mut ViewContext<T>,
1876− // ) -> AnyElement<T> {
1877− // let mut container = tab_style.container.clone();
1878− // if first {
1879− // container.border.left = false;
1880− // }
1881−
1882− // let buffer_jewel_element = {
1883− // let diameter = 7.0;
1884− // let icon_color = if item.has_conflict(cx) {
1885− // Some(tab_style.icon_conflict)
1886− // } else if item.is_dirty(cx) {
1887− // Some(tab_style.icon_dirty)
1888− // } else {
1889− // None
1890− // };
1891−
1892− // Canvas::new(move |bounds, _, _, cx| {
1893− // if let Some(color) = icon_color {
1894− // let square = RectF::new(bounds.origin(), vec2f(diameter, diameter));
1895− // cx.scene().push_quad(Quad {
1896− // bounds: square,
1897− // background: Some(color),
1898− // border: Default::default(),
1899− // corner_radii: (diameter / 2.).into(),
1900− // });
1901− // }
1902− // })
1903− // .constrained()
1904− // .with_width(diameter)
1905− // .with_height(diameter)
1906− // .aligned()
1907− // };
1908−
1909− // let title_element = title.aligned().contained().with_style(ContainerStyle {
1910− // margin: Margin {
1911− // left: tab_style.spacing,
1912− // right: tab_style.spacing,
1913− // ..Default::default()
1914− // },
1915− // ..Default::default()
1916− // });
1917−
1918− // let close_element = if hovered {
1919− // let item_id = item.id();
1920− // enum TabCloseButton {}
1921− // let icon = Svg::new("icons/x.svg");
1922− // MouseEventHandler::new::<TabCloseButton, _>(item_id, cx, |mouse_state, _| {
1923− // if mouse_state.hovered() {
1924− // icon.with_color(tab_style.icon_close_active)
1925− // } else {
1926− // icon.with_color(tab_style.icon_close)
1927− // }
1928− // })
1929− // .with_padding(Padding::uniform(4.))
1930− // .with_cursor_style(CursorStyle::PointingHand)
1931− // .on_click(MouseButton::Left, {
1932− // let pane = pane.clone();
1933− // move |_, _, cx| {
1934− // let pane = pane.clone();
1935− // cx.window_context().defer(move |cx| {
1936− // if let Some(pane) = pane.upgrade(cx) {
1937− // pane.update(cx, |pane, cx| {
1938− // pane.close_item_by_id(item_id, SaveIntent::Close, cx)
1939− // .detach_and_log_err(cx);
1940− // });
1941− // }
1942− // });
1943− // }
1944− // })
1945− // .into_any_named("close-tab-icon")
1946− // .constrained()
1947− // } else {
1948− // Empty::new().constrained()
1949− // }
1950− // .with_width(tab_style.close_icon_width)
1951− // .aligned();
1952−
1953− // let close_right = settings::get::<ItemSettings>(cx).close_position.right();
1954−
1955− // if close_right {
1956− // Flex::row()
1957− // .with_child(buffer_jewel_element)
1958− // .with_child(title_element)
1959− // .with_child(close_element)
1960− // } else {
1961− // Flex::row()
1962− // .with_child(close_element)
1963− // .with_child(title_element)
1964− // .with_child(buffer_jewel_element)
1965− // }
1966− // .contained()
1967− // .with_style(container)
1968− // .constrained()
1969− // .with_height(tab_style.height)
1970− // .into_any()
1971− // }
1972−
1973− // pub fn render_tab_bar_button<
1974− // F1: 'static + Fn(&mut Pane, &mut EventContext<Pane>),
1975− // F2: 'static + Fn(&mut Pane, &mut EventContext<Pane>),
1976− // >(
1977− // index: usize,
1978− // icon: &'static str,
1979− // is_active: bool,
1980− // tooltip: Option<(&'static str, Option<Box<dyn Action>>)>,
1981− // cx: &mut ViewContext<Pane>,
1982− // on_click: F1,
1983− // on_down: F2,
1984− // context_menu: Option<ViewHandle<ContextMenu>>,
1985− // ) -> AnyElement<Pane> {
1986− // enum TabBarButton {}
1987−
1988− // let mut button = MouseEventHandler::new::<TabBarButton, _>(index, cx, |mouse_state, cx| {
1989− // let theme = &settings2::get::<ThemeSettings>(cx).theme.workspace.tab_bar;
1990− // let style = theme.pane_button.in_state(is_active).style_for(mouse_state);
1991− // Svg::new(icon)
1992− // .with_color(style.color)
1993− // .constrained()
1994− // .with_width(style.icon_width)
1995− // .aligned()
1996− // .constrained()
1997− // .with_width(style.button_width)
1998− // .with_height(style.button_width)
1999− // })
2000− // .with_cursor_style(CursorStyle::PointingHand)
2001− // .on_down(MouseButton::Left, move |_, pane, cx| on_down(pane, cx))
2002− // .on_click(MouseButton::Left, move |_, pane, cx| on_click(pane, cx))
2003− // .into_any();
2004− // if let Some((tooltip, action)) = tooltip {
2005− // let tooltip_style = settings::get::<ThemeSettings>(cx).theme.tooltip.clone();
2006− // button = button
2007− // .with_tooltip::<TabBarButton>(index, tooltip, action, tooltip_style, cx)
2008− // .into_any();
2009− // }
2010−
2011− // Stack::new()
2012− // .with_child(button)
2013− // .with_children(
2014− // context_menu.map(|menu| ChildView::new(&menu, cx).aligned().bottom().right()),
2015− // )
2016− // .flex(1., false)
2017− // .into_any_named("tab bar button")
2018− // }
2019−
2020− // fn render_blank_pane(&self, theme: &Theme, _cx: &mut ViewContext<Self>) -> AnyElement<Self> {
2021− // let background = theme.workspace.background;
2022− // Empty::new()
2023− // .contained()
2024− // .with_background_color(background)
2025− // .into_any()
2026− // }
2027−
20281727 pub fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext<Self>) {
20291728 self.zoomed = zoomed;
20301729 cx.notify();
@@ -2033,6 +1732,25 @@ impl Pane { · 2 unmodified lines
20331732 pub fn is_zoomed(&self) -> bool {
20341733 self.zoomed
20351734 }
1735+
1736+ fn handle_tab_drop(
1737+ &mut self,
1738+ dragged_tab: &View<DraggedTab>,
1739+ ix: usize,
1740+ cx: &mut ViewContext<'_, Pane>,
1741+ ) {
1742+ let dragged_tab = dragged_tab.read(cx);
1743+ let item_id = dragged_tab.item_id;
1744+ let from_pane = dragged_tab.pane.clone();
1745+ let to_pane = cx.view().clone();
1746+ self.workspace
1747+ .update(cx, |workspace, cx| {
1748+ cx.defer(move |workspace, cx| {
1749+ workspace.move_item(from_pane, to_pane, item_id, ix, cx);
1750+ });
1751+ })
1752+ .log_err();
1753+ }
20361754 }
20371755
20381756 impl FocusableView for Pane {
@@ -2134,148 +1852,6 @@ impl Render for Pane { · 95 unmodified lines
21341852 .justify_center()
21351853 .child(Label::new("Open a file or project to get started.").color(Color::Muted))
21361854 })
2137− // enum MouseNavigationHandler {}
2138− // MouseEventHandler::new::<MouseNavigationHandler, _>(0, cx, |_, cx| {
2139− // let active_item_index = self.active_item_index;
2140− // if let Some(active_item) = self.active_item() {
2141− // Flex::column()
2142− // .with_child({
2143− // let theme = theme::current(cx).clone();
2144− // let mut stack = Stack::new();
2145− // enum TabBarEventHandler {}
2146− // stack.add_child(
2147− // MouseEventHandler::new::<TabBarEventHandler, _>(0, cx, |_, _| {
2148− // Empty::new()
2149− // .contained()
2150− // .with_style(theme.workspace.tab_bar.container)
2151− // })
2152− // .on_down(
2153− // MouseButton::Left,
2154− // move |_, this, cx| {
2155− // this.activate_item(active_item_index, true, true, cx);
2156− // },
2157− // ),
2158− // );
2159− // let tooltip_style = theme.tooltip.clone();
2160− // let tab_bar_theme = theme.workspace.tab_bar.clone();
2161− // let nav_button_height = tab_bar_theme.height;
2162− // let button_style = tab_bar_theme.nav_button;
2163− // let border_for_nav_buttons = tab_bar_theme
2164− // .tab_style(false, false)
2165− // .container
2166− // .border
2167− // .clone();
2168− // let mut tab_row = Flex::row()
2169− // .with_child(nav_button(
2170− // "icons/arrow_left.svg",
2171− // button_style.clone(),
2172− // nav_button_height,
2173− // tooltip_style.clone(),
2174− // self.can_navigate_backward(),
2175− // {
2176− // move |pane, cx| {
2177− // if let Some(workspace) = pane.workspace.upgrade(cx) {
2178− // let pane = cx.weak_handle();
2179− // cx.window_context().defer(move |cx| {
2180− // workspace.update(cx, |workspace, cx| {
2181− // workspace
2182− // .go_back(pane, cx)
2183− // .detach_and_log_err(cx)
2184− // })
2185− // })
2186− // }
2187− // }
2188− // },
2189− // super::GoBack,
2190− // "Go Back",
2191− // cx,
2192− // ))
2193− // .with_child(
2194− // nav_button(
2195− // "icons/arrow_right.svg",
2196− // button_style.clone(),
2197− // nav_button_height,
2198− // tooltip_style,
2199− // self.can_navigate_forward(),
2200− // {
2201− // move |pane, cx| {
2202− // if let Some(workspace) = pane.workspace.upgrade(cx) {
2203− // let pane = cx.weak_handle();
2204− // cx.window_context().defer(move |cx| {
2205− // workspace.update(cx, |workspace, cx| {
2206− // workspace
2207− // .go_forward(pane, cx)
2208− // .detach_and_log_err(cx)
2209− // })
2210− // })
2211− // }
2212− // }
2213− // },
2214− // super::GoForward,
2215− // "Go Forward",
2216− // cx,
2217− // )
2218− // .contained()
2219− // .with_border(border_for_nav_buttons),
2220− // )
2221− // .with_child(self.render_tabs(cx).flex(1., true).into_any_named("tabs"));
2222− // if self.has_focus {
2223− // let render_tab_bar_buttons = self.render_tab_bar_buttons.clone();
2224− // tab_row.add_child(
2225− // (render_tab_bar_buttons)(self, cx)
2226− // .contained()
2227− // .with_style(theme.workspace.tab_bar.pane_button_container)
2228− // .flex(1., false)
2229− // .into_any(),
2230− // )
2231− // }
2232− // stack.add_child(tab_row);
2233− // stack
2234− // .constrained()
2235− // .with_height(theme.workspace.tab_bar.height)
2236− // .flex(1., false)
2237− // .into_any_named("tab bar")
2238− // })
2239− // .with_child({
2240− // enum PaneContentTabDropTarget {}
2241− // dragged_item_receiver::<PaneContentTabDropTarget, _, _>(
2242− // self,
2243− // 0,
2244− // self.active_item_index + 1,
2245− // !self.can_split,
2246− // if self.can_split { Some(100.) } else { None },
2247− // cx,
2248− // {
2249− // let toolbar = self.toolbar.clone();
2250− // let toolbar_hidden = toolbar.read(cx).hidden();
2251− // move |_, cx| {
2252− // Flex::column()
2253− // .with_children(
2254− // (!toolbar_hidden)
2255− // .then(|| ChildView::new(&toolbar, cx).expanded()),
2256− // )
2257− // .with_child(
2258− // ChildView::new(active_item.as_any(), cx).flex(1., true),
2259− // )
2260− // }
2261− // },
2262− // )
2263− // .flex(1., true)
2264− // })
2265− // .with_child(ChildView::new(&self.tab_context_menu, cx))
2266− // .into_any()
2267− // } else {
2268− // enum EmptyPane {}
2269− // let theme = theme::current(cx).clone();
2270− // dragged_item_receiver::<EmptyPane, _, _>(self, 0, 0, false, None, cx, |_, cx| {
2271− // self.render_blank_pane(&theme, cx)
2272− // })
2273− // .on_down(MouseButton::Left, |_, _, cx| {
2274− // cx.focus_parent();
2275− // })
2276− // .into_any()
2277− // }
2278− // })
22791855 .on_mouse_down(
22801856 MouseButton::Navigate(NavigationDirection::Back),
22811857 cx.listener(|pane, _, cx| {
@@ -2302,7 +1878,6 @@ impl Render for Pane { · 20 unmodified lines
23021878 }
23031879 }),
23041880 )
2305− // .into_any_named("pane")
23061881 }
23071882
23081883 // fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
@@ -3131,15 +2706,17 @@ fn dirty_message_for(buffer_path: Option<ProjectPath>) -> String { · 822 unmodified lines
31312706 // }
31322707 // }
31332708
3134−#[derive(Clone, Debug)]
3135−struct DraggedTab {
3136− title: String,
3137−}
3138−
31392709 impl Render for DraggedTab {
3140− type Element = Div;
2710+ type Element = <Tab as RenderOnce>::Rendered;
31412711
31422712 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
3143− div().w_8().h_4().bg(gpui::red())
2713+ let ui_font = ThemeSettings::get_global(cx).ui_font.family.clone();
2714+ let item = &self.pane.read(cx).items[self.ix];
2715+ let label = item.tab_content(Some(self.detail), false, cx);
2716+ Tab::new("")
2717+ .selected(self.is_active)
2718+ .child(label)
2719+ .render(cx)
2720+ .font(ui_font)
31442721 }
31452722 }

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t