Since https://github.com/w3c/csswg-drafts/issues/6863, if the scroll destination is located within the large snap area, the snapport no longer needs to overlap with the large area. So for example, the snapport size is 100px, and a larger snap area is located at [0px, 350px], and a small snap area is located at [350px, 400px] and both areas scroll-snap-align is `start`, the current scroll offset is at the bottom of the scroll range, 300px. In this case `scrollBy(-50px)` snaps to 0px in the previous implementation, now with this change it snaps to 250px. destination = 250px (scrollBy(-50px) from current offset 300px) v 0 100 250 350 400 | | | | | [ large area: align start ][sml] old: strict start-align jumps the snapport back to 0px <--------> new: nearest valid offset keeps the snapport at 250px <--------> (100px snapport still fully inside the large area) Because the large area (350px) is bigger than the snapport (100px), every offset in [0px, 250px] is a valid `start` snap (250 = 350 - 100). The destination 250px already lands in that range, so there is no reason to jump all the way back to 0px. Differential Revision: https://phabricator.services.mozilla.com/D305781
170 lines
5.6 KiB
C++
170 lines
5.6 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef mozilla_layout_ScrollSnapInfo_h_
|
|
#define mozilla_layout_ScrollSnapInfo_h_
|
|
|
|
#include <iosfwd>
|
|
|
|
#include "mozilla/AppUnits.h"
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/ScrollSnapTargetId.h"
|
|
#include "mozilla/ScrollTypes.h"
|
|
#include "mozilla/ServoStyleConsts.h"
|
|
#include "mozilla/WritingModes.h"
|
|
#include "mozilla/layers/LayersTypes.h"
|
|
#include "nsPoint.h"
|
|
|
|
class nsIContent;
|
|
class nsIFrame;
|
|
struct nsRect;
|
|
struct nsSize;
|
|
struct nsStyleDisplay;
|
|
|
|
namespace mozilla {
|
|
|
|
enum class StyleScrollSnapStrictness : uint8_t;
|
|
class WritingMode;
|
|
|
|
struct SnapPoint {
|
|
SnapPoint() = default;
|
|
explicit SnapPoint(const nsPoint& aPoint)
|
|
: mX(Some(aPoint.x)), mY(Some(aPoint.y)) {}
|
|
SnapPoint(Maybe<nscoord>&& aX, Maybe<nscoord>&& aY)
|
|
: mX(std::move(aX)), mY(std::move(aY)) {}
|
|
|
|
bool operator==(const SnapPoint&) const = default;
|
|
|
|
Maybe<nscoord> mX;
|
|
Maybe<nscoord> mY;
|
|
|
|
const Maybe<nscoord>& I(WritingMode aWM) const {
|
|
return aWM.IsVertical() ? mY : mX;
|
|
}
|
|
const Maybe<nscoord>& B(WritingMode aWM) const {
|
|
return aWM.IsVertical() ? mX : mY;
|
|
}
|
|
};
|
|
|
|
struct ScrollSnapRange {
|
|
using ScrollDirection = layers::ScrollDirection;
|
|
ScrollSnapRange() = default;
|
|
|
|
ScrollSnapRange(const nsRect& aSnapArea, ScrollDirection aDirection,
|
|
ScrollSnapTargetId aTargetId)
|
|
: mSnapArea(aSnapArea), mDirection(aDirection), mTargetId(aTargetId) {}
|
|
|
|
nsRect mSnapArea;
|
|
ScrollDirection mDirection;
|
|
ScrollSnapTargetId mTargetId;
|
|
|
|
bool operator==(const ScrollSnapRange& aOther) const = default;
|
|
|
|
nscoord Start() const {
|
|
return mDirection == ScrollDirection::eHorizontal ? mSnapArea.X()
|
|
: mSnapArea.Y();
|
|
}
|
|
|
|
nscoord End() const {
|
|
return mDirection == ScrollDirection::eHorizontal ? mSnapArea.XMost()
|
|
: mSnapArea.YMost();
|
|
}
|
|
|
|
// Returns true if |aPoint| is a valid snap position in this range.
|
|
bool IsValid(nscoord aPoint, nscoord aSnapportSize) const {
|
|
MOZ_ASSERT(End() - Start() > aSnapportSize);
|
|
return Start() <= aPoint && aPoint <= End();
|
|
}
|
|
|
|
// Returns the scroll offset nearest to |aDestination| that keeps the snapport
|
|
// (of size |aSnapportSize|) within this range's snap area.
|
|
nscoord FindNearestSnapPoint(nscoord aDestination,
|
|
nscoord aSnapportSize) const;
|
|
};
|
|
|
|
struct ScrollSnapInfo {
|
|
using ScrollSnapRange = mozilla::ScrollSnapRange;
|
|
ScrollSnapInfo();
|
|
|
|
bool operator==(const ScrollSnapInfo&) const = default;
|
|
|
|
bool HasScrollSnapping() const;
|
|
bool HasSnapPositions() const;
|
|
|
|
void InitializeScrollSnapStrictness(WritingMode aWritingMode,
|
|
const nsStyleDisplay* aDisplay);
|
|
|
|
// The scroll frame's scroll-snap-type.
|
|
StyleScrollSnapStrictness mScrollSnapStrictnessX;
|
|
StyleScrollSnapStrictness mScrollSnapStrictnessY;
|
|
|
|
StyleScrollSnapStrictness StrictnessInline(WritingMode aWM) const {
|
|
return aWM.IsVertical() ? mScrollSnapStrictnessY : mScrollSnapStrictnessX;
|
|
}
|
|
StyleScrollSnapStrictness StrictnessBlock(WritingMode aWM) const {
|
|
return aWM.IsVertical() ? mScrollSnapStrictnessX : mScrollSnapStrictnessY;
|
|
}
|
|
|
|
struct SnapTarget {
|
|
// The scroll positions corresponding to scroll-snap-align values.
|
|
SnapPoint mSnapPoint;
|
|
|
|
// https://drafts.csswg.org/css-scroll-snap/#scroll-snap-area
|
|
nsRect mSnapArea;
|
|
|
|
// https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-snap-stop
|
|
StyleScrollSnapStop mScrollSnapStop = StyleScrollSnapStop::Normal;
|
|
|
|
// Use for tracking the last snapped target.
|
|
ScrollSnapTargetId mTargetId = ScrollSnapTargetId::None;
|
|
|
|
SnapTarget() = default;
|
|
|
|
SnapTarget(Maybe<nscoord>&& aSnapPositionX, Maybe<nscoord>&& aSnapPositionY,
|
|
const nsRect& aSnapArea, StyleScrollSnapStop aScrollSnapStop,
|
|
ScrollSnapTargetId aTargetId)
|
|
: mSnapPoint(std::move(aSnapPositionX), std::move(aSnapPositionY)),
|
|
mSnapArea(aSnapArea),
|
|
mScrollSnapStop(aScrollSnapStop),
|
|
mTargetId(aTargetId) {}
|
|
|
|
bool operator==(const SnapTarget& aOther) const = default;
|
|
};
|
|
|
|
CopyableTArray<SnapTarget> mSnapTargets;
|
|
|
|
// A utility function to iterate over the valid snap targets for the given
|
|
// |aDestination| until |aFunc| returns false.
|
|
void ForEachValidTargetFor(
|
|
const nsPoint& aDestination,
|
|
const std::function<bool(const SnapTarget&)>& aFunc) const;
|
|
|
|
// An array of the range that the target element is larger than the snapport
|
|
// on the axis.
|
|
// Snap positions in this range will be valid snap positions in the case where
|
|
// the distance between the closest snap position and the second closest snap
|
|
// position is still larger than the snapport size.
|
|
// See https://drafts.csswg.org/css-scroll-snap-1/#snap-overflow
|
|
//
|
|
// Note: This range contains scroll-margin values.
|
|
CopyableTArray<ScrollSnapRange> mXRangeWiderThanSnapport;
|
|
CopyableTArray<ScrollSnapRange> mYRangeWiderThanSnapport;
|
|
|
|
// Note: This snapport size has been already deflated by scroll-padding.
|
|
nsSize mSnapportSize;
|
|
};
|
|
|
|
std::ostream& operator<<(std::ostream& aStream,
|
|
const ScrollSnapInfo::SnapTarget& aTarget);
|
|
|
|
// For convenience, allow printing a pointer to a SnapTarget without
|
|
// explicitly dereferencing it. This makes it easier to print an
|
|
// array of pointers.
|
|
std::ostream& operator<<(std::ostream& aStream,
|
|
const ScrollSnapInfo::SnapTarget* aTarget);
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_layout_ScrollSnapInfo_h_
|