Pane tabs: Scroll entire new tab into view (#36827)

The state of the child bounds is not up-to-date when `scroll_to_item`
gets triggered, causing the new tab to not scroll completely into view.

Closes #36317 

Release Notes:

- Fix an issue where a new tab is only partially visible on creation.
This commit is contained in:
hrou0003
2025-09-30 11:04:34 +02:00
committed by GitHub
parent 624e448492
commit 49335d54be
3 changed files with 94 additions and 34 deletions
+37 -21
View File
@@ -1384,6 +1384,10 @@ impl Element for Div {
(child_max - child_min).into()
};
if let Some(scroll_handle) = self.interactivity.tracked_scroll_handle.as_ref() {
scroll_handle.scroll_to_active_item();
}
self.interactivity.prepaint(
global_id,
inspector_id,
@@ -2986,8 +2990,7 @@ where
}
/// Represents an element that can be scrolled *to* in its parent element.
///
/// Contrary to [ScrollHandle::scroll_to_item], an anchored element does not have to be an immediate child of the parent.
/// Contrary to [ScrollHandle::scroll_to_active_item], an anchored element does not have to be an immediate child of the parent.
#[derive(Clone)]
pub struct ScrollAnchor {
handle: ScrollHandle,
@@ -3022,6 +3025,7 @@ struct ScrollHandleState {
child_bounds: Vec<Bounds<Pixels>>,
scroll_to_bottom: bool,
overflow: Point<Overflow>,
active_item: Option<usize>,
}
/// A handle to the scrollable aspects of an element.
@@ -3081,32 +3085,44 @@ impl ScrollHandle {
self.0.borrow().child_bounds.get(ix).cloned()
}
/// scroll_to_item scrolls the minimal amount to ensure that the child is
/// fully visible
/// Update [ScrollHandleState]'s active item for scrolling to in prepaint
pub fn scroll_to_item(&self, ix: usize) {
let state = self.0.borrow();
let mut state = self.0.borrow_mut();
state.active_item = Some(ix);
}
let Some(bounds) = state.child_bounds.get(ix) else {
/// Scrolls the minimal amount to ensure that the child is
/// fully visible
fn scroll_to_active_item(&self) {
let mut state = self.0.borrow_mut();
let Some(active_item_index) = state.active_item else {
return;
};
let active_item = match state.child_bounds.get(active_item_index) {
Some(bounds) => {
let mut scroll_offset = state.offset.borrow_mut();
let mut scroll_offset = state.offset.borrow_mut();
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.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();
}
}
None
}
}
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();
}
}
None => Some(active_item_index),
};
state.active_item = active_item;
}
/// Scrolls to the bottom.