Added loop setting to vertical drum picker.

This commit is contained in:
23rd 2022-04-01 17:02:01 +03:00
parent 7b7016c4b3
commit 7fda14ccca
2 changed files with 72 additions and 23 deletions

View file

@ -46,20 +46,28 @@ VerticalDrumPicker::VerticalDrumPicker(
PaintItemCallback &&paintCallback, PaintItemCallback &&paintCallback,
int itemsCount, int itemsCount,
int itemHeight, int itemHeight,
int startIndex) int startIndex,
bool looped)
: RpWidget(parent) : RpWidget(parent)
, _itemsCount(itemsCount) , _itemsCount(itemsCount)
, _itemHeight(itemHeight) , _itemHeight(itemHeight)
, _paintCallback(std::move(paintCallback)) , _paintCallback(std::move(paintCallback))
, _pendingStartIndex(startIndex) { , _pendingStartIndex(startIndex)
, _loopData({ .looped = looped }) {
Expects(_paintCallback != nullptr); Expects(_paintCallback != nullptr);
sizeValue( sizeValue(
) | rpl::start_with_next([=](const QSize &s) { ) | rpl::start_with_next([=](const QSize &s) {
_itemsVisibleCount = std::ceil(float64(s.height()) / _itemHeight); _itemsVisible.count = std::ceil(float64(s.height()) / _itemHeight);
if (_pendingStartIndex && _itemsVisibleCount) { _itemsVisible.centerOffset = _itemsVisible.count / 2;
if (_pendingStartIndex && _itemsVisible.count) {
_index = normalizedIndex(base::take(_pendingStartIndex) _index = normalizedIndex(base::take(_pendingStartIndex)
- _itemsVisibleCount / 2); - _itemsVisible.centerOffset);
}
if (!_loopData.looped) {
_loopData.minIndex = -_itemsVisible.centerOffset;
_loopData.maxIndex = _itemsCount - 1 - _itemsVisible.centerOffset;
} }
}, lifetime()); }, lifetime());
@ -70,11 +78,15 @@ VerticalDrumPicker::VerticalDrumPicker(
const auto outerWidth = width(); const auto outerWidth = width();
const auto centerY = height() / 2.; const auto centerY = height() / 2.;
const auto shiftedY = _itemHeight * _shift; const auto shiftedY = _itemHeight * _shift;
for (auto i = -1; i < (_itemsVisibleCount + 1); i++) { for (auto i = -1; i < (_itemsVisible.count + 1); i++) {
const auto index = normalizedIndex(i + _index);
if (!isIndexInRange(index)) {
continue;
}
const auto y = (_itemHeight * i + shiftedY); const auto y = (_itemHeight * i + shiftedY);
_paintCallback( _paintCallback(
p, p,
normalizedIndex(i + _index), index,
y, y,
((y + _itemHeight / 2.) - centerY) / centerY, ((y + _itemHeight / 2.) - centerY) / centerY,
outerWidth); outerWidth);
@ -88,15 +100,32 @@ VerticalDrumPicker::VerticalDrumPicker(
} }
void VerticalDrumPicker::increaseShift(float64 by) { void VerticalDrumPicker::increaseShift(float64 by) {
_shift += by; // Guard input.
if (_shift >= 1.) { if (by >= 1.) {
_shift -= 1.; by = .99;
_index--; }
_index = normalizedIndex(_index);
} else if (_shift <= -1.) { auto shift = _shift;
_shift += 1.; auto index = _index;
_index++; shift += by;
_index = normalizedIndex(_index); if (shift >= 1.) {
shift -= 1.;
index--;
index = normalizedIndex(index);
} else if (shift <= -1.) {
shift += 1.;
index++;
index = normalizedIndex(index);
}
if (!_loopData.looped && (index <= _loopData.minIndex)) {
_shift = std::min(0., shift);
_index = _loopData.minIndex;
} else if (!_loopData.looped && (index >= _loopData.maxIndex)) {
_shift = std::max(0., shift);
_index = _loopData.maxIndex;
} else {
_shift = shift;
_index = index;
} }
update(); update();
} }
@ -109,7 +138,7 @@ void VerticalDrumPicker::handleWheelEvent(not_null<QWheelEvent*> e) {
const auto delta = e->pixelDelta().y() const auto delta = e->pixelDelta().y()
? e->pixelDelta().y() ? e->pixelDelta().y()
: e->angleDelta().y(); : e->angleDelta().y();
increaseShift(std::min(delta / float64(_itemHeight), 0.99)); increaseShift(delta / float64(_itemHeight));
if (e->phase() == Qt::ScrollEnd) { if (e->phase() == Qt::ScrollEnd) {
animationDataFromIndex(); animationDataFromIndex();
_animation.jumpToOffset(0); _animation.jumpToOffset(0);
@ -121,11 +150,11 @@ void VerticalDrumPicker::handleKeyEvent(not_null<QKeyEvent*> e) {
if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Up) { if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Up) {
_animation.jumpToOffset(1); _animation.jumpToOffset(1);
} else if (e->key() == Qt::Key_PageUp && !e->isAutoRepeat()) { } else if (e->key() == Qt::Key_PageUp && !e->isAutoRepeat()) {
_animation.jumpToOffset(_itemsVisibleCount); _animation.jumpToOffset(_itemsVisible.count);
} else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Down) { } else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Down) {
_animation.jumpToOffset(-1); _animation.jumpToOffset(-1);
} else if (e->key() == Qt::Key_PageDown && !e->isAutoRepeat()) { } else if (e->key() == Qt::Key_PageDown && !e->isAutoRepeat()) {
_animation.jumpToOffset(-_itemsVisibleCount); _animation.jumpToOffset(-_itemsVisible.count);
} }
} }
@ -147,7 +176,7 @@ void VerticalDrumPicker::handleMouseEvent(not_null<QMouseEvent*> e) {
_animation.jumpToOffset(0); _animation.jumpToOffset(0);
} else { } else {
_mouse.lastPositionY = e->pos().y(); _mouse.lastPositionY = e->pos().y();
const auto toOffset = (_itemsVisibleCount / 2) const auto toOffset = _itemsVisible.centerOffset
- (_mouse.lastPositionY / _itemHeight); - (_mouse.lastPositionY / _itemHeight);
_animation.jumpToOffset(toOffset); _animation.jumpToOffset(toOffset);
} }
@ -183,7 +212,14 @@ void VerticalDrumPicker::animationDataFromIndex() {
std::round(_index + _shift)); std::round(_index + _shift));
} }
bool VerticalDrumPicker::isIndexInRange(int index) const {
return (index >= 0) && (index < _itemsCount);
}
int VerticalDrumPicker::normalizedIndex(int index) const { int VerticalDrumPicker::normalizedIndex(int index) const {
if (!_loopData.looped) {
return index;
}
if (index < 0) { if (index < 0) {
index += _itemsCount; index += _itemsCount;
} else if (index >= _itemsCount) { } else if (index >= _itemsCount) {
@ -193,7 +229,7 @@ int VerticalDrumPicker::normalizedIndex(int index) const {
} }
int VerticalDrumPicker::index() const { int VerticalDrumPicker::index() const {
return normalizedIndex(_index + _itemsVisibleCount / 2); return normalizedIndex(_index + _itemsVisible.centerOffset);
} }
} // namespace Ui } // namespace Ui

View file

@ -48,7 +48,8 @@ public:
PaintItemCallback &&paintCallback, PaintItemCallback &&paintCallback,
int itemsCount, int itemsCount,
int itemHeight, int itemHeight,
int startIndex = 0); int startIndex = 0,
bool looped = false);
[[nodiscard]] int index() const; [[nodiscard]] int index() const;
@ -67,6 +68,7 @@ private:
void increaseShift(float64 by); void increaseShift(float64 by);
void animationDataFromIndex(); void animationDataFromIndex();
[[nodiscard]] int normalizedIndex(int index) const; [[nodiscard]] int normalizedIndex(int index) const;
[[nodiscard]] bool isIndexInRange(int index) const;
const int _itemsCount; const int _itemsCount;
const int _itemHeight; const int _itemHeight;
@ -74,10 +76,21 @@ private:
PaintItemCallback _paintCallback; PaintItemCallback _paintCallback;
int _pendingStartIndex = 0; int _pendingStartIndex = 0;
int _itemsVisibleCount = 0;
struct {
int count = 0;
int centerOffset = 0;
} _itemsVisible;
int _index = 0; int _index = 0;
float64 _shift = 0.; float64 _shift = 0.;
struct {
const bool looped;
int minIndex = 0;
int maxIndex = 0;
} _loopData;
PickerAnimation _animation; PickerAnimation _animation;
struct { struct {