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
-=
47 host_impl_
->ScrollLayer(OuterScrollLayer(), pending_content_delta
,
48 viewport_point
, is_direct_manipulation
);
52 // TODO(bokan): This shouldn't be needed but removing it causes subtle
53 // viewport movement during top controls manipulation.
54 if (gfx::ToRoundedVector2d(pending_content_delta
).IsZero()) {
55 result
.consumed_delta
= delta
;
57 pending_content_delta
-=
58 host_impl_
->ScrollLayer(InnerScrollLayer(), pending_content_delta
,
59 viewport_point
, is_direct_manipulation
);
60 result
.consumed_delta
= delta
- AdjustOverscroll(pending_content_delta
);
63 result
.content_scrolled_delta
= content_delta
- pending_content_delta
;
67 void Viewport::SnapPinchAnchorIfWithinMargin(const gfx::Point
& anchor
) {
68 gfx::SizeF viewport_size
=
69 host_impl_
->active_tree()->InnerViewportContainerLayer()->bounds();
71 if (anchor
.x() < kPinchZoomSnapMarginDips
)
72 pinch_anchor_adjustment_
.set_x(-anchor
.x());
73 else if (anchor
.x() > viewport_size
.width() - kPinchZoomSnapMarginDips
)
74 pinch_anchor_adjustment_
.set_x(viewport_size
.width() - anchor
.x());
76 if (anchor
.y() < kPinchZoomSnapMarginDips
)
77 pinch_anchor_adjustment_
.set_y(-anchor
.y());
78 else if (anchor
.y() > viewport_size
.height() - kPinchZoomSnapMarginDips
)
79 pinch_anchor_adjustment_
.set_y(viewport_size
.height() - anchor
.y());
82 void Viewport::PinchUpdate(float magnify_delta
, const gfx::Point
& anchor
) {
83 if (!pinch_zoom_active_
) {
84 // If this is the first pinch update and the pinch is within a margin-
85 // length of the screen edge, offset all updates by the amount so that we
86 // effectively snap the pinch zoom to the edge of the screen. This makes it
87 // easy to zoom in on position: fixed elements.
88 if (host_impl_
->settings().invert_viewport_scroll_order
)
89 SnapPinchAnchorIfWithinMargin(anchor
);
91 pinch_zoom_active_
= true;
94 LayerTreeImpl
* active_tree
= host_impl_
->active_tree();
96 // Keep the center-of-pinch anchor specified by (x, y) in a stable
97 // position over the course of the magnify.
98 gfx::Point adjusted_anchor
= anchor
+ pinch_anchor_adjustment_
;
99 float page_scale
= active_tree
->current_page_scale_factor();
100 gfx::PointF previous_scale_anchor
=
101 gfx::ScalePoint(adjusted_anchor
, 1.f
/ page_scale
);
102 active_tree
->SetPageScaleOnActiveTree(page_scale
* magnify_delta
);
103 page_scale
= active_tree
->current_page_scale_factor();
104 gfx::PointF new_scale_anchor
=
105 gfx::ScalePoint(adjusted_anchor
, 1.f
/ page_scale
);
106 gfx::Vector2dF move
= previous_scale_anchor
- new_scale_anchor
;
108 // Scale back to viewport space since that's the coordinate space ScrollBy
110 move
.Scale(page_scale
);
112 // If clamping the inner viewport scroll offset causes a change, it should
113 // be accounted for from the intended move.
114 move
-= InnerScrollLayer()->ClampScrollToMaxScrollOffset();
116 if (host_impl_
->settings().invert_viewport_scroll_order
) {
119 gfx::Point viewport_point
;
120 bool is_wheel_event
= false;
121 bool affect_top_controls
= false;
122 ScrollBy(move
, viewport_point
, is_wheel_event
, affect_top_controls
);
126 void Viewport::PinchEnd() {
127 pinch_anchor_adjustment_
= gfx::Vector2d();
128 pinch_zoom_active_
= false;
131 gfx::Vector2dF
Viewport::ScrollTopControls(const gfx::Vector2dF
& delta
) {
132 gfx::Vector2dF excess_delta
=
133 host_impl_
->top_controls_manager()->ScrollBy(delta
);
135 return delta
- excess_delta
;
138 bool Viewport::ShouldTopControlsConsumeScroll(
139 const gfx::Vector2dF
& scroll_delta
) const {
140 // Always consume if it's in the direction to show the top controls.
141 if (scroll_delta
.y() < 0)
144 if (TotalScrollOffset().y() < MaxTotalScrollOffset().y())
150 gfx::Vector2dF
Viewport::AdjustOverscroll(const gfx::Vector2dF
& delta
) const {
151 // TODO(tdresser): Use a more rational epsilon. See crbug.com/510550 for
153 const float kEpsilon
= 0.1f
;
154 gfx::Vector2dF adjusted
= delta
;
156 if (std::abs(adjusted
.x()) < kEpsilon
)
157 adjusted
.set_x(0.0f
);
158 if (std::abs(adjusted
.y()) < kEpsilon
)
159 adjusted
.set_y(0.0f
);
164 gfx::ScrollOffset
Viewport::MaxTotalScrollOffset() const {
165 gfx::ScrollOffset offset
;
167 offset
+= InnerScrollLayer()->MaxScrollOffset();
169 if (OuterScrollLayer())
170 offset
+= OuterScrollLayer()->MaxScrollOffset();
175 gfx::ScrollOffset
Viewport::TotalScrollOffset() const {
176 gfx::ScrollOffset offset
;
178 offset
+= InnerScrollLayer()->CurrentScrollOffset();
180 if (OuterScrollLayer())
181 offset
+= OuterScrollLayer()->CurrentScrollOffset();
186 LayerImpl
* Viewport::InnerScrollLayer() const {
187 return host_impl_
->InnerViewportScrollLayer();
190 LayerImpl
* Viewport::OuterScrollLayer() const {
191 return host_impl_
->OuterViewportScrollLayer();