| // 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. |
| |
| #ifndef CC_INPUT_SNAP_SELECTION_STRATEGY_H_ |
| #define CC_INPUT_SNAP_SELECTION_STRATEGY_H_ |
| |
| #include <memory> |
| |
| #include "cc/input/scroll_snap_data.h" |
| |
| namespace cc { |
| |
| enum class SnapStopAlwaysFilter { kIgnore, kRequire }; |
| enum class SnapTargetsPrioritization { kIgnore, kRequire }; |
| |
| // This class represents an abstract strategy that decide which snap selection |
| // should be considered valid. There are concrete implementations for three core |
| // scrolling types: scroll with end position only, scroll with direction only, |
| // and scroll with end position and direction. |
| class CC_EXPORT SnapSelectionStrategy { |
| public: |
| SnapSelectionStrategy() = default; |
| virtual ~SnapSelectionStrategy() = default; |
| // Strategy for scrolling to a particular position in a non-directional |
| // manner. |
| static std::unique_ptr<SnapSelectionStrategy> CreateForEndPosition( |
| const gfx::PointF& current_position, |
| bool scrolled_x, |
| bool scrolled_y); |
| |
| // Strategy for scrolling in a direction by some small amount, |
| // giving preference to stop at snap areas. |
| // |use_fractional_offsets| should be true when the current position is |
| // provided in fractional pixels. |
| static std::unique_ptr<SnapSelectionStrategy> CreateForDirection( |
| gfx::PointF current_position, |
| gfx::Vector2dF step, |
| bool use_fractional_offsets, |
| SnapStopAlwaysFilter filter = SnapStopAlwaysFilter::kIgnore); |
| |
| // Strategy for scrolling by some large offset in a particular direction. |
| // Unlike CreateForDirection, prefers scrolling by the given displacement |
| // over snapping to nearby points. |
| // |use_fractional_offsets| should be true when the current position is |
| // provided in fractional pixels. |
| static std::unique_ptr<SnapSelectionStrategy> CreateForDisplacement( |
| gfx::PointF current_position, |
| gfx::Vector2dF displacement, |
| bool use_fractional_offsets, |
| SnapStopAlwaysFilter filter = SnapStopAlwaysFilter::kIgnore); |
| |
| // This prefers scrolling by the optimal displacement of about a page size |
| // in the given displacement unit vector direction. |
| // It additionally prefers snap points that scroll at most a page. |
| // |use_fractional_offsets| should be true when the current position is |
| // provided in fractional pixels. |
| static std::unique_ptr<SnapSelectionStrategy> CreateForPageScroll( |
| gfx::PointF current_position, |
| gfx::Vector2dF direction, |
| gfx::Size page_size, |
| bool use_fractional_offsets, |
| SnapStopAlwaysFilter filter = SnapStopAlwaysFilter::kIgnore); |
| |
| // Creates a selection strategy that attempts to snap to previously snapped |
| // targets if possible, but defaults to finding the closest snap point if |
| // the target no longer exists. |
| static std::unique_ptr<SnapSelectionStrategy> CreateForTargetElement( |
| gfx::PointF current_position); |
| |
| // Returns whether it's snappable on x or y depending on the scroll performed. |
| virtual bool ShouldSnapOnX() const = 0; |
| virtual bool ShouldSnapOnY() const = 0; |
| |
| // Returns whether snapping should attempt to snap to the previously snapped |
| // area if possible. |
| virtual bool ShouldPrioritizeSnapTargets() const; |
| |
| // Returns the end position of the scroll if no snap interferes. |
| virtual gfx::PointF intended_position() const = 0; |
| // Returns the scroll position from which the snap position should minimize |
| // its distance. |
| virtual gfx::PointF base_position() const = 0; |
| // Returns the current scroll position of the snap container. |
| const gfx::PointF& current_position() const { return current_position_; } |
| |
| // Returns true if the given snap offset matches the strategy's preference. |
| virtual bool IsPreferredSnapPosition(SearchAxis axis, |
| float position) const = 0; |
| |
| // Returns true if the selection strategy considers the given snap offset |
| // valid for the current axis. |
| virtual bool IsValidSnapPosition(SearchAxis axis, float position) const = 0; |
| virtual bool IsValidSnapArea(SearchAxis axis, const SnapAreaData& data) const; |
| |
| virtual bool HasIntendedDirection() const; |
| |
| // Returns true if a snap area with scroll-snap-stop:always should not be |
| // bypassed. |
| virtual bool ShouldRespectSnapStop() const; |
| |
| // Returns the best result according to snap selection strategy. This method |
| // is called at the end of selection process to make the final decision. |
| // |
| // -closest: snap search result representing closest match. |
| // -covering: snap search result representing the original target if it makes |
| // a snaparea covering the snapport. |
| virtual const std::optional<SnapSearchResult>& PickBestResult( |
| const std::optional<SnapSearchResult>& closest, |
| const std::optional<SnapSearchResult>& covering) const = 0; |
| |
| // Returns true when the current scroll offset is provided in fractional |
| // pixels. |
| virtual bool UsingFractionalOffsets() const; |
| |
| virtual std::unique_ptr<SnapSelectionStrategy> Clone() const = 0; |
| |
| protected: |
| explicit SnapSelectionStrategy(const gfx::PointF& current_position) |
| : current_position_(current_position) {} |
| const gfx::PointF current_position_; |
| }; |
| |
| // Examples for intended end position scrolls include |
| // - a panning gesture, released without momentum |
| // - manupulating the scrollbar "thumb" explicitly |
| // - programmatically scrolling via APIs such as scrollTo() |
| // - tabbing through the document's focusable elements |
| // - navigating to an anchor within the page |
| // - homing operations such as the Home/End keys |
| // For this type of scrolls, we want to |
| // * Minimize the distance between the snap position and the end position. |
| // * Return the end position if that makes a snap area covers the snapport. |
| class EndPositionStrategy : public SnapSelectionStrategy { |
| public: |
| EndPositionStrategy(const gfx::PointF& current_position, |
| bool scrolled_x, |
| bool scrolled_y, |
| SnapTargetsPrioritization snap_targets_prioritization = |
| SnapTargetsPrioritization::kIgnore) |
| : SnapSelectionStrategy(current_position), |
| scrolled_x_(scrolled_x), |
| scrolled_y_(scrolled_y), |
| snap_targets_prioritization_(snap_targets_prioritization) {} |
| EndPositionStrategy(const EndPositionStrategy& other) = default; |
| ~EndPositionStrategy() override = default; |
| |
| bool ShouldSnapOnX() const override; |
| bool ShouldSnapOnY() const override; |
| |
| gfx::PointF intended_position() const override; |
| gfx::PointF base_position() const override; |
| |
| bool IsPreferredSnapPosition(SearchAxis axis, float position) const override; |
| bool IsValidSnapPosition(SearchAxis axis, float position) const override; |
| bool HasIntendedDirection() const override; |
| bool ShouldPrioritizeSnapTargets() const override; |
| |
| const std::optional<SnapSearchResult>& PickBestResult( |
| const std::optional<SnapSearchResult>& closest, |
| const std::optional<SnapSearchResult>& covering) const override; |
| std::unique_ptr<SnapSelectionStrategy> Clone() const override; |
| |
| private: |
| // Whether the x axis and y axis have been scrolled in this scroll gesture. |
| const bool scrolled_x_; |
| const bool scrolled_y_; |
| SnapTargetsPrioritization snap_targets_prioritization_; |
| }; |
| |
| // Examples for intended direction scrolls include |
| // - pressing an arrow key on the keyboard |
| // - a swiping gesture interpreted as a fixed (rather than inertial) scroll |
| // - a “fling” gesture, interpreted with momentum |
| // - programmatically scrolling via APIs such as scrollBy() |
| // - paging operations such as the PgUp/PgDn keys (or equivalent operations on |
| // the scrollbar) |
| // For this type of scroll, we want to |
| // * Minimize the distance between the snap position and |
| // the starting position if we only prefer the direction |
| // so that we stop at the first snap position in that direction. |
| // * When the step distance is preferred, prefer skipping snap positions |
| // to scroll closer to the step distance. |
| // * Return the default intended position (using the default step) if that makes |
| // a snap area covers the snapport. |
| class DirectionStrategy : public SnapSelectionStrategy { |
| public: |
| enum class StepPreference { |
| // Prefer only the direction, but otherwise choose a closer snap position. |
| kDirection, |
| // Prefer snap areas close to the specified step distance. |
| kDistance |
| }; |
| |
| // |use_fractional_offsets| should be true when the current position is |
| // provided in fractional pixels. |
| DirectionStrategy(const gfx::PointF& current_position, |
| const gfx::Vector2dF& step, |
| StepPreference preferred_step, |
| const gfx::Vector2dF preferred_min_displacement, |
| const gfx::Vector2dF preferred_max_displacement, |
| SnapStopAlwaysFilter filter, |
| bool use_fractional_offsets) |
| : SnapSelectionStrategy(current_position), |
| step_(step), |
| preferred_step_(preferred_step), |
| preferred_min_displacement_(preferred_min_displacement), |
| preferred_max_displacement_(preferred_max_displacement), |
| snap_stop_always_filter_(filter), |
| use_fractional_offsets_(use_fractional_offsets) {} |
| DirectionStrategy(const DirectionStrategy& other) = default; |
| ~DirectionStrategy() override = default; |
| |
| bool ShouldSnapOnX() const override; |
| bool ShouldSnapOnY() const override; |
| |
| gfx::PointF intended_position() const override; |
| gfx::PointF base_position() const override; |
| |
| bool IsPreferredSnapPosition(SearchAxis axis, float position) const override; |
| bool IsValidSnapPosition(SearchAxis axis, float position) const override; |
| bool IsValidSnapArea(SearchAxis axis, |
| const SnapAreaData& area) const override; |
| |
| bool ShouldRespectSnapStop() const override; |
| |
| const std::optional<SnapSearchResult>& PickBestResult( |
| const std::optional<SnapSearchResult>& closest, |
| const std::optional<SnapSearchResult>& covering) const override; |
| |
| bool UsingFractionalOffsets() const override; |
| |
| std::unique_ptr<SnapSelectionStrategy> Clone() const override; |
| |
| private: |
| // The default step for this DirectionStrategy. |
| const gfx::Vector2dF step_; |
| // How strictly to prefer the step's magnitude. |
| const StepPreference preferred_step_; |
| |
| // Some operations, e.g. scrolling by a page, prefer snap areas that |
| // scroll no more than a certain amount and at least a certain amount. |
| // 0 represents unrestricted displacement. |
| const gfx::Vector2dF preferred_min_displacement_; |
| const gfx::Vector2dF preferred_max_displacement_; |
| |
| SnapStopAlwaysFilter snap_stop_always_filter_; |
| bool use_fractional_offsets_; |
| }; |
| |
| } // namespace cc |
| |
| #endif // CC_INPUT_SNAP_SELECTION_STRATEGY_H_ |