1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/layers/viewport.h"
7 #include "base/logging.h"
8 #include "cc/input/top_controls_manager.h"
9 #include "cc/trees/layer_tree_host_impl.h"
10 #include "cc/trees/layer_tree_impl.h"
11 #include "ui/gfx/geometry/vector2d_conversions.h"
12 #include "ui/gfx/geometry/vector2d_f.h"
17 scoped_ptr
<Viewport
> Viewport::Create(
18 LayerTreeHostImpl
* host_impl
) {
19 return make_scoped_ptr(new Viewport(host_impl
));
22 Viewport::Viewport(LayerTreeHostImpl
* host_impl
)
23 : host_impl_(host_impl
)
24 , pinch_zoom_active_(false) {
28 void Viewport::Pan(const gfx::Vector2dF
& delta
) {
29 gfx::Vector2dF pending_delta
= delta
;
30 float page_scale
= host_impl_
->active_tree()->current_page_scale_factor();
31 pending_delta
.Scale(1 / page_scale
);
32 InnerScrollLayer()->ScrollBy(pending_delta
);
35 Viewport::ScrollResult
Viewport::ScrollBy(const gfx::Vector2dF
& delta
,
36 const gfx::Point
& viewport_point
,
37 bool is_direct_manipulation
,
38 bool affect_top_controls
) {
39 gfx::Vector2dF content_delta
= delta
;
41 if (affect_top_controls
&& ShouldTopControlsConsumeScroll(delta
))
42 content_delta
-= ScrollTopControls(delta
);
44 gfx::Vector2dF pending_content_delta
= content_delta
;
46 pending_content_delta
-= host_impl_
->ScrollLayer(InnerScrollLayer(),
47 pending_content_delta
,
49 is_direct_manipulation
);
53 // TODO(bokan): This shouldn't be needed but removing it causes subtle
54 // viewport movement during top controls manipulation.
55 if (gfx::ToRoundedVector2d(pending_content_delta
).IsZero()) {
56 result
.consumed_delta
= delta
;
58 pending_content_delta
-= host_impl_
->ScrollLayer(OuterScrollLayer(),
59 pending_content_delta
,
61 is_direct_manipulation
);
62 result
.consumed_delta
= delta
- AdjustOverscroll(pending_content_delta
);
65 result
.content_scrolled_delta
= content_delta
- pending_content_delta
;
69 void Viewport::SnapPinchAnchorIfWithinMargin(const gfx::Point
& anchor
) {
70 gfx::SizeF viewport_size
=
71 host_impl_
->active_tree()->InnerViewportContainerLayer()->bounds();
73 if (anchor
.x() < kPinchZoomSnapMarginDips
)
74 pinch_anchor_adjustment_
.set_x(-anchor
.x());
75 else if (anchor
.x() > viewport_size
.width() - kPinchZoomSnapMarginDips
)
76 pinch_anchor_adjustment_
.set_x(viewport_size
.width() - anchor
.x());
78 if (anchor
.y() < kPinchZoomSnapMarginDips
)
79 pinch_anchor_adjustment_
.set_y(-anchor
.y());
80 else if (anchor
.y() > viewport_size
.height() - kPinchZoomSnapMarginDips
)
81 pinch_anchor_adjustment_
.set_y(viewport_size
.height() - anchor
.y());
84 void Viewport::PinchUpdate(float magnify_delta
, const gfx::Point
& anchor
) {
85 if (!pinch_zoom_active_
) {
86 // If this is the first pinch update and the pinch is within a margin-
87 // length of the screen edge, offset all updates by the amount so that we
88 // effectively snap the pinch zoom to the edge of the screen. This makes it
89 // easy to zoom in on position: fixed elements.
90 SnapPinchAnchorIfWithinMargin(anchor
);
92 pinch_zoom_active_
= true;
95 LayerTreeImpl
* active_tree
= host_impl_
->active_tree();
97 // Keep the center-of-pinch anchor specified by (x, y) in a stable
98 // position over the course of the magnify.
99 gfx::Point adjusted_anchor
= anchor
+ pinch_anchor_adjustment_
;
100 float page_scale
= active_tree
->current_page_scale_factor();
101 gfx::PointF previous_scale_anchor
=
102 gfx::ScalePoint(adjusted_anchor
, 1.f
/ page_scale
);
103 active_tree
->SetPageScaleOnActiveTree(page_scale
* magnify_delta
);
104 page_scale
= active_tree
->current_page_scale_factor();
105 gfx::PointF new_scale_anchor
=
106 gfx::ScalePoint(adjusted_anchor
, 1.f
/ page_scale
);
107 gfx::Vector2dF move
= previous_scale_anchor
- new_scale_anchor
;
109 // Scale back to viewport space since that's the coordinate space ScrollBy
111 move
.Scale(page_scale
);
113 // If clamping the inner viewport scroll offset causes a change, it should
114 // be accounted for from the intended move.
115 move
-= InnerScrollLayer()->ClampScrollToMaxScrollOffset();
120 void Viewport::PinchEnd() {
121 pinch_anchor_adjustment_
= gfx::Vector2d();
122 pinch_zoom_active_
= false;
125 gfx::Vector2dF
Viewport::ScrollTopControls(const gfx::Vector2dF
& delta
) {
126 gfx::Vector2dF excess_delta
=
127 host_impl_
->top_controls_manager()->ScrollBy(delta
);
129 return delta
- excess_delta
;
132 bool Viewport::ShouldTopControlsConsumeScroll(
133 const gfx::Vector2dF
& scroll_delta
) const {
134 // Always consume if it's in the direction to show the top controls.
135 if (scroll_delta
.y() < 0)
138 if (TotalScrollOffset().y() < MaxTotalScrollOffset().y())
144 gfx::Vector2dF
Viewport::AdjustOverscroll(const gfx::Vector2dF
& delta
) const {
145 // TODO(tdresser): Use a more rational epsilon. See crbug.com/510550 for
147 const float kEpsilon
= 0.1f
;
148 gfx::Vector2dF adjusted
= delta
;
150 if (std::abs(adjusted
.x()) < kEpsilon
)
151 adjusted
.set_x(0.0f
);
152 if (std::abs(adjusted
.y()) < kEpsilon
)
153 adjusted
.set_y(0.0f
);
158 gfx::ScrollOffset
Viewport::MaxTotalScrollOffset() const {
159 gfx::ScrollOffset offset
;
161 offset
+= InnerScrollLayer()->MaxScrollOffset();
163 if (OuterScrollLayer())
164 offset
+= OuterScrollLayer()->MaxScrollOffset();
169 gfx::ScrollOffset
Viewport::TotalScrollOffset() const {
170 gfx::ScrollOffset offset
;
172 offset
+= InnerScrollLayer()->CurrentScrollOffset();
174 if (OuterScrollLayer())
175 offset
+= OuterScrollLayer()->CurrentScrollOffset();
180 LayerImpl
* Viewport::InnerScrollLayer() const {
181 return host_impl_
->InnerViewportScrollLayer();
184 LayerImpl
* Viewport::OuterScrollLayer() const {
185 return host_impl_
->OuterViewportScrollLayer();