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 bool invert_scroll_order
=
47 host_impl_
->settings().invert_viewport_scroll_order
;
48 LayerImpl
* primary_layer
=
49 invert_scroll_order
? InnerScrollLayer() : OuterScrollLayer();
50 LayerImpl
* secondary_layer
=
51 invert_scroll_order
? OuterScrollLayer() : InnerScrollLayer();
53 pending_content_delta
-= host_impl_
->ScrollLayer(primary_layer
,
54 pending_content_delta
,
56 is_direct_manipulation
);
60 // TODO(bokan): This shouldn't be needed but removing it causes subtle
61 // viewport movement during top controls manipulation.
62 if (gfx::ToRoundedVector2d(pending_content_delta
).IsZero()) {
63 result
.consumed_delta
= delta
;
65 pending_content_delta
-= host_impl_
->ScrollLayer(secondary_layer
,
66 pending_content_delta
,
68 is_direct_manipulation
);
69 result
.consumed_delta
= delta
- AdjustOverscroll(pending_content_delta
);
72 result
.content_scrolled_delta
= content_delta
- pending_content_delta
;
76 void Viewport::SnapPinchAnchorIfWithinMargin(const gfx::Point
& anchor
) {
77 gfx::SizeF viewport_size
=
78 host_impl_
->active_tree()->InnerViewportContainerLayer()->bounds();
80 if (anchor
.x() < kPinchZoomSnapMarginDips
)
81 pinch_anchor_adjustment_
.set_x(-anchor
.x());
82 else if (anchor
.x() > viewport_size
.width() - kPinchZoomSnapMarginDips
)
83 pinch_anchor_adjustment_
.set_x(viewport_size
.width() - anchor
.x());
85 if (anchor
.y() < kPinchZoomSnapMarginDips
)
86 pinch_anchor_adjustment_
.set_y(-anchor
.y());
87 else if (anchor
.y() > viewport_size
.height() - kPinchZoomSnapMarginDips
)
88 pinch_anchor_adjustment_
.set_y(viewport_size
.height() - anchor
.y());
91 void Viewport::PinchUpdate(float magnify_delta
, const gfx::Point
& anchor
) {
92 if (!pinch_zoom_active_
) {
93 // If this is the first pinch update and the pinch is within a margin-
94 // length of the screen edge, offset all updates by the amount so that we
95 // effectively snap the pinch zoom to the edge of the screen. This makes it
96 // easy to zoom in on position: fixed elements.
97 if (host_impl_
->settings().invert_viewport_scroll_order
)
98 SnapPinchAnchorIfWithinMargin(anchor
);
100 pinch_zoom_active_
= true;
103 LayerTreeImpl
* active_tree
= host_impl_
->active_tree();
105 // Keep the center-of-pinch anchor specified by (x, y) in a stable
106 // position over the course of the magnify.
107 gfx::Point adjusted_anchor
= anchor
+ pinch_anchor_adjustment_
;
108 float page_scale
= active_tree
->current_page_scale_factor();
109 gfx::PointF previous_scale_anchor
=
110 gfx::ScalePoint(adjusted_anchor
, 1.f
/ page_scale
);
111 active_tree
->SetPageScaleOnActiveTree(page_scale
* magnify_delta
);
112 page_scale
= active_tree
->current_page_scale_factor();
113 gfx::PointF new_scale_anchor
=
114 gfx::ScalePoint(adjusted_anchor
, 1.f
/ page_scale
);
115 gfx::Vector2dF move
= previous_scale_anchor
- new_scale_anchor
;
117 // Scale back to viewport space since that's the coordinate space ScrollBy
119 move
.Scale(page_scale
);
121 // If clamping the inner viewport scroll offset causes a change, it should
122 // be accounted for from the intended move.
123 move
-= InnerScrollLayer()->ClampScrollToMaxScrollOffset();
125 if (host_impl_
->settings().invert_viewport_scroll_order
) {
128 gfx::Point viewport_point
;
129 bool is_wheel_event
= false;
130 bool affect_top_controls
= false;
131 ScrollBy(move
, viewport_point
, is_wheel_event
, affect_top_controls
);
135 void Viewport::PinchEnd() {
136 pinch_anchor_adjustment_
= gfx::Vector2d();
137 pinch_zoom_active_
= false;
140 gfx::Vector2dF
Viewport::ScrollTopControls(const gfx::Vector2dF
& delta
) {
141 gfx::Vector2dF excess_delta
=
142 host_impl_
->top_controls_manager()->ScrollBy(delta
);
144 return delta
- excess_delta
;
147 bool Viewport::ShouldTopControlsConsumeScroll(
148 const gfx::Vector2dF
& scroll_delta
) const {
149 // Always consume if it's in the direction to show the top controls.
150 if (scroll_delta
.y() < 0)
153 if (TotalScrollOffset().y() < MaxTotalScrollOffset().y())
159 gfx::Vector2dF
Viewport::AdjustOverscroll(const gfx::Vector2dF
& delta
) const {
160 // TODO(tdresser): Use a more rational epsilon. See crbug.com/510550 for
162 const float kEpsilon
= 0.1f
;
163 gfx::Vector2dF adjusted
= delta
;
165 if (std::abs(adjusted
.x()) < kEpsilon
)
166 adjusted
.set_x(0.0f
);
167 if (std::abs(adjusted
.y()) < kEpsilon
)
168 adjusted
.set_y(0.0f
);
173 gfx::ScrollOffset
Viewport::MaxTotalScrollOffset() const {
174 gfx::ScrollOffset offset
;
176 offset
+= InnerScrollLayer()->MaxScrollOffset();
178 if (OuterScrollLayer())
179 offset
+= OuterScrollLayer()->MaxScrollOffset();
184 gfx::ScrollOffset
Viewport::TotalScrollOffset() const {
185 gfx::ScrollOffset offset
;
187 offset
+= InnerScrollLayer()->CurrentScrollOffset();
189 if (OuterScrollLayer())
190 offset
+= OuterScrollLayer()->CurrentScrollOffset();
195 LayerImpl
* Viewport::InnerScrollLayer() const {
196 return host_impl_
->InnerViewportScrollLayer();
199 LayerImpl
* Viewport::OuterScrollLayer() const {
200 return host_impl_
->OuterViewportScrollLayer();