blob: e44b9d882e2e9c3ad4c20f9c201bbcc2a930d6cd [file] [log] [blame]
// Copyright 2017 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 <vector>
#include "base/optional.h"
#include "cc/cc_export.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/scroll_offset.h"
namespace cc {
// See
enum class SnapAxis : unsigned {
// A helper enum to specify the the axis when doing calculations.
enum class SearchAxis : unsigned { kX, kY };
// See
// TODO(sunyunjia): Add kNone for SnapStrictness to match the spec.
enum class SnapStrictness : unsigned { kProximity, kMandatory };
// See
enum class SnapAlignment : unsigned { kNone, kStart, kEnd, kCenter };
struct ScrollSnapType {
: is_none(true),
strictness(SnapStrictness::kProximity) {}
ScrollSnapType(bool snap_type_none, SnapAxis axis, SnapStrictness strictness)
: is_none(snap_type_none), axis(axis), strictness(strictness) {}
bool operator==(const ScrollSnapType& other) const {
return is_none == other.is_none && axis == other.axis &&
strictness == other.strictness;
bool operator!=(const ScrollSnapType& other) const {
return !(*this == other);
// Whether the scroll-snap-type is none or the snap-strictness field has the
// value None.
// TODO(sunyunjia): Consider combining is_none with SnapStrictness.
bool is_none;
SnapAxis axis;
SnapStrictness strictness;
struct ScrollSnapAlign {
: alignment_inline(SnapAlignment::kNone),
alignment_block(SnapAlignment::kNone) {}
explicit ScrollSnapAlign(SnapAlignment alignment)
: alignment_inline(alignment), alignment_block(alignment) {}
ScrollSnapAlign(SnapAlignment i, SnapAlignment b)
: alignment_inline(i), alignment_block(b) {}
bool operator==(const ScrollSnapAlign& other) const {
return alignment_inline == other.alignment_inline &&
alignment_block == other.alignment_block;
bool operator!=(const ScrollSnapAlign& other) const {
return !(*this == other);
SnapAlignment alignment_inline;
SnapAlignment alignment_block;
// We should really use gfx::RangeF. However, it includes windows.h which would
// bring in complexity to the compilation.
class SnapVisibleRange {
SnapVisibleRange() {}
SnapVisibleRange(float start, float end) : start_(start), end_(end) {}
bool Contains(float value) const;
float start() const { return start_; }
float end() const { return end_; }
float start_;
float end_;
// This class includes snap offset and visible range needed to perform a snap
// operation on one axis for a specific area. The data can be used to determine
// whether this snap area provides a valid snap position for the current scroll.
class SnapSearchResult {
SnapSearchResult() {}
SnapSearchResult(float offset, const SnapVisibleRange& range);
// Clips the |snap_offset| between 0 and |max_snap|. And clips the
// |visible_range| between 0 and |max_visible|.
void Clip(float max_snap, float max_visible);
// Union the visible_range of the two SnapSearchResult if they represent two
// snap areas that are both covering the snapport at the current offset.
void Union(const SnapSearchResult& other);
float snap_offset() const { return snap_offset_; }
void set_snap_offset(float offset) { snap_offset_ = offset; }
SnapVisibleRange visible_range() const { return visible_range_; }
void set_visible_range(const SnapVisibleRange& range);
float snap_offset_;
// This is the range on the cross axis, within which the SnapArea generating
// this |snap_offset| is visible. We expect the range to be in order (as
// opposed to reversed), i.e., start < end.
SnapVisibleRange visible_range_;
// Snap area is a bounding box that could be snapped to when a scroll happens in
// its scroll container.
// This data structure describes the data needed for SnapCoordinator if we want
// to snap to this snap area.
struct SnapAreaData {
// kInvalidScrollOffset is used to mark that the snap_position on a specific
// axis is not applicable, thus should not be considered when snapping on that
// axis. This is because the snap area has SnapAlignmentNone on that axis.
static const int kInvalidScrollPosition = -1;
SnapAreaData() {}
SnapAreaData(const ScrollSnapAlign& align, const gfx::RectF& rec, bool msnap)
: scroll_snap_align(align), rect(rec), must_snap(msnap) {}
bool operator==(const SnapAreaData& other) const {
return (other.scroll_snap_align == scroll_snap_align) &&
(other.rect == rect) && (other.must_snap == must_snap);
bool operator!=(const SnapAreaData& other) const { return !(*this == other); }
// Specifies how the snap area should be aligned with its snap container when
// snapped. The alignment_inline and alignment_block represent the alignments
// on x axis and y axis repectively.
ScrollSnapAlign scroll_snap_align;
// The snap area rect relative to its snap container's boundary
gfx::RectF rect;
// Whether this area has scroll-snap-stop: always.
// See
bool must_snap;
typedef std::vector<SnapAreaData> SnapAreaList;
// Snap container is a scroll container that has non-'none' value for
// scroll-snap-type. It can be snapped to one of its snap areas when a scroll
// happens.
// This data structure describes the data needed for SnapCoordinator to perform
// snapping in the snap container.
class CC_EXPORT SnapContainerData {
explicit SnapContainerData(ScrollSnapType type);
SnapContainerData(ScrollSnapType type,
const gfx::RectF& rect,
const gfx::ScrollOffset& max);
SnapContainerData(const SnapContainerData& other);
SnapContainerData(SnapContainerData&& other);
SnapContainerData& operator=(const SnapContainerData& other);
SnapContainerData& operator=(SnapContainerData&& other);
bool operator==(const SnapContainerData& other) const {
return (other.scroll_snap_type_ == scroll_snap_type_) &&
(other.rect_ == rect_) && (other.max_position_ == max_position_) &&
(other.proximity_range_ == proximity_range_) &&
(other.snap_area_list_ == snap_area_list_);
bool operator!=(const SnapContainerData& other) const {
return !(*this == other);
bool FindSnapPosition(const gfx::ScrollOffset& current_position,
bool should_snap_on_x,
bool should_snap_on_y,
gfx::ScrollOffset* snap_position) const;
void AddSnapAreaData(SnapAreaData snap_area_data);
size_t size() const { return snap_area_list_.size(); }
const SnapAreaData& at(int index) const { return snap_area_list_[index]; }
void set_scroll_snap_type(ScrollSnapType type) { scroll_snap_type_ = type; }
ScrollSnapType scroll_snap_type() const { return scroll_snap_type_; }
void set_rect(const gfx::RectF& rect) { rect_ = rect; }
gfx::RectF rect() const { return rect_; }
void set_max_position(gfx::ScrollOffset position) {
max_position_ = position;
gfx::ScrollOffset max_position() const { return max_position_; }
void set_proximity_range(const gfx::ScrollOffset& range) {
proximity_range_ = range;
gfx::ScrollOffset proximity_range() const { return proximity_range_; }
// Finds the best SnapArea candidate that minimizes the distance between
// current and candidate positions, while satisfying two invariants:
// - |candidate.snap_offset| is within |cross_axis_snap_result|'s visible
// range on |axis|.
// - |cross_axis_snap_result.snap_offset| is within |candidate|'s visible
// range on the cross axis.
// |cross_axis_snap_result| is what we've found to snap on the cross axis,
// or the original scroll offset if this is the first iteration of search.
// Returns the candidate as SnapSearchResult that includes the area's
// |snap_offset| and its visible range on the cross axis.
base::Optional<SnapSearchResult> FindClosestValidArea(
SearchAxis axis,
float current_offset,
const SnapSearchResult& cross_axis_snap_result) const;
// Returns all the info needed to snap at this area on the given axis,
// including:
// - The offset at which the snap area and the snap container meet the
// requested alignment.
// - The visible range within which the snap area is visible on the cross
// axis.
SnapSearchResult GetSnapSearchResult(SearchAxis axis,
const SnapAreaData& data) const;
bool IsSnapportCoveredOnAxis(SearchAxis axis,
float current_offset,
const gfx::RectF& area_rect) const;
// Specifies whether a scroll container is a scroll snap container, how
// strictly it snaps, and which axes are considered.
// See for details.
ScrollSnapType scroll_snap_type_;
// The rect of the snap_container relative to its boundary.
gfx::RectF rect_;
// The maximal scroll position of the SnapContainer, in the same coordinate
// with blink's scroll position.
gfx::ScrollOffset max_position_;
// A valid snap position should be within the |proximity_range_| of the
// current offset on the snapping axis.
gfx::ScrollOffset proximity_range_;
// The SnapAreaData for the snap areas in this snap container. When a scroll
// happens, we iterate through the snap_area_list to find the best snap
// position.
std::vector<SnapAreaData> snap_area_list_;
CC_EXPORT std::ostream& operator<<(std::ostream&, const SnapAreaData&);
CC_EXPORT std::ostream& operator<<(std::ostream&, const SnapContainerData&);
} // namespace cc