From 636222136374d3b16385a84c5c264c88edf32851 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 12 Dec 2023 15:42:33 -0800 Subject: [PATCH] Scroll the tab bar to show the active tab Co-authored-by: Nathan Co-authored-by: Marshall --- crates/gpui2/src/elements/div.rs | 25 ++++++++++++++++++++----- crates/ui2/src/components/tab_bar.rs | 27 +++++++++++++++------------ crates/workspace2/src/pane.rs | 23 ++++++++++++++--------- 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 48a21372e6..371f554337 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -1016,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) @@ -1420,6 +1424,7 @@ struct ScrollHandleState { bounds: Bounds, child_bounds: Vec>, requested_scroll_top: Option<(usize, Pixels)>, + overflow: Point, } #[derive(Clone)] @@ -1465,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/ui2/src/components/tab_bar.rs b/crates/ui2/src/components/tab_bar.rs index e3edbad5dd..7cff2f51bd 100644 --- a/crates/ui2/src/components/tab_bar.rs +++ b/crates/ui2/src/components/tab_bar.rs @@ -1,26 +1,33 @@ -use gpui::{AnyElement, Stateful}; +use gpui::{AnyElement, ScrollHandle, Stateful}; use smallvec::SmallVec; use crate::prelude::*; #[derive(IntoElement)] pub struct TabBar { - div: Stateful
, + id: ElementId, 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) -> Self { Self { - div: div().id(id), + id: id.into(), 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 } @@ -81,21 +88,14 @@ impl ParentElement for TabBar { } } -impl InteractiveElement for TabBar { - fn interactivity(&mut self) -> &mut gpui::Interactivity { - self.div.interactivity() - } -} - -impl StatefulInteractiveElement for TabBar {} - impl RenderOnce for TabBar { type Rendered = Stateful
; fn render(self, cx: &mut WindowContext) -> Self::Rendered { const HEIGHT_IN_REMS: f32 = 30. / 16.; - self.div + div() + .id(self.id) .group("tab_bar") .flex() .flex_none() @@ -134,6 +134,9 @@ impl RenderOnce for TabBar { .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 f4baa1d7ef..2068245159 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}; @@ -177,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, @@ -190,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 { @@ -353,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(), @@ -364,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, @@ -469,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); } @@ -478,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| { @@ -794,7 +799,7 @@ impl Pane { self.focus_active_item(cx); } - self.autoscroll = true; + self.tab_bar_scroll_handle.scroll_to_item(index); cx.notify(); } } @@ -1584,6 +1589,7 @@ impl Pane { fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement { TabBar::new("tab_bar") + .track_scroll(self.tab_bar_scroll_handle.clone()) .start_child( IconButton::new("navigate_backward", Icon::ArrowLeft) .icon_size(IconSize::Small) @@ -1669,7 +1675,6 @@ impl Pane { }), ), ) - .track_focus(&self.tab_bar_focus_handle) } fn render_menu_overlay(menu: &View) -> Div {