Add post-layout
callback to the ScrollView
This change adds a new `post-layout` callback to the `ScrollView` to allow the scroll view to invoke the callback at the end of every layout operation. This is a useful feature to allow the user to explicitly scroll the contents to a specific position after a layout and update the scrollbars. It would allow e.g. to restore the position of the scrolled view in a large content view when the contents is scrolled out of the view. Bug: b:413733359 Change-Id: I9e58136dbbedb3c128b31bba669db060900a87ac Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6498548 Auto-Submit: Leonid Baraz <lbaraz@chromium.org> Commit-Queue: Keren Zhu <kerenzhu@chromium.org> Reviewed-by: Keren Zhu <kerenzhu@chromium.org> Commit-Queue: Leonid Baraz <lbaraz@chromium.org> Cr-Commit-Position: refs/heads/main@{#1454078}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
30fe1b3622
commit
a4b960a7ad
ui/views/controls
@ -834,6 +834,15 @@ void ScrollView::Layout(PassKey) {
|
||||
if (contents_) {
|
||||
UpdateOverflowIndicatorVisibility(CurrentOffset());
|
||||
}
|
||||
|
||||
// If registered, run the post-layout callback. This is used to move the
|
||||
// scroll view contents to the appropriate position that's different from the
|
||||
// position assigned above.
|
||||
if (post_layout_callback_) {
|
||||
const bool layout_needed = needs_layout();
|
||||
post_layout_callback_.Run(this);
|
||||
CHECK_EQ(layout_needed, needs_layout());
|
||||
}
|
||||
}
|
||||
|
||||
bool ScrollView::OnKeyPressed(const ui::KeyEvent& event) {
|
||||
@ -1362,6 +1371,11 @@ void ScrollView::UpdateOverflowIndicatorVisibility(const gfx::PointF& offset) {
|
||||
offset.x() < horiz_sb_->GetMaxPosition() && draw_overflow_indicator_);
|
||||
}
|
||||
|
||||
void ScrollView::RegisterPostLayoutCallback(
|
||||
base::RepeatingCallback<void(ScrollView*)> post_layout_callback) {
|
||||
post_layout_callback_ = post_layout_callback;
|
||||
}
|
||||
|
||||
View* ScrollView::GetContentsViewportForTest() const {
|
||||
return contents_viewport_;
|
||||
}
|
||||
|
@ -242,6 +242,15 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
|
||||
bool is_positive) override;
|
||||
void OnScrollEnded() override;
|
||||
|
||||
// Registers a callback to be called after the layout is complete. This
|
||||
// callback can be used e.g. to scroll the view to the appropriate position
|
||||
// in the contents by explicitly calling `ScrollToOffset` or `ScrollByOffset`
|
||||
// and to update the scrollbars to reflect the new position.
|
||||
// The callback should not trigger any new layouts on the scroll view,
|
||||
// otherwise it will lead to a CHECK failure.
|
||||
void RegisterPostLayoutCallback(
|
||||
base::RepeatingCallback<void(ScrollView*)> post_layout_callback);
|
||||
|
||||
bool is_scrolling() const {
|
||||
return horiz_sb_->is_scrolling() || vert_sb_->is_scrolling();
|
||||
}
|
||||
@ -399,6 +408,9 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController {
|
||||
// Scrolling callbacks.
|
||||
ScrollViewCallbackList on_contents_scrolled_;
|
||||
ScrollViewCallbackList on_contents_scroll_ended_;
|
||||
|
||||
// Post-layout callback.
|
||||
base::RepeatingCallback<void(ScrollView*)> post_layout_callback_;
|
||||
};
|
||||
|
||||
// When building with GCC this ensures that an instantiation of the
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "base/test/gtest_util.h"
|
||||
#include "base/test/icu_test_util.h"
|
||||
#include "base/test/mock_callback.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/test/test_timeouts.h"
|
||||
#include "base/timer/timer.h"
|
||||
@ -563,6 +564,17 @@ TEST_F(ScrollViewTest, BoundedViewportSizedToFit) {
|
||||
EXPECT_EQ(96, contents->width());
|
||||
}
|
||||
|
||||
// Verifies that the scroll view calls post-layout callback on every layout.
|
||||
TEST_F(ScrollViewTest, LayoutCallbackCalledOnEveryLayout) {
|
||||
auto mock_post_layout_cb_ =
|
||||
base::MockCallback<base::RepeatingCallback<void(ScrollView*)>>();
|
||||
scroll_view_->RegisterPostLayoutCallback(mock_post_layout_cb_.Get());
|
||||
EXPECT_CALL(mock_post_layout_cb_, Run(scroll_view_.get())).Times(1);
|
||||
InstallContents();
|
||||
ASSERT_FALSE(scroll_view_->GetContentsBounds().IsEmpty());
|
||||
views::test::RunScheduledLayout(scroll_view_.get());
|
||||
}
|
||||
|
||||
// Verifies that the vertical scrollbar does not unnecessarily appear for a
|
||||
// contents whose height always matches the height of the viewport.
|
||||
TEST_F(ScrollViewTest, VerticalScrollbarDoesNotAppearUnnecessarily) {
|
||||
|
Reference in New Issue
Block a user