Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(314)

Unified Diff: ui/views/controls/scroll_view.cc

Issue 2189583004: [not for review - epic CL] Adding Elastic+Momentum+Layered scrolling to views::ScrollView Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Combined rebase Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/views/controls/scroll_view.cc
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
index a3e1e9787a2ae32b3118c2d0ed9837d808f94d25..f75b629bfcb91eaf10a083ba9ef53d41a52349b6 100644
--- a/ui/views/controls/scroll_view.cc
+++ b/ui/views/controls/scroll_view.cc
@@ -4,14 +4,17 @@
#include "ui/views/controls/scroll_view.h"
+#include "base/feature_list.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "ui/compositor/overscroll/ui_scroll_input_manager.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/native_theme/native_theme.h"
+#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/style/platform_style.h"
-#include "ui/views/widget/root_view.h"
+#include "ui/views/widget/widget.h"
namespace views {
@@ -19,6 +22,15 @@ const char ScrollView::kViewClassName[] = "ScrollView";
namespace {
+const base::Feature kToolkitViewsScrollWithLayers {
+ "ToolkitViewsScrollWithLayers",
+#if defined(OS_MACOSX)
+ base::FEATURE_ENABLED_BY_DEFAULT
+#else
+ base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
+
// Subclass of ScrollView that resets the border when the theme changes.
class ScrollViewWithBorder : public views::ScrollView {
public:
@@ -64,15 +76,23 @@ int CheckScrollBounds(int viewport_size, int content_size, int current_pos) {
}
// Make sure the content is not scrolled out of bounds
-void CheckScrollBounds(View* viewport, View* view) {
+void CheckScrollBounds(View* viewport, View* container, View* view) {
if (!view)
return;
- int x = CheckScrollBounds(viewport->width(), view->width(), -view->x());
- int y = CheckScrollBounds(viewport->height(), view->height(), -view->y());
+ gfx::ScrollOffset offset = container
+ ? container->layer()->CurrentScrollOffset()
+ : gfx::ScrollOffset(-view->x(), -view->y());
+
+ int x = CheckScrollBounds(viewport->width(), view->width(), offset.x());
+ int y = CheckScrollBounds(viewport->height(), view->height(), offset.y());
- // This is no op if bounds are the same
- view->SetBounds(-x, -y, view->width(), view->height());
+ if (container) {
+ container->layer()->SetScrollOffset(gfx::ScrollOffset(x, y));
+ } else {
+ // This is no op if bounds are the same
+ view->SetBounds(-x, -y, view->width(), view->height());
+ }
}
// Used by ScrollToPosition() to make sure the new position fits within the
@@ -89,6 +109,19 @@ int AdjustPosition(int current_position,
return (new_position > max_position) ? max_position : new_position;
}
+class ScrollViewContainer : public View {
+ public:
+ ScrollViewContainer() {}
+
+ // View:
+ void ChildPreferredSizeChanged(View* child) override {
+ PreferredSizeChanged();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScrollViewContainer);
+};
+
} // namespace
// Viewport contains the contents View of the ScrollView.
@@ -105,9 +138,17 @@ class ScrollView::Viewport : public View {
View* contents = child_at(0);
gfx::Rect scroll_rect(rect);
+
+ ScrollView* scroll_view = static_cast<ScrollView*>(parent());
+ if (contents == scroll_view->contents_container_) {
+ // With layer scrolling, there's no need to "undo" the offset done in the
+ // child's View::ScrollRectToVisible() before it calls this.
+ DCHECK_EQ(0, contents->x());
+ DCHECK_EQ(0, contents->y());
+ }
+
scroll_rect.Offset(-contents->x(), -contents->y());
- static_cast<ScrollView*>(parent())->ScrollContentsRegionToBeVisible(
- scroll_rect);
+ scroll_view->ScrollContentsRegionToBeVisible(scroll_rect);
}
void ChildPreferredSizeChanged(View* child) override {
@@ -121,6 +162,7 @@ class ScrollView::Viewport : public View {
ScrollView::ScrollView()
: contents_(NULL),
+ contents_container_(nullptr),
contents_viewport_(new Viewport()),
header_(NULL),
header_viewport_(new Viewport()),
@@ -142,6 +184,23 @@ ScrollView::ScrollView()
vert_sb_->SetVisible(false);
vert_sb_->set_controller(this);
corner_view_->SetVisible(false);
+
+ if (!base::FeatureList::IsEnabled(kToolkitViewsScrollWithLayers))
+ return;
+
+ contents_container_ = new ScrollViewContainer();
+ contents_viewport_->AddChildView(contents_container_);
+ contents_viewport_->SetPaintToLayer(true);
+ contents_viewport_->set_background(
+ Background::CreateSolidBackground(SK_ColorWHITE));
+ contents_viewport_->layer()->SetMasksToBounds(true);
+
+ contents_container_->SetPaintToLayer(true);
sky 2016/08/02 17:29:45 Would it be possible to to turn on paint to layer
tapted 2016/08/03 00:18:58 One issue was that there's no guarantee that |cont
+ contents_container_->set_background(
+ Background::CreateSolidBackground(SK_ColorWHITE));
+ contents_container_->layer()->SetScrollable(
+ contents_viewport_->layer(), true /* can_overscroll */,
+ base::Bind(&ScrollView::OnLayerScrolled, base::Unretained(this)));
}
ScrollView::~ScrollView() {
@@ -158,7 +217,8 @@ ScrollView* ScrollView::CreateScrollViewWithBorder() {
}
void ScrollView::SetContents(View* a_view) {
- SetHeaderOrContents(contents_viewport_, a_view, &contents_);
+ View* parent = contents_container_ ? contents_container_ : contents_viewport_;
+ SetHeaderOrContents(parent, a_view, &contents_);
}
void ScrollView::SetHeader(View* header) {
@@ -168,8 +228,9 @@ void ScrollView::SetHeader(View* header) {
gfx::Rect ScrollView::GetVisibleRect() const {
if (!contents_)
return gfx::Rect();
- return gfx::Rect(-contents_->x(), -contents_->y(),
- contents_viewport_->width(), contents_viewport_->height());
+ gfx::ScrollOffset offset = CurrentOffset();
+ return gfx::Rect(offset.x(), offset.y(), contents_viewport_->width(),
+ contents_viewport_->height());
}
void ScrollView::ClipHeightTo(int min_height, int max_height) {
@@ -267,10 +328,15 @@ void ScrollView::Layout() {
// Update the bounds right now so the inner views can fit in it.
contents_viewport_->SetBoundsRect(viewport_bounds);
- // Give |contents_| a chance to update its bounds if it depends on the
- // viewport.
- if (contents_)
+ // Give |contents_| a chance to update its bounds if it depends on its parent
+ // bounds.
+ if (contents_) {
+ // The container bounds may need to be made larger later, but use the
+ // viewport bounds to layout the contents.
+ if (contents_container_)
+ contents_container_->SetBoundsRect(viewport_bounds);
contents_->Layout();
+ }
bool should_layout_contents = false;
bool horiz_sb_required = false;
@@ -326,16 +392,29 @@ void ScrollView::Layout() {
// Update to the real client size with the visible scrollbars.
contents_viewport_->SetBoundsRect(viewport_bounds);
- if (should_layout_contents && contents_)
+ if (should_layout_contents && contents_) {
+ if (contents_container_)
+ contents_container_->SetBoundsRect(viewport_bounds);
contents_->Layout();
+ }
+
+ // Even when |contents_| needs to scroll, it can still be narrower or wider
+ // the viewport. So ensure the scrolling layer can fill the viewport, so that
+ // events will correctly hit it, and overscroll looks correct.
+ if (contents_container_) {
+ gfx::Size container_size = contents_ ? contents_->size() : gfx::Size();
+ container_size.SetToMax(viewport_bounds.size());
+ contents_container_->SetBoundsRect(gfx::Rect(container_size));
+ }
header_viewport_->SetBounds(contents_x, contents_y,
viewport_bounds.width(), header_height);
if (header_)
header_->Layout();
- CheckScrollBounds(header_viewport_, header_);
- CheckScrollBounds(contents_viewport_, contents_);
+ CheckScrollBounds(header_viewport_, nullptr, header_);
+ CheckScrollBounds(contents_viewport_, contents_container_, contents_);
+
SchedulePaint();
UpdateScrollBarPositions();
}
@@ -379,6 +458,15 @@ void ScrollView::OnMouseExited(const ui::MouseEvent& event) {
vert_sb_->OnMouseExitedScrollView(event);
}
+void ScrollView::OnScrollEvent(ui::ScrollEvent* event) {
+#if defined(OS_MACOSX)
+ ui::UIScrollInputManager* compositor_scroller =
+ GetWidget()->GetCompositor()->scroll_input_manager();
+ if (compositor_scroller)
+ compositor_scroller->OnScrollEvent(*event);
+#endif
+}
+
void ScrollView::OnGestureEvent(ui::GestureEvent* event) {
// If the event happened on one of the scrollbars, then those events are
// sent directly to the scrollbars. Otherwise, only scroll events are sent to
@@ -402,28 +490,32 @@ const char* ScrollView::GetClassName() const {
return kViewClassName;
}
+ScrollView* ScrollView::EnclosingScrollView() {
+ return this;
+}
+
void ScrollView::ScrollToPosition(ScrollBar* source, int position) {
if (!contents_)
return;
+ gfx::ScrollOffset offset = CurrentOffset();
if (source == horiz_sb_ && horiz_sb_->visible()) {
- position = AdjustPosition(contents_->x(), position, contents_->width(),
+ position = AdjustPosition(offset.x(), position, contents_->width(),
contents_viewport_->width());
- if (-contents_->x() == position)
+ if (offset.x() == position)
return;
- contents_->SetX(-position);
- if (header_) {
- header_->SetX(-position);
- header_->SchedulePaintInRect(header_->GetVisibleBounds());
- }
+ offset.set_x(position);
} else if (source == vert_sb_ && vert_sb_->visible()) {
- position = AdjustPosition(contents_->y(), position, contents_->height(),
+ position = AdjustPosition(offset.y(), position, contents_->height(),
contents_viewport_->height());
- if (-contents_->y() == position)
+ if (offset.y() == position)
return;
- contents_->SetY(-position);
+ offset.set_y(position);
}
- contents_->SchedulePaintInRect(contents_->GetVisibleBounds());
+ ScrollToOffset(offset);
+
+ if (!contents_container_)
+ contents_->SchedulePaintInRect(contents_->GetVisibleBounds());
}
int ScrollView::GetScrollIncrement(ScrollBar* source, bool is_page,
@@ -504,10 +596,7 @@ void ScrollView::ScrollContentsRegionToBeVisible(const gfx::Rect& rect) {
(vis_rect.y() > y) ? y : std::max(0, max_y -
contents_viewport_->height());
- contents_->SetX(-new_x);
- if (header_)
- header_->SetX(-new_x);
- contents_->SetY(-new_y);
+ ScrollToOffset(gfx::ScrollOffset(new_x, new_y));
UpdateScrollBarPositions();
}
@@ -555,17 +644,52 @@ void ScrollView::UpdateScrollBarPositions() {
if (!contents_)
return;
+ const gfx::ScrollOffset offset = CurrentOffset();
if (horiz_sb_->visible()) {
int vw = contents_viewport_->width();
int cw = contents_->width();
- int origin = contents_->x();
- horiz_sb_->Update(vw, cw, -origin);
+ horiz_sb_->Update(vw, cw, offset.x());
}
if (vert_sb_->visible()) {
int vh = contents_viewport_->height();
int ch = contents_->height();
- int origin = contents_->y();
- vert_sb_->Update(vh, ch, -origin);
+ vert_sb_->Update(vh, ch, offset.y());
+ }
+}
+
+gfx::ScrollOffset ScrollView::CurrentOffset() const {
+ if (contents_container_)
+ return contents_container_->layer()->CurrentScrollOffset();
+ return gfx::ScrollOffset(-contents_->x(), -contents_->y());
+}
+
+void ScrollView::ScrollToOffset(const gfx::ScrollOffset& offset) {
+ if (contents_container_) {
+ contents_container_->layer()->SetScrollOffset(offset);
+
+ // TODO(tapted): Remove this call to OnLayerScrolled(). It's unnecessary,
+ // but will only be invoked (asynchronously) when a Compositor is present
+ // and commits a frame, which isn't true in some tests.
+ OnLayerScrolled();
+ } else {
+ contents_->SetPosition(gfx::Point(-offset.x(), -offset.y()));
+ ScrollHeader();
+ }
+}
+
+void ScrollView::OnLayerScrolled() {
+ UpdateScrollBarPositions();
+ ScrollHeader();
+}
+
+void ScrollView::ScrollHeader() {
+ if (!header_)
+ return;
+
+ int x_offset = CurrentOffset().x();
+ if (header_->x() != -x_offset) {
+ header_->SetX(-x_offset);
+ header_->SchedulePaintInRect(header_->GetVisibleBounds());
}
}

Powered by Google App Engine
This is Rietveld 408576698