diff --git a/crates/collab_ui2/src/chat_panel.rs b/crates/collab_ui2/src/chat_panel.rs index 6715ddf5138d2f..587efbe95f2724 100644 --- a/crates/collab_ui2/src/chat_panel.rs +++ b/crates/collab_ui2/src/chat_panel.rs @@ -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) diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index bfa2e3ecdce88b..371f55433702cb 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -29,7 +29,7 @@ pub struct GroupStyle { pub style: Box, } -pub trait InteractiveElement: Sized + Element { +pub trait InteractiveElement: Sized { fn interactivity(&mut self) -> &mut Interactivity; fn group(mut self, group: impl Into) -> 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 IntoElement for Focusable where - E: Element, + E: IntoElement, { - type Element = E; + type Element = E::Element; fn element_id(&self) -> Option { self.element.element_id() } fn into_element(self) -> Self::Element { - self.element + self.element.into_element() } } @@ -1417,6 +1424,7 @@ struct ScrollHandleState { bounds: Bounds, child_bounds: Vec>, requested_scroll_top: Option<(usize, Pixels)>, + overflow: Point, } #[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(); + } } } diff --git a/crates/gpui2/src/styled.rs b/crates/gpui2/src/styled.rs index 209169a9a6ecf0..a39e5f9cf95588 100644 --- a/crates/gpui2/src/styled.rs +++ b/crates/gpui2/src/styled.rs @@ -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 } diff --git a/crates/picker2/src/picker2.rs b/crates/picker2/src/picker2.rs index 54df321cb1202d..98b6ce5ff09932 100644 --- a/crates/picker2/src/picker2.rs +++ b/crates/picker2/src/picker2.rs @@ -228,7 +228,7 @@ impl Render for Picker { .when(self.delegate.match_count() > 0, |el| { el.child( v_stack() - .grow() + .flex_grow() .child( uniform_list( cx.view().clone(), diff --git a/crates/storybook2/src/story_selector.rs b/crates/storybook2/src/story_selector.rs index 039cd46ee8a070..bd87e1fde9d60b 100644 --- a/crates/storybook2/src/story_selector.rs +++ b/crates/storybook2/src/story_selector.rs @@ -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(), diff --git a/crates/ui2/src/components/list/list_item.rs b/crates/ui2/src/components/list/list_item.rs index 5a337c316af97f..28a8b8cecbf159 100644 --- a/crates/ui2/src/components/list/list_item.rs +++ b/crates/ui2/src/components/list/list_item.rs @@ -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 diff --git a/crates/ui2/src/components/stories/tab_bar.rs b/crates/ui2/src/components/stories/tab_bar.rs index 44ca59afe26e0a..bd2b5a534aaabd 100644 --- a/crates/ui2/src/components/stories/tab_bar.rs +++ b/crates/ui2/src/components/stories/tab_bar.rs @@ -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 { - 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), diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index 7a40a6ed0d321e..be1ce8dd12e9df 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -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
, selected: bool, position: TabPosition, close_side: TabCloseSide, - on_click: Option>, - tooltip: Option AnyView + 'static>>, start_slot: Option, end_slot: Option, children: SmallVec<[AnyElement; 2]>, @@ -43,12 +38,10 @@ pub struct Tab { impl Tab { pub fn new(id: impl Into) -> 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(mut self, element: impl Into>) -> 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
; 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() diff --git a/crates/ui2/src/components/tab_bar.rs b/crates/ui2/src/components/tab_bar.rs index dc31eefd0a69b4..7cff2f51bd80f4 100644 --- a/crates/ui2/src/components/tab_bar.rs +++ b/crates/ui2/src/components/tab_bar.rs @@ -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, } impl TabBar { - pub fn new(id: impl Into, focus_handle: FocusHandle) -> Self { + pub fn new(id: impl Into) -> 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>; + type Rendered = Stateful
; 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), ), ) diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index 9674e282631124..20682451596f98 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -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, - autoscroll: bool, nav_history: NavHistory, toolbar: View, - tab_bar_focus_handle: FocusHandle, new_item_menu: Option>, split_item_menu: Option>, // tab_context_menu: ViewHandle, @@ -189,6 +188,7 @@ pub struct Pane { can_split: bool, // render_tab_bar_buttons: Rc) -> AnyElement>, subscriptions: Vec, + tab_bar_scroll_handle: ScrollHandle, } pub struct ItemNavHistory { @@ -231,6 +231,14 @@ pub struct NavigationEntry { pub timestamp: usize, } +struct DraggedTab { + pub pane: View, + pub ix: usize, + pub item_id: EntityId, + pub detail: usize, + pub is_active: bool, +} + // pub struct DraggedItem { // pub handle: Box, // pub pane: WeakView, @@ -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) -> 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.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::(|d| d.bg(cx.theme().colors().element_drop_target)) - // .on_drop(|_view, state: View, 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::(|tab| tab.bg(cx.theme().colors().tab_active_background)) + .on_drop( + cx.listener(move |this, dragged_tab: &View, 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::(|bar| { + bar.bg(cx.theme().colors().tab_active_background) + }) + .on_drop( + cx.listener(move |this, dragged_tab: &View, cx| { + this.handle_tab_drop(dragged_tab, this.items.len(), cx) + }), + ), + ) } fn render_menu_overlay(menu: &View) -> Div { @@ -1647,161 +1687,6 @@ impl Pane { .child(overlay().anchor(AnchorCorner::TopRight).child(menu.clone())) } - // fn render_tabs(&mut self, cx: &mut ViewContext) -> impl Element { - // 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::(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::(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::(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::(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::( - // 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| { - // 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::(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 { let mut tab_details = self.items.iter().map(|_| 0).collect::>(); @@ -1839,192 +1724,6 @@ impl Pane { tab_details } - // fn render_tab( - // item: &Box, - // pane: WeakView, - // first: bool, - // detail: Option, - // hovered: bool, - // tab_style: &theme::Tab, - // cx: &mut ViewContext, - // ) -> AnyElement { - // 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, - // pane: WeakView, - // first: bool, - // detail: Option, - // hovered: bool, - // tab_style: &theme::Tab, - // cx: &mut ViewContext, - // ) -> AnyElement { - // 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( - // title: AnyElement, - // item: &Box, - // pane: WeakView, - // first: bool, - // hovered: bool, - // tab_style: &theme::Tab, - // cx: &mut ViewContext, - // ) -> AnyElement { - // 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::(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::(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), - // F2: 'static + Fn(&mut Pane, &mut EventContext), - // >( - // index: usize, - // icon: &'static str, - // is_active: bool, - // tooltip: Option<(&'static str, Option>)>, - // cx: &mut ViewContext, - // on_click: F1, - // on_down: F2, - // context_menu: Option>, - // ) -> AnyElement { - // enum TabBarButton {} - - // let mut button = MouseEventHandler::new::(index, cx, |mouse_state, cx| { - // let theme = &settings2::get::(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::(cx).theme.tooltip.clone(); - // button = button - // .with_tooltip::(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) -> AnyElement { - // 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.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, + 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::(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::(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::( - // 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::(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) { @@ -3131,15 +2706,17 @@ fn dirty_message_for(buffer_path: Option) -> String { // } // } -#[derive(Clone, Debug)] -struct DraggedTab { - title: String, -} - impl Render for DraggedTab { - type Element = Div; + type Element = ::Rendered; fn render(&mut self, cx: &mut ViewContext) -> 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) } }