Scroll the tab bar to show the active tab

Co-authored-by: Nathan <nathan@zed.dev>
Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
Max Brunsfeld 2023-12-12 15:42:33 -08:00
parent a579713a45
commit 6362221363
3 changed files with 49 additions and 26 deletions

View file

@ -1016,6 +1016,10 @@ impl Interactivity {
let overflow = style.overflow; let overflow = style.overflow;
if overflow.x == Overflow::Scroll || overflow.y == Overflow::Scroll { 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 let scroll_offset = element_state
.scroll_offset .scroll_offset
.get_or_insert_with(Rc::default) .get_or_insert_with(Rc::default)
@ -1420,6 +1424,7 @@ struct ScrollHandleState {
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
child_bounds: Vec<Bounds<Pixels>>, child_bounds: Vec<Bounds<Pixels>>,
requested_scroll_top: Option<(usize, Pixels)>, requested_scroll_top: Option<(usize, Pixels)>,
overflow: Point<Overflow>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -1465,12 +1470,22 @@ impl ScrollHandle {
return; return;
}; };
let scroll_offset = state.offset.borrow().y; let mut scroll_offset = state.offset.borrow_mut();
if bounds.top() + scroll_offset < state.bounds.top() { if state.overflow.y == Overflow::Scroll {
state.offset.borrow_mut().y = state.bounds.top() - bounds.top(); if bounds.top() + scroll_offset.y < state.bounds.top() {
} else if bounds.bottom() + scroll_offset > state.bounds.bottom() { scroll_offset.y = state.bounds.top() - bounds.top();
state.offset.borrow_mut().y = state.bounds.bottom() - bounds.bottom(); } 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();
}
} }
} }

View file

@ -1,26 +1,33 @@
use gpui::{AnyElement, Stateful}; use gpui::{AnyElement, ScrollHandle, Stateful};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct TabBar { pub struct TabBar {
div: Stateful<Div>, id: ElementId,
start_children: SmallVec<[AnyElement; 2]>, start_children: SmallVec<[AnyElement; 2]>,
children: SmallVec<[AnyElement; 2]>, children: SmallVec<[AnyElement; 2]>,
end_children: SmallVec<[AnyElement; 2]>, end_children: SmallVec<[AnyElement; 2]>,
scroll_handle: Option<ScrollHandle>,
} }
impl TabBar { impl TabBar {
pub fn new(id: impl Into<ElementId>) -> Self { pub fn new(id: impl Into<ElementId>) -> Self {
Self { Self {
div: div().id(id), id: id.into(),
start_children: SmallVec::new(), start_children: SmallVec::new(),
children: SmallVec::new(), children: SmallVec::new(),
end_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]> { pub fn start_children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
&mut self.start_children &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 { impl RenderOnce for TabBar {
type Rendered = Stateful<Div>; type Rendered = Stateful<Div>;
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
const HEIGHT_IN_REMS: f32 = 30. / 16.; const HEIGHT_IN_REMS: f32 = 30. / 16.;
self.div div()
.id(self.id)
.group("tab_bar") .group("tab_bar")
.flex() .flex()
.flex_none() .flex_none()
@ -134,6 +134,9 @@ impl RenderOnce for TabBar {
.z_index(2) .z_index(2)
.flex_grow() .flex_grow()
.overflow_x_scroll() .overflow_x_scroll()
.when_some(self.scroll_handle, |cx, scroll_handle| {
cx.track_scroll(&scroll_handle)
})
.children(self.children), .children(self.children),
), ),
) )

View file

@ -10,7 +10,7 @@ use gpui::{
actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext, actions, impl_actions, overlay, prelude::*, Action, AnchorCorner, AnyWeakView, AppContext,
AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable, AsyncWindowContext, DismissEvent, Div, EntityId, EventEmitter, FocusHandle, Focusable,
FocusableView, Model, MouseButton, NavigationDirection, Pixels, Point, PromptLevel, Render, 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 parking_lot::Mutex;
use project::{Project, ProjectEntryId, ProjectPath}; use project::{Project, ProjectEntryId, ProjectPath};
@ -177,10 +177,8 @@ pub struct Pane {
was_focused: bool, was_focused: bool,
active_item_index: usize, active_item_index: usize,
last_focused_view_by_item: HashMap<EntityId, FocusHandle>, last_focused_view_by_item: HashMap<EntityId, FocusHandle>,
autoscroll: bool,
nav_history: NavHistory, nav_history: NavHistory,
toolbar: View<Toolbar>, toolbar: View<Toolbar>,
tab_bar_focus_handle: FocusHandle,
new_item_menu: Option<View<ContextMenu>>, new_item_menu: Option<View<ContextMenu>>,
split_item_menu: Option<View<ContextMenu>>, split_item_menu: Option<View<ContextMenu>>,
// tab_context_menu: ViewHandle<ContextMenu>, // tab_context_menu: ViewHandle<ContextMenu>,
@ -190,6 +188,7 @@ pub struct Pane {
can_split: bool, can_split: bool,
// render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>, // render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> AnyElement<Pane>>,
subscriptions: Vec<Subscription>, subscriptions: Vec<Subscription>,
tab_bar_scroll_handle: ScrollHandle,
} }
pub struct ItemNavHistory { pub struct ItemNavHistory {
@ -353,7 +352,6 @@ impl Pane {
zoomed: false, zoomed: false,
active_item_index: 0, active_item_index: 0,
last_focused_view_by_item: Default::default(), last_focused_view_by_item: Default::default(),
autoscroll: false,
nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState { nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState {
mode: NavigationMode::Normal, mode: NavigationMode::Normal,
backward_stack: Default::default(), backward_stack: Default::default(),
@ -364,9 +362,9 @@ impl Pane {
next_timestamp, next_timestamp,
}))), }))),
toolbar: cx.build_view(|_| Toolbar::new()), toolbar: cx.build_view(|_| Toolbar::new()),
tab_bar_focus_handle: cx.focus_handle(),
new_item_menu: None, new_item_menu: None,
split_item_menu: None, split_item_menu: None,
tab_bar_scroll_handle: ScrollHandle::new(),
// tab_bar_context_menu: TabBarContextMenu { // tab_bar_context_menu: TabBarContextMenu {
// kind: TabBarContextMenuKind::New, // kind: TabBarContextMenuKind::New,
// handle: context_menu, // handle: context_menu,
@ -469,8 +467,8 @@ impl Pane {
} }
active_item.focus_handle(cx).focus(cx); active_item.focus_handle(cx).focus(cx);
} else if !self.tab_bar_focus_handle.contains_focused(cx) { } else if let Some(focused) = cx.focused() {
if let Some(focused) = cx.focused() { if !self.context_menu_focused(cx) {
self.last_focused_view_by_item self.last_focused_view_by_item
.insert(active_item.item_id(), focused); .insert(active_item.item_id(), focused);
} }
@ -478,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>) { fn focus_out(&mut self, cx: &mut ViewContext<Self>) {
self.was_focused = false; self.was_focused = false;
self.toolbar.update(cx, |toolbar, cx| { self.toolbar.update(cx, |toolbar, cx| {
@ -794,7 +799,7 @@ impl Pane {
self.focus_active_item(cx); self.focus_active_item(cx);
} }
self.autoscroll = true; self.tab_bar_scroll_handle.scroll_to_item(index);
cx.notify(); cx.notify();
} }
} }
@ -1584,6 +1589,7 @@ impl Pane {
fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement { fn render_tab_bar(&mut self, cx: &mut ViewContext<'_, Pane>) -> impl IntoElement {
TabBar::new("tab_bar") TabBar::new("tab_bar")
.track_scroll(self.tab_bar_scroll_handle.clone())
.start_child( .start_child(
IconButton::new("navigate_backward", Icon::ArrowLeft) IconButton::new("navigate_backward", Icon::ArrowLeft)
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
@ -1669,7 +1675,6 @@ impl Pane {
}), }),
), ),
) )
.track_focus(&self.tab_bar_focus_handle)
} }
fn render_menu_overlay(menu: &View<ContextMenu>) -> Div { fn render_menu_overlay(menu: &View<ContextMenu>) -> Div {