910e62b5创建于 1月15日历史提交
// 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