Scroll expanded emoji category icons by wheel.

This commit is contained in:
John Preston 2022-07-13 16:06:50 +03:00
parent 007cb9d156
commit f1144965c0
2 changed files with 109 additions and 98 deletions

View file

@ -115,17 +115,42 @@ void StickerIcon::ensureMediaCreated() const {
} }
} }
template <typename UpdateCallback>
StickersListFooter::ScrollState::ScrollState(UpdateCallback &&callback)
: animation([=](crl::time now) {
callback();
return animationCallback(now);
}) {
}
bool StickersListFooter::ScrollState::animationCallback(crl::time now) {
if (anim::Disabled()) {
now += st::stickerIconMove;
}
if (!animationStart) {
return false;
}
const auto dt = (now - animationStart) / float64(st::stickerIconMove);
if (dt >= 1.) {
animationStart = 0;
x.finish();
selectionX.finish();
selectionWidth.finish();
return false;
}
x.update(dt, anim::linear);
selectionX.update(dt, anim::linear);
selectionWidth.update(dt, anim::linear);
return true;
}
StickersListFooter::StickersListFooter(Descriptor &&descriptor) StickersListFooter::StickersListFooter(Descriptor &&descriptor)
: InnerFooter(descriptor.parent) : InnerFooter(descriptor.parent)
, _controller(descriptor.controller) , _controller(descriptor.controller)
, _searchButtonVisible(descriptor.searchButtonVisible) , _searchButtonVisible(descriptor.searchButtonVisible)
, _settingsButtonVisible(descriptor.settingsButtonVisible) , _settingsButtonVisible(descriptor.settingsButtonVisible)
, _iconsAnimation([=](crl::time now) { , _iconState([=] { update(); })
return iconsAnimationCallback(_iconState, now); , _subiconState([=] { update(); })
})
, _subiconsAnimation([=](crl::time now) {
return iconsAnimationCallback(_subiconState, now);
})
, _selectionBg(st::roundRadiusSmall, st::windowBgRipple) , _selectionBg(st::roundRadiusSmall, st::windowBgRipple)
, _barSelection(descriptor.barSelection) { , _barSelection(descriptor.barSelection) {
setMouseTracking(true); setMouseTracking(true);
@ -409,13 +434,15 @@ void StickersListFooter::updateEmojiSectionWidth() {
} }
void StickersListFooter::updateEmojiWidthCallback() { void StickersListFooter::updateEmojiWidthCallback() {
update(); refreshScrollableDimensions();
const auto shift = int(base::SafeRound(_iconState.x.current()));
const auto info = iconInfo(_iconState.selected); const auto info = iconInfo(_iconState.selected);
UpdateAnimated(_iconState.selectionX, info.left); UpdateAnimated(_iconState.selectionX, shift + info.left);
UpdateAnimated(_iconState.selectionWidth, info.width); UpdateAnimated(_iconState.selectionWidth, info.width);
if (_iconsAnimation.animating()) { if (_iconState.animation.animating()) {
iconsAnimationCallback(_iconState, crl::now()); _iconState.animationCallback(crl::now());
} }
update();
} }
void StickersListFooter::setSelectedIcon( void StickersListFooter::setSelectedIcon(
@ -438,11 +465,11 @@ void StickersListFooter::setSelectedIcon(
_iconState.max); _iconState.max);
if (animations == ValidateIconAnimations::None) { if (animations == ValidateIconAnimations::None) {
_iconState.x = anim::value(iconsXFinal, iconsXFinal); _iconState.x = anim::value(iconsXFinal, iconsXFinal);
_iconsAnimation.stop(); _iconState.animation.stop();
} else { } else {
_iconState.x.start(iconsXFinal); _iconState.x.start(iconsXFinal);
_iconState.animationStart = crl::now(); _iconState.animationStart = crl::now();
_iconsAnimation.start(); _iconState.animation.start();
} }
updateSelected(); updateSelected();
update(); update();
@ -467,11 +494,11 @@ void StickersListFooter::setSelectedSubicon(
_subiconState.max); _subiconState.max);
if (animations == ValidateIconAnimations::None) { if (animations == ValidateIconAnimations::None) {
_subiconState.x = anim::value(subiconsXFinal, subiconsXFinal); _subiconState.x = anim::value(subiconsXFinal, subiconsXFinal);
_subiconsAnimation.stop(); _subiconState.animation.stop();
} else { } else {
_subiconState.x.start(subiconsXFinal); _subiconState.x.start(subiconsXFinal);
_subiconState.animationStart = crl::now(); _subiconState.animationStart = crl::now();
_subiconsAnimation.start(); _subiconState.animation.start();
} }
updateSelected(); updateSelected();
update(); update();
@ -479,12 +506,12 @@ void StickersListFooter::setSelectedSubicon(
void StickersListFooter::processHideFinished() { void StickersListFooter::processHideFinished() {
_selected = _pressed = SpecialOver::None; _selected = _pressed = SpecialOver::None;
_iconsAnimation.stop(); _iconState.animation.stop();
_iconState.animationStart = 0; _iconState.animationStart = 0;
_iconState.x.finish(); _iconState.x.finish();
_iconState.selectionX.finish(); _iconState.selectionX.finish();
_iconState.selectionWidth.finish(); _iconState.selectionWidth.finish();
_subiconsAnimation.stop(); _subiconState.animation.stop();
_subiconState.animationStart = 0; _subiconState.animationStart = 0;
_subiconState.x.finish(); _subiconState.x.finish();
_subiconState.selectionX.finish(); _subiconState.selectionX.finish();
@ -668,13 +695,11 @@ void StickersListFooter::mouseMoveEvent(QMouseEvent *e) {
: _iconState).dragging = true; : _iconState).dragging = true;
} }
} }
checkDragging(_iconState, _iconsAnimation); checkDragging(_iconState);
checkDragging(_subiconState, _subiconsAnimation); checkDragging(_subiconState);
} }
void StickersListFooter::checkDragging( void StickersListFooter::checkDragging(ScrollState &state) {
ScrollState &state,
Ui::Animations::Basic &animation) {
if (state.dragging) { if (state.dragging) {
const auto newX = std::clamp( const auto newX = std::clamp(
(rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()) (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x())
@ -684,7 +709,7 @@ void StickersListFooter::checkDragging(
if (newX != qRound(state.x.current())) { if (newX != qRound(state.x.current())) {
state.x = anim::value(newX, newX); state.x = anim::value(newX, newX);
state.animationStart = 0; state.animationStart = 0;
animation.stop(); state.animation.stop();
update(); update();
} }
} }
@ -705,13 +730,18 @@ void StickersListFooter::mouseReleaseEvent(QMouseEvent *e) {
updateSelected(); updateSelected();
if (wasDown == _selected) { if (wasDown == _selected) {
if (const auto icon = std::get_if<IconId>(&_selected)) { if (const auto icon = std::get_if<IconId>(&_selected)) {
const auto shift = int(base::SafeRound(_iconState.x.current()));
const auto info = iconInfo(icon->index); const auto info = iconInfo(icon->index);
_iconState.selectionX = anim::value(info.left, info.left); _iconState.selectionX = anim::value(
shift + info.left,
shift + info.left);
_iconState.selectionWidth = anim::value(info.width, info.width); _iconState.selectionWidth = anim::value(info.width, info.width);
const auto subshift = int(base::SafeRound(
_subiconState.x.current()));
const auto subinfo = subiconInfo(icon->subindex); const auto subinfo = subiconInfo(icon->subindex);
_subiconState.selectionX = anim::value( _subiconState.selectionX = anim::value(
subinfo.left, subshift + subinfo.left,
subinfo.left); subshift + subinfo.left);
_subiconState.selectionWidth = anim::value( _subiconState.selectionWidth = anim::value(
subinfo.width, subinfo.width,
subinfo.width); subinfo.width);
@ -725,14 +755,12 @@ void StickersListFooter::mouseReleaseEvent(QMouseEvent *e) {
} }
bool StickersListFooter::finishDragging() { bool StickersListFooter::finishDragging() {
const auto icon = finishDragging(_iconState, _iconsAnimation); const auto icon = finishDragging(_iconState);
const auto subicon = finishDragging(_subiconState, _subiconsAnimation); const auto subicon = finishDragging(_subiconState);
return icon || subicon; return icon || subicon;
} }
bool StickersListFooter::finishDragging( bool StickersListFooter::finishDragging(ScrollState &state) {
ScrollState &state,
Ui::Animations::Basic &animation) {
if (!state.dragging) { if (!state.dragging) {
return false; return false;
} }
@ -743,7 +771,7 @@ bool StickersListFooter::finishDragging(
if (newX != qRound(state.x.current())) { if (newX != qRound(state.x.current())) {
state.x = anim::value(newX, newX); state.x = anim::value(newX, newX);
state.animationStart = 0; state.animationStart = 0;
animation.stop(); state.animation.stop();
update(); update();
} }
state.dragging = false; state.dragging = false;
@ -767,32 +795,37 @@ void StickersListFooter::scrollByWheelEvent(
not_null<QWheelEvent*> e) { not_null<QWheelEvent*> e) {
auto horizontal = (e->angleDelta().x() != 0); auto horizontal = (e->angleDelta().x() != 0);
auto vertical = (e->angleDelta().y() != 0); auto vertical = (e->angleDelta().y() != 0);
if (!horizontal && !vertical) {
return;
}
if (horizontal) { if (horizontal) {
_horizontal = true; _horizontal = true;
} }
auto newX = qRound(_iconState.x.current()); auto delta = horizontal
if (/*_horizontal && */horizontal) { ? ((rtl() ? -1 : 1) * (e->pixelDelta().x()
newX = std::clamp( ? e->pixelDelta().x()
newX - (rtl() ? -1 : 1) * (e->pixelDelta().x() : e->angleDelta().x()))
? e->pixelDelta().x() : (e->pixelDelta().y()
: e->angleDelta().x()), ? e->pixelDelta().y()
0, : e->angleDelta().y());
_iconState.max); const auto use = [&](ScrollState &state) {
} else if (/*!_horizontal && */vertical) { const auto now = qRound(state.x.current());
newX = std::clamp( const auto used = now - delta;
newX - (e->pixelDelta().y() const auto next = std::clamp(used, 0, state.max);
? e->pixelDelta().y() delta = next - used;
: e->angleDelta().y()), if (next != now) {
0, state.x = anim::value(next, next);
_iconState.max); state.animationStart = 0;
} state.animation.stop();
if (newX != qRound(_iconState.x.current())) { updateSelected();
_iconState.x = anim::value(newX, newX); update();
_iconState.animationStart = 0; }
_iconsAnimation.stop(); };
updateSelected(); const auto index = v::get<IconId>(_selected).index;
update(); if (_icons[index].setId == AllEmojiSectionSetId()) {
use(_subiconState);
} }
use(_iconState);
} }
void StickersListFooter::clipCallback( void StickersListFooter::clipCallback(
@ -856,7 +889,6 @@ void StickersListFooter::updateSelected() {
&& y < _iconsTop + st::emojiFooterHeight && y < _iconsTop + st::emojiFooterHeight
&& x >= _iconsLeft && x >= _iconsLeft
&& x < width() - _iconsRight) { && x < width() - _iconsRight) {
x += qRound(_iconState.x.current());
enumerateIcons([&](const IconInfo &info) { enumerateIcons([&](const IconInfo &info) {
if (x >= info.left && x < info.left + info.width) { if (x >= info.left && x < info.left + info.width) {
newOver = IconId{ .index = info.index }; newOver = IconId{ .index = info.index };
@ -927,14 +959,7 @@ void StickersListFooter::refreshIcons(
refreshIconsGeometry(animations); refreshIconsGeometry(animations);
} }
void StickersListFooter::refreshIconsGeometry( void StickersListFooter::refreshScrollableDimensions() {
ValidateIconAnimations animations) {
_selected = _pressed = SpecialOver::None;
_iconState.x.finish();
_iconState.selectionX.finish();
_iconState.selectionWidth.finish();
_iconState.animationStart = 0;
_iconsAnimation.stop();
const auto &last = iconInfo(_icons.size() - 1); const auto &last = iconInfo(_icons.size() - 1);
_iconState.max = std::max( _iconState.max = std::max(
last.left + last.width + _iconsRight - width(), last.left + last.width + _iconsRight - width(),
@ -942,9 +967,19 @@ void StickersListFooter::refreshIconsGeometry(
if (_iconState.x.current() > _iconState.max) { if (_iconState.x.current() > _iconState.max) {
_iconState.x = anim::value(_iconState.max, _iconState.max); _iconState.x = anim::value(_iconState.max, _iconState.max);
} }
}
void StickersListFooter::refreshIconsGeometry(
ValidateIconAnimations animations) {
_selected = _pressed = SpecialOver::None;
_iconState.x.finish();
_iconState.selectionX.finish();
_iconState.selectionWidth.finish();
_iconState.animationStart = 0;
_iconState.animation.stop();
refreshScrollableDimensions();
updateSelected(); updateSelected();
validateSelectedIcon(_activeByScrollId, animations); validateSelectedIcon(_activeByScrollId, animations);
refreshSubiconsGeometry();
update(); update();
} }
@ -954,7 +989,7 @@ void StickersListFooter::refreshSubiconsGeometry() {
_subiconState.selectionX.finish(); _subiconState.selectionX.finish();
_subiconState.selectionWidth.finish(); _subiconState.selectionWidth.finish();
_subiconState.animationStart = 0; _subiconState.animationStart = 0;
_subiconsAnimation.stop(); _subiconState.animation.stop();
const auto single = st::stickerIconWidth; const auto single = st::stickerIconWidth;
const auto half = single / 2; const auto half = single / 2;
const auto count = int(Section::Symbols) - int(Section::Recent); const auto count = int(Section::Symbols) - int(Section::Recent);
@ -975,6 +1010,7 @@ void StickersListFooter::refreshSubiconsGeometry() {
if (_subiconState.x.current() > _subiconState.max) { if (_subiconState.x.current() > _subiconState.max) {
_subiconState.x = anim::value(_subiconState.max, _subiconState.max); _subiconState.x = anim::value(_subiconState.max, _subiconState.max);
} }
updateEmojiWidthCallback();
} }
bool StickersListFooter::hasOnlyFeaturedSets() const { bool StickersListFooter::hasOnlyFeaturedSets() const {
@ -1220,30 +1256,4 @@ void StickersListFooter::paintSetIcon(
} }
} }
bool StickersListFooter::iconsAnimationCallback(
ScrollState &state,
crl::time now) {
if (anim::Disabled()) {
now += st::stickerIconMove;
}
if (state.animationStart) {
const auto dt = (now - state.animationStart)
/ float64(st::stickerIconMove);
if (dt >= 1.) {
state.animationStart = 0;
state.x.finish();
state.selectionX.finish();
state.selectionWidth.finish();
} else {
state.x.update(dt, anim::linear);
state.selectionX.update(dt, anim::linear);
state.selectionWidth.update(dt, anim::linear);
}
}
update();
return (state.animationStart != 0);
}
} // namespace ChatHelpers } // namespace ChatHelpers

View file

@ -143,6 +143,11 @@ private:
bool visible = false; bool visible = false;
}; };
struct ScrollState { struct ScrollState {
template <typename UpdateCallback>
explicit ScrollState(UpdateCallback &&callback);
bool animationCallback(crl::time now);
int selected = 0; int selected = 0;
int max = 0; int max = 0;
int draggingStartX = 0; int draggingStartX = 0;
@ -151,6 +156,7 @@ private:
anim::value selectionX; anim::value selectionX;
anim::value selectionWidth; anim::value selectionWidth;
crl::time animationStart = 0; crl::time animationStart = 0;
Ui::Animations::Basic animation;
}; };
void enumerateVisibleIcons(Fn<void(const IconInfo &)> callback) const; void enumerateVisibleIcons(Fn<void(const IconInfo &)> callback) const;
@ -160,7 +166,6 @@ private:
[[nodiscard]] IconInfo subiconInfo(int index) const; [[nodiscard]] IconInfo subiconInfo(int index) const;
[[nodiscard]] std::shared_ptr<Lottie::FrameRenderer> getLottieRenderer(); [[nodiscard]] std::shared_ptr<Lottie::FrameRenderer> getLottieRenderer();
bool iconsAnimationCallback(ScrollState &state, crl::time now);
void setSelectedIcon( void setSelectedIcon(
int newSelected, int newSelected,
ValidateIconAnimations animations); ValidateIconAnimations animations);
@ -173,13 +178,12 @@ private:
void refreshIconsGeometry(ValidateIconAnimations animations); void refreshIconsGeometry(ValidateIconAnimations animations);
void refreshSubiconsGeometry(); void refreshSubiconsGeometry();
void refreshScrollableDimensions();
void updateSelected(); void updateSelected();
void updateSetIcon(uint64 setId); void updateSetIcon(uint64 setId);
void updateSetIconAt(int left); void updateSetIconAt(int left);
void checkDragging(ScrollState &state, Ui::Animations::Basic &animation); void checkDragging(ScrollState &state);
bool finishDragging( bool finishDragging(ScrollState &state);
ScrollState &state,
Ui::Animations::Basic &animation);
bool finishDragging(); bool finishDragging();
void paintStickerSettingsIcon(Painter &p) const; void paintStickerSettingsIcon(Painter &p) const;
void paintSearchIcon(Painter &p) const; void paintSearchIcon(Painter &p) const;
@ -223,10 +227,7 @@ private:
int _iconsTop = 0; int _iconsTop = 0;
ScrollState _iconState; ScrollState _iconState;
Ui::Animations::Basic _iconsAnimation;
ScrollState _subiconState; ScrollState _subiconState;
Ui::Animations::Basic _subiconsAnimation;
Ui::RoundRect _selectionBg; Ui::RoundRect _selectionBg;
Ui::Animations::Simple _subiconsWidthAnimation; Ui::Animations::Simple _subiconsWidthAnimation;