Display selected emoji set in footer by background.

This commit is contained in:
John Preston 2022-07-12 22:33:52 +03:00
parent 0bd9d5f7ae
commit 3dfe48a407
3 changed files with 217 additions and 108 deletions

View file

@ -1213,8 +1213,15 @@ std::vector<StickerIcon> EmojiListWidget::fillIcons() {
auto result = std::vector<StickerIcon>(); auto result = std::vector<StickerIcon>();
result.reserve(2 + _custom.size()); result.reserve(2 + _custom.size());
result.emplace_back(EmojiSectionSetId(Ui::Emoji::Section::Recent)); result.emplace_back(RecentEmojiSectionSetId());
result.emplace_back(EmojiSectionSetId(Ui::Emoji::Section::People)); if (_custom.empty()) {
using Section = Ui::Emoji::Section;
for (auto i = int(Section::People); i <= int(Section::Symbols); ++i) {
result.emplace_back(EmojiSectionSetId(Section(i)));
}
} else {
result.emplace_back(AllEmojiSectionSetId());
}
for (const auto &custom : _custom) { for (const auto &custom : _custom) {
const auto set = custom.set; const auto set = custom.set;
const auto s = custom.list[0].document; const auto s = custom.list[0].document;

View file

@ -32,25 +32,42 @@ constexpr auto kEmojiSectionSetIdBase = uint64(0x77FF'FFFF'FFFF'FFF0ULL);
using EmojiSection = Ui::Emoji::Section; using EmojiSection = Ui::Emoji::Section;
void UpdateAnimated(anim::value &value, int to) {
value = anim::value(
(value.from() != value.to()) ? value.from() : to,
to);
}
void UpdateAnimated(
anim::value &value,
int to,
ValidateIconAnimations animations) {
if (animations == ValidateIconAnimations::Full) {
value.start(to);
} else {
value = anim::value(to, to);
}
}
} // namespace } // namespace
uint64 EmojiSectionSetId(EmojiSection section) { uint64 EmojiSectionSetId(EmojiSection section) {
Expects(section >= EmojiSection::Recent Expects(section >= EmojiSection::Recent
&& section <= EmojiSection::Symbols); && section <= EmojiSection::Symbols);
return kEmojiSectionSetIdBase + static_cast<uint64>(section); return kEmojiSectionSetIdBase + static_cast<uint64>(section) + 1;
} }
uint64 RecentEmojiSectionSetId() { uint64 RecentEmojiSectionSetId() {
return EmojiSectionSetId(EmojiSection::Recent); return EmojiSectionSetId(EmojiSection::Recent);
} }
uint64 FirstEmojiSectionSetId() { uint64 AllEmojiSectionSetId() {
return EmojiSectionSetId(EmojiSection::People); return kEmojiSectionSetIdBase;
} }
std::optional<EmojiSection> SetIdEmojiSection(uint64 id) { std::optional<EmojiSection> SetIdEmojiSection(uint64 id) {
const auto base = EmojiSectionSetId(EmojiSection::Recent); const auto base = RecentEmojiSectionSetId();
if (id < base) { if (id < base) {
return {}; return {};
} }
@ -68,11 +85,11 @@ StickerIcon::StickerIcon(
DocumentData *sticker, DocumentData *sticker,
int pixw, int pixw,
int pixh) int pixh)
: setId(set->id) : setId(set->id)
, set(set) , set(set)
, sticker(sticker) , sticker(sticker)
, pixw(pixw) , pixw(pixw)
, pixh(pixh) { , pixh(pixh) {
} }
StickerIcon::StickerIcon(StickerIcon&&) = default; StickerIcon::StickerIcon(StickerIcon&&) = default;
@ -103,6 +120,9 @@ StickersListFooter::StickersListFooter(Descriptor &&descriptor)
, _iconsAnimation([=](crl::time now) { , _iconsAnimation([=](crl::time now) {
return iconsAnimationCallback(now); return iconsAnimationCallback(now);
}) })
, _subiconsAnimation([=](crl::time now) {
return iconsAnimationCallback(now);
})
, _selectionBg(st::roundRadiusSmall, st::windowBgRipple) , _selectionBg(st::roundRadiusSmall, st::windowBgRipple)
, _emojiIconWidth(st::stickerIconWidth) , _emojiIconWidth(st::stickerIconWidth)
, _barSelection(descriptor.barSelection) { , _barSelection(descriptor.barSelection) {
@ -145,7 +165,7 @@ void StickersListFooter::validatePremiumIcon() const {
gradient.setStops({ gradient.setStops({
{ 0., st::stickerPanPremium1->c }, { 0., st::stickerPanPremium1->c },
{ 1., st::stickerPanPremium2->c }, { 1., st::stickerPanPremium2->c },
}); });
p.fillRect(QRect(QPoint(), size), gradient); p.fillRect(QRect(QPoint(), size), gradient);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.drawImage(QRect(QPoint(), size), mask); p.drawImage(QRect(QPoint(), size), mask);
@ -153,7 +173,7 @@ void StickersListFooter::validatePremiumIcon() const {
void StickersListFooter::clearHeavyData() { void StickersListFooter::clearHeavyData() {
const auto count = int(_icons.size()); const auto count = int(_icons.size());
const auto iconsX = qRound(_iconsX.current()); const auto iconsX = qRound(_iconState.x.current());
enumerateIcons([&](const IconInfo &info) { enumerateIcons([&](const IconInfo &info) {
auto &icon = _icons[info.index]; auto &icon = _icons[info.index];
icon.webm = nullptr; icon.webm = nullptr;
@ -187,13 +207,13 @@ void StickersListFooter::initSearch() {
_searchRequests.fire({ _searchRequests.fire({
.text = _searchField->getLastText(), .text = _searchField->getLastText(),
.forced = true, .forced = true,
}); });
}); });
connect(_searchField, &Ui::InputField::cancelled, cancelSearch); connect(_searchField, &Ui::InputField::cancelled, cancelSearch);
connect(_searchField, &Ui::InputField::changed, [=] { connect(_searchField, &Ui::InputField::changed, [=] {
_searchRequests.fire({ _searchRequests.fire({
.text = _searchField->getLastText(), .text = _searchField->getLastText(),
}); });
}); });
_searchCancel->setClickedCallback(cancelSearch); _searchCancel->setClickedCallback(cancelSearch);
@ -236,7 +256,7 @@ void StickersListFooter::returnFocus() {
} }
void StickersListFooter::enumerateVisibleIcons( void StickersListFooter::enumerateVisibleIcons(
Fn<void(const IconInfo &)> callback) const { Fn<void(const IconInfo &)> callback) const {
enumerateIcons([&](const IconInfo &info) { enumerateIcons([&](const IconInfo &info) {
if (info.visible) { if (info.visible) {
callback(info); callback(info);
@ -249,9 +269,9 @@ void StickersListFooter::enumerateVisibleIcons(
void StickersListFooter::enumerateIcons( void StickersListFooter::enumerateIcons(
Fn<bool(const IconInfo &)> callback) const { Fn<bool(const IconInfo &)> callback) const {
auto iconsX = int(base::SafeRound(_iconsX.current())); auto iconsX = int(base::SafeRound(_iconState.x.current()));
auto left = _iconsLeft - iconsX; auto left = _iconsLeft - iconsX;
const auto emojiId = FirstEmojiSectionSetId(); const auto emojiId = AllEmojiSectionSetId();
const auto right = width(); const auto right = width();
for (auto i = 0, count = int(_icons.size()); i != count; ++i) { for (auto i = 0, count = int(_icons.size()); i != count; ++i) {
auto &icon = _icons[i]; auto &icon = _icons[i];
@ -272,6 +292,28 @@ void StickersListFooter::enumerateIcons(
} }
} }
void StickersListFooter::enumerateSubicons(
Fn<bool(const IconInfo &)> callback) const {
auto iconsX = int(base::SafeRound(_subiconState.x.current()));
auto left = -iconsX;
const auto right = _emojiIconWidth;
using Section = Ui::Emoji::Section;
for (auto i = int(Section::People); i <= int(Section::Symbols); ++i) {
const auto width = st::stickerIconWidth;
const auto visible = (left + width > 0 && left < right);
const auto result = callback({
.index = i,
.left = left,
.width = int(base::SafeRound(width)),
.visible = visible,
});
if (!result) {
break;
}
left += width;
}
}
auto StickersListFooter::iconInfo(int index) const -> IconInfo { auto StickersListFooter::iconInfo(int index) const -> IconInfo {
auto result = IconInfo(); auto result = IconInfo();
enumerateIcons([&](const IconInfo &info) { enumerateIcons([&](const IconInfo &info) {
@ -284,6 +326,18 @@ auto StickersListFooter::iconInfo(int index) const -> IconInfo {
return result; return result;
} }
auto StickersListFooter::subiconInfo(int index) const -> IconInfo {
auto result = IconInfo();
enumerateSubicons([&](const IconInfo &info) {
if (info.index == index) {
result = info;
return false;
}
return true;
});
return result;
}
void StickersListFooter::preloadImages() { void StickersListFooter::preloadImages() {
enumerateVisibleIcons([&](const IconInfo &info) { enumerateVisibleIcons([&](const IconInfo &info) {
const auto &icon = _icons[info.index]; const auto &icon = _icons[info.index];
@ -299,17 +353,18 @@ void StickersListFooter::preloadImages() {
} }
void StickersListFooter::validateSelectedIcon( void StickersListFooter::validateSelectedIcon(
uint64 setId, uint64 setId,
ValidateIconAnimations animations) { ValidateIconAnimations animations) {
_activeByScrollId = setId; _activeByScrollId = setId;
using EmojiSection = Ui::Emoji::Section; using EmojiSection = Ui::Emoji::Section;
auto favedIconIndex = -1; auto favedIconIndex = -1;
auto newSelected = -1; auto newSelected = -1;
auto newSubSelected = -1;
const auto emojiSection = SetIdEmojiSection(setId); const auto emojiSection = SetIdEmojiSection(setId);
const auto isEmojiSection = emojiSection.has_value() const auto isEmojiSection = emojiSection.has_value()
&& (emojiSection != EmojiSection::Recent); && (emojiSection != EmojiSection::Recent);
const auto firstEmojiSetId = FirstEmojiSectionSetId(); const auto allEmojiSetId = AllEmojiSectionSetId();
for (auto i = 0, l = int(_icons.size()); i != l; ++i) { for (auto i = 0, l = int(_icons.size()); i != l; ++i) {
if (_icons[i].setId == setId if (_icons[i].setId == setId
|| (_icons[i].setId == Data::Stickers::FavedSetId || (_icons[i].setId == Data::Stickers::FavedSetId
@ -318,8 +373,9 @@ void StickersListFooter::validateSelectedIcon(
break; break;
} else if (_icons[i].setId == Data::Stickers::FavedSetId) { } else if (_icons[i].setId == Data::Stickers::FavedSetId) {
favedIconIndex = i; favedIconIndex = i;
} else if (isEmojiSection && _icons[i].setId == firstEmojiSetId) { } else if (isEmojiSection && _icons[i].setId == allEmojiSetId) {
newSelected = i; newSelected = i;
newSubSelected = setId - EmojiSectionSetId(EmojiSection::People);
} }
} }
setSelectedIcon( setSelectedIcon(
@ -327,12 +383,15 @@ void StickersListFooter::validateSelectedIcon(
? newSelected ? newSelected
: (favedIconIndex >= 0) ? favedIconIndex : 0), : (favedIconIndex >= 0) ? favedIconIndex : 0),
animations); animations);
setSelectedSubicon(
(newSubSelected >= 0 ? newSubSelected : 0),
animations);
} }
void StickersListFooter::updateEmojiSectionWidth() { void StickersListFooter::updateEmojiSectionWidth() {
_emojiIconExpanded = (_iconSel >= 0) _emojiIconExpanded = (_iconState.selected >= 0)
&& (_iconSel < _icons.size()) && (_iconState.selected < _icons.size())
&& (_icons[_iconSel].setId == FirstEmojiSectionSetId()); && (_icons[_iconState.selected].setId == AllEmojiSectionSetId());
const auto desired = _emojiIconExpanded const auto desired = _emojiIconExpanded
? (9 * st::stickerIconWidth / 2) ? (9 * st::stickerIconWidth / 2)
: st::stickerIconWidth; : st::stickerIconWidth;
@ -349,60 +408,83 @@ void StickersListFooter::updateEmojiSectionWidth() {
void StickersListFooter::updateEmojiWidthCallback() { void StickersListFooter::updateEmojiWidthCallback() {
update(); update();
const auto info = iconInfo(_iconSel); const auto info = iconInfo(_iconState.selected);
if (_iconSelX.from() != _iconSelX.to()) { UpdateAnimated(_iconState.selectionX, info.left);
_iconSelX = anim::value(_iconSelX.from(), info.left); UpdateAnimated(_iconState.selectionWidth, info.width);
} else {
_iconSelX = anim::value(info.left, info.left);
}
if (_iconSelWidth.from() != _iconSelWidth.to()) {
_iconSelWidth = anim::value(_iconSelWidth.from(), info.width);
} else {
_iconSelWidth = anim::value(info.width, info.width);
}
} }
void StickersListFooter::setSelectedIcon( void StickersListFooter::setSelectedIcon(
int newSelected, int newSelected,
ValidateIconAnimations animations) { ValidateIconAnimations animations) {
if (_iconSel == newSelected) { if (_iconState.selected == newSelected) {
return; return;
} }
_iconSel = newSelected; _iconState.selected = newSelected;
const auto info = iconInfo(_iconSel);
updateEmojiSectionWidth(); updateEmojiSectionWidth();
if (animations == ValidateIconAnimations::Full) { const auto info = iconInfo(_iconState.selected);
_iconSelX.start(info.left); UpdateAnimated(_iconState.selectionX, info.left, animations);
_iconSelWidth.start(info.width); UpdateAnimated(_iconState.selectionWidth, info.width, animations);
} else {
_iconSelX = anim::value(info.left, info.left);
_iconSelWidth = anim::value(info.width, info.width);
}
const auto relativeLeft = info.left - _iconsLeft; const auto relativeLeft = info.left - _iconsLeft;
const auto iconsWidthForCentering = 2 * relativeLeft + info.width; const auto iconsWidthForCentering = 2 * relativeLeft + info.width;
const auto iconsXFinal = std::clamp( const auto iconsXFinal = std::clamp(
(_iconsLeft + iconsWidthForCentering + _iconsRight - width()) / 2, (_iconsLeft + iconsWidthForCentering + _iconsRight - width()) / 2,
0, 0,
_iconsMax); _iconState.max);
if (animations == ValidateIconAnimations::None) { if (animations == ValidateIconAnimations::None) {
_iconsX = anim::value(iconsXFinal, iconsXFinal); _iconState.x = anim::value(iconsXFinal, iconsXFinal);
_iconsAnimation.stop(); _iconsAnimation.stop();
} else { } else {
_iconsX.start(iconsXFinal); _iconState.x.start(iconsXFinal);
_iconsStartAnim = crl::now(); _iconState.animationStart = crl::now();
_iconsAnimation.start(); _iconsAnimation.start();
} }
updateSelected(); updateSelected();
update(); update();
} }
void StickersListFooter::setSelectedSubicon(
int newSelected,
ValidateIconAnimations animations) {
if (_subiconState.selected == newSelected) {
return;
}
_subiconState.selected = newSelected;
const auto info = subiconInfo(_subiconState.selected);
updateEmojiSectionWidth();
UpdateAnimated(_subiconState.selectionX, info.left, animations);
UpdateAnimated(_subiconState.selectionWidth, info.width, animations);
const auto relativeLeft = info.left;
const auto subiconsWidthForCentering = 2 * relativeLeft + info.width;
const auto subiconsXFinal = std::clamp(
(subiconsWidthForCentering - width()) / 2,
0,
_subiconState.max);
if (animations == ValidateIconAnimations::None) {
_subiconState.selectionX = anim::value(
subiconsXFinal,
subiconsXFinal);
_subiconsAnimation.stop();
} else {
_subiconState.selectionX.start(subiconsXFinal);
_subiconState.animationStart = crl::now();
_subiconsAnimation.start();
}
updateSelected();
update();
}
void StickersListFooter::processHideFinished() { void StickersListFooter::processHideFinished() {
_iconOver = _iconDown = SpecialOver::None; _iconOver = _iconDown = SpecialOver::None;
_iconsStartAnim = 0;
_iconsAnimation.stop(); _iconsAnimation.stop();
_iconsX.finish(); _iconState.animationStart = 0;
_iconSelX.finish(); _iconState.x.finish();
_iconSelWidth.finish(); _iconState.selectionX.finish();
_iconState.selectionWidth.finish();
_subiconsAnimation.stop();
_subiconState.animationStart = 0;
_subiconState.x.finish();
_subiconState.selectionX.finish();
_subiconState.selectionWidth.finish();
_horizontal = false; _horizontal = false;
} }
@ -459,9 +541,9 @@ void StickersListFooter::paintEvent(QPaintEvent *e) {
} }
void StickersListFooter::paintSelectionBg(Painter &p) const { void StickersListFooter::paintSelectionBg(Painter &p) const {
auto selxrel = qRound(_iconSelX.current()); auto selxrel = qRound(_iconState.selectionX.current());
auto selx = selxrel - qRound(_iconsX.current()); auto selx = selxrel - qRound(_iconState.x.current());
const auto selw = qRound(_iconSelWidth.current()); const auto selw = qRound(_iconState.selectionWidth.current());
if (rtl()) { if (rtl()) {
selx = width() - selx - selw; selx = width() - selx - selw;
} }
@ -475,9 +557,9 @@ void StickersListFooter::paintSelectionBg(Painter &p) const {
} }
void StickersListFooter::paintSelectionBar(Painter &p) const { void StickersListFooter::paintSelectionBar(Painter &p) const {
auto selxrel = qRound(_iconSelX.current()); auto selxrel = qRound(_iconState.selectionX.current());
auto selx = selxrel - qRound(_iconsX.current()); auto selx = selxrel - qRound(_iconState.x.current());
const auto selw = qRound(_iconSelWidth.current()); const auto selw = qRound(_iconState.selectionWidth.current());
if (rtl()) { if (rtl()) {
selx = width() - selx - selw; selx = width() - selx - selw;
} }
@ -491,7 +573,7 @@ void StickersListFooter::paintSelectionBar(Painter &p) const {
void StickersListFooter::paintLeftRightFading(Painter &p) const { void StickersListFooter::paintLeftRightFading(Painter &p) const {
auto o_left = std::clamp( auto o_left = std::clamp(
_iconsX.current() / st::stickerIconLeft.width(), _iconState.x.current() / st::stickerIconLeft.width(),
0., 0.,
1.); 1.);
if (o_left > 0) { if (o_left > 0) {
@ -500,12 +582,18 @@ void StickersListFooter::paintLeftRightFading(Painter &p) const {
p.setOpacity(1.); p.setOpacity(1.);
} }
auto o_right = std::clamp( auto o_right = std::clamp(
(_iconsMax - _iconsX.current()) / st::stickerIconRight.width(), (_iconState.max - _iconState.x.current()) / st::stickerIconRight.width(),
0., 0.,
1.); 1.);
if (o_right > 0) { if (o_right > 0) {
p.setOpacity(o_right); p.setOpacity(o_right);
st::stickerIconRight.fill(p, style::rtlrect(width() - _iconsRight - st::stickerIconRight.width(), _iconsTop, st::stickerIconRight.width(), st::emojiFooterHeight, width())); st::stickerIconRight.fill(
p,
style::rtlrect(
width() - _iconsRight - st::stickerIconRight.width(),
_iconsTop,
st::stickerIconRight.width(),
st::emojiFooterHeight, width()));
p.setOpacity(1.); p.setOpacity(1.);
} }
} }
@ -556,7 +644,7 @@ void StickersListFooter::mousePressEvent(QMouseEvent *e) {
} else { } else {
_iconDown = _iconOver; _iconDown = _iconOver;
_iconsMouseDown = _iconsMousePos; _iconsMouseDown = _iconsMousePos;
_iconsStartX = qRound(_iconsX.current()); _iconsStartX = qRound(_iconState.x.current());
} }
} }
@ -576,10 +664,10 @@ void StickersListFooter::mouseMoveEvent(QMouseEvent *e) {
(rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x()) (rtl() ? -1 : 1) * (_iconsMouseDown.x() - _iconsMousePos.x())
+ _iconsStartX, + _iconsStartX,
0, 0,
_iconsMax); _iconState.max);
if (newX != qRound(_iconsX.current())) { if (newX != qRound(_iconState.x.current())) {
_iconsX = anim::value(newX, newX); _iconState.x = anim::value(newX, newX);
_iconsStartAnim = 0; _iconState.animationStart = 0;
_iconsAnimation.stop(); _iconsAnimation.stop();
update(); update();
} }
@ -603,8 +691,8 @@ void StickersListFooter::mouseReleaseEvent(QMouseEvent *e) {
if (wasDown == _iconOver) { if (wasDown == _iconOver) {
if (const auto index = std::get_if<int>(&_iconOver)) { if (const auto index = std::get_if<int>(&_iconOver)) {
const auto info = iconInfo(*index); const auto info = iconInfo(*index);
_iconSelX = anim::value(info.left, info.left); _iconState.selectionX = anim::value(info.left, info.left);
_iconSelWidth = anim::value(info.width, info.width); _iconState.selectionWidth = anim::value(info.width, info.width);
_setChosen.fire_copy(_icons[*index].setId); _setChosen.fire_copy(_icons[*index].setId);
} }
} }
@ -614,10 +702,10 @@ void StickersListFooter::finishDragging() {
auto newX = std::clamp( auto newX = std::clamp(
_iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(), _iconsStartX + _iconsMouseDown.x() - _iconsMousePos.x(),
0, 0,
_iconsMax); _iconState.max);
if (newX != qRound(_iconsX.current())) { if (newX != qRound(_iconState.x.current())) {
_iconsX = anim::value(newX, newX); _iconState.x = anim::value(newX, newX);
_iconsStartAnim = 0; _iconState.animationStart = 0;
_iconsAnimation.stop(); _iconsAnimation.stop();
update(); update();
} }
@ -644,25 +732,25 @@ void StickersListFooter::scrollByWheelEvent(
if (horizontal) { if (horizontal) {
_horizontal = true; _horizontal = true;
} }
auto newX = qRound(_iconsX.current()); auto newX = qRound(_iconState.x.current());
if (/*_horizontal && */horizontal) { if (/*_horizontal && */horizontal) {
newX = std::clamp( newX = std::clamp(
newX - (rtl() ? -1 : 1) * (e->pixelDelta().x() newX - (rtl() ? -1 : 1) * (e->pixelDelta().x()
? e->pixelDelta().x() ? e->pixelDelta().x()
: e->angleDelta().x()), : e->angleDelta().x()),
0, 0,
_iconsMax); _iconState.max);
} else if (/*!_horizontal && */vertical) { } else if (/*!_horizontal && */vertical) {
newX = std::clamp( newX = std::clamp(
newX - (e->pixelDelta().y() newX - (e->pixelDelta().y()
? e->pixelDelta().y() ? e->pixelDelta().y()
: e->angleDelta().y()), : e->angleDelta().y()),
0, 0,
_iconsMax); _iconState.max);
} }
if (newX != qRound(_iconsX.current())) { if (newX != qRound(_iconState.x.current())) {
_iconsX = anim::value(newX, newX); _iconState.x = anim::value(newX, newX);
_iconsStartAnim = 0; _iconState.animationStart = 0;
_iconsAnimation.stop(); _iconsAnimation.stop();
updateSelected(); updateSelected();
update(); update();
@ -730,7 +818,7 @@ void StickersListFooter::updateSelected() {
&& y < _iconsTop + st::emojiFooterHeight && y < _iconsTop + st::emojiFooterHeight
&& x >= _iconsLeft && x >= _iconsLeft
&& x < width() - _iconsRight) { && x < width() - _iconsRight) {
x += qRound(_iconsX.current()); 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 = info.index; newOver = info.index;
@ -794,17 +882,17 @@ void StickersListFooter::refreshIcons(
void StickersListFooter::refreshIconsGeometry( void StickersListFooter::refreshIconsGeometry(
ValidateIconAnimations animations) { ValidateIconAnimations animations) {
_iconOver = _iconDown = SpecialOver::None; _iconOver = _iconDown = SpecialOver::None;
_iconsX.finish(); _iconState.x.finish();
_iconSelX.finish(); _iconState.selectionX.finish();
_iconSelWidth.finish(); _iconState.selectionWidth.finish();
_iconsStartAnim = 0; _iconState.animationStart = 0;
_iconsAnimation.stop(); _iconsAnimation.stop();
const auto &last = iconInfo(_icons.size() - 1); const auto &last = iconInfo(_icons.size() - 1);
_iconsMax = std::max( _iconState.max = std::max(
last.left + last.width + _iconsRight - width(), last.left + last.width + _iconsRight - width(),
0); 0);
if (_iconsX.current() > _iconsMax) { if (_iconState.x.current() > _iconState.max) {
_iconsX = anim::value(_iconsMax, _iconsMax); _iconState.x = anim::value(_iconState.max, _iconState.max);
} }
updateSelected(); updateSelected();
validateSelectedIcon(_activeByScrollId, animations); validateSelectedIcon(_activeByScrollId, animations);
@ -1002,7 +1090,7 @@ void StickersListFooter::paintSetIcon(
_iconsTop + (st::emojiFooterHeight - icon->height()) / 2, _iconsTop + (st::emojiFooterHeight - icon->height()) / 2,
width()); width());
}; };
if (_icons[info.index].setId == FirstEmojiSectionSetId() if (_icons[info.index].setId == AllEmojiSectionSetId()
&& info.width > st::stickerIconWidth) { && info.width > st::stickerIconWidth) {
const auto skip = st::emojiIconSelectSkip; const auto skip = st::emojiIconSelectSkip;
p.save(); p.save();
@ -1039,24 +1127,24 @@ bool StickersListFooter::iconsAnimationCallback(crl::time now) {
if (anim::Disabled()) { if (anim::Disabled()) {
now += st::stickerIconMove; now += st::stickerIconMove;
} }
if (_iconsStartAnim) { if (_iconState.animationStart) {
const auto dt = (now - _iconsStartAnim) const auto dt = (now - _iconState.animationStart)
/ float64(st::stickerIconMove); / float64(st::stickerIconMove);
if (dt >= 1.) { if (dt >= 1.) {
_iconsStartAnim = 0; _iconState.animationStart = 0;
_iconsX.finish(); _iconState.x.finish();
_iconSelX.finish(); _iconState.selectionX.finish();
_iconSelWidth.finish(); _iconState.selectionWidth.finish();
} else { } else {
_iconsX.update(dt, anim::linear); _iconState.x.update(dt, anim::linear);
_iconSelX.update(dt, anim::linear); _iconState.selectionX.update(dt, anim::linear);
_iconSelWidth.update(dt, anim::linear); _iconState.selectionWidth.update(dt, anim::linear);
} }
} }
update(); update();
return (_iconsStartAnim != 0); return (_iconState.animationStart != 0);
} }
} // namespace ChatHelpers } // namespace ChatHelpers

View file

@ -42,7 +42,7 @@ enum class ValidateIconAnimations {
[[nodiscard]] uint64 EmojiSectionSetId(Ui::Emoji::Section section); [[nodiscard]] uint64 EmojiSectionSetId(Ui::Emoji::Section section);
[[nodiscard]] uint64 RecentEmojiSectionSetId(); [[nodiscard]] uint64 RecentEmojiSectionSetId();
[[nodiscard]] uint64 FirstEmojiSectionSetId(); [[nodiscard]] uint64 AllEmojiSectionSetId();
[[nodiscard]] std::optional<Ui::Emoji::Section> SetIdEmojiSection(uint64 id); [[nodiscard]] std::optional<Ui::Emoji::Section> SetIdEmojiSection(uint64 id);
struct StickerIcon { struct StickerIcon {
@ -134,16 +134,29 @@ private:
int width = 0; int width = 0;
bool visible = false; bool visible = false;
}; };
struct ScrollState {
int selected = 0;
int max = 0;
anim::value x;
anim::value selectionX;
anim::value selectionWidth;
crl::time animationStart = 0;
};
void enumerateVisibleIcons(Fn<void(const IconInfo &)> callback) const; void enumerateVisibleIcons(Fn<void(const IconInfo &)> callback) const;
void enumerateIcons(Fn<bool(const IconInfo &)> callback) const; void enumerateIcons(Fn<bool(const IconInfo &)> callback) const;
void enumerateSubicons(Fn<bool(const IconInfo &)> callback) const;
[[nodiscard]] IconInfo iconInfo(int index) const; [[nodiscard]] IconInfo iconInfo(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(crl::time now); bool iconsAnimationCallback(crl::time now);
void setSelectedIcon( void setSelectedIcon(
int newSelected, int newSelected,
ValidateIconAnimations animations); ValidateIconAnimations animations);
void setSelectedSubicon(
int newSelected,
ValidateIconAnimations animations);
void validateIconLottieAnimation(const StickerIcon &icon); void validateIconLottieAnimation(const StickerIcon &icon);
void validateIconWebmAnimation(const StickerIcon &icon); void validateIconWebmAnimation(const StickerIcon &icon);
void validateIconAnimation(const StickerIcon &icon); void validateIconAnimation(const StickerIcon &icon);
@ -186,21 +199,22 @@ private:
Fn<std::shared_ptr<Lottie::FrameRenderer>()> _renderer; Fn<std::shared_ptr<Lottie::FrameRenderer>()> _renderer;
uint64 _activeByScrollId = 0; uint64 _activeByScrollId = 0;
OverState _iconOver = SpecialOver::None; OverState _iconOver = SpecialOver::None;
int _iconSel = 0;
OverState _iconDown = SpecialOver::None; OverState _iconDown = SpecialOver::None;
bool _iconsDragging = false;
Ui::Animations::Basic _iconsAnimation;
QPoint _iconsMousePos, _iconsMouseDown; QPoint _iconsMousePos, _iconsMouseDown;
mutable QImage _premiumIcon; mutable QImage _premiumIcon;
int _iconsLeft = 0; int _iconsLeft = 0;
int _iconsRight = 0; int _iconsRight = 0;
int _iconsTop = 0; int _iconsTop = 0;
int _iconsStartX = 0; int _iconsStartX = 0;
int _iconsMax = 0; bool _iconsDragging = false;
anim::value _iconsX;
anim::value _iconSelX; ScrollState _iconState;
anim::value _iconSelWidth; Ui::Animations::Basic _iconsAnimation;
crl::time _iconsStartAnim = 0;
ScrollState _subiconState;
Ui::Animations::Basic _subiconsAnimation;
Ui::RoundRect _selectionBg; Ui::RoundRect _selectionBg;
Ui::Animations::Simple _emojiIconWidthAnimation; Ui::Animations::Simple _emojiIconWidthAnimation;