blob: 606a84c3fd13bb12aa0f6a18eb998df4ab643719 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/input/snap_selection_strategy.h"
#include <cmath>
#include <limits>
#include "cc/input/scroll_utils.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace cc {
std::unique_ptr<SnapSelectionStrategy>
SnapSelectionStrategy::CreateForEndPosition(const gfx::PointF& current_position,
bool scrolled_x,
bool scrolled_y) {
return std::make_unique<EndPositionStrategy>(current_position, scrolled_x,
scrolled_y);
}
std::unique_ptr<SnapSelectionStrategy>
SnapSelectionStrategy::CreateForDirection(gfx::PointF current_position,
gfx::Vector2dF step,
bool use_fractional_offsets,
SnapStopAlwaysFilter filter) {
return std::make_unique<DirectionStrategy>(
current_position, step, DirectionStrategy::StepPreference::kDirection,
gfx::Vector2dF(), gfx::Vector2dF(), filter, use_fractional_offsets);
}
std::unique_ptr<SnapSelectionStrategy>
SnapSelectionStrategy::CreateForDisplacement(gfx::PointF current_position,
gfx::Vector2dF displacement,
bool use_fractional_offsets,
SnapStopAlwaysFilter filter) {
return std::make_unique<DirectionStrategy>(
current_position, displacement,
DirectionStrategy::StepPreference::kDistance, gfx::Vector2dF(),
gfx::Vector2dF(std::numeric_limits<float>::max(),
std::numeric_limits<float>::max()),
filter, use_fractional_offsets);
}
std::unique_ptr<SnapSelectionStrategy>
SnapSelectionStrategy::CreateForPageScroll(
gfx::PointF current_position,
gfx::Vector2dF direction,
gfx::Size page_size,
bool use_fractional_offsets,
SnapStopAlwaysFilter filter) {
// When scrolling by a page, we prefer that we scroll no more than a page,
// but at least by a reasonable proportion of that page.
gfx::Vector2dF displacement(
direction.x() * ScrollUtils::CalculatePageStep(page_size.width()),
direction.y() * ScrollUtils::CalculatePageStep(page_size.height()));
gfx::Vector2dF min_displacement = gfx::Vector2dF(
direction.x() * ScrollUtils::CalculateMinPageSnap(page_size.width()),
direction.y() * ScrollUtils::CalculateMinPageSnap(page_size.height()));
gfx::Vector2dF max_displacement = gfx::Vector2dF(
direction.x() * ScrollUtils::CalculateMaxPageSnap(page_size.width()),
direction.y() * ScrollUtils::CalculateMaxPageSnap(page_size.height()));
// No limit to the maximum displacement of preferred snap areas in the
// other axis.
if (direction.x() == 0.f) {
max_displacement.set_x(std::numeric_limits<float>::max());
}
if (direction.y() == 0.f) {
max_displacement.set_y(std::numeric_limits<float>::max());
}
return std::make_unique<DirectionStrategy>(
current_position, displacement,
DirectionStrategy::StepPreference::kDistance, min_displacement,
max_displacement, filter, use_fractional_offsets);
}
std::unique_ptr<SnapSelectionStrategy>
SnapSelectionStrategy::CreateForTargetElement(gfx::PointF current_position) {
return std::make_unique<EndPositionStrategy>(
current_position, true /* scrolled_x */, true /* scrolled_y */,
SnapTargetsPrioritization::kRequire);
}
bool SnapSelectionStrategy::HasIntendedDirection() const {
return true;
}
bool SnapSelectionStrategy::ShouldRespectSnapStop() const {
return false;
}
bool SnapSelectionStrategy::IsValidSnapArea(SearchAxis axis,
const SnapAreaData& area) const {
return axis == SearchAxis::kX
? area.scroll_snap_align.alignment_inline != SnapAlignment::kNone
: area.scroll_snap_align.alignment_block != SnapAlignment::kNone;
}
bool SnapSelectionStrategy::ShouldPrioritizeSnapTargets() const {
return false;
}
bool SnapSelectionStrategy::UsingFractionalOffsets() const {
return false;
}
bool EndPositionStrategy::ShouldSnapOnX() const {
return scrolled_x_;
}
bool EndPositionStrategy::ShouldSnapOnY() const {
return scrolled_y_;
}
gfx::PointF EndPositionStrategy::intended_position() const {
return current_position_;
}
gfx::PointF EndPositionStrategy::base_position() const {
return current_position_;
}
bool EndPositionStrategy::IsPreferredSnapPosition(SearchAxis axis,
float position) const {
return true;
}
// |position| is unused in this method.
bool EndPositionStrategy::IsValidSnapPosition(SearchAxis axis,
float position) const {
return (scrolled_x_ && axis == SearchAxis::kX) ||
(scrolled_y_ && axis == SearchAxis::kY);
}
bool EndPositionStrategy::HasIntendedDirection() const {
return false;
}
bool EndPositionStrategy::ShouldPrioritizeSnapTargets() const {
return snap_targets_prioritization_ == SnapTargetsPrioritization::kRequire;
}
const std::optional<SnapSearchResult>& EndPositionStrategy::PickBestResult(
const std::optional<SnapSearchResult>& closest,
const std::optional<SnapSearchResult>& covering) const {
return covering.has_value() ? covering : closest;
}
std::unique_ptr<SnapSelectionStrategy> EndPositionStrategy::Clone() const {
return std::make_unique<EndPositionStrategy>(*this);
}
bool DirectionStrategy::ShouldSnapOnX() const {
return step_.x() != 0;
}
bool DirectionStrategy::ShouldSnapOnY() const {
return step_.y() != 0;
}
gfx::PointF DirectionStrategy::intended_position() const {
return current_position_ + step_;
}
gfx::PointF DirectionStrategy::base_position() const {
return preferred_step_ == StepPreference::kDirection
? current_position_
: current_position_ + step_;
}
bool DirectionStrategy::IsPreferredSnapPosition(SearchAxis axis,
float position) const {
if (axis == SearchAxis::kX) {
float delta = position - current_position_.x();
return std::abs(delta) >= std::abs(preferred_min_displacement_.x()) &&
std::abs(delta) <= std::abs(preferred_max_displacement_.x());
} else {
float delta = position - current_position_.y();
return std::abs(delta) >= std::abs(preferred_min_displacement_.y()) &&
std::abs(delta) <= std::abs(preferred_max_displacement_.y());
}
}
bool DirectionStrategy::IsValidSnapPosition(SearchAxis axis,
float position) const {
// If not using fractional offsets then it is possible for the currently
// snapped area's offset, which is fractional, to not be equal to the current
// scroll offset, which is not fractional. Therefore we truncate the offsets
// so that any position within 1 of the current position is ignored.
if (axis == SearchAxis::kX) {
float delta = position - current_position_.x();
if (!use_fractional_offsets_)
delta = delta > 0 ? std::floor(delta) : std::ceil(delta);
return (step_.x() > 0 && delta > 0) || // "Right" arrow
(step_.x() < 0 && delta < 0); // "Left" arrow
} else {
float delta = position - current_position_.y();
if (!use_fractional_offsets_)
delta = delta > 0 ? std::floor(delta) : std::ceil(delta);
return (step_.y() > 0 && delta > 0) || // "Down" arrow
(step_.y() < 0 && delta < 0); // "Up" arrow
}
}
bool DirectionStrategy::IsValidSnapArea(SearchAxis axis,
const SnapAreaData& area) const {
return SnapSelectionStrategy::IsValidSnapArea(axis, area) &&
(snap_stop_always_filter_ == SnapStopAlwaysFilter::kIgnore ||
area.must_snap);
}
bool DirectionStrategy::ShouldRespectSnapStop() const {
return true;
}
const std::optional<SnapSearchResult>& DirectionStrategy::PickBestResult(
const std::optional<SnapSearchResult>& closest,
const std::optional<SnapSearchResult>& covering) const {
// We choose the |closest| result only if the default landing position (using
// the default step) is not a valid snap position (not making a snap area
// covering the snapport), or the |closest| is closer than the default landing
// position.
if (!closest.has_value())
return covering;
if (!covering.has_value())
return closest;
// If covering and closest represent the same snap area, covering best
// preserves the intended scroll position.
if (covering->element_id() == closest->element_id()) {
return covering;
}
// If we only intend to scroll in the given direction, prefer the closer
// snap position.
if (preferred_step_ == StepPreference::kDirection) {
// Scroll right or down.
if ((step_.x() > 0 || step_.y() > 0) &&
closest.value().snap_offset() < covering.value().snap_offset()) {
return closest;
}
// Scroll left or up.
if ((step_.x() < 0 || step_.y() < 0) &&
closest.value().snap_offset() > covering.value().snap_offset()) {
return closest;
}
}
return covering;
}
bool DirectionStrategy::UsingFractionalOffsets() const {
return use_fractional_offsets_;
}
std::unique_ptr<SnapSelectionStrategy> DirectionStrategy::Clone() const {
return std::make_unique<DirectionStrategy>(*this);
}
} // namespace cc