blob: 38f4c27df22928ad121dc8208defe2dc9bd99c57 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/layers/viewport.h"
#include "base/logging.h"
#include "cc/input/top_controls_manager.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "ui/gfx/geometry/vector2d_conversions.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace cc {
// static
scoped_ptr<Viewport> Viewport::Create(
LayerTreeHostImpl* host_impl) {
return make_scoped_ptr(new Viewport(host_impl));
}
Viewport::Viewport(LayerTreeHostImpl* host_impl)
: host_impl_(host_impl)
, pinch_zoom_active_(false) {
DCHECK(host_impl_);
}
void Viewport::Pan(const gfx::Vector2dF& delta) {
gfx::Vector2dF pending_delta = delta;
float page_scale = host_impl_->active_tree()->current_page_scale_factor();
pending_delta.Scale(1 / page_scale);
InnerScrollLayer()->ScrollBy(pending_delta);
}
Viewport::ScrollResult Viewport::ScrollBy(const gfx::Vector2dF& delta,
const gfx::Point& viewport_point,
bool is_direct_manipulation,
bool affect_top_controls) {
gfx::Vector2dF content_delta = delta;
if (affect_top_controls && ShouldTopControlsConsumeScroll(delta))
content_delta -= ScrollTopControls(delta);
gfx::Vector2dF pending_content_delta = content_delta;
bool invert_scroll_order =
host_impl_->settings().invert_viewport_scroll_order;
LayerImpl* primary_layer =
invert_scroll_order ? InnerScrollLayer() : OuterScrollLayer();
LayerImpl* secondary_layer =
invert_scroll_order ? OuterScrollLayer() : InnerScrollLayer();
pending_content_delta -= host_impl_->ScrollLayer(primary_layer,
pending_content_delta,
viewport_point,
is_direct_manipulation);
ScrollResult result;
// TODO(bokan): This shouldn't be needed but removing it causes subtle
// viewport movement during top controls manipulation.
if (gfx::ToRoundedVector2d(pending_content_delta).IsZero()) {
result.consumed_delta = delta;
} else {
pending_content_delta -= host_impl_->ScrollLayer(secondary_layer,
pending_content_delta,
viewport_point,
is_direct_manipulation);
result.consumed_delta = delta - AdjustOverscroll(pending_content_delta);
}
result.content_scrolled_delta = content_delta - pending_content_delta;
return result;
}
void Viewport::SnapPinchAnchorIfWithinMargin(const gfx::Point& anchor) {
gfx::SizeF viewport_size =
host_impl_->active_tree()->InnerViewportContainerLayer()->bounds();
if (anchor.x() < kPinchZoomSnapMarginDips)
pinch_anchor_adjustment_.set_x(-anchor.x());
else if (anchor.x() > viewport_size.width() - kPinchZoomSnapMarginDips)
pinch_anchor_adjustment_.set_x(viewport_size.width() - anchor.x());
if (anchor.y() < kPinchZoomSnapMarginDips)
pinch_anchor_adjustment_.set_y(-anchor.y());
else if (anchor.y() > viewport_size.height() - kPinchZoomSnapMarginDips)
pinch_anchor_adjustment_.set_y(viewport_size.height() - anchor.y());
}
void Viewport::PinchUpdate(float magnify_delta, const gfx::Point& anchor) {
if (!pinch_zoom_active_) {
// If this is the first pinch update and the pinch is within a margin-
// length of the screen edge, offset all updates by the amount so that we
// effectively snap the pinch zoom to the edge of the screen. This makes it
// easy to zoom in on position: fixed elements.
if (host_impl_->settings().invert_viewport_scroll_order)
SnapPinchAnchorIfWithinMargin(anchor);
pinch_zoom_active_ = true;
}
LayerTreeImpl* active_tree = host_impl_->active_tree();
// Keep the center-of-pinch anchor specified by (x, y) in a stable
// position over the course of the magnify.
gfx::Point adjusted_anchor = anchor + pinch_anchor_adjustment_;
float page_scale = active_tree->current_page_scale_factor();
gfx::PointF previous_scale_anchor =
gfx::ScalePoint(adjusted_anchor, 1.f / page_scale);
active_tree->SetPageScaleOnActiveTree(page_scale * magnify_delta);
page_scale = active_tree->current_page_scale_factor();
gfx::PointF new_scale_anchor =
gfx::ScalePoint(adjusted_anchor, 1.f / page_scale);
gfx::Vector2dF move = previous_scale_anchor - new_scale_anchor;
// Scale back to viewport space since that's the coordinate space ScrollBy
// uses.
move.Scale(page_scale);
// If clamping the inner viewport scroll offset causes a change, it should
// be accounted for from the intended move.
move -= InnerScrollLayer()->ClampScrollToMaxScrollOffset();
if (host_impl_->settings().invert_viewport_scroll_order) {
Pan(move);
} else {
gfx::Point viewport_point;
bool is_wheel_event = false;
bool affect_top_controls = false;
ScrollBy(move, viewport_point, is_wheel_event, affect_top_controls);
}
}
void Viewport::PinchEnd() {
pinch_anchor_adjustment_ = gfx::Vector2d();
pinch_zoom_active_ = false;
}
gfx::Vector2dF Viewport::ScrollTopControls(const gfx::Vector2dF& delta) {
gfx::Vector2dF excess_delta =
host_impl_->top_controls_manager()->ScrollBy(delta);
return delta - excess_delta;
}
bool Viewport::ShouldTopControlsConsumeScroll(
const gfx::Vector2dF& scroll_delta) const {
// Always consume if it's in the direction to show the top controls.
if (scroll_delta.y() < 0)
return true;
if (TotalScrollOffset().y() < MaxTotalScrollOffset().y())
return true;
return false;
}
gfx::Vector2dF Viewport::AdjustOverscroll(const gfx::Vector2dF& delta) const {
// TODO(tdresser): Use a more rational epsilon. See crbug.com/510550 for
// details.
const float kEpsilon = 0.1f;
gfx::Vector2dF adjusted = delta;
if (std::abs(adjusted.x()) < kEpsilon)
adjusted.set_x(0.0f);
if (std::abs(adjusted.y()) < kEpsilon)
adjusted.set_y(0.0f);
return adjusted;
}
gfx::ScrollOffset Viewport::MaxTotalScrollOffset() const {
gfx::ScrollOffset offset;
offset += InnerScrollLayer()->MaxScrollOffset();
if (OuterScrollLayer())
offset += OuterScrollLayer()->MaxScrollOffset();
return offset;
}
gfx::ScrollOffset Viewport::TotalScrollOffset() const {
gfx::ScrollOffset offset;
offset += InnerScrollLayer()->CurrentScrollOffset();
if (OuterScrollLayer())
offset += OuterScrollLayer()->CurrentScrollOffset();
return offset;
}
LayerImpl* Viewport::InnerScrollLayer() const {
return host_impl_->InnerViewportScrollLayer();
}
LayerImpl* Viewport::OuterScrollLayer() const {
return host_impl_->OuterViewportScrollLayer();
}
} // namespace cc