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:
parent
a579713a45
commit
6362221363
3 changed files with 49 additions and 26 deletions
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue